Browse Source

Merge pull request #13960 from electron/miniak/fix-promisify-2.0

fix: some APIs modified for ASAR support cannot be util.promisify'ed (backport: 2-0-x)
John Kleinschmidt 6 years ago
parent
commit
e8e542b3e8
2 changed files with 123 additions and 5 deletions
  1. 44 5
      lib/common/asar.js
  2. 79 0
      spec/asar-spec.js

+ 44 - 5
lib/common/asar.js

@@ -3,6 +3,7 @@
   const {Buffer} = require('buffer')
   const childProcess = require('child_process')
   const path = require('path')
+  const util = require('util')
 
   const hasProp = {}.hasOwnProperty
 
@@ -217,6 +218,28 @@
       arguments[arg] = newPath
       return old.apply(this, arguments)
     }
+    if (old[util.promisify.custom]) {
+      module[name][util.promisify.custom] = function () {
+        const p = arguments[arg]
+        const [isAsar, asarPath, filePath] = splitPath(p)
+        if (!isAsar) {
+          return old[util.promisify.custom].apply(this, arguments)
+        }
+
+        const archive = getOrCreateArchive(asarPath)
+        if (!archive) {
+          return new Promise(() => invalidArchiveError(asarPath))
+        }
+
+        const newPath = archive.copyFileOut(filePath)
+        if (!newPath) {
+          return new Promise(() => notFoundError(asarPath, filePath))
+        }
+
+        arguments[arg] = newPath
+        return old[util.promisify.custom].apply(this, arguments)
+      }
+    }
   }
 
   // Override fs APIs.
@@ -373,6 +396,18 @@
       })
     }
 
+    fs.exists[util.promisify.custom] = function (p) {
+      const [isAsar, asarPath, filePath] = splitPath(p)
+      if (!isAsar) {
+        return exists[util.promisify.custom](p)
+      }
+      const archive = getOrCreateArchive(asarPath)
+      if (!archive) {
+        return new Promise(() => invalidArchiveError(asarPath))
+      }
+      return Promise.resolve(archive.stat(filePath) !== false)
+    }
+
     const {existsSync} = fs
     fs.existsSync = function (p) {
       const [isAsar, asarPath, filePath] = splitPath(p)
@@ -680,18 +715,22 @@
     // called by `childProcess.{exec,execSync}`, causing
     // Electron to consider the full command as a single path
     // to an archive.
-    ['exec', 'execSync'].forEach(function (functionName) {
-      const old = childProcess[functionName]
-      childProcess[functionName] = function () {
+    const {exec, execSync} = childProcess
+    childProcess.exec = invokeWithNoAsar(exec)
+    childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom])
+    childProcess.execSync = invokeWithNoAsar(execSync)
+
+    function invokeWithNoAsar (func) {
+      return function () {
         const processNoAsarOriginalValue = process.noAsar
         process.noAsar = true
         try {
-          return old.apply(this, arguments)
+          return func.apply(this, arguments)
         } finally {
           process.noAsar = processNoAsarOriginalValue
         }
       }
-    })
+    }
 
     overrideAPI(fs, 'open')
     overrideAPI(childProcess, 'execFile')

+ 79 - 0
spec/asar-spec.js

@@ -2,6 +2,7 @@ const assert = require('assert')
 const ChildProcess = require('child_process')
 const fs = require('fs')
 const path = require('path')
+const util = require('util')
 const {closeWindow} = require('./window-helpers')
 
 const nativeImage = require('electron').nativeImage
@@ -549,6 +550,60 @@ describe('asar package', function () {
       })
     })
 
+    describe('fs.exists', function () {
+      it('handles an existing file', function (done) {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
+        // eslint-disable-next-line
+        fs.exists(p, function (exists) {
+          assert.equal(exists, true)
+          done()
+        })
+      })
+
+      it('handles a non-existent file', function (done) {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
+        // eslint-disable-next-line
+        fs.exists(p, function (exists) {
+          assert.equal(exists, false)
+          done()
+        })
+      })
+
+      it('promisified version handles an existing file', (done) => {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
+        // eslint-disable-next-line
+        util.promisify(fs.exists)(p).then(exists => {
+          assert.equal(exists, true)
+          done()
+        })
+      })
+
+      it('promisified version handles a non-existent file', function (done) {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
+        // eslint-disable-next-line
+        util.promisify(fs.exists)(p).then(exists => {
+          assert.equal(exists, false)
+          done()
+        })
+      })
+    })
+
+    describe('fs.existsSync', function () {
+      it('handles an existing file', function () {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
+        assert.doesNotThrow(function () {
+          assert.equal(fs.existsSync(p), true)
+        })
+      })
+
+      it('handles a non-existent file', function () {
+        var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
+        assert.doesNotThrow(function () {
+          assert.equal(fs.existsSync(p), false)
+        })
+      })
+    })
+
     describe('fs.access', function () {
       it('accesses a normal file', function (done) {
         var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
@@ -644,6 +699,12 @@ describe('asar package', function () {
           done()
         })
       })
+
+      it('can be promisified', () => {
+        return util.promisify(ChildProcess.exec)('echo ' + echo + ' foo bar').then(({ stdout }) => {
+          assert.equal(stdout.toString().replace(/\r/g, ''), echo + ' foo bar\n')
+        })
+      })
     })
 
     describe('child_process.execSync', function () {
@@ -680,6 +741,12 @@ describe('asar package', function () {
         var output = execFileSync(echo, ['test'])
         assert.equal(String(output), 'test\n')
       })
+
+      it('can be promisified', () => {
+        return util.promisify(ChildProcess.execFile)(echo, ['test']).then(({ stdout }) => {
+          assert.equal(stdout, 'test\n')
+        })
+      })
     })
 
     describe('internalModuleReadFile', function () {
@@ -700,6 +767,18 @@ describe('asar package', function () {
       })
     })
 
+    describe('util.promisify', function () {
+      it('can promisify all fs functions', function () {
+        const originalFs = require('original-fs')
+
+        for (const key in originalFs) {
+          if (originalFs[key][util.promisify.custom] && !fs[key][util.promisify.custom]) {
+            assert(false, `fs.${key}[util.promisify.custom] missing`)
+          }
+        }
+      })
+    })
+
     describe('process.noAsar', function () {
       var errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR'