-
-
Notifications
You must be signed in to change notification settings - Fork 539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement ERR_REQUIRE_ESM when a file is require()
d which should be loaded as ESM
#1031
Changes from 13 commits
2bee85e
c2ab1c3
d82df95
a10c7a9
7243123
eb07ff6
567acb6
f5fab01
dcc7e0d
8076998
d70658b
b1c35b8
5e8090a
da76779
9f9efd6
6b15230
7a1a55b
1a21a7d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// copied from https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
|
||
module.exports.assertScriptCanLoadAsCJSImpl = assertScriptCanLoadAsCJSImpl; | ||
|
||
// copied from Module._extensions['.js'] | ||
function assertScriptCanLoadAsCJSImpl(filename) { | ||
const pkg = readPackageScope(filename); | ||
// Function require shouldn't be used in ES modules. | ||
if (pkg && pkg.data && pkg.data.type === 'module') { | ||
const parentPath = module.parent && module.parent.filename; | ||
const packageJsonPath = path.resolve(pkg.path, 'package.json'); | ||
throw createErrRequireEsm(filename, parentPath, packageJsonPath); | ||
} | ||
} | ||
|
||
function readPackageScope(checkPath) { | ||
const rootSeparatorIndex = checkPath.indexOf(path.sep); | ||
let separatorIndex; | ||
while ( | ||
(separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex | ||
) { | ||
checkPath = checkPath.slice(0, separatorIndex); | ||
if (checkPath.endsWith(path.sep + 'node_modules')) | ||
return false; | ||
const pjson = readPackage(checkPath); | ||
if (pjson) return { | ||
path: checkPath, | ||
data: pjson | ||
}; | ||
} | ||
return false; | ||
} | ||
|
||
const packageJsonCache = new Map(); | ||
|
||
function readPackage(requestPath) { | ||
const jsonPath = path.resolve(requestPath, 'package.json'); | ||
|
||
const existing = packageJsonCache.get(jsonPath); | ||
if (existing !== undefined) return existing; | ||
|
||
const json = internalModuleReadJSON(path.toNamespacedPath(jsonPath)); | ||
if (json === undefined) { | ||
packageJsonCache.set(jsonPath, false); | ||
return false; | ||
} | ||
|
||
// TODO Related to `--experimental-policy`? Disabling for now | ||
// if (manifest) { | ||
// const jsonURL = pathToFileURL(jsonPath); | ||
// manifest.assertIntegrity(jsonURL, json); | ||
// } | ||
|
||
try { | ||
const parsed = JSON.parse(json); | ||
const filtered = { | ||
name: parsed.name, | ||
main: parsed.main, | ||
exports: parsed.exports, | ||
type: parsed.type | ||
}; | ||
packageJsonCache.set(jsonPath, filtered); | ||
return filtered; | ||
} catch (e) { | ||
e.path = jsonPath; | ||
e.message = 'Error parsing ' + jsonPath + ': ' + e.message; | ||
throw e; | ||
} | ||
} | ||
|
||
function internalModuleReadJSON(path) { | ||
try { | ||
return fs.readFileSync(path, 'utf8') | ||
} catch (e) { | ||
if (e.code === 'ENOENT') return undefined | ||
throw e | ||
} | ||
} | ||
|
||
function createErrRequireEsm(filename, parentPath, packageJsonPath) { | ||
// Attempt to create an error object that matches node's native error close enough | ||
const code = 'ERR_REQUIRE_ESM' | ||
const err = new Error(getMessage(filename, parentPath, packageJsonPath)) | ||
err.name = `Error [${ code }]` | ||
err.stack | ||
Object.defineProperty(err, 'name', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is pretty funky, might want to add a comment on why you do this since the first look makes it seem this line would erase There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
value: 'Error', | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
}) | ||
err.code = code | ||
return err | ||
|
||
// copy-pasted from https://github.com/nodejs/node/blob/master/lib/internal/errors.js#L1294-L1311 | ||
function getMessage(filename, parentPath = null, packageJsonPath = null) { | ||
const ext = path.extname(filename) | ||
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 ${ext} file whose nearest parent ` + | ||
`package.json contains "type": "module" which defines all ${ext} ` + | ||
'files in that package scope as ES modules.\nInstead ' + | ||
'change the requiring code to use ' + | ||
'import(), or remove "type": "module" from ' + | ||
`${packageJsonPath}.\n`; | ||
return msg; | ||
} | ||
return msg; | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Log if this file is loaded as ESM or CommonJS | ||
if(typeof module !== 'undefined') | ||
console.log('CommonJS') | ||
else | ||
console.log('ESM') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"type": "module" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
require('./esm-package/loaded-as') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this whole file copied or which parts should be reviewed? Curious on things like we read JSON ourselves instead of relying on the
require
cache.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, this is actually coming from several files in node's source. I added comments to each item in this file with a permalink to the span of node's source code on github.