Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESM should require file extensions #9885

Open
SimenB opened this issue Apr 25, 2020 · 14 comments
Open

ESM should require file extensions #9885

SimenB opened this issue Apr 25, 2020 · 14 comments

Comments

@SimenB
Copy link
Member

SimenB commented Apr 25, 2020

馃悰 Bug Report

import t from './file';
import d from './directory/'

These should both fail to resolve, but since we use the same resolver as for CJS, it doesn't, and the import will work while it would have failed at runtime. This is also the case for dynamic imports.

This might need some support in resolve whenever they add support for ESM? browserify/resolve#222

Place where we call resolve in jest: https://github.com/facebook/jest/blob/c024dec130d9914dcc3418ea74c26f667db3dbfa/packages/jest-runtime/src/index.ts#L395-L398
https://github.com/facebook/jest/blob/c024dec130d9914dcc3418ea74c26f667db3dbfa/packages/jest-runtime/src/index.ts#L423

Node docs: https://nodejs.org/api/esm.html#esm_mandatory_file_extensions
Node issue for better errors: nodejs/node#30603

@aledalgrande
Copy link
Contributor

Would this change fail also when --es-module-specifier-resolution=node is used?

@SimenB
Copy link
Member Author

SimenB commented May 29, 2020

--experimental-specifier-resolution=node is the flag. And yeah, we'd need to keep the logic around for that case... Not sure how to best detect if it has been passed. It's not present in process.argv so we might need to have our own option for this 馃檨

@nicolo-ribaudo
Copy link
Contributor

Maybe you can get it from process.execArgv.

@cspotcode
Copy link
Contributor

For what it's worth, ts-node is doing this today. We check both positional args and also the NODE_OPTIONS environment variable. If you would like, I'll point you at our implementation. We could also expose it via our API surface.

There's probably going to be a fair bit of overlap here, both for this and for mapping between rootDir and outDir and between file extensions. Let me know if collaborating on a shared module to handle this stuff makes sense to you.

@mikicho
Copy link

mikicho commented Oct 1, 2021

I'm using eslint-plugin-import with "import/extensions": ["error", "ignorePackages"] rule to make sure all imports have extensions

@SimenB
Copy link
Member Author

SimenB commented Oct 4, 2021

OK, now that TS has announced they'll be forcing Node ESM to use extensions (https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/#new-file-extensions, https://twitter.com/orta/status/1444958295865937920) we need to figure this out (preferably releasing a few weeks before 4.5 goes stable to get ahead of all the people who will complain that Jest doesn't work).


Current plan is to enforce file extensions for ESM resolution (import and import()), but allow some sort of opt-in to the current (i.e. CJS) behavior (like --experimental-specifier-resolution mentioned above does). I don't think I'd like to auto-detect the presence of that flag.

This also impacts https://jestjs.io/docs/configuration#extensionstotreatasesm-arraystring - we might have to start throwing on that and tell people to use type...

/cc @ahnpnl

@SimenB
Copy link
Member Author

SimenB commented Oct 4, 2021

@cspotcode I'd be very interested to hear how this impacts ts-node 馃檪

@cspotcode
Copy link
Contributor

The file extensions behaviour shouldn't affect ts-node much. We already align with TS and node as far as file extensions go, and we resolve correctly.

import statements should point to the emitted js (that's what you want to import, after all) and we resolve that to the source .ts, since it's our job to essential emit in-memory, not on the filesystem. For production you can pre-compile to the filesystem and get rid of ts-node and everything works.

ts-node matches node's behavior regarding --experimental-specifier-resolution, which is to say, if you omit that flag, then file extensions are required. If you pass that flag via NODE_OPTIONS or whatever, then you get the behavior of that flag.

@ahnpnl
Copy link
Contributor

ahnpnl commented Oct 5, 2021

Currently, ts-jest logic follows what Jest asks for. IIRC extensiosToTreatAsESM is a short-term solution to adopt ESM? If we can switch to use type instead, it would be better that we can remove one Jest config option.

In general, for transformer, I think we should just stick to the current approach that transformer should give what Jest needs. Maybe small changes from ts-jest side.

@SimenB
Copy link
Member Author

SimenB commented Oct 5, 2021

I don't think we can remove extensionsToTreatAsESM, if nothing else since TS doesn't have any plans (I asked Orta) for enforcing extensions when outputting ESM for non-node (even though it's technically invalid ESM, they cannot break everybody outputting it and then consumed by bundlers). So Jest shouldn't force people to use extensions if they don't want. extensionsToTreatAsESM is a nice escape hatch for those cases.

But we should probably expand our current handling to first check extensionsToTreatAsESM, then look at type also for .ts and .tsx.

But defaulting to requiring the extension for ESM mode seems sensible to me. Then maybe some way of specifying you want CJS resolution mode (I don't wanna sniff out options passed to node, I'd rather have a CLI options ourselves - people can toggle that on or off via looking at node flags if they want)

@cspotcode
Copy link
Contributor

even though it's technically invalid ESM

Isn't the format of module specifiers host-defined, so it's valid ESM? According to the spec.

@SimenB
Copy link
Member Author

SimenB commented Oct 5, 2021

Sure, but it's invalid in node (without experimental flag) and browsers, which should cover (completely made up number) 99% of hosts running the code natively, no?

@cspotcode
Copy link
Contributor

Bundlers are effectively a runtime target. I simply mean that, in terms of the TS team's decision-making, it is technically valid ESM, which is why they're supporting it. It's not merely a backwards-compat issue. A lot of the ecosystem's problems tend to get blamed on TS, when it's actually TS playing along with the ecosystem's desire to be custom at every turn.

@SimenB
Copy link
Member Author

SimenB commented Oct 5, 2021

Ah right. Fair enough 馃檪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants