【问题标题】:Jest won't accept top-level-awaits with NodeJS16 & TypeScriptJest 不接受 NodeJS16 和 TypeScript 的顶级等待
【发布时间】:2022-04-26 06:43:58
【问题描述】:

我正在尝试将我的 NodeJS12 和 TypeScript 应用程序更新到 Node16,如果原因是需要使用顶级等待。

更新后代码编译正确,但是Jest不接受具体的top-level-await代码:

ts-jest[ts-compiler] (WARN) src/xxx.ts:11:17 - error TS1378: Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', or 'nodenext', and the 'target' option is set to 'es2017' or higher.

11 const project = await client.getProjectId();
                   ~~~~~
 FAIL  src/xxx.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

package.json:

{
    "name": "functions",
    "scripts": {
        "lint": "eslint --ext .js,.ts .",
        "lint:fix": "eslint --ext .js,.ts . --fix",
        "build": "tsc -b",
        "build:watch": "tsc-watch",
        "serve": "...",
        "test": "env-cmd -f .env.json jest --runInBand --verbose"
    },
    "type": "module",
    "engines": {
        "node": "16"
    },
    "main": "lib/index.js",
    "exports": "./lib/index.js",
    "dependencies": {
        "test": "^1.0.0"
    },
    "devDependencies": {
        "@google-cloud/functions-framework": "^2.1.0",
        "@types/busboy": "^1.3.0",
        "@types/compression": "1.7.2",
        "@types/cors": "^2.8.12",
        "@types/express": "^4.17.12",
        "@types/express-serve-static-core": "^4.17.28",
        "@types/google-libphonenumber": "^7.4.23",
        "@types/jest": "^27.4.0",
        "@types/jsonwebtoken": "^8.5.7",
        "@types/luxon": "^2.0.9",
        "@types/node-zendesk": "^2.0.6",
        "@types/sinon": "^10.0.6",
        "@types/supertest": "^2.0.11",
        "@types/swagger-ui-express": "^4.1.3",
        "@types/uuid": "^8.3.4",
        "@types/yamljs": "^0.2.31",
        "@typescript-eslint/eslint-plugin": "^5.9.0",
        "@typescript-eslint/parser": "^5.9.0",
        "env-cmd": "^10.1.0",
        "eslint": "^8.6.0",
        "eslint-config-google": "^0.14.0",
        "eslint-config-prettier": "^8.3.0",
        "eslint-plugin-import": "^2.25.4",
        "eslint-plugin-prettier": "^4.0.0",
        "eslint-plugin-react-hooks": "^4.2.1-beta-a65ceef37-20211130",
        "jest": "^27.4.7",
        "prettier": "^2.5.1",
        "sinon": "^12.0.1",
        "supertest": "^6.2.1",
        "ts-jest": "^27.1.3",
        "ts-node": "^10.4.0",
        "tsc-watch": "^4.6.0",
        "typescript": "^4.5.4"
    },
    "private": true
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "es2022",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": false,
    "strict": true,
    "target": "es2021",
    "moduleResolution": "Node",
    "resolveJsonModule": true
  },
  "compileOnSave": true,
  "include": [
    "src"
  ],
  "ts-node": {
    "moduleTypes": {
      "jest.config.ts": "cjs"
    }
  }
}

jest.config.ts:

export default {
  roots: [
    '<rootDir>/src'
  ],
  setupFiles: ['./src/setupJestEnv.ts'],
  preset: 'ts-jest',
  testEnvironment: 'node',
  testPathIgnorePatterns: ['/node_modules/'],
  coverageDirectory: './coverage',
  coveragePathIgnorePatterns: ['node_modules', 'src/database', 'src/test', 'src/types'],
  globals: { 'ts-jest': { diagnostics: false } },
};

我真的不明白这里出了什么问题。有什么想法吗?

