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

cli: ensure --check flag works for piped-in code #11689

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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]');
}
});
}
}
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
50 changes: 50 additions & 0 deletions test/parallel/test-cli-syntax.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All ==s here should probably be ===, but that can be fixed while landing

});

// 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);
});
});