Skip to content

Commit 6ca8fb5

Browse files
guybedfordMylesBorins
authored andcommittedNov 16, 2020
module: refine module type mismatch error cases
Backport-PR-URL: #35757 PR-URL: #35426 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com>
1 parent 9eb1fa1 commit 6ca8fb5

File tree

5 files changed

+58
-28
lines changed

5 files changed

+58
-28
lines changed
 

‎lib/internal/modules/cjs/loader.js

+1-25
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ const {
105105
} = require('internal/constants');
106106

107107
const asyncESM = require('internal/process/esm_loader');
108+
const { enrichCJSError } = require('internal/modules/esm/translators');
108109
const { kEvaluated } = internalBinding('module_wrap');
109110
const {
110111
encodedSepRegEx,
@@ -119,31 +120,6 @@ const relativeResolveCache = ObjectCreate(null);
119120
let requireDepth = 0;
120121
let statCache = null;
121122

122-
function enrichCJSError(err) {
123-
const stack = err.stack.split('\n');
124-
125-
const lineWithErr = stack[1];
126-
127-
/*
128-
The regular expression below targets the most common import statement
129-
usage. However, some cases are not matching, cases like import statement
130-
after a comment block and/or after a variable definition.
131-
*/
132-
if (err.message.startsWith('Unexpected token \'export\'') ||
133-
(RegExpPrototypeTest(/^\s*import(?=[ {'"*])\s*(?![ (])/, lineWithErr))) {
134-
// Emit the warning synchronously because we are in the middle of handling
135-
// a SyntaxError that will throw and likely terminate the process before an
136-
// asynchronous warning would be emitted.
137-
process.emitWarning(
138-
'To load an ES module, set "type": "module" in the package.json or use ' +
139-
'the .mjs extension.',
140-
undefined,
141-
undefined,
142-
undefined,
143-
true);
144-
}
145-
}
146-
147123
function stat(filename) {
148124
filename = path.toNamespacedPath(filename);
149125
if (statCache !== null) {

‎lib/internal/modules/esm/translators.js

+40-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ const {
99
ObjectKeys,
1010
PromisePrototypeCatch,
1111
PromiseReject,
12+
RegExpPrototypeTest,
1213
SafeMap,
1314
SafeSet,
1415
StringPrototypeReplace,
16+
StringPrototypeSplit,
17+
StringPrototypeStartsWith,
1518
} = primordials;
1619

1720
let _TYPES = null;
@@ -57,6 +60,7 @@ const cjsParse = require('internal/deps/cjs-module-lexer/lexer');
5760

5861
const translators = new SafeMap();
5962
exports.translators = translators;
63+
exports.enrichCJSError = enrichCJSError;
6064

6165
let DECODER = null;
6266
function assertBufferSource(body, allowString, hookName) {
@@ -130,6 +134,29 @@ translators.set('module', async function moduleStrategy(url) {
130134
return module;
131135
});
132136

137+
138+
function enrichCJSError(err) {
139+
const stack = StringPrototypeSplit(err.stack, '\n');
140+
/*
141+
The regular expression below targets the most common import statement
142+
usage. However, some cases are not matching, cases like import statement
143+
after a comment block and/or after a variable definition.
144+
*/
145+
if (StringPrototypeStartsWith(err.message, 'Unexpected token \'export\'') ||
146+
(RegExpPrototypeTest(/^\s*import(?=[ {'"*])\s*(?![ (])/, stack[1]))) {
147+
// Emit the warning synchronously because we are in the middle of handling
148+
// a SyntaxError that will throw and likely terminate the process before an
149+
// asynchronous warning would be emitted.
150+
process.emitWarning(
151+
'To load an ES module, set "type": "module" in the package.json or use ' +
152+
'the .mjs extension.',
153+
undefined,
154+
undefined,
155+
undefined,
156+
true);
157+
}
158+
}
159+
133160
// Strategy for loading a node-style CommonJS module
134161
const isWindows = process.platform === 'win32';
135162
const winSepRegEx = /\//g;
@@ -152,7 +179,12 @@ translators.set('commonjs', async function commonjsStrategy(url, isMain) {
152179
exports = asyncESM.ESMLoader.cjsCache.get(module);
153180
asyncESM.ESMLoader.cjsCache.delete(module);
154181
} else {
155-
exports = CJSModule._load(filename, undefined, isMain);
182+
try {
183+
exports = CJSModule._load(filename, undefined, isMain);
184+
} catch (err) {
185+
enrichCJSError(err);
186+
throw err;
187+
}
156188
}
157189

158190
for (const exportName of exportNames) {
@@ -190,7 +222,13 @@ function cjsPreparseModuleExports(filename) {
190222
source = readFileSync(filename, 'utf8');
191223
} catch {}
192224

193-
const { exports, reexports } = cjsParse(source || '');
225+
let exports, reexports;
226+
try {
227+
({ exports, reexports } = cjsParse(source || ''));
228+
} catch {
229+
exports = [];
230+
reexports = [];
231+
}
194232

195233
const exportNames = new SafeSet(exports);
196234

‎test/es-module/test-esm-cjs-exports.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const assert = require('assert');
77

88
const entry = fixtures.path('/es-modules/cjs-exports.mjs');
99

10-
const child = spawn(process.execPath, [entry]);
10+
let child = spawn(process.execPath, [entry]);
1111
child.stderr.setEncoding('utf8');
1212
let stdout = '';
1313
child.stdout.setEncoding('utf8');
@@ -19,3 +19,17 @@ child.on('close', common.mustCall((code, signal) => {
1919
assert.strictEqual(signal, null);
2020
assert.strictEqual(stdout, 'ok\n');
2121
}));
22+
23+
const entryInvalid = fixtures.path('/es-modules/cjs-exports-invalid.mjs');
24+
child = spawn(process.execPath, [entryInvalid]);
25+
let stderr = '';
26+
child.stderr.setEncoding('utf8');
27+
child.stderr.on('data', (data) => {
28+
stderr += data;
29+
});
30+
child.on('close', common.mustCall((code, signal) => {
31+
assert.strictEqual(code, 1);
32+
assert.strictEqual(signal, null);
33+
assert.ok(stderr.includes('Warning: To load an ES module'));
34+
assert.ok(stderr.includes('Unexpected token \'export\''));
35+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import cjs from './invalid-cjs.js';
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export var name = 5;

0 commit comments

Comments
 (0)
Please sign in to comment.