diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 3580614481acc2..d7e240fbea5761 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -259,7 +259,10 @@ function readPackageScope(checkPath) { if (checkPath.endsWith(path.sep + 'node_modules')) return false; const pjson = readPackage(checkPath); - if (pjson) return pjson; + if (pjson) return { + path: checkPath, + data: pjson + }; } return false; } @@ -959,13 +962,32 @@ Module.prototype._compile = function(content, filename) { return result; }; - // Native extension for .js +let warnRequireESM = true; Module._extensions['.js'] = function(module, filename) { - if (experimentalModules && filename.endsWith('.js')) { + if (filename.endsWith('.js')) { const pkg = readPackageScope(filename); - if (pkg && pkg.type === 'module') { - throw new ERR_REQUIRE_ESM(filename); + 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')}.` + ); + warnRequireESM = false; + } + if (experimentalModules) { + throw new ERR_REQUIRE_ESM(filename); + } } } 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 new file mode 100644 index 00000000000000..ec368c73e2ef2d --- /dev/null +++ b/test/es-module/test-cjs-esm-warn.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const { spawn } = require('child_process'); +const assert = require('assert'); +const path = require('path'); + +const requiring = path.resolve(fixtures.path('/es-modules/cjs-esm.js')); +const required = path.resolve( + fixtures.path('/es-modules/package-type-module/cjs.js') +); +const pjson = path.resolve( + fixtures.path('/es-modules/package-type-module/package.json') +); + +const basename = 'cjs.js'; + +const child = spawn(process.execPath, [requiring]); +let stderr = ''; +child.stderr.setEncoding('utf8'); +child.stderr.on('data', (data) => { + stderr += data; +}); +child.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + + assert.strictEqual(stderr, `(node:${child.pid}) Warning: ` + + 'require() 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`); +})); diff --git a/test/fixtures/es-modules/cjs-esm.js b/test/fixtures/es-modules/cjs-esm.js new file mode 100644 index 00000000000000..3599178996800d --- /dev/null +++ b/test/fixtures/es-modules/cjs-esm.js @@ -0,0 +1 @@ +require('./package-type-module/cjs.js'); diff --git a/test/fixtures/es-modules/package-type-module/cjs.js b/test/fixtures/es-modules/package-type-module/cjs.js new file mode 100644 index 00000000000000..683f2d8ba623a7 --- /dev/null +++ b/test/fixtures/es-modules/package-type-module/cjs.js @@ -0,0 +1 @@ +module.exports = 'asdf';