Skip to content

Commit 18d9b9f

Browse files
authored
feat: run scripts on background by default (#459)
closes #458
1 parent 774a466 commit 18d9b9f

6 files changed

Lines changed: 57 additions & 22 deletions

File tree

bin/install.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Object.assign(argv, parseArgs(originalArgv, {
5656
'save-exact',
5757
'china',
5858
'ignore-scripts',
59+
// run scripts on foreground, default is background
60+
'foreground-scripts',
5961
// install ignore optionalDependencies
6062
'optional',
6163
'detail',
@@ -134,8 +136,9 @@ Options:
134136
--workspaces: install new package on all workspaces, e.g: npminstall foo --workspaces
135137
--trace: show memory and cpu usages traces of installation
136138
--ignore-scripts: ignore all preinstall / install and postinstall scripts during the installation
139+
--foreground-scripts: scripts run in the background by default, to see the output, run with: --foreground-scripts
137140
--no-optional: ignore all optionalDependencies during the installation
138-
--forbidden-licenses: forbit install packages which used these licenses
141+
--forbidden-licenses: forbidden install packages which used these licenses
139142
--engine-strict: refuse to install (or even consider installing) any package that claims to not be compatible with the current Node.js version.
140143
--flatten: flatten dependencies by matching ancestors' dependencies
141144
--registry-only: make sure all packages install from registry. Any package is installed from remote(e.g.: git, remote url) cause install fail.
@@ -309,6 +312,7 @@ debug('argv: %j, env: %j', argv, env);
309312
// get config from npm settings instead of following user's specification,
310313
// should migrate to ?? or typeof.
311314
config.ignoreScripts = argv['ignore-scripts'] || getIgnoreScripts();
315+
config.foregroundScripts = argv['foreground-scripts'];
312316
config.ignoreOptionalDependencies = !argv.optional;
313317
config.detail = argv.detail;
314318
config.forceLinkLatest = !!argv['force-link-latest'];

lib/lifecycle_scripts.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,27 @@ exports.DEFAULT_DEP_SCRIPTS = [
2424
'postinstall',
2525
];
2626

27-
exports.runLifecycleScripts = async function runLifecycleScripts(pkg, root, originPkg, displayName, options) {
27+
exports.runLifecycleScripts = async function runLifecycleScripts(pkg, root, originPkg, displayName, globalOptions) {
2828
const scripts = pkg.scripts || {};
2929

3030
// https://docs.npmjs.com/misc/scripts#default-values
3131
// "install": "node-gyp rebuild"
3232
// If there is a binding.gyp file in the root of your package,
3333
// npm will default the install command to compile using node-gyp.
3434
if (!scripts.install && (await utils.exists(path.join(root, 'binding.gyp')))) {
35-
options.console.warn(
35+
globalOptions.console.warn(
3636
'[npminstall:runscript] %s found binding.gyp file, auto run "node-gyp rebuild", root: %j',
3737
displayName, root
3838
);
3939
scripts.install = 'node-gyp rebuild';
4040
}
4141

4242
let scriptList = exports.DEFAULT_DEP_SCRIPTS;
43-
44-
if ((root === options.root && !options.global) || LOCAL_TYPES.includes(npa(`${originPkg.name}@${originPkg.version}`).type)) {
43+
let runInForeground = !!globalOptions.foregroundScripts;
44+
if ((root === globalOptions.root && !globalOptions.global)
45+
|| LOCAL_TYPES.includes(npa(`${originPkg.name}@${originPkg.version}`).type)) {
4546
scriptList = exports.DEFAULT_ROOT_SCRIPTS;
47+
runInForeground = true;
4648
}
4749

4850
for (const script of scriptList) {
@@ -51,7 +53,7 @@ exports.runLifecycleScripts = async function runLifecycleScripts(pkg, root, orig
5153
continue;
5254
}
5355

54-
console.info(
56+
runInForeground && console.info(
5557
'> %s %s %s %s> %s',
5658
displayName,
5759
script,
@@ -61,26 +63,26 @@ exports.runLifecycleScripts = async function runLifecycleScripts(pkg, root, orig
6163
);
6264
const startTime = Date.now();
6365
try {
64-
await utils.runScript(root, cmd, options);
66+
await utils.runScript(root, cmd, globalOptions, runInForeground);
6567
} catch (error) {
66-
options.console.warn('[npminstall:runscript:error] %s run %s %s error: %s', chalk.red(displayName), script, cmd, error);
68+
globalOptions.console.warn('[npminstall:runscript:error] %s run %s %s error: %s', chalk.red(displayName), script, cmd, error);
6769
// If post install execute error, make sure this package won't be skipped during next installation.
6870
try {
6971
await utils.unsetInstallDone(root);
7072
} catch (e) {
71-
options.console.warn(chalk.yellow(`unsetInstallDone: ${root} error: ${e}, ignore it`));
73+
globalOptions.console.warn(chalk.yellow(`unsetInstallDone: ${root} error: ${e}, ignore it`));
7274
}
7375
if (originPkg.optional) {
74-
options.console.warn(chalk.red('%s optional error: %s'), displayName, error.stack);
76+
globalOptions.console.warn(chalk.red('%s optional error: %s'), displayName, error.stack);
7577
continue;
7678
}
7779
error.message = `run ${script} error, please remove node_modules before retry!\n${error.message}`;
7880
throw error;
7981
} finally {
8082
const ts = Date.now() - startTime;
81-
console.info('> %s %s, finished in %s', displayName, script, ms(ts));
82-
options.runscriptCount += 1;
83-
options.runscriptTime += ts;
83+
runInForeground && console.info('> %s %s, finished in %s', displayName, script, ms(ts));
84+
globalOptions.runscriptCount += 1;
85+
globalOptions.runscriptTime += ts;
8486
}
8587
}
8688
};

lib/utils.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,7 @@ exports.parseTarballUrls = tarball => {
204204
/*
205205
* Runs an npm script.
206206
*/
207-
208-
exports.runScript = async (pkgDir, script, options) => {
207+
exports.runScript = async (pkgDir, script, globalOptions, runInForeground = false) => {
209208
// merge config.env <= process.env <= options.env
210209
const env = {};
211210

@@ -221,12 +220,12 @@ exports.runScript = async (pkgDir, script, options) => {
221220
env[key] = process.env[key];
222221
}
223222

224-
for (const key in options.env) {
223+
for (const key in globalOptions.env) {
225224
// ignore `Path` env on Windows
226225
if (/^path$/i.test(key)) {
227226
continue;
228227
}
229-
env[key] = options.env[key];
228+
env[key] = globalOptions.env[key];
230229
}
231230

232231
// set npm_package_* env from package.json
@@ -237,7 +236,7 @@ exports.runScript = async (pkgDir, script, options) => {
237236

238237
env.PATH = [
239238
path.join(__dirname, '../node-gyp-bin'),
240-
path.join(options.root, 'node_modules', '.bin'),
239+
path.join(globalOptions.root, 'node_modules', '.bin'),
241240
path.join(pkgDir, 'node_modules', '.bin'),
242241
process.env.PATH,
243242
].join(path.delimiter);
@@ -247,7 +246,7 @@ exports.runScript = async (pkgDir, script, options) => {
247246
if (NPM_INSTALL_RE.test(script)) {
248247
const npminstall = path.join(__dirname, '../bin/install.js');
249248
const newScript = script.replace(NPM_INSTALL_RE, `${process.execPath} ${npminstall} `);
250-
options.console.info('[npminstall:runScript] replace %j to %j', script, newScript);
249+
globalOptions.console.info('[npminstall:runScript] replace %j to %j', script, newScript);
251250
script = newScript;
252251
}
253252

@@ -262,12 +261,12 @@ exports.runScript = async (pkgDir, script, options) => {
262261
return await command(script, {
263262
cwd: pkgDir,
264263
env,
265-
stdio: 'inherit',
264+
stdio: runInForeground ? 'inherit' : 'ignore',
266265
shell: true,
267266
});
268267
} catch (err) {
269268
if (ignoreError) {
270-
options.console.info('[npminstall:runScript] ignore runscript error: %s', err);
269+
globalOptions.console.info('[npminstall:runScript] ignore runscript error: %s', err);
271270
} else {
272271
throw err;
273272
}

test/fixtures/postinstall/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"prepare": "node index.js prepare"
1010
},
1111
"dependencies": {
12+
"postinstall-hello": "1.0.0",
1213
"utility": "1.6.0",
1314
"a-dep-b": "*"
1415
}

test/postinstall.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,35 @@ describe('test/postinstall.test.js', () => {
1818
await coffee.fork(helper.npminstall, [], { cwd: root })
1919
.debug()
2020
.expect('code', 0)
21+
// should run deps scripts on background by default
22+
.notExpect('stdout', /run on postinstall-hello/)
23+
.notExpect('stdout', /postinstall-hello@1.0.0 postinstall/)
24+
.expect('stdout', /node index.js preinstall/)
25+
.end();
26+
const pkg = await readJSON(path.join(root, 'node_modules', 'utility', 'package.json'));
27+
assert.equal(pkg.name, 'utility');
28+
assert.equal(pkg.version, '1.6.0');
29+
30+
// preinstall pass
31+
assert.equal(fs.readFileSync(path.join(root, '.preinstall.txt'), 'utf8'), 'success: preinstall');
32+
// install pass
33+
assert.equal(fs.readFileSync(path.join(root, 'node_modules', '.install.txt'), 'utf8'), 'success: install');
34+
// postinstall pass
35+
assert.equal(fs.readFileSync(path.join(root, 'node_modules', '.postinstall.txt'), 'utf8'), 'success: postinstall');
36+
// prepublish pass
37+
assert.equal(fs.readFileSync(path.join(root, 'node_modules', '.prepublish.txt'), 'utf8'), 'success: prepublish');
38+
// prepare pass
39+
assert.equal(fs.readFileSync(path.join(root, 'node_modules', '.prepare.txt'), 'utf8'), 'success: prepare');
40+
});
41+
42+
it('should run preinstall, install, postinstall and prepublish --foreground-scripts', async () => {
43+
await coffee.fork(helper.npminstall, [ '--foreground-scripts' ], { cwd: root })
44+
.debug()
45+
.expect('code', 0)
46+
// should run deps scripts on foreground
47+
.expect('stdout', /run on postinstall-hello/)
48+
.expect('stdout', /postinstall-hello@1.0.0 postinstall/)
49+
.expect('stdout', /node index.js preinstall/)
2150
.end();
2251
const pkg = await readJSON(path.join(root, 'node_modules', 'utility', 'package.json'));
2352
assert.equal(pkg.name, 'utility');

test/runscript.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('test/runscript.test.js', () => {
99
const cleanup = helper.cleanup(root);
1010

1111
beforeEach(cleanup);
12-
// afterEach(cleanup);
12+
afterEach(cleanup);
1313

1414
it('should run preinstall and postinstall', async () => {
1515
// ignore windows

0 commit comments

Comments
 (0)