Skip to content

Commit

Permalink
Merge pull request #814 from electron-userland/optional-sequential-hooks
Browse files Browse the repository at this point in the history
Add a utility function to execute hooks serially
  • Loading branch information
malept committed Mar 24, 2018
2 parents a02ccc0 + 9dcc68c commit e5116d0
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 12 deletions.
9 changes: 0 additions & 9 deletions common.js
Expand Up @@ -2,7 +2,6 @@

const os = require('os')
const path = require('path')
const pify = require('pify')
const sanitize = require('sanitize-filename')
const yargs = require('yargs-parser')

Expand Down Expand Up @@ -144,14 +143,6 @@ module.exports = {
generateFinalPath: generateFinalPath,
sanitizeAppName: sanitizeAppName,

promisifyHooks: function promisifyHooks (hooks, args) {
if (!hooks || !Array.isArray(hooks)) {
return Promise.resolve()
}

return Promise.all(hooks.map(hookFn => pify(hookFn).apply(this, args)))
},

info: info,
warning: warning
}
78 changes: 78 additions & 0 deletions docs/api.md
Expand Up @@ -41,6 +41,32 @@ An array of functions to be called after your app directory has been copied to a
- `arch` (*String*): The target architecture you are packaging for
- `callback` (*Function*): Must be called once you have completed your actions

By default, the functions are called in parallel (via
[`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)).
If you need the functions called serially, there is a utility function provided:

```javascript
const packager = require('electron-packager')
const serialHooks = require('electron-packager/hooks').serialHooks

packager({
// ...
afterCopy: [serialHooks([
(buildPath, electronVersion, platform, arch, callback) => {
setTimeout(() => {
console.log('first function')
callback()
}, 1000)
},
(buildPath, electronVersion, platform, arch, callback) => {
console.log('second function')
callback()
}
])],
// ...
})
```

##### `afterExtract`

*Array of Functions*
Expand All @@ -53,6 +79,32 @@ An array of functions to be called after Electron has been extracted to a tempor
- `arch` (*String*): The target architecture you are packaging for
- `callback` (*Function*): Must be called once you have completed your actions

By default, the functions are called in parallel (via
[`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)).
If you need the functions called serially, there is a utility function provided:

```javascript
const packager = require('electron-packager')
const serialHooks = require('electron-packager/hooks').serialHooks

packager({
// ...
afterExtract: [serialHooks([
(buildPath, electronVersion, platform, arch, callback) => {
setTimeout(() => {
console.log('first function')
callback()
}, 1000)
},
(buildPath, electronVersion, platform, arch, callback) => {
console.log('second function')
callback()
}
])],
// ...
})
```

##### `afterPrune`

*Array of Functions*
Expand All @@ -68,6 +120,32 @@ in the temporary directory. Each function is called with five parameters:

**NOTE:** None of these functions will be called if the `prune` option is `false`.

