Skip to content

Commit e5116d0

Browse files
authored
Merge pull request #814 from electron-userland/optional-sequential-hooks
Add a utility function to execute hooks serially
2 parents a02ccc0 + 9dcc68c commit e5116d0

File tree

6 files changed

+146
-12
lines changed

6 files changed

+146
-12
lines changed

common.js

-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const os = require('os')
44
const path = require('path')
5-
const pify = require('pify')
65
const sanitize = require('sanitize-filename')
76
const yargs = require('yargs-parser')
87

@@ -144,14 +143,6 @@ module.exports = {
144143
generateFinalPath: generateFinalPath,
145144
sanitizeAppName: sanitizeAppName,
146145

147-
promisifyHooks: function promisifyHooks (hooks, args) {
148-
if (!hooks || !Array.isArray(hooks)) {
149-
return Promise.resolve()
150-
}
151-
152-
return Promise.all(hooks.map(hookFn => pify(hookFn).apply(this, args)))
153-
},
154-
155146
info: info,
156147
warning: warning
157148
}

docs/api.md

+78
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,32 @@ An array of functions to be called after your app directory has been copied to a
4141
- `arch` (*String*): The target architecture you are packaging for
4242
- `callback` (*Function*): Must be called once you have completed your actions
4343

44+
By default, the functions are called in parallel (via
45+
[`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)).
46+
If you need the functions called serially, there is a utility function provided:
47+
48+
```javascript
49+
const packager = require('electron-packager')
50+
const serialHooks = require('electron-packager/hooks').serialHooks
51+
52+
packager({
53+
// ...
54+
afterCopy: [serialHooks([
55+
(buildPath, electronVersion, platform, arch, callback) => {
56+
setTimeout(() => {
57+
console.log('first function')
58+
callback()
59+
}, 1000)
60+
},
61+
(buildPath, electronVersion, platform, arch, callback) => {
62+
console.log('second function')
63+
callback()
64+
}
65+
])],
66+
// ...
67+
})
68+
```
69+
4470
##### `afterExtract`
4571

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

82+
By default, the functions are called in parallel (via
83+
[`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)).
84+
If you need the functions called serially, there is a utility function provided:
85+
86+
```javascript
87+
const packager = require('electron-packager')
88+
const serialHooks = require('electron-packager/hooks').serialHooks
89+
90+
packager({
91+
// ...
92+
afterExtract: [serialHooks([
93+
(buildPath, electronVersion, platform, arch, callback) => {
94+
setTimeout(() => {
95+
console.log('first function')
96+
callback()
97+
}, 1000)
98+
},
99+
(buildPath, electronVersion, platform, arch, callback) => {
100+
console.log('second function')
101+
callback()
102+
}
103+
])],
104+
// ...
105+
})
106+
```
107+
56108
##### `afterPrune`
57109

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

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

