module: print better message on esm syntax error

Include the offending line in the output and underline the bad token.

Before this commit, it printed "SyntaxError: Unexpected reserved word"
without indicating where the syntax error is.

Now it prints the line and underlines the offending token, like it does
for syntax errors in CJS scripts.

Minor changes are made to the test runner in order to support `*.mjs`
files in test/message.

Fixes: https://github.com/nodejs/node/issues/17277
PR-URL: https://github.com/nodejs/node/pull/17281
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
Ben Noordhuis 2017-11-24 01:50:28 +01:00
parent 6c47033024
commit 597b3d1941
10 changed files with 40 additions and 7 deletions

View File

@ -3,6 +3,7 @@ lib/internal/v8_prof_polyfill.js
lib/punycode.js lib/punycode.js
test/addons/??_* test/addons/??_*
test/fixtures test/fixtures
test/message/esm_display_syntax_error.mjs
tools/eslint tools/eslint
tools/icu tools/icu
tools/remark-* tools/remark-*

View File

@ -23,6 +23,7 @@
const NativeModule = require('native_module'); const NativeModule = require('native_module');
const util = require('util'); const util = require('util');
const { decorateErrorStack } = require('internal/util');
const internalModule = require('internal/module'); const internalModule = require('internal/module');
const { getURLFromFilePath } = require('internal/url'); const { getURLFromFilePath } = require('internal/url');
const vm = require('vm'); const vm = require('vm');
@ -474,6 +475,7 @@ Module._load = function(request, parent, isMain) {
await ESMLoader.import(getURLFromFilePath(request).pathname); await ESMLoader.import(getURLFromFilePath(request).pathname);
})() })()
.catch((e) => { .catch((e) => {
decorateErrorStack(e);
console.error(e); console.error(e);
process.exit(1); process.exit(1);
}); });

View File

@ -103,9 +103,17 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
False(isolate), // is opaque (?) False(isolate), // is opaque (?)
False(isolate), // is WASM False(isolate), // is WASM
True(isolate)); // is ES6 module True(isolate)); // is ES6 module
TryCatch try_catch(isolate);
ScriptCompiler::Source source(source_text, origin); ScriptCompiler::Source source(source_text, origin);
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
CHECK(try_catch.HasCaught());
CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty());
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
ErrorHandlingMode::MODULE_ERROR);
try_catch.ReThrow();
return; return;
}
} }
Local<Object> that = args.This(); Local<Object> that = args.This();

View File

@ -269,7 +269,7 @@ constexpr size_t arraysize(const T(&)[N]) { return N; }
bool IsExceptionDecorated(Environment* env, v8::Local<v8::Value> er); bool IsExceptionDecorated(Environment* env, v8::Local<v8::Value> er);
enum ErrorHandlingMode { FATAL_ERROR, CONTEXTIFY_ERROR }; enum ErrorHandlingMode { CONTEXTIFY_ERROR, FATAL_ERROR, MODULE_ERROR };
void AppendExceptionLine(Environment* env, void AppendExceptionLine(Environment* env,
v8::Local<v8::Value> er, v8::Local<v8::Value> er,
v8::Local<v8::Message> message, v8::Local<v8::Message> message,

View File

@ -0,0 +1,2 @@
'use strict';
await async () => 0;

View File

@ -0,0 +1,3 @@
// Flags: --experimental-modules
'use strict';
await async () => 0;

View File

@ -0,0 +1,7 @@
(node:*) ExperimentalWarning: The ESM module loader is experimental.
file:///*/test/message/esm_display_syntax_error.mjs:3
await async () => 0;
^^^^^
SyntaxError: Unexpected reserved word
at loaders.set (internal/loader/ModuleRequest.js:*:*)
at <anonymous>

View File

@ -0,0 +1,3 @@
// Flags: --experimental-modules
import '../common';
import '../fixtures/es-module-loaders/syntax-error';

View File

@ -0,0 +1,7 @@
(node:*) ExperimentalWarning: The ESM module loader is experimental.
file:///*/test/fixtures/es-module-loaders/syntax-error.mjs:2
await async () => 0;
^^^^^
SyntaxError: Unexpected reserved word
at loaders.set (internal/loader/ModuleRequest.js:*:*)
at <anonymous>

View File

@ -114,18 +114,18 @@ class MessageTestConfiguration(test.TestConfiguration):
def Ls(self, path): def Ls(self, path):
if isdir(path): if isdir(path):
return [f[:-3] for f in os.listdir(path) if f.endswith('.js')] return [f for f in os.listdir(path)
if f.endswith('.js') or f.endswith('.mjs')]
else: else:
return [] return []
def ListTests(self, current_path, path, arch, mode): def ListTests(self, current_path, path, arch, mode):
all_tests = [current_path + [t] for t in self.Ls(self.root)] all_tests = [current_path + [t] for t in self.Ls(self.root)]
result = [] result = []
for test in all_tests: for test in all_tests:
if self.Contains(path, test): if self.Contains(path, test):
file_prefix = join(self.root, reduce(join, test[1:], "")) file_path = join(self.root, reduce(join, test[1:], ''))
file_path = file_prefix + ".js" output_path = file_path[:file_path.rfind('.')] + '.out'
output_path = file_prefix + ".out"
if not exists(output_path): if not exists(output_path):
raise Exception("Could not find %s" % output_path) raise Exception("Could not find %s" % output_path)
result.append(MessageTestCase(test, file_path, output_path, result.append(MessageTestCase(test, file_path, output_path,