-
-
Notifications
You must be signed in to change notification settings - Fork 532
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
[WIP] ESM support for node v13 #1010
Conversation
Looks like |
@evg656e Good catch; I added the missing entry to our files array. How do you propose we support |
@cspotcode Is it flag experimental or the resolution itself? As far as i concerned, this resolution algorithm (sort of) is used by typescript itself when it comes to module loading. And i don't see much utility in esm modules in node without it. Can we inspect node options to see, whether this flag was used or not? And switch to appropriate algorithm? |
@evg656e I think it would help if you share a specific example of what you want to achieve, and how it is not working currently. ESM hooks are experimental. This includes When TypeScript is set to
If you find out, let us know. You'll probably need to ask the relevant people on the node team, since the hooks feature is still in flux and they are changing things. |
@cspotcode I've created sample repo: https://github.com/evg656e/rebenchmark-example. I've also forked this branch and add some changes to make my example work (it did not work with the explicit resolution algorithm). I also briefly looked at whether it's possible to somehow access the node options through process.argv inside loader, and it seems like it's impossible (options like --loader or --experimental-specifier-resolution disappear) |
@evg656e excellent, thank you. Here's a link to your branch, for anyone else who might be reading this: https://github.com/evg656e/ts-node/tree/ab/esm-support I probably will not have time to look at this till the weekend, but I appreciate the help. |
@cspotcode Actually, there is Last question is why there no '.js' extension in |
@evg656e Cool beans, I have a few questions about the
The missing What is the intention of your example? I see it running a benchmark tool, and there's only a single |
@cspotcode The idea of this example that i have a benchmark tool, that internally uses esm modules (even with 3rdparty non-esm modules). And it works fine with More over, i'm experimenting with esm modules in node for a while, and have other projects that rely on this flag. I suppose, that without this flag any legacy cjs module will not work in Node. As i can see, this flag actively maintained for now (there is bug in resolve algorihtm actively being worked on). So i don't think ignoring this flag at all is a good idea.
I used npm
I have thought about this too, but since i found
One more thought: I get an error even when I have not loaded a single ts file. This means that the current custom loader applies its internal resolution algorithm even for js files, although logically, it should fallback to the default implementation ( |
@evg656e if I understand correctly, you want to let users write their benchmark files in ESM? If so, can we update the example so that the benchmark files are loaded as ESM? Right now the Even without
Please post a copy of errors when you encounter them; it helps everyone debug.
Yeah, sounds like the same oversight as mentioned here #1010 (comment) I noticed your benchmark tool doesn't work on Linux; is that intentional? |
I think i tried both variants and all variants failed with your initial custom loader. Over more, it doesn't matter at all when i don't use custom loader (both variants work). I think it work because tool itself has proper package.json with specified
Internally it uses dynamic import. It's by design. There is similar bench-runner build upon cjs modules.
It's a bug, i have never tested this in Linux, copied this line from stackoverflow.
I can create some abstract repo with corner cases if you wish.
An error occurs on |
Linux does not allow passing arguments via shebangs. This is controlled by the Linux kernel. We have a bunch of other tickets where this has caught people off-guard, but to clarify, it's not a ts-node or a shell issue. Since it may affect developer experience, do you know how you want to support Linux? I can think of a few ways you can do it, but it will help to know which you prefer.
Actually this should succeed without When you don't use our ESM hook, the benchmark files load as CommonJS. When you do use our ESM hook, the missing .js extension means the resolver doesn't see |
@evg656e I added the missing extension. Good catch, thanks. Our |
Yep, you are right. Problem with my projects that import statements does not use explicit extension. Since then i need to set flag. |
@evg656e Ok, that's good. Our ESM loader should be adding missing file extensions, so this should automatically work for you without the I explained why we need to auto-add missing file extensions in the proposal: #1007 |
I tried to use your loader with mocha (recent versions of mocha support native ESM modules) and came across with a new bug: node --loader ts-node/esm node_modules/mocha/bin/mocha
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for C:\Users\fromh\Projects\sandbox\github\ts-node\esm-usage-example\mocha\node_modules\mocha\bin\mocha
at defaultGetFormat (internal/modules/esm/get_format.js:65:15)
at defer (C:\Users\fromh\Projects\sandbox\github\ts-node\esm-usage-example\mocha\node_modules\ts-node\src\esm.ts:57:33)
at C:\Users\fromh\Projects\sandbox\github\ts-node\esm-usage-example\mocha\node_modules\ts-node\src\esm.ts:79:18
at Generator.next (<anonymous>)
at C:\Users\fromh\Projects\sandbox\github\ts-node\esm-usage-example\mocha\node_modules\ts-node\dist\esm.js:8:71
at new Promise (<anonymous>)
at __awaiter (C:\Users\fromh\Projects\sandbox\github\ts-node\esm-usage-example\mocha\node_modules\ts-node\dist\esm.js:4:12)
at getFormat (C:\Users\fromh\Projects\sandbox\github\ts-node\esm-usage-example\mocha\node_modules\ts-node\src\esm.ts:55:5)
at Loader.getFormat (internal/modules/esm/loader.js:113:42)
at Loader.getModuleJob (internal/modules/esm/loader.js:244:31) Without a loader, everything works fine. I added an example to my branch: https://github.com/evg656e/ts-node/tree/ab/esm-support/esm-usage-example/mocha |
…don't need to support 2x emit flavors
@evg656e Looks like this is caused by node v14 and not related to
If you think it's a bug, you can file a ticket with node. Otherwise, |
…ort for experimental-specifier-resolution=node
The commit above is untested but it implements the following:
|
Added some tests; fixed the tests. I'll merge and publish tonight. |
This is released as an experimental feature in v8.10.0. Please test and share your feedback in #1007. |
I started implementing ESM support, because it's an interesting problem and I wanted to see if I could get it working in a day.
Implements #1007
Related: #935
Example
Checkout the branch, build the code, and look at
./esm-experiment/README.md
Roughly, usage is
node --loader ts-node/esm ./index.js
Try it on your own project
npm lets you install a dependency directly from a git branch.
Limitations
compileEsm
falls back tots.transpileModule
when the regularcompile
code-path would otherwise emit CommonJS. When this happens, no typechecking. If user setsmodule: "esnext"
in their tsconfig, then this is not a problem and we use the main codepath to emit ESM.sourceMapSupport will break if the same file is loaded as both ESM and CJS, because our outputCache can't have 2 emits for the same filename.
We avoid this by detecting when a file is being loaded by both ESM and CJS, and throwing an error.
In practice, I can't think of a reason why the same .ts file would be loaded as both ESM and CJS, so this should never throw. If it does, the error message will show up in our issue tracker.
Development work
esm-script.mjs
to do--script-mode
?process.argv
for config resolutionMisc
resolve()
logicWe need to add additional file extensions to ESM's module resolution behavior. We also need to automatically add extensions when they're missing, and check if we should rewrite
.js
into.ts
.I've done this by copy-pasting a copy of node's source code, patched to tweak the behavior. This approach looks messy when you see it in our source tree, but it's a great way to match node's behavior without much complexity.
Ideally this functionality is maintained as a third-party dependency. So far, the only similar library I'm aware of is this one, used by webpack:
https://www.npmjs.com/package/enhanced-resolve
It implements
require.resolve
, but it's async and very configurable.