Skip to content

Commit

Permalink
Allow to pass flags to node.js (#1195)
Browse files Browse the repository at this point in the history
* feat(cli): allow to pass flags to node.js
closes #1084
closes #289

* chore(cli): fix snapshot

* chore(cli): drop nodejs 8 from ci

* chore(cli): fix test

* feat(cli): add parseArgs helper

* feat(cli): parseArgs now supports --node-args="..." syntax
  • Loading branch information
smelukov committed Jan 30, 2020
1 parent fb6e3fe commit da08bb1
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 30 deletions.
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ language: node_js
node_js:
- "12"
- "10"
- "8"
cache:
directories:
- ~/.npm
Expand All @@ -20,9 +19,6 @@ matrix:
- os: linux
node_js: "10"
env: JOB_PART=integration
- os: linux
node_js: "8"
env: JOB_PART=integration

before_install:
- "[[ $(node -v) =~ ^v9.*$ ]] || npm install -g npm@latest" # skipped when using node 9
Expand Down
4 changes: 1 addition & 3 deletions azure-pipelines-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ jobs:
node-12:
node_version: ^12.0.0
node-10:
node_version: ^10.10.0
node-8:
node_version: ^8.12.0
node_version: ^10.13.0
steps:
- task: NodeTool@0
inputs:
Expand Down
15 changes: 15 additions & 0 deletions lib/bootstrap.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
const execa = require('execa');
const WebpackCLI = require('./webpack-cli');
const { core, commands } = require('./utils/cli-flags');
const cmdArgs = require('command-line-args');
const logger = require('./utils/logger');
const parseArgs = require('./utils/parse-args');

require('./utils/process-log');

const cliPath = require.resolve('../cli.js');
const isFlagPresent = (args, flag) => args.find(arg => [flag, `--${flag}`].includes(arg));
const isArgCommandName = (arg, cmd) => arg === cmd.name || arg === cmd.alias;
const removeCmdFromArgs = (args, cmd) => args.filter(arg => !isArgCommandName(arg, cmd));
Expand Down Expand Up @@ -45,13 +48,25 @@ async function runCLI(cli, commandIsUsed) {
let args;
const helpFlagExists = isFlagPresent(process.argv, 'help');
const versionFlagExists = isFlagPresent(process.argv, 'version');
const nodeArgsExists = process.argv.find(arg => arg.includes('--node-args'));

if (helpFlagExists) {
cli.runHelp(process.argv);
return;
} else if (versionFlagExists) {
cli.runVersion();
return;
} else if (nodeArgsExists) {
const [, , ...rawArgs] = process.argv;
const { cliArgs, nodeArgs } = parseArgs(rawArgs);

try {
const childProcess = execa('node', [...nodeArgs, cliPath, ...cliArgs], { stdio: 'inherit' });
await childProcess;
process.exit();
} catch (e) {
process.exit(e.exitCode);
}
}

if (commandIsUsed) {
Expand Down
8 changes: 8 additions & 0 deletions lib/utils/cli-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ module.exports = {
group: BASIC_GROUP,
description: 'Get current version',
},
{
name: 'node-args',
usage: '--node-args "--max-old-space-size=1024"',
type: String,
multiple: true,
group: BASIC_GROUP,
description: 'NodeJS flags',
},
/* {
name: "analyze",
type: Boolean,
Expand Down
29 changes: 29 additions & 0 deletions lib/utils/parse-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */

/**
* Parse cli args and split these to args for node js and the rest
*
* @param {string[]} rawArgs raw cli args
* @returns {{cliArgs: string[], nodeArgs: string[]}} cli and nodejs args
*/
module.exports = rawArgs => {
const cliArgs = [];
const nodeArgs = [];
let isNodeArg = false;

for (const value of rawArgs) {
if (value === '--node-args') {
isNodeArg = true;
} else if (value.startsWith('--node-args=')) {
const [, argValue] = value.match(/^--node-args="?(.+?)"?$/);
nodeArgs.push(argValue);
} else if (isNodeArg) {
isNodeArg = false;
nodeArgs.push(...value.split(' '));
} else {
cliArgs.push(value);
}
}

return { cliArgs, nodeArgs };
};
28 changes: 7 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"main": "cli.js",
"engines": {
"node": ">=8.0.0"
"node": ">=10.13.0"
},
"keywords": [
"webpack",
Expand Down Expand Up @@ -119,6 +119,7 @@
"cli-table3": "^0.5.1",
"command-line-args": "^5.1.1",
"command-line-usage": "^6.1.0",
"execa": "^3.2.0",
"import-local": "^3.0.2",
"interpret": "^2.0.0",
"terser-webpack-plugin": "^2.3.2",
Expand All @@ -144,7 +145,6 @@
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-prettier": "^3.1.1",
"execa": "^3.2.0",
"husky": "^3.0.9",
"jest": "^24.9.0",
"jest-cli": "^24.9.0",
Expand Down
1 change: 1 addition & 0 deletions test/help/__snapshots__/help-single-arg.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Options
-d, --dev Run development build
-p, --prod Run production build
--version Get current version
--node-args string[] NodeJS flags
Made with ♥️ by the webpack team "
`;
1 change: 1 addition & 0 deletions test/node/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'a.js';
1 change: 1 addition & 0 deletions test/node/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('---from bootstrap.js---');
1 change: 1 addition & 0 deletions test/node/bootstrap2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('---from bootstrap2.js---');
47 changes: 47 additions & 0 deletions test/node/node.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';
const { stat } = require('fs');
const { resolve, sep } = require('path');
const { run, extractSummary } = require('../utils/test-utils');
const parseArgs = require('../../lib/utils/parse-args');

describe('node flags', () => {
it('parseArgs helper must work correctly', () => {
[
{
rawArgs: ['--foo', '--bar', '--baz=quux'],
expectedCliArgs: ['--foo', '--bar', '--baz=quux'],
expectedNodeArgs: [],
},
{
rawArgs: ['--foo', '--bar', '--baz=quux', '--node-args', '--name1=value1', '--node-args', '--name2 value2'],
expectedCliArgs: ['--foo', '--bar', '--baz=quux'],
expectedNodeArgs: ['--name1=value1', '--name2', 'value2'],
},
{
rawArgs: ['--node-args', '--name1=value1', '--node-args', '--name2="value2"', '--node-args', '--name3 value3', '--node-args', '-k v'],
expectedCliArgs: [],
expectedNodeArgs: ['--name1=value1', '--name2="value2"', '--name3', 'value3', '-k', 'v'],
},
].map(({ rawArgs, expectedNodeArgs, expectedCliArgs }) => {
const { nodeArgs, cliArgs } = parseArgs(rawArgs);
expect(nodeArgs).toEqual(expectedNodeArgs);
expect(cliArgs).toEqual(expectedCliArgs);
});
});

it('is able to pass the options flags to node js', done => {
const { stdout } = run(__dirname, ['--node-args', `--require=${resolve(__dirname, 'bootstrap.js')}`, '--node-args', `-r ${resolve(__dirname, 'bootstrap2.js')}`, '--output', './bin/[name].bundle.js'], false);
expect(stdout).toContain('---from bootstrap.js---');
expect(stdout).toContain('---from bootstrap2.js---');
const summary = extractSummary(stdout);
const outputDir = 'node/bin';
const outDirectoryFromCompiler = summary['Output Directory'].split(sep);
const outDirToMatch = outDirectoryFromCompiler.slice(outDirectoryFromCompiler.length - 2, outDirectoryFromCompiler.length).join('/');
expect(outDirToMatch).toContain(outputDir);
stat(resolve(__dirname, './bin/main.bundle.js'), (err, stats) => {
expect(err).toBe(null);
expect(stats.isFile()).toBe(true);
done();
});
});
});
9 changes: 9 additions & 0 deletions test/node/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { resolve } = require('path');

module.exports = {
entry: './a.js',
output: {
path: resolve(__dirname, 'binary'),
filename: 'a.bundle.js',
},
};

0 comments on commit da08bb1

Please sign in to comment.