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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: tslib 2.5.2 package.json exports change breaks Jest when using ESM modules #14173

Closed
josundt opened this issue May 31, 2023 · 10 comments
Closed

Comments

@josundt
Copy link

josundt commented May 31, 2023

Version

29.5.0 (latest)

Steps to reproduce

Context

I have a TypeScript project that compiles to true ESM modules (moduleResolution/module NodeNext), and all ".ts" imports use ".js" extensions as recommended in TypeScript. The project uses f.ex. TypeScript experimentalDecorators and it uses importHelpers which adds a dependency to tslib.

My jest config looks as follows:

{
    moduleNameMapper: {
        "^(\\.{1,2}/.*)\\.js$": "$1"
    },
    transform: {
        "^.+\\.tsx?$": ["ts-jest", {
            tsconfig: "tsconfig.json",
            useESM: true
        }]
    },
    extensionsToTreatAsEsm: [".ts"],
    testEnvironment: "jsdom"
}

To support ESM, I run the tests with:
node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js

This setup is following the jest/ts-jest documentation, and should be standard for ESM support in TypeScript projects.

The Jest bug

After upgrading tslib from v2.5.0 to v2.5.2, the Jest tests fail.
This is because tslib's exports in package.json has changed with the update, and it seems like the Jest's resolver does not support the more verbose export syntax.

tslib@2.5.0 package.json exports:

{
    //...
    "exports": {
        ".": {
            "module": "./tslib.es6.js",
            "import": "./modules/index.js",
            "default": "./tslib.js"
        },
        "./*": "./*",
        "./": "./"
    }
    //...
}

tslib@2.5.2 package.json exports:

{
    //...
    "exports": {
        ".": {
            "module": {
                "types": "./tslib.d.ts",
                "default": "./tslib.es6.js"
            },
            "import": {
                "node": "./modules/index.js",
                "default": {
                    "types": "./tslib.d.ts",
                    "default": "./tslib.es6.js"
                }
            },
            "default": "./tslib.js"
        },
        "./*": "./*",
        "./": "./"
    }
    //...
}

When I just copy the exports from 2.5.0 and replace the ones in 2.5.2 in *node_modules/tslib/package.json, the tests run just fine.

PS! This error only seems to happen when using jest config envirnoment: "jsdom". When setting it to "node", the problem is not triggered. It looks like this may be a jest-environment-jsom specific issue.

I have tested configuring jest with moduleNameMapper to force tslib to use:

  • node_modules/tslib/tslib.js
  • node_modules/tslib/tslib.es6.js
  • node_modules/tslib/modules/index.js
    ...but none of these fixed the issue.

Expected behavior

The tests should work like before

Actual behavior

                                                                                                                                                                                                                        
 FAIL test/views/app/app.spec.ts                                                                                                                                                                                        
  ● Test suite failed to run                                                                                                                                                                                             
                                                                                                                                                                                                                         
    Jest encountered an unexpected token                                                                                                                                                                                 
                                                                                                                                                                                                                         
    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.                                         
                                                                                                                                                                                                                         
    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.                                                                                      

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    C:\_Git\Adra.Reference.App\src\Adra.Reference.App\node_modules\tslib\tslib.es6.js:24
    export function __extends(d, b) {
    ^^^^^^

    SyntaxError: Unexpected token 'export'

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1495:14)


ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From test/views/app/app.spec.ts.                                                                                        
                                                                                                                                                                                                                         
                                                                                                                                                                                                                         
                                                                                                                                                                                                                         
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From test/views/app/app.spec.ts.



ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From test/views/app/app.spec.ts.



ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From test/views/app/app.spec.ts.                                                                                        
                                                                                                                                                                                                                         

Additional context

My tsconfig.json file looks as follows:

{
  "compilerOptions": {
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "esModuleInterop": true,
    "exactOptionalPropertyTypes": false,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "importHelpers": true,
    "lib": ["dom", "es2019"],
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": false,
    "noUnusedLocals": true,
    "noUnusedParameters": false,
    "preserveConstEnums": true,
    "sourceMap": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "stripInternal": true,
    "target": "es2019",
    "useUnknownInCatchVariables": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src", "test"]
}

Environment

System:
    OS: Windows 10 10.0.22621
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-11370H @ 3.30GHz
  Binaries:
    Node: 18.16.0 - C:\Program Files\nodejs\node.EXE
    npm: 9.5.1 - C:\Program Files\nodejs\npm.CMD
  npmPackages:
    jest: 29.5.0 => 29.5.0
@SimenB
Copy link
Member

SimenB commented May 31, 2023

The issue is that Jest now resolves to https://www.runpkg.com/?tslib@2.5.2/tslib.es6.js instead of https://www.runpkg.com/?tslib@2.5.2/modules/index.js (this is correct according to the stanza)

The latter is in a directory with a package.json with a type: module field. The new one does not, so the .js is interpreted as CJS and not ESM.


I think Jest's behaviour here is correct (we cannot know which condition we hit, so we cannot know if the returned file should be interpreted as ESM or not), but it seems unlikely tslib is doing the wrong thing, which makes me think the bug is here 😅

@SimenB
Copy link
Member

SimenB commented May 31, 2023

microsoft/tslib#201, fwiw

@SimenB
Copy link
Member

SimenB commented May 31, 2023

Commented over there.

microsoft/tslib#201 (comment)

@josundt
Copy link
Author

josundt commented May 31, 2023

@SimenB I have added a minimal project to demonstrate the bug here:
https://github.com/josundt/jest-tslib-bug

@josundt
Copy link
Author

josundt commented May 31, 2023

@SimenB The issue was introduced in tslib@2.5.2. Notice that the issue disappears if you downgrade tslib in my sample to 2.5.0.
Also notice that the problem disappears if you change jest config testEnvironment to node instead of jsdom.

Jest should support tslib >= 2.5.2 out of the box, even if it may need special handling for this package.
To me it looks like the tslib exports are not interpreted correctly by the Jest resolver/transformer pipeline.

We anyway need a temporary workaround. Any suggestion?

@SimenB
Copy link
Member

SimenB commented May 31, 2023

Temporary workaround is to plug in your own resolver (using resolver option) and special case tslib within it.

@josundt
Copy link
Author

josundt commented May 31, 2023

@SimenB I have updated my sample project with a custom resolver built on enhanced-resolve - I used this earlier with jest before jest's built-in resolver could handle ESM.
Notice that with enhanced-resolve I don't need any special handling for tslib.
Any example on a custom resolver that can handle tslib@2.5.2 without using a third party library?

@josundt
Copy link
Author

josundt commented Jun 2, 2023

@SimenB Please see my comment microsoft/tslib#201 (comment).

Maybe it would be better to align with enhanced-resolve whenever this this type of exports ambiguity is encountered?

FYI: I was anyway able to make a temporary workaround by adding node as a fallback custom export condition to my jest config for now....

{
    //...
    testEnvironmentOptions: {
        customExportConditions: ["browser", "node"] // "node" is required for a tslib issue
    }
}

@SimenB
Copy link
Member

SimenB commented Jun 2, 2023

As mentioned in that issue, enhanced-resolve behaves the same.


That said, they fixed it: https://github.com/microsoft/tslib/releases/tag/v2.5.3

@SimenB SimenB closed this as completed Jun 2, 2023
@github-actions
Copy link

github-actions bot commented Jul 3, 2023

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants