jest icon indicating copy to clipboard operation
jest copied to clipboard

ES import of typescript with .js extension fails

Open janus-reith opened this issue 2 years ago • 11 comments

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.

janus-reith avatar Jan 03 '22 10:01 janus-reith

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?

janus-reith avatar Jan 07 '22 10:01 janus-reith

This seems to be related: https://github.com/swc-project/swc/issues/3043

janus-reith avatar Jan 07 '22 11:01 janus-reith

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',
  },
};

ewized avatar Jan 09 '22 14:01 ewized

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🙄).

janus-reith avatar Jan 09 '22 15:01 janus-reith

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:

Mad-Kat avatar Feb 04 '22 07:02 Mad-Kat

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.

Mad-Kat avatar Feb 04 '22 08:02 Mad-Kat

Thanks @Mad-Kat, will give that a try!

janus-reith avatar Feb 04 '22 09:02 janus-reith

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?

simonhammes avatar May 22 '22 13:05 simonhammes

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

eric-burel avatar Oct 03 '22 12:10 eric-burel

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.

patricklafrance avatar Mar 01 '23 20:03 patricklafrance

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"
    },

IdrisAkintobi avatar Apr 20 '24 17:04 IdrisAkintobi