Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a utility function to execute hooks serially #814

Merged
merged 2 commits into from Mar 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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'))
})