123+
By default, the functions are called in parallel (via
124+
[`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)).
125+
If you need the functions called serially, there is a utility function provided:
126+
127+
```javascript
128+
const packager = require('electron-packager')
129+
const serialHooks = require('electron-packager/hooks').serialHooks
130+
131+
packager({
132+
// ...
133+
afterPrune: [serialHooks([
134+
(buildPath, electronVersion, platform, arch, callback) => {
135+
setTimeout(() => {
136+
console.log('first function')
137+
callback()
138+
}, 1000)
139+
},
140+
(buildPath, electronVersion, platform, arch, callback) => {
141+
console.log('second function')
142+
callback()
143+
}
144+
])],
145+
// ...
146+
})
147+
```
148+
71149
##### `all`
72150

73151
*Boolean*

hooks.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict'
2+
3+
const pify = require('pify')
4+
5+
module.exports = {
6+
promisifyHooks: function promisifyHooks (hooks, args) {
7+
if (!hooks || !Array.isArray(hooks)) {
8+
return Promise.resolve()
9+
}
10+
11+
return Promise.all(hooks.map(hookFn => pify(hookFn).apply(this, args)))
12+
},
13+
serialHooks: function serialHooks (hooks) {
14+
return function () {
15+
const args = Array.prototype.splice.call(arguments, 0, arguments.length - 1)
16+
const done = arguments[arguments.length - 1]
17+
let result = Promise.resolve()
18+
for (const hook of hooks) {
19+
result = result.then(() => hook.apply(this, args))
20+
}
21+
22+
return result.then(() => done()) // eslint-disable-line promise/no-callback-in-promise
23+
}
24+
}
25+
}

index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const download = require('./download')
66
const extract = require('extract-zip')
77
const fs = require('fs-extra')
88
const getMetadataFromPackageJSON = require('./infer')
9+
const hooks = require('./hooks')
910
const ignore = require('./ignore')
1011
const metadata = require('./package.json')
1112
const nodeify = require('nodeify')
@@ -71,7 +72,7 @@ class Packager {
7172
extractElectronZip (comboOpts, zipPath, buildDir) {
7273
debug(`Extracting ${zipPath} to ${buildDir}`)
7374
return pify(extract)(zipPath, { dir: buildDir })
74-
.then(() => common.promisifyHooks(this.opts.afterExtract, [buildDir, comboOpts.electronVersion, comboOpts.platform, comboOpts.arch]))
75+
.then(() => hooks.promisifyHooks(this.opts.afterExtract, [buildDir, comboOpts.electronVersion, comboOpts.platform, comboOpts.arch]))
7576
}
7677

7778
createApp (comboOpts, zipPath) {

platform.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const fs = require('fs-extra')
66
const path = require('path')
77
const pify = require('pify')
88

9+
const hooks = require('./hooks')
910
const ignore = require('./ignore')
1011
const pruneModules = require('./prune').pruneModules
1112

@@ -101,7 +102,7 @@ class App {
101102
return fs.copy(this.opts.dir, this.originalResourcesAppDir, {
102103
filter: ignore.userIgnoreFilter(this.opts),
103104
dereference: this.opts.derefSymlinks
104-
}).then(() => common.promisifyHooks(this.opts.afterCopy, [
105+
}).then(() => hooks.promisifyHooks(this.opts.afterCopy, [
105106
this.originalResourcesAppDir,
106107
this.opts.electronVersion,
107108
this.opts.platform,
@@ -132,7 +133,7 @@ class App {
132133
prune () {
133134
if (this.opts.prune || this.opts.prune === undefined) {
134135
return pruneModules(this.opts, this.originalResourcesAppDir)
135-
.then(() => common.promisifyHooks(this.opts.afterPrune, [this.originalResourcesAppDir, this.opts.electronVersion, this.opts.platform, this.opts.arch]))
136+
.then(() => hooks.promisifyHooks(this.opts.afterPrune, [this.originalResourcesAppDir, this.opts.electronVersion, this.opts.platform, this.opts.arch]))
136137
}
137138

138139
return Promise.resolve()

test/hooks.js

+38
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
'use strict'
22

33
const config = require('./config.json')
4+
const hooks = require('../hooks')
45
const packager = require('..')
6+
const test = require('ava')
57
const util = require('./_util')
68

79
function createHookTest (hookName) {
@@ -32,3 +34,39 @@ function createHookTest (hookName) {
3234
createHookTest('afterCopy')
3335
createHookTest('afterPrune')
3436
createHookTest('afterExtract')
37+
38+
test('promisifyHooks executes functions in parallel', t => {
39+
let output = '0'
40+
const timeoutFunc = (number, msTimeout) => {
41+
return done => {
42+
setTimeout(() => {
43+
output += ` ${number}`
44+
done()
45+
}, msTimeout)
46+
}
47+
}
48+
const testHooks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(number =>
49+
timeoutFunc(number, number % 2 === 0 ? 1000 : 0)
50+
)
51+
52+
return hooks.promisifyHooks(testHooks)
53+
.then(() => t.not(output, '0 1 2 3 4 5 6 7 8 9 10', 'should not be in sequential order'))
54+
})
55+
56+
test('serialHooks executes functions serially', t => {
57+
let output = '0'
58+
const timeoutFunc = (number, msTimeout) => {
59+
return () => new Promise(resolve => { // eslint-disable-line promise/avoid-new
60+
setTimeout(() => {
61+
output += ` ${number}`
62+
resolve()
63+
}, msTimeout)
64+
})
65+
}
66+
const testHooks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(number =>
67+
timeoutFunc(number, number % 2 === 0 ? 1000 : 0)
68+
)
69+
70+
return hooks.serialHooks(testHooks)(() => output)
71+
.then(result => t.is(result, '0 1 2 3 4 5 6 7 8 9 10', 'should be in sequential order'))
72+
})

0 commit comments

Comments
 (0)