diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index aea83fc331e771..b4ed16573e1e07 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -132,18 +132,11 @@ // check if user passed `-c` or `--check` arguments to Node. if (process._syntax_check_only != null) { - const vm = NativeModule.require('vm'); const fs = NativeModule.require('fs'); - const internalModule = NativeModule.require('internal/module'); // read the source const filename = Module._resolveFilename(process.argv[1]); var source = fs.readFileSync(filename, 'utf-8'); - // remove shebang and BOM - source = internalModule.stripBOM(source.replace(/^#!.*/, '')); - // wrap it - source = Module.wrap(source); - // compile the script, this will throw if it fails - new vm.Script(source, {filename: filename, displayErrors: true}); + checkScriptSyntax(source, filename); process.exit(0); } @@ -184,8 +177,12 @@ }); process.stdin.on('end', function() { - process._eval = code; - evalScript('[stdin]'); + if (process._syntax_check_only != null) { + checkScriptSyntax(code, '[stdin]'); + } else { + process._eval = code; + evalScript('[stdin]'); + } }); } } @@ -445,6 +442,19 @@ } } + function checkScriptSyntax(source, filename) { + const Module = NativeModule.require('module'); + const vm = NativeModule.require('vm'); + const internalModule = NativeModule.require('internal/module'); + + // remove shebang and BOM + source = internalModule.stripBOM(source.replace(/^#!.*/, '')); + // wrap it + source = Module.wrap(source); + // compile the script, this will throw if it fails + new vm.Script(source, {displayErrors: true, filename}); + } + // Below you find a minimal module system, which is used to load the node // core modules found in lib/*.js. All core modules are compiled into the // node binary, so they can be loaded faster. diff --git a/src/node.cc b/src/node.cc index 77e6a5826ee957..2b5b6152c3b949 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3800,6 +3800,12 @@ static void ParseArgs(int* argc, } #endif + if (eval_string != nullptr && syntax_check_only) { + fprintf(stderr, + "%s: either --check or --eval can be used, not both\n", argv[0]); + exit(9); + } + // Copy remaining arguments. const unsigned int args_left = nargs - index; memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); diff --git a/test/parallel/test-cli-syntax.js b/test/parallel/test-cli-syntax.js index d7781eddffbb42..2275ce40a50d85 100644 --- a/test/parallel/test-cli-syntax.js +++ b/test/parallel/test-cli-syntax.js @@ -52,6 +52,9 @@ const syntaxArgs = [ // no stdout should be produced assert.strictEqual(c.stdout, '', 'stdout produced'); + // stderr should include the filename + assert(c.stderr.startsWith(file), "stderr doesn't start with the filename"); + // stderr should have a syntax error message const match = c.stderr.match(/^SyntaxError: Unexpected identifier$/m); assert(match, 'stderr incorrect'); @@ -82,3 +85,50 @@ const syntaxArgs = [ assert.strictEqual(c.status, 1, 'code == ' + c.status); }); }); + +// should not execute code piped from stdin with --check +// loop each possible option, `-c` or `--check` +syntaxArgs.forEach(function(args) { + const stdin = 'throw new Error("should not get run");'; + const c = spawnSync(node, args, {encoding: 'utf8', input: stdin}); + + // no stdout or stderr should be produced + assert.strictEqual(c.stdout, '', 'stdout produced'); + assert.strictEqual(c.stderr, '', 'stderr produced'); + + assert.strictEqual(c.status, 0, 'code == ' + c.status); +}); + +// should throw if code piped from stdin with --check has bad syntax +// loop each possible option, `-c` or `--check` +syntaxArgs.forEach(function(args) { + const stdin = 'var foo bar;'; + const c = spawnSync(node, args, {encoding: 'utf8', input: stdin}); + + // stderr should include '[stdin]' as the filename + assert(c.stderr.startsWith('[stdin]'), "stderr doesn't start with [stdin]"); + + // no stdout or stderr should be produced + assert.strictEqual(c.stdout, '', 'stdout produced'); + + // stderr should have a syntax error message + const match = c.stderr.match(/^SyntaxError: Unexpected identifier$/m); + assert(match, 'stderr incorrect'); + + assert.strictEqual(c.status, 1, 'code == ' + c.status); +}); + +// should throw if -c and -e flags are both passed +['-c', '--check'].forEach(function(checkFlag) { + ['-e', '--eval'].forEach(function(evalFlag) { + const args = [checkFlag, evalFlag, 'foo']; + const c = spawnSync(node, args, {encoding: 'utf8'}); + + assert.strictEqual( + c.stderr, + `${node}: either --check or --eval can be used, not both\n` + ); + + assert.strictEqual(c.status, 9, 'code === ' + c.status); + }); +});