频道
bg

Vitest无法加载Internal Packages

coding十二月 22, 20221mins
Frontend Vitest

所谓的Internal Packages是指一个没有包含tsconfig.json 的Typescript Packages,并且它的main 字段指向的是未编译的源码文件。

在Vite项目中,因为它是支持ESM模块的,所以把main 指向编译后的cjs代码,把module执行未编译的esm代码。这样在Vite是没有问题的,但是vitest无法通过module来加载模块代码。

查找原因H2

调试代码resolve.ts#resolvePackageEntry 会发现,

  1. 从根据config 定义的mainFields 去加载代码,否则会从main 字段加载

    jsx

    if (!entryPoint || entryPoint.endsWith('.mjs')) {
    for (const field of options.mainFields) {
    if (field === 'browser') continue // already checked above
    if (typeof data[field] === 'string') {
    entryPoint = data[field]
    break
    }
    }
    }
    entryPoint ||= data.main
  2. config的逻辑为,如果config配置中的resolve 属性为空,则会配置默认值。默认是的Main Fields中包含module,所以在vite中可以加载module定义的代码。

    jsx

    const DEFAULT_MAIN_FIELDS = [
    'module',
    'jsnext:main',
    'jsnext'
    ];
    const resolveOptions = {
    mainFields: config.resolve?.mainFields ?? DEFAULT_MAIN_FIELDS,
    browserField: config.resolve?.browserField ?? true,
    conditions: config.resolve?.conditions ?? [],
    extensions: config.resolve?.extensions ?? DEFAULT_EXTENSIONS$1,
    dedupe: config.resolve?.dedupe ?? [],
    preserveSymlinks: config.resolve?.preserveSymlinks ?? false,
    alias: resolvedAlias
    };
  3. vitest plugin会把mainFields设置为空,也就最终导致了无法加载module定义的代码

    jsx

    const config: ViteConfig = {
    esbuild: {
    sourcemap: 'external',
    // Enables using ignore hint for coverage providers with @preserve keyword
    legalComments: 'inline',
    },
    resolve: {
    // by default Vite resolves `module` field, which not always a native ESM module
    // setting this option can bypass that and fallback to cjs version
    mainFields: [],
    alias: preOptions.alias,
    conditions: ['node'],
    // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
    // @ts-ignore we support Vite ^3.0, but browserField is available in Vite ^3.2
    browserField: false,
    },
    server: {
    ...preOptions.api,
    watch: {
    ignored: preOptions.watchExclude,
    },
    open,
    hmr: false,
    preTransformRequests: false,
    },
    }

具体原因

https://github.com/vitest-dev/vitest/issues/1387

解决H2

其实在resolve.ts#resolvePackageEntry 在根据mainFields 加载之前,首先会使用resolve.exports 去加载代码,resolve.exports为多入口的模块提供了统一的方案,这个方案是兼容nodejs中的exports的。

所以最终的解决办法是,在package.json中定义exports

评论


新的评论

匹配您的Gravatar头像

Joen Yu

@2022 JoenYu, all rights reserved. Made with love.