TS Jest不接受NodeJS 16和TypeScript的顶级等待

p4tfgftt  于 2023-02-21  发布在  Node.js
关注(0)|答案(2)|浏览(213)

我正在尝试更新我的NodeJS 12 & TypeScript应用程序到Node16,原因之一是需要使用顶级等待。
代码在更新后可以正确编译,但是Jest不接受特定的顶层等待代码:

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

我真的不明白这是怎么回事你知道吗?

anauzrmj

anauzrmj1#

经过一番研究,我可以肯定地告诉你,我们离可靠的解决方案还很远,主要问题是顶级的await是可用的
当"模块"选项设置为"es2022"、"esnext"、"system"或"nodenext",并且"目标"选项设置为"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模块所必需的。

至于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(如果您需要它们),而globals部分将进一步解释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属性可以用错误中显示的其他选项初始化,但我不确定它们是否都能正常工作。
现在到了最糟糕的部分,包与ES模块的兼容性。我还没有足够的研究来告诉你什么时候某个包中的import需要修改,但是我会提供一些例子。

内置库/config/timespan

内置库应该可以,在我的项目中我也使用了configtimespan

  • timespanimport * as timespan from "timespan"。例如,可以使用new timespan.Timespan(...)访问Timespan对象。
  • 一米二十九分一秒一米三十分一秒。
  • util:通过内置的包,我认为你可以导入任何导出的函数,就像你使用CommonJS语法一样。

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

我正在使用sequelize和typescript,但我必须更改import语法。我遇到的错误是:
语法错误:请求的模块'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>
    ...
}

其他来源

bvuwiixz

bvuwiixz2#

最近我突然想到,据我所知,CJS不支持顶层等待,这意味着您需要将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

相关问题