From 94f237e5acb2dafb87399534de26da765cd9d537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Arboleda?= Date: Wed, 27 Nov 2019 20:26:36 -0500 Subject: [PATCH] lib,test: improves ERR_REQUIRE_ESM message PR-URL: https://github.com/nodejs/node/pull/30694 Fixes: https://github.com/nodejs/node/issues/30599 Reviewed-By: Guy Bedford --- lib/internal/errors.js | 21 +++++++++++++++- lib/internal/modules/cjs/loader.js | 27 +++------------------ test/es-module/test-cjs-esm-warn.js | 10 +++++--- test/es-module/test-esm-type-flag-errors.js | 5 +++- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index f85253ec444256..3db047cd3274c4 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1123,7 +1123,26 @@ E('ERR_OUT_OF_RANGE', msg += ` It must be ${range}. Received ${received}`; return msg; }, RangeError); -E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s', Error); +E('ERR_REQUIRE_ESM', + (filename, parentPath = null, packageJsonPath = null) => { + let msg = `Must use import to load ES Module: ${filename}`; + if (parentPath && packageJsonPath) { + const path = require('path'); + const basename = path.basename(filename) === path.basename(parentPath) ? + filename : path.basename(filename); + msg += + '\nrequire() of ES modules is not supported.\nrequire() of ' + + `${filename} ${parentPath ? `from ${parentPath} ` : ''}` + + 'is an ES module file as it is a .js file whose nearest parent ' + + 'package.json contains "type": "module" which defines all .js ' + + 'files in that package scope as ES modules.\nInstead rename ' + + `${basename} to end in .cjs, change the requiring code to use ` + + 'import(), or remove "type": "module" from ' + + `${packageJsonPath}.\n`; + return msg; + } + return msg; + }, Error); E('ERR_SCRIPT_EXECUTION_INTERRUPTED', 'Script execution was interrupted by `SIGINT`', Error); E('ERR_SERVER_ALREADY_LISTEN', diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 11e3d59f781dc3..319f89078025dd 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -1139,33 +1139,14 @@ Module.prototype._compile = function(content, filename) { }; // Native extension for .js -let warnRequireESM = true; Module._extensions['.js'] = function(module, filename) { if (filename.endsWith('.js')) { const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. if (pkg && pkg.data && pkg.data.type === 'module') { - if (warnRequireESM) { - const parentPath = module.parent && module.parent.filename; - const basename = parentPath && - path.basename(filename) === path.basename(parentPath) ? - filename : path.basename(filename); - process.emitWarning( - 'require() of ES modules is not supported.\nrequire() of ' + - `${filename} ${parentPath ? `from ${module.parent.filename} ` : ''}` + - 'is an ES module file as it is a .js file whose nearest parent ' + - 'package.json contains "type": "module" which defines all .js ' + - 'files in that package scope as ES modules.\nInstead rename ' + - `${basename} to end in .cjs, change the requiring code to use ` + - 'import(), or remove "type": "module" from ' + - `${path.resolve(pkg.path, 'package.json')}.`, - undefined, - undefined, - undefined, - true - ); - warnRequireESM = false; - } - throw new ERR_REQUIRE_ESM(filename); + const parentPath = module.parent && module.parent.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath); } } const content = fs.readFileSync(filename, 'utf8'); diff --git a/test/es-module/test-cjs-esm-warn.js b/test/es-module/test-cjs-esm-warn.js index b1b2e7f434256f..b800a47d0515d5 100644 --- a/test/es-module/test-cjs-esm-warn.js +++ b/test/es-module/test-cjs-esm-warn.js @@ -26,15 +26,19 @@ child.on('close', common.mustCall((code, signal) => { assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.ok(stderr.startsWith(`(node:${child.pid}) Warning: ` + - 'require() of ES modules is not supported.\nrequire() of ' + + assert.ok(stderr.indexOf( + `Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ${required}` + + '\nrequire() of ES modules is not supported.\nrequire() of ' + `${required} from ${requiring} ` + 'is an ES module file as it is a .js file whose nearest parent ' + 'package.json contains "type": "module" which defines all .js ' + 'files in that package scope as ES modules.\nInstead rename ' + `${basename} to end in .cjs, change the requiring code to use ` + 'import(), or remove "type": "module" from ' + - `${pjson}.\n`)); + `${pjson}.\n`) !== -1); assert.ok(stderr.indexOf( 'Error [ERR_REQUIRE_ESM]: Must use import to load ES Module') !== -1); + + assert.strictEqual( + stderr.match(/Must use import to load ES Module/g).length, 1); })); diff --git a/test/es-module/test-esm-type-flag-errors.js b/test/es-module/test-esm-type-flag-errors.js index 5d19cedd84a844..e0dedc16cabf8c 100644 --- a/test/es-module/test-esm-type-flag-errors.js +++ b/test/es-module/test-esm-type-flag-errors.js @@ -27,7 +27,10 @@ try { require('../fixtures/es-modules/package-type-module/index.js'); assert.fail('Expected CJS to fail loading from type: module package.'); } catch (e) { - assert(e.toString().match(/Error \[ERR_REQUIRE_ESM\]: Must use import to load ES Module:/)); + assert.strictEqual(e.name, 'Error'); + assert.strictEqual(e.code, 'ERR_REQUIRE_ESM'); + assert(e.toString().match(/Must use import to load ES Module/g)); + assert(e.message.match(/Must use import to load ES Module/g)); } function expect(opt = '', inputFile, want, wantsError = false) {