jest
jest copied to clipboard
ES import of typescript with .js extension fails
I'm using swc/node in ts-node to strip typings from my nodejs codebase using their ts-node/esm
loader.
To keep tsc
happy while also following the esm spec to keep the extension of all my imports, all my imports end as .js despite the source files being ts.
While this works for both tsc
and ts-node
with swc
, all Jest tests fail due to imports of .ts
files with .js
that can't be resolved. I took the necessary steps to make Jest work with ESM, which used to work until I introduced TS there. Likewise, the tests work if I specify the ts extension for each of these imports, which would however break tsc.
I'm currently failing to fit the pieces together and have a hard time finding documentation or maybe a simple example that utilizes Node with ESM, SWC and imports ts files in a test suite.
I would also be happy to follow the Deno approach and explicitely keep the actual .ts extension on imports - However, I don’t know how I would typecheck my code then without tsc.
The @swc/jest repo, which exists specifically for jest, even mentions using Jest with ESM, however that simply won't work once I actually want to type-check my code (Which I would still need tsc for)
Right now my best bet is to actually keep the explicit .ts imports, which I prefer anyways, and to fork tsc to disable that .ts file import error, similar to how Deno does it. However I can't believe this is the only available option?
This seems to be related: https://github.com/swc-project/swc/issues/3043
What does your config look like? You might have to tell @swc/jest
to transform the files in node_modules
by setting transformIgnorePatterns
to an empty array.
module.exports = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
transformIgnorePatterns: [],
testEnvironment: 'node',
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
};
You might have to tell @swc/jest to transform the files in node_modules by setting transformIgnorePatterns to an empty array.
Thanks for the suggestion @ewized. No, all of my modules which are distributed as ESM are already listed there to be ignored. (That part of the setup also works for me with Jest in ESM Mode)
What fails is the import of my actual source files which are about to be tested if they are .ts files and .js is specified in the import ( As demanded by Typescript for now🙄).
I have exactly the same problem. Everything works with SWC and imports specified with the additional .js
despite being typescript files. But the import mechanism fails when using jest :thinking:
In case you stumble upon this. I could fix the issue with the help of the ts-jest docs.
You need to add
module.exports = {
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
}
to your jest.config.js
in order that Jest transforms the import statement correctly.
Thanks @Mad-Kat, will give that a try!
In case you stumble upon this. I could fix the issue with the help of the ts-jest docs.
You need to add
module.exports = { moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, }
to your
jest.config.js
in order that Jest transforms the import statement correctly.
I also had to add this. Can this get added to the README and/or the docs?
Related issue at Storybook: https://github.com/storybookjs/storybook/issues/15962 Their solution use a webpack plugin that probably does something similar.
Just to clarify, having ".js" extension in a TypeScript barrel file (an index that reexport internal modules) sounds weird but seems a normal pattern when working with ESM. So if Storybook or Jest doesn't accept that, the solution is indeed to tweak Storybook or Jest configuration, not the source code of the app or library.
@Mad-Kat approach seems to work also with pure Jest, not using SWC
Hey @Mad-Kat, how did you made SWC imports specified with the additional .js
extension works outside of Jest?
I keep getting a "Module not found error" whenever I add a .js
extension to any import.
Thank you 🙏🏻
Here's my config:
{
jsc: {
parser: {
syntax: "typescript",
tsx: true
},
target: "es2022",
baseUrl: ".",
paths: {
"@host/*": ["src/*"]
}
},
module: {
type: "es6"
},
sourceMaps: true
}
UPDATE:
Found out that my issue is related to how Webpack handles import with file extensions (I am using swc-loader
) rather than SWC cli.
The below works for me in my Node.js backend project. The rule applies to relative imports starting with a period (.) or double periods (..)
"moduleNameMapper": {
"@src/(.*)": "<rootDir>/src/$1",
"^(\\./.+|\\../.+).js$": "$1"
}
OR
"moduleNameMapper": {
"@src/(.*)": "<rootDir>/src/$1",
"^(..?/.+).js?$": "$1"
},