By default, the functions are called in parallel (via
[`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)).
If you need the functions called serially, there is a utility function provided:

```javascript
const packager = require('electron-packager')
const serialHooks = require('electron-packager/hooks').serialHooks

packager({
// ...
afterPrune: [serialHooks([
(buildPath, electronVersion, platform, arch, callback) => {
setTimeout(() => {
console.log('first function')
callback()
}, 1000)
},
(buildPath, electronVersion, platform, arch, callback) => {
console.log('second function')
callback()
}
])],
// ...
})
```

##### `all`

*Boolean*
Expand Down
25 changes: 25 additions & 0 deletions hooks.js
@@ -0,0 +1,25 @@
'use strict'

const pify = require('pify')

module.exports = {
promisifyHooks: function promisifyHooks (hooks, args) {
if (!hooks || !Array.isArray(hooks)) {
return Promise.resolve()
}

return Promise.all(hooks.map(hookFn => pify(hookFn).apply(this, args)))
},
serialHooks: function serialHooks (hooks) {
return function () {
const args = Array.prototype.splice.call(arguments, 0, arguments.length - 1)
const done = arguments[arguments.length - 1]
let result = Promise.resolve()
for (const hook of hooks) {
result = result.then(() => hook.apply(this, args))
}

return result.then(() => done()) // eslint-disable-line promise/no-callback-in-promise
}
}
}
3 changes: 2 additions & 1 deletion index.js
Expand Up @@ -6,6 +6,7 @@ const download = require('./download')
const extract = require('extract-zip')
const fs = require('fs-extra')
const getMetadataFromPackageJSON = require('./infer')
const hooks = require('./hooks')
const ignore = require('./ignore')
const metadata = require('./package.json')
const nodeify = require('nodeify')
Expand Down Expand Up @@ -71,7 +72,7 @@ class Packager {
extractElectronZip (comboOpts, zipPath, buildDir) {
debug(`Extracting ${zipPath} to ${buildDir}`)
return pify(extract)(zipPath, { dir: buildDir })
.then(() => common.promisifyHooks(this.opts.afterExtract, [buildDir, comboOpts.electronVersion, comboOpts.platform, comboOpts.arch]))
.then(() => hooks.promisifyHooks(this.opts.afterExtract, [buildDir, comboOpts.electronVersion, comboOpts.platform, comboOpts.arch]))
}

createApp (comboOpts, zipPath) {
Expand Down
5 changes: 3 additions & 2 deletions platform.js
Expand Up @@ -6,6 +6,7 @@ const fs = require('fs-extra')
const path = require('path')
const pify = require('pify')

const hooks = require('./hooks')
const ignore = require('./ignore')
const pruneModules = require('./prune').pruneModules

Expand Down Expand Up @@ -101,7 +102,7 @@ class App {
return fs.copy(this.opts.dir, this.originalResourcesAppDir, {
filter: ignore.userIgnoreFilter(this.opts),
dereference: this.opts.derefSymlinks
}).then(() => common.promisifyHooks(this.opts.afterCopy, [
}).then(() => hooks.promisifyHooks(this.opts.afterCopy, [
this.originalResourcesAppDir,
this.opts.electronVersion,
this.opts.platform,
Expand Down Expand Up @@ -132,7 +133,7 @@ class App {
prune () {
if (this.opts.prune || this.opts.prune === undefined) {
return pruneModules(this.opts, this.originalResourcesAppDir)
.then(() => common.promisifyHooks(this.opts.afterPrune, [this.originalResourcesAppDir, this.opts.electronVersion, this.opts.platform, this.opts.arch]))
.then(() => hooks.promisifyHooks(this.opts.afterPrune, [this.originalResourcesAppDir, this.opts.electronVersion, this.opts.platform, this.opts.arch]))
}

return Promise.resolve()
Expand Down
38 changes: 38 additions & 0 deletions test/hooks.js
@@ -1,7 +1,9 @@
'use strict'

const config = require('./config.json')
const hooks = require('../hooks')
const packager = require('..')
const test = require('ava')
const util = require('./_util')

function createHookTest (hookName) {
Expand Down Expand Up @@ -32,3 +34,39 @@ function createHookTest (hookName) {
createHookTest('afterCopy')
createHookTest('afterPrune')
createHookTest('afterExtract')

test('promisifyHooks executes functions in parallel', t => {
let output = '0'
const timeoutFunc = (number, msTimeout) => {
return done => {
setTimeout(() => {
output += ` ${number}`
done()
}, msTimeout)
}
}
const testHooks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(number =>
timeoutFunc(number, number % 2 === 0 ? 1000 : 0)
)

return hooks.promisifyHooks(testHooks)
.then(() => t.not(output, '0 1 2 3 4 5 6 7 8 9 10', 'should not be in sequential order'))
})

test('serialHooks executes functions serially', t => {
let output = '0'
const timeoutFunc = (number, msTimeout) => {
return () => new Promise(resolve => { // eslint-disable-line promise/avoid-new
setTimeout(() => {
output += ` ${number}`
resolve()
}, msTimeout)
})
}
const testHooks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(number =>
timeoutFunc(number, number % 2 === 0 ? 1000 : 0)
)

return hooks.serialHooks(testHooks)(() => output)
.then(result => t.is(result, '0 1 2 3 4 5 6 7 8 9 10', 'should be in sequential order'))
})

0 comments on commit e5116d0

Please sign in to comment.