【问题讨论】:

    标签: node.js typescript jestjs top-level-await


    【解决方案1】:

    经过一些研究,我可以肯定地告诉您,我们离可靠的解决方案还很远。主要问题是顶级await可用

    当 'module' 选项设置为 'es2022'、'esnext'、'system' 或 'nodenext',并且 'target' 选项设置为 'es2017' 或更高版本时。

    即使您在tsconfig.json 中将moduletarget 都设置为es2022,您也必须了解并解决很多错误,正如您所经历的那样。我刚刚发现了一个适合我的配置现在,但它可能会带来我不知道的其他问题。


    $ node --version
    v17.7.2
    $ npm list
    myproject@1.0.0 /home/me/folder
    ├── @types/jest@27.4.1
    ├── @types/node@17.0.25
    ├── config@3.3.7
    ├── jest@27.5.1
    ├── sequelize@6.18.0
    ├── timespan@2.3.0
    ├── ts-jest@27.1.4
    ├── ts-node@10.7.0
    ├── ...
    └── typescript@4.6.3
    

    我有其他库,但不必重新调整它们。我使用jest 进行测试,使用sequelize 作为ORM:它们需要适当配置。让我们从package.json开始:

    {
        "type": "module",
        "scripts": {
            "prestart": "npx tsc",
            "start": "NODE_ENV=production node --es-module-specifier-resolution=node out/index.js",
            "test": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js tests/"
        },
        ...
    }
    
    • "type": "module" 解释 here
    • scripts 部分中的start 命令需要选项--es-module-specifier-resolution=node,这是启用 ES 模块而不是默认的 CommonJS 模块所必需的。
    • (仅供开玩笑)--no-warnings 不是强制性的,但 --experimental-vm-modules 是强制性的。

    至于 Jest 配置:

    $ cat jest.config.ts
    import type { Config } from '@jest/types';
    
    const config: Config.InitialOptions = {
        globals: {
            "ts-jest": {
                useESM: true
            }
        },
        preset: 'ts-jest/presets/default-esm',
        roots: ["tests/"],
        modulePathIgnorePatterns: [
            "<rootDir>/node_modules/"
        ]
    };
    
    export default config;
    

    当然,您可以添加新属性,但这或多或少是最低配置。您可以找到其他预设here(以备不时之需),而ts-jestglobals 部分将进一步解释here


    让我们看看tsconfig.json,它有点复杂:

    $ cat tsconfig.json
    {
        "ts-node": {
            "moduleTypes": {
                "jest.config.ts": "cjs"
            }
        },
        "compilerOptions": {
            "lib": ["ES2021"],
            "module": "ESNext",
            "esModuleInterop": true,
            "moduleResolution": "node",
            "target": "ESNext",
            "outDir": "out",
            "resolveJsonModule": true
        },
        "compileOnSave": true,
        "include": ["src/"],
        "exclude": ["node_modules", "**/*.spec.ts"]
    }
    

    再次,它或多或少是最小配置,我猜 moduletarget 属性可以用尝试使用顶级 await 时显示的错误中的其他替代方法进行初始化,但我是不确定他们都在工作。


    现在最糟糕的是,软件包与 ES 模块的兼容性。我还没有研究到足以告诉您何时需要调整某个包中的import,但我会为您提供一些示例。

    内置库/config/timespan

    内置库应该没问题。在我的项目中,我也在使用configtimespan

    • timespanimport * as timespan from "timespan"。例如,Timespan 对象可以使用 new timespan.Timespan(...) 访问。
    • config: import config from 'config'.
    • util:使用内置包,我认为您可以像使用 CommonJS 语法一样导入任何导出的函数。例如,import { format } from 'util'

    sequelize(以及许多其他人......)

    我将sequelize 与打字稿一起使用,但我不得不更改import 语法。我遇到的错误是:

    SyntaxError: 请求的模块“sequelize”未在 ... 处提供名为“DataTypes”的导出

    问题是experimental modules do not support named exports。新的import 语法取决于您需要导入的内容:

    • 对于类型,您仍然可以像以前一样导入它们,例如import { Type1, Type2 } from 'your_library'
    • 对于类(但我认为一切都来自值空间),您首先需要导入一个默认对象,然后使用对象解构来提取所需的值。

    例如,sequelize:

    import Sequelize, 
        { InferAttributes, InferCreationAttributes, CreationOptional }
        from 'sequelize';                         // InferAttributes, InferCreationAttributes, CreationOptional are types 
    const { Op, DataTypes, Model } = Sequelize    // these are classes
    
    class UserModel extends Model<InferAttributes<UserModel>, 
        InferCreationAttributes<UserModel>> 
    {
        declare id: CreationOptional<number>
        ...
    }
    

    其他来源

    【讨论】:

      【解决方案2】:

      最近突然想到,据我所知,CJS不支持顶级await,这意味着你需要使用ts-jest和esm。

      我的 jest.config.json

      {
          "extensionsToTreatAsEsm": [".ts"],
          "globals": {
              "ts-jest": {
                  "useESM": true
              }
          },
          "preset": "ts-jest/presets/default-esm",
          "moduleFileExtensions": ["js", "json", "ts"],
          "rootDir": ".",
          "testEnvironment": "node",
          "testRegex": "spec.ts$",
          "modulePathIgnorePatterns": ["<rootDir>/dist/", "<rootDir>/node_modules/"],
          "moduleNameMapper": {
              "^src/(.*)": "<rootDir>/src/$1"
          },
          "collectCoverage": true,
          "coverageDirectory": "./coverage",
          "collectCoverageFrom": ["src/**/*.(t|j)s"],
          "coveragePathIgnorePatterns": [
              ".module.ts$",
              ".spec.ts$",
              "src/database/",
              "src/server.ts"
          ],
          "verbose": true
      }
      

      您还需要使用 --experimental-vm-modules 选项运行 jest

      node --experimental-vm-modules -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand --config jest.overall.json
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-13
        • 2021-09-21
        • 2022-09-28
        • 1970-01-01
        • 2021-03-26
        • 2021-03-09
        • 2023-03-05
        相关资源
        最近更新 更多