我有一个使用 TypeScript、WebPack 和 ts-jest 的 monorepo。它构建正确,但单元测试在其中一个子项目上失败
./demo
,原因是:
Cannot find module '@mlhaufe/graphics/adapters' or its corresponding type declarations.
{
"compilerOptions": {
"baseUrl": "./packages",
"paths": {
"@mlhaufe/graphics/*": [
"lib/src/*"
]
}
},
"references": [
{
"path": "./packages/lib"
},
{
"path": "./packages/demo"
}
],
"files": [],
"exclude": [
"node_modules"
]
}
{
...
"references": [
{
"path": "../lib"
}
]
import fs from 'fs';
import path from 'path';
import url from 'url';
import { pathsToModuleNameMapper } from 'ts-jest';
import tsconfig from './tsconfig.json' assert { type: 'json' };
const { compilerOptions } = tsconfig,
__filename = url.fileURLToPath(import.meta.url),
__dirname = path.dirname(__filename),
packageNames = fs.readdirSync(path.resolve(__dirname, './packages'));
/** @type {import('jest').Config} */
export default {
rootDir: compilerOptions.baseUrl,
verbose: true,
testPathIgnorePatterns: [
'<rootDir>/node_modules/',
],
reporters: [
'default',
['jest-junit', { outputDirectory: './coverage' }]
],
// <https://jestjs.io/docs/next/configuration#projects-arraystring--projectconfig>
projects: packageNames.map((name) => ({
displayName: name,
transform: {
'^.+\\.mts$': ['ts-jest', { useESM: true }]
},
moduleFileExtensions: ['js', 'mjs', 'mts'],
roots: [`<rootDir>`],
modulePaths: [compilerOptions.baseUrl],
// required due to ts-jest limitation
// <https://kulshekhar.github.io/ts-jest/docs/guides/esm-support/#support-mts-extension>
resolver: '<rootDir>/mjs-resolver.ts',
// Used the path aliases in tsconfig.json
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>',
useESM: true
}),
// moduleNameMapper: {
// '@mlhaufe/graphics/(.+)': '<rootDir>/packages/lib/src',
// '^(\\.\\.?/.*)\\.mjs$': ['$1.mts', '$0']
// },
testMatch: [`<rootDir>/packages/${name}/**/*.test.mts`],
testPathIgnorePatterns: [`<rootDir>/packages/${name}/dist/`]
}))
};
{
...
"workspaces": [
"packages/*"
],
"engines": {
"node": ">=16.0.0"
},
"scripts": {
"build": "npm run build --workspaces",
"build:lib": "npm run build --workspace=packages/lib",
"build:demo": "npm run build --workspace=packages/demo",
"test": "jest --coverage",
"test:lib": "jest --selectProjects=lib --coverage",
"test:demo": "jest --selectProjects=demo --coverage",
"serve:demo": "npm run serve --workspace=packages/demo"
},
...
}
我不明白为什么 ts-jest 找不到模块,而 webpack+typescript 没有问题。我还没有弄清楚
projects
属性之外的设置与内部设置之间的关系。我的假设是这些将是全球性的并适用于每个项目,但我怀疑这不是真的。
如有任何反馈,我们将不胜感激。我还没有看到一篇关于如何实现这一点的一致(现代)文章。
据我了解这个问题:jest 正在解析 monorepo 中的本地模块,而无需事先转译它们。我将这个答案中的解决方案视为一种解决方法,而不是最终的事情。
我提出的解决方案基本上是将导入路径转换为不被视为外部包,而是作为路径导入。
我的
jest.config.ts
文件:
const config: Config = {
preset: 'ts-jest/presets/js-with-ts-esm',
resolver: '<rootDir>/jest.resolve.cjs',
// the rest of the config is omitted, the above two lines are the important part
}
export default config
jest.resolve.cjs
文件:
const fs = require('node:fs')
const path = require('node:path')
const resolver = require('ts-jest-resolver')
const localModules = new Map(
fs.readdirSync(path.join(__dirname, 'packages'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => {
const directory = path.join(__dirname, 'packages', dirent.name)
const pkg = require(path.join(directory, 'package.json'))
const main = path.join(directory, pkg.main)
return [pkg.name, main]
}),
)
/**
* @param {string} path
* @param {import('jest-resolve').ResolverOptions} options
* @returns {string}
*/
module.exports = function resolve (path, options) {
return resolver(localModules.get(path) ?? path, options)
}
您可能已经知道了,我的例子中的所有本地模块都存储在
packages
目录中 - 因此,如果您的情况不同,您可能需要相应地调整它。
亲爱的读者:如果您遇到了这个问题,我可以放心地假设您已经足够先进来理解代码,所以我将省略对其的解释:)