-
Notifications
You must be signed in to change notification settings - Fork 43
Script-based configuration files in "type": "module" packages #389
Comments
This is a major breaking change for |
Perhaps once again consider a pragma-based approach as an escape hatch (e.g. While there's a little burden on package users it gives them a functioning solution until the tools can update. |
I've added nodejs/node#29732 to put this check back behind the |
i'd like to really strongly advocate that we look for solutions that reduce the occurrences of package.json files. they slow loading down quite a bit (which is why we traditionally never supported any fields besides "main") and are obviously incompatible with non-node runtimes. |
https://nodejs.org/api/esm.html#esm_code_import_code_expressions suggests that dynamic This does not resolve the issue with test runners executing scripts, I'm not sure how that can be solved. |
it's a syntax error the same way trying to use numeric separators in node 8 is a syntax error. i don't think we should try to circumnavigate that. |
Numeric separators throwing SyntaxError in node 8 is not an issue because not using numeric separators is never a SyntaxError. A way to load |
import is syntax because it's not just a function call, it has side-channel data about where it's called from that is needed to resolve import requests. aside from that though, one of our goals is to make node code more interoperable by default, and adding some new import api would interfere with that. |
I think the point @coreyfarrell is trying to make (that i agree with) is that since most ecosystem packages support "not just the latest node", it is simply not a viable workaround to recommend use of |
@ljharb |
Thanks for the correction; in that case, that's probably the best workaround for the time being. |
It's true that tools can add support for loading config from Would it reduce your concerns if |
@coreyfarrell I'm thinking about this for Babel: async function tryLoad(path) {
try {
return require(path);
} catch (e) {
try {
return await require("./import")(path);
} catch {
throw e;
}
}
}
// import.js
module.exports = p => import(p); By doing so, the file containing |
for better or worse, that is what i would suggest, assuming eval is out of the question. |
@devsnek Is there some way to evaluate the ESM source code as a string? Like (psuedocode): const configSource = readFileSync('./.eslintrc.js', 'utf8');
const config = somehowEvalAsESM(configSource); Reading the file as a string dodges the “load as CommonJS or ESM” issue. The tool would then need to figure out somehow whether to eval as CommonJS or as ESM, perhaps via something like nodejs/node#27808; and use |
vm.SourceTextModule, although it's still experimental and I'm about to open a pr reworking the API of it. Evaluation is always exposed as async though, since TLA is about to land. I think requiring/evaling import() is the way to go |
If you could rework the SourceTextModule API to be as simple as Aside from that, though, are you saying that while I can synchronously eval CommonJS strings via |
correct. all the exposed edges of esm graphs are async. |
Okay, so thinking more outside the box then the only ways to achieve this are to use node-fibers to wrap the async calls in a Future or to use |
So you'd like |
No, I was just suggesting for node.js to expose |
|
This comment has been minimized.
This comment has been minimized.
Except such an API breaks every time the esm loader gains support for, eg, loading json. I'd think phrasing it in terms of the cjs loader would handily work around that. |
Can you elaborate which API you're talking about and in what way it breaks? |
If you have an |
Node.js 13.2.0 emits a warning upon attempt to use |
There was a blocking issue with ESLint configs where ESLint only supports |
If detection via |
try {
await import(pathToFilename);
} catch (exception) {
require(pathToFilename);
}
|
Ok currently I'm doing something conceptually similar to this (don't judge me for the sync/async thing, I promise that the exposed API is nicer 😆) function loadConfig(path, isAsync) {
let config;
try {
config = require(path);
if (isAsync) config = Promise.resolve(config);
} catch (e) {
if (e.code !== "ERR_REQUIRE_ESM") throw e;
if (!isAsync) throw new Error("Cannot use esm synchronously");
config = import(path);
}
return config;
} I don't think that I could do the same without "manually" looking up the |
How about: function loadConfig(path, isAsync) {
if (isAsync) return import(path);
try {
return require(path);
} catch (e) {
if (e.code !== "ERR_REQUIRE_ESM") throw e;
throw new Error("Cannot use esm synchronously");
}
} |
Interesting note, the following code executed by CJS does not produce any warning or error in node.js 13.2.0: import('./nyc.config.mjs')
.then(console.log)
.catch(e => console.error(e.message)); I suspect it is a bug that it doesn't emit Beyond this if we were to use @GeoffreyBooth using the fallback method has issues with error handling. Your example code would swallow the true error if try {
return await import(pathToFilename);
} catch (importError) {
try {
// XXX if a CJS module throws this will cause it to
// be evaluated a second time.
return require(pathToFilename);
} catch (requireError) {
if (importError.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
throw requireError;
} else {
throw importError;
}
}
} Thankfully I'm not facing the same restriction as babel, config load for the next version of nyc will always be async. |
@coreyfarrell I think that your suggestion has a problem: if the config is a commonjs file which throws an error, anything before that error will be executed twice. |
I think that's a great idea! |
@nicolo-ribaudo good catch, I edited my code to include a comment so no-one uses my example unaware. For my config loader I'm using @jkrems should I create a separate issue to get feedback on adding a detection flag? |
function isDynamicImportSupported() {
try {
new Function("import('')");
return true;
} catch (exception) {
return false;
}
} Returns |
@GeoffreyBooth your example returns |
@GeoffreyBooth Also, even doing the test already may print a warning I assume. I don't think there's a clean, no-side effects, way of testing if ESM could be imported. Especially not if that test is supposed to be sync. |
Also there’s the question of whether you want the test to pass or not for the old < 12.0.0 implementation, where the semantics of specifiers were very different; so ESM that could be imported in Node 11 might not work in 12, and vice versa. There’s always the option of just testing for the Node version number. Not exactly a feature detection, I know, but it would work and be sync. |
Perhaps you'd make a folder, and put a {
"main": "./false.js",
"exports": { ".": "./true.js" }
} and Then, your feature test is |
This is a great suggestion @coreyfarrell @nicolo-ribaudo. I've created an issue to track at nodejs/node#30599. Ideally we can just combine the warning message and error into one now. See https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js#L1193 for where this is happening. We were still not sure if we would be able to ship the error or if it would break tools. But if tools will genuinely benefit from this being a reliable error then certainly this sounds like a good pattern to me. I don't have time right now to work on an implementation, but would gladly approve any PR and see that in a patch. |
|
Tracking issue specifically for the ESLint issue that was raised here - eslint/eslint#12319.
Tooling often rely on configuration scripts. Examples include
.eslintrc.js
,karma.conf.js
,webpack.config.js
,babel.config.js
to name a few.These tools typically load those configuration files with a
require
.When an existing project with such a configuration adds
"type": "module"
to the package.json, they get the ERR_REQUIRE_ESM dual hazard error that we added recently in nodejs/node#29492.The ideal upgrade path is to have the tools support using
import()
for loading their configurations. Alternatively they could support.cjs
as well.We should probably reach out with this information to tools and get further feedback as well.
The text was updated successfully, but these errors were encountered: