-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
EXPORT_ES6: Add testing and support for node.js (replace require(), __dirname) #11792
Comments
I think we don't have tests for it because at the time there wasn't a version of node that supported it. If there is now, we should add some. I think we have 12.10 on CI, and can maybe add another version. Another option is to use v8, which we already install a new version of, if it has ES6 module support. It would be good if someone could write up a test for this and then we can check the options. |
Node 12.10 will definitely be enough. |
Just FYI, it looks like out current version of node that we use in emsdk and in all our testing is |
Could this issue be extended to cover also "require" vs "import"? I wonder if replacing all lines in the output such as var nodeFS = require('fs');` with #if EXPORT_ES6
const { default: nodeFS } = await import('fs');
#else
var nodeFS = require('fs'); would be sufficient. Unfortunately my insight in the build system is very limited. But if someone could start then I could help at least with testing. |
Realised this is going to be difficult to handle for multi-environment builds. ES import statements can't be put in conditionals, and while the import function can, it needs to be handled async. |
They don't need to, Emscripten could emit static import declarations instead. |
I said it would be difficult for multi-environment builds. A static import declaration for node will fail in the browser. |
Ah, right, I see. In that case, the async import sounds fine. The fact that it's async shouldn't be an issue - Emscripten already has a set of async runtime dependencies and a way to wait on them. Take a look at usages of Line 488 in 0cc4a66
|
@RReverser You mentioned working on this in #13571 , do you have a test case that you were using? |
I said I stopped working on it and just waiting for someone else to fix it :)
Absolutely any file in import Module from './temp.mjs';
Module(); somewhere else. You'll run into the code trying to use The fix for just one configuration wouldn't be hard, but the general issue is that |
What is the best workaround for this at the moment ? Not using EXPORT_ES6 ? |
Yes, just don't use EXPORT_ES6. If you're targeting Node that won't be a problem, even if the rest of your app uses ES modules you'll be able to make it work (possibly by changing the file extension to .cjs, I can't remember the details.) |
@lovasoa I still use |
@matthewp I'm using |
In case it’s helpful, this has been my workaround for making import {dirname} from "path";
globalThis.__dirname = dirname(import.meta.url);
import { createRequire } from 'module';
globalThis.require = createRequire(import.meta.url);
import emscriptenModule from "./my-emscripten-module.js";
// ... business as usual |
@yhung119 I just use sed to find and replace the require calls with references to |
Here's smaller example of roughly the workaround described by @matthewp above along with a |
It looks like this is the best we have to a tracking issue for the general problems with the current Following that, some offline discussion raised the following ideas:
Thoughts? |
I'm not really sure what you're referring to there, sorry. Nothing in that issue is talking about injecting globals? |
I just mean, again, if you look at the linked js, at the very end I do export { writeAsciiToMemory, UTF8ToString } so it's possible to actually use the library. Otherwise it's relying on the fact that ES5 on web has a default |
You should be listing those in |
+1 for getting this solved. As a WASM library author, it's key that the library work transparently on both browser and server (nodeJS also simplifies CI testing considerably). As is I'll just publish as a CommonJS module, since that works, but I'd prefer to move to a modern ES6 module. |
As described in emscripten-core#11792, `require()` and `__dirname` doesn't exist in an ES6 module. Emscripten uses this to import built-in core Node.js modules. For example, the `node:fs` module is used for synchronously importing the `*.wasm` binary, when not linking with `-sSINGLE_FILE`. To work around this, ES6 modules on Node.js may import `createRequire()` from `node:module` to construct the `require()` function, allowing modules to be imported in a CommonJS manner. Emscripten targets a variety of environments, which can be categorized as: 1. Multi-environment builds, which is the default when `-sENVIRONMENT=*` is not specified at link time. 2. Single-environment, e.g. only web or Node.js as target. For use case (1), this commit ensures that an `async` function is emitted, allowing Node.js modules to be dynamically imported. This is necessary given that static import declarations cannot be put in conditionals. Inside the module, for Node.js only, it's using the above-mentioned `createRequire()`-construction. For use case (2), when only Node.js is targeted, a static import declaration utilize the same `createRequire()`-construction. For both use cases, `-sUSE_ES6_IMPORT_META=0` is not allowed, when Node.js is among the targets, since it is not possible to mimic `__dirname` when `import.meta` support is not available. This commit does not change anything for use case (2), when only the web is targeted (`-sENVIRONMENT=web`). Resolves: emscripten-core#11792.
As described in emscripten-core#11792, `require()` and `__dirname` doesn't exist in an ES6 module. Emscripten uses this to import built-in core Node.js modules. For example, the `node:fs` module is used for synchronously importing the `*.wasm` binary, when not linking with `-sSINGLE_FILE`. To work around this, ES6 modules on Node.js may import `createRequire()` from `node:module` to construct the `require()` function, allowing modules to be imported in a CommonJS manner. Emscripten targets a variety of environments, which can be categorized as: 1. Multi-environment builds, which is the default when `-sENVIRONMENT=*` is not specified at link time. 2. Single-environment, e.g. only web or Node.js as target. For use case (1), this commit ensures that an `async` function is emitted, allowing Node.js modules to be dynamically imported. This is necessary given that static import declarations cannot be put in conditionals. Inside the module, for Node.js only, it's using the above-mentioned `createRequire()`-construction. For use case (2), when only Node.js is targeted, a static import declaration utilize the same `createRequire()`-construction. For both use cases, `-sUSE_ES6_IMPORT_META=0` is not allowed, when Node.js is among the targets, since it is not possible to mimic `__dirname` when `import.meta` support is not available. This commit does not change anything for use case (2), when only the web is targeted (`-sENVIRONMENT=web`). Resolves: emscripten-core#11792.
This will be fixed with PR #17915, see #17915 (comment) for the emsdk install instructions to test this. |
As described in #11792, `require()` and `__dirname` doesn't exist in an ES6 module. Emscripten uses this to import built-in core Node.js modules. For example, the `node:fs` module is used for synchronously importing the `*.wasm` binary, when not linking with `-sSINGLE_FILE`. To work around this, ES6 modules on Node.js may import `createRequire()` from `node:module` to construct the `require()` function, allowing modules to be imported in a CommonJS manner. Emscripten targets a variety of environments, which can be categorized as: 1. Multi-environment builds, which is the default when `-sENVIRONMENT=*` is not specified at link time. 2. Single-environment, e.g. only web or Node.js as target. For use case (1), this commit ensures that an `async` function is emitted, allowing Node.js modules to be dynamically imported. This is necessary given that static import declarations cannot be put in conditionals. Inside the module, for Node.js only, it's using the above-mentioned `createRequire()`-construction. For use case (2), when only Node.js is targeted, a static import declaration utilize the same `createRequire()`-construction. For both use cases, `-sUSE_ES6_IMPORT_META=0` is not allowed, when Node.js is among the targets, since it is not possible to mimic `__dirname` when `import.meta` support is not available. This commit does not change anything for use case (2), when only the web is targeted (`-sENVIRONMENT=web`). Resolves: #11792.
I don't think this should be closed yet as I don't think web+node builds will work when the output is put through a bundler. #17915 was great progress but it's not enough. |
Did you try / run into specific issues? As far as I can tell from my usage, it does work, but you need to tweak the bundler config obviously to either ignore or polyfill Node modules (I chose ignoring as it's a better option to save code size, but either one should work). But then, you have to do that either way. I suggest to track any bundler-specific improvements in separate issues. Native ES6 works now in both Node and browsers, and that's the primary goal. |
I haven't tested it. But for it to continue working a bundler would have to be able to output And no, users shouldn't need to configure their bundler beyond selecting ESM output. We need to move to dynamic |
Hm, why would you want |
Could this help? https://github.com/rollup/plugins/tree/master/packages/commonjs#transformmixedesmodules I agree that ideally the JS file should still workin in Node and the browser after being bundled by Rollup, Webpack, etc. |
Because that's how #17915 works. It will work if you just use the output as is, but if you try to bundle it the bundler will convert the require calls into imports and then it won't work in both web and node. |
Oh, interesting. So yes, there is at least one bundler that could handle it. |
Could the new Node API Doc: https://nodejs.org/docs/latest-v20.x/api/process.html#processgetbuiltinmoduleid Maybe the code could use this when the min Node version is 20.16 or greater. |
So I'm finally getting around to trying EXPORT_ES6 mode, and it just doesn't work in node (when loaded as an actual ES6 Module), because it still refers to
__dirname
. I assume other people have only been using it Webpack/rollup/whatever?It doesn't look like there are any tests for EXPORT_ES6 either.
The text was updated successfully, but these errors were encountered: