Skip to content

Commit 4396beb

Browse files
guybedfordjkrems
authored andcommitted
module: error for CJS .js load within type: module
PR-URL: #29492 Reviewed-By: Jan Krems <[email protected]>
1 parent ac41959 commit 4396beb

File tree

5 files changed

+87
-34
lines changed

5 files changed

+87
-34
lines changed

Diff for: lib/internal/modules/cjs/loader.js

+42-33
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,17 @@ Module._debug = deprecate(debug, 'Module._debug is deprecated.', 'DEP0077');
212212
// -> a.<ext>
213213
// -> a/index.<ext>
214214

215-
// Check if the directory is a package.json dir.
216-
const packageMainCache = Object.create(null);
217-
// Explicit exports from package.json files
218-
const packageExportsCache = new SafeMap();
215+
const packageJsonCache = new SafeMap();
219216

220-
function readPackageRaw(requestPath) {
217+
function readPackage(requestPath) {
221218
const jsonPath = path.resolve(requestPath, 'package.json');
222-
const json = internalModuleReadJSON(path.toNamespacedPath(jsonPath));
223219

220+
const existing = packageJsonCache.get(jsonPath);
221+
if (existing !== undefined) return existing;
222+
223+
const json = internalModuleReadJSON(path.toNamespacedPath(jsonPath));
224224
if (json === undefined) {
225+
packageJsonCache.set(jsonPath, false);
225226
return false;
226227
}
227228

@@ -232,45 +233,47 @@ function readPackageRaw(requestPath) {
232233

233234
try {
234235
const parsed = JSON.parse(json);
235-
packageMainCache[requestPath] = parsed.main;
236-
if (experimentalExports) {
237-
packageExportsCache.set(requestPath, parsed.exports);
238-
}
239-
return parsed;
236+
const filtered = {
237+
main: parsed.main,
238+
exports: parsed.exports,
239+
type: parsed.type
240+
};
241+
packageJsonCache.set(jsonPath, filtered);
242+
return filtered;
240243
} catch (e) {
241244
e.path = jsonPath;
242245
e.message = 'Error parsing ' + jsonPath + ': ' + e.message;
243246
throw e;
244247
}
245248
}
246249

247-
function readPackage(requestPath) {
248-
const entry = packageMainCache[requestPath];
249-
if (entry)
250-
return entry;
251-
252-
const pkg = readPackageRaw(requestPath);
253-
if (pkg === false) return false;
254-
255-
return pkg.main;
256-
}
257-
258-
function readExports(requestPath) {
259-
if (packageExportsCache.has(requestPath)) {
260-
return packageExportsCache.get(requestPath);
250+
function readPackageScope(checkPath) {
251+
const rootSeparatorIndex = checkPath.indexOf(path.sep);
252+
let separatorIndex;
253+
while (
254+
(separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex
255+
) {
256+
checkPath = checkPath.slice(0, separatorIndex);
257+
if (checkPath.endsWith(path.sep + 'node_modules'))
258+
return false;
259+
const pjson = readPackage(checkPath);
260+
if (pjson) return pjson;
261261
}
262+
return false;
263+
}
262264

263-
const pkg = readPackageRaw(requestPath);
264-
if (!pkg) {
265-
packageExportsCache.set(requestPath, null);
266-
return null;
267-
}
265+
function readPackageMain(requestPath) {
266+
const pkg = readPackage(requestPath);
267+
return pkg ? pkg.main : undefined;
268+
}
268269

269-
return pkg.exports;
270+
function readPackageExports(requestPath) {
271+
const pkg = readPackage(requestPath);
272+
return pkg ? pkg.exports : undefined;
270273
}
271274

272275
function tryPackage(requestPath, exts, isMain, originalPath) {
273-
const pkg = readPackage(requestPath);
276+
const pkg = readPackageMain(requestPath);
274277

275278
if (!pkg) {
276279
return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain);
@@ -371,7 +374,7 @@ function resolveExports(nmPath, request, absoluteRequest) {
371374
}
372375

373376
const basePath = path.resolve(nmPath, name);
374-
const pkgExports = readExports(basePath);
377+
const pkgExports = readPackageExports(basePath);
375378
const mappingKey = `.${expansion}`;
376379

377380
if (typeof pkgExports === 'object' && pkgExports !== null) {
@@ -947,6 +950,12 @@ Module.prototype._compile = function(content, filename) {
947950

948951
// Native extension for .js
949952
Module._extensions['.js'] = function(module, filename) {
953+
if (filename.endsWith('.js')) {
954+
const pkg = readPackageScope(filename);
955+
if (pkg && pkg.type === 'module') {
956+
throw new ERR_REQUIRE_ESM(filename);
957+
}
958+
}
950959
const content = fs.readFileSync(filename, 'utf8');
951960
module._compile(content, filename);
952961
};

Diff for: src/node_file.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,8 @@ static void InternalModuleReadJSON(const FunctionCallbackInfo<Value>& args) {
875875
const size_t size = offset - start;
876876
if (size == 0 || (
877877
size == SearchString(&chars[start], size, "\"main\"") &&
878-
size == SearchString(&chars[start], size, "\"exports\""))) {
878+
size == SearchString(&chars[start], size, "\"exports\"") &&
879+
size == SearchString(&chars[start], size, "\"type\""))) {
879880
return;
880881
} else {
881882
Local<String> chars_string =

Diff for: test/es-module/test-esm-type-flag-errors.js

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ expect('', packageWithoutTypeMain, 'package-without-type');
2323
expect('--input-type=module', packageTypeModuleMain,
2424
'ERR_INPUT_TYPE_NOT_ALLOWED', true);
2525

26+
try {
27+
require('../fixtures/es-modules/package-type-module/index.js');
28+
assert.fail('Expected CJS to fail loading from type: module package.');
29+
} catch (e) {
30+
assert(e.toString().match(/Error \[ERR_REQUIRE_ESM\]: Must use import to load ES Module:/));
31+
}
32+
2633
function expect(opt = '', inputFile, want, wantsError = false) {
2734
// TODO: Remove when --experimental-modules is unflagged
2835
opt = `--experimental-modules ${opt}`;

Diff for: test/parallel/test-policy-integrity.js

+28
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ test({
212212
shouldFail: false,
213213
entry: parentFilepath,
214214
resources: {
215+
[packageURL]: {
216+
body: packageBody,
217+
match: true,
218+
},
215219
[parentURL]: {
216220
body: parentBody,
217221
match: true,
@@ -227,6 +231,10 @@ test({
227231
preload: [depFilepath],
228232
entry: parentFilepath,
229233
resources: {
234+
[packageURL]: {
235+
body: packageBody,
236+
match: true,
237+
},
230238
[parentURL]: {
231239
body: parentBody,
232240
match: true,
@@ -279,6 +287,10 @@ test({
279287
shouldFail: false,
280288
entry: depFilepath,
281289
resources: {
290+
[packageURL]: {
291+
body: packageBody,
292+
match: true,
293+
},
282294
[depURL]: {
283295
body: depBody,
284296
match: true,
@@ -289,6 +301,10 @@ test({
289301
shouldFail: false,
290302
entry: depFilepath,
291303
resources: {
304+
[packageURL]: {
305+
body: packageBody,
306+
match: true,
307+
},
292308
[policyToDepRelativeURLString]: {
293309
body: depBody,
294310
match: true,
@@ -309,6 +325,10 @@ test({
309325
shouldFail: false,
310326
entry: depFilepath,
311327
resources: {
328+
[packageURL]: {
329+
body: packageBody,
330+
match: true,
331+
},
312332
[policyToDepRelativeURLString]: {
313333
body: depBody,
314334
match: true,
@@ -351,6 +371,10 @@ test({
351371
shouldFail: false,
352372
entry: workerSpawningFilepath,
353373
resources: {
374+
[packageURL]: {
375+
body: packageBody,
376+
match: true,
377+
},
354378
[workerSpawningURL]: {
355379
body: workerSpawningBody,
356380
match: true,
@@ -370,6 +394,10 @@ test({
370394
entry: workerSpawningFilepath,
371395
preload: [parentFilepath],
372396
resources: {
397+
[packageURL]: {
398+
body: packageBody,
399+
match: true,
400+
},
373401
[workerSpawningURL]: {
374402
body: workerSpawningBody,
375403
match: true,

Diff for: test/parallel/test-policy-parse-integrity.js

+8
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,16 @@ if (!tmpdirURL.pathname.endsWith('/')) {
3636
tmpdirURL.pathname += '/';
3737
}
3838

39+
const packageFilepath = path.join(tmpdir.path, 'package.json');
40+
const packageURL = pathToFileURL(packageFilepath);
41+
const packageBody = '{"main": "dep.js"}';
42+
3943
function test({ shouldFail, integrity }) {
4044
const resources = {
45+
[packageURL]: {
46+
body: packageBody,
47+
integrity: `sha256-${hash('sha256', packageBody)}`
48+
},
4149
[depURL]: {
4250
body: depBody,
4351
integrity

0 commit comments

Comments
 (0)