资讯动态

Vitest 配置项 injectCjsGlobals 深度解析:控制 CommonJS 全局变量的注入与模块类型检测

发布时间:2026/9/14 18:31:40 来源:尧图企业网站定制
Vitest 配置项 injectCjsGlobals 深度解析控制 CommonJS 全局变量的注入与模块类型检测【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitestVitest 默认会为每一个经它转换transform的模块注入module、exports、require、__filename、__dirname这些 CommonJS 全局变量即便你的测试文件完全使用 ESM 语法也能直接访问它们。injectCjsGlobals配置项就是用来控制这一行为的开关关闭它可以让测试环境的模块语义更贴近真实运行时浏览器不支持 CommonJS 变量Node.js 的 ES 模块中同样不暴露它们同时开启基于 Node.js 规则的模块类型自动检测。读完本文你将掌握该配置的完整语义、Node 式模块类型检测的三级规则、CLI 用法以及它在源码层面的实现原理与报错处理机制。配置速览injectCjsGlobals是一个布尔类型的测试配置项归属test命名空间类型boolean默认值trueCLI 开关--no-inject-cjs-globals、--injectCjsGlobalsfalse在 packages/vitest/src/defaults.ts 中configDefaults将默认值固定为true其类型声明位于 packages/vitest/src/node/types/config.ts并在 packages/vitest/src/node/config/serializeConfig.ts 中被序列化到最终的解析配置中。默认行为是Vitest 转换的每个文件都能访问这些 CommonJS 变量即使文件本身是用 ESM 语法编写的。这样做虽然方便但不符合真实世界中模块的运行规律——浏览器不提供 CommonJS 变量Node.js 也不会在 ES 模块中暴露它们。如果希望模块环境更严格、更贴近目标运行时可以关闭这一行为import { defineConfig } from vitest/config export default defineConfig({ test: { injectCjsGlobals: false, }, })CLI 等价写法npx vitest run --no-inject-cjs-globals # 或 npx vitest run --injectCjsGlobalsfalse该 CLI 开关定义在 packages/vitest/src/node/cli/cli-config.ts其说明文本为Inject CommonJS variables (module,exports,require,__filename,__dirname) into every test module. To disable, use--no-inject-cjs-globals(default:true)。关闭后的模块类型检测规则当该选项被禁用时只有被检测为 CommonJS 的模块才会获得这些变量。CommonJS 模块必须始终保留它们因为这些变量属于模块作用域本身没有它们模块根本无法求值。模块类型的判定方式与 Node.js 完全一致按以下优先级逐级判定文件扩展名.cjs、.cts文件始终视为 CommonJS.mjs、.mts文件始终视为 ES 模块。最近的package.json中的type字段module表示 ES 模块commonjs表示 CommonJS。与 Node.js 相同查找在第一个package.json处停止且绝不会跨越node_modules边界因此依赖不会继承你项目的type。文件中是否存在 ESM 语法如果文件没有静态import/export声明也不引用import.meta则视为 CommonJS。注释和字符串内部的语法不影响检测动态import()在 CommonJS 模块中是允许的因此不计入 ESM 语法仅类型的 TypeScript 导入type-only imports在转换阶段被擦除同样不计入。需要特别强调的是语法检测始终开启。Vitest 不遵循 Node.js 中那些会修改模块类型解析的 CLI 标志例如--no-experimental-detect-module、--input-type它只作用于 Node.js 的字符串输入以及 Node.js 23 中已移除的--experimental-default-type标志。源码级实现原理模块类型检测的纯函数实现模块类型检测的核心实现位于 packages/vitest/src/node/resolver.ts 的detectModuleType函数它完整镜像了上述 Node.js 检测算法先用cleanUrl清理文件路径再按扩展名直接判定.cjs/.cts返回cjs.mjs/.mts返回esm通过lookupPackageScopeType读取目录所在 package 作用域的type字段该查找同样在node_modules边界前停止如果以上都无法判定则基于转换后的代码检查 ESM 语法标记。其中第三级检测有两个关键实现细节值得注意优先检查转换后的代码SSR 转换总是会把静态import/export和import.meta重写为__vite_ssr_形式的内部 helper见ESM_SYNTAX_MARKERSpackages/vitest/src/node/resolver.ts。检查转换后的产物能反映编译输出type-only 导入此时已被擦除且不包含标记的模块不可能是 ES 模块。标记命中后用词法分析器复核因为转换会保留注释和字符串而这些位置可能恰好提到这些标记所以一旦命中标记且代码中同时出现CJS_GLOBALS_REFERENCE_RE匹配module|exports|require|__filename|__dirname词就会用es-module-lexer读取原始源码重新解析确认词法分析器无法解析 TypeScript 类型或非 JS 源码时则信任标记结果。运行时注入逻辑在运行时侧模块求值发生在 packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts。injectCjsGlobals的最终判定是const injectCjsGlobals this.options.injectCjsGlobals ! false // the module type is provided by the server only when injectCjsGlobals is disabled. // CommonJS modules always receive these variables because they are part // of the module scope, without them the module cannot be evaluated at all || (module.meta as VitestFetchResult | undefined)?.moduleType cjs即配置未关闭时一律注入配置关闭时只有服务端标记为cjs的模块即被detectModuleType判定为 CommonJS 的模块才注入。注入时通过_createCJSGlobals生成__filename、__dirname、module、exports、require五个变量并作为模块包装函数的参数传入执行上下文。注意moduleType只有在injectCjsGlobals关闭时才会由服务端提供。检测逻辑位于 packages/vitest/src/node/environments/fetchModule.ts 的cachedModuleType由于模块类型是模块的纯函数Vitest 会在this.detectModuleType即config.injectCjsGlobals false见该文件第 43 行为真时最多检测一次并将结论缓存在 transform result 上后续重复 fetch、磁盘缓存cached以及fetchWarmModules快照都会复用该结论而不会重复检测。文件系统缓存与配置联动模块类型检测的结果会影响编译产物因此injectCjsGlobals也被纳入 Vitest 文件系统模块缓存的哈希键。在 packages/vitest/src/node/cache/fsModuleCache.ts 中injectCjsGlobal: vitestConfig.injectCjsGlobals与其他配置项root、base、mode、plugins 等一起参与环境哈希的JSON.stringify。这意味着切换该选项会导致缓存键变化避免复用旧产物造成语义不一致。运行时配置的传递在 Worker 侧模块运行器通过 packages/vitest/src/runtime/moduleRunner/startVitestModuleRunner.ts 暴露get injectCjsGlobals() { return state().config.injectCjsGlobals }将解析后的配置同步到运行环境类型声明位于 packages/vitest/src/runtime/config.ts。在 ES 模块中引用 CommonJS 变量的报错与修复提示关闭该选项后在 ES 模块中引用 CommonJS 变量会抛出ReferenceError与 Vitest 外部原生 Node.js 环境的行为一致ReferenceError: __dirname is not defined __dirname is a CommonJS variable that is not available in ES modules, and injectCjsGlobals is disabled. If this module is meant to be an ES module, use import.meta.dirname instead of __dirname. If it is meant to be a CommonJS module, use the .cjs file extension, set type: commonjs in the nearest package.json, or externalize it with server.deps.external.这条增强后的错误信息并非原生错误而是 Vitest 的二次加工。其实现位于 packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts正则CJS_GLOBALS_REFERENCE_ERROR_RE /^(module|exports|require|__filename|__dirname) is not defined$/精确匹配原生ReferenceError消息命中后依据ESM_HINTS表见第 627-633 行给出针对性的 ESM 替换建议module/exports用export声明替代require用import声明或createRequire(import.meta.url)__filename用import.meta.filename__dirname用import.meta.dirname若模块本意是 CommonJS则提示三种修复路径改用.cjs扩展名、在最近的package.json中设置type: commonjs、或通过server.deps.external将其外部化该函数还会同步替换错误对象的stack中对应的消息文本并保证增强只执行一次消息已锚定不会被二次加工。运行时在模块求值抛错时只有当injectCjsGlobals关闭!injectCjsGlobals才调用enhanceMissingCjsGlobalsError对错误做增强见 packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts。使用场景与注意事项何时应该关闭你的测试目标是浏览器或 Node.js 原生 ESM 运行时希望尽早暴露ES 模块错误引用 CommonJS 变量这类问题你希望测试环境的模块语义与线上运行时一致避免测试通过、发布后却在真实运行时抛ReferenceError的假通过你正在做模块系统迁移从 CommonJS 迁移到 ESM希望借助编译错误清单逐文件修复。注意事项官方警告该选项不影响外部化externalized模块外部化模块始终由原生运行时执行Node.js 会自行向外部化的 CommonJS 模块提供 CommonJS 变量与injectCjsGlobals无关。内联inlined的 CommonJS 模块即使启用该选项也不会经过 Vite 插件处理require调用总是离开模块运行器因此 mocking 等特性对它们不生效。相关替代配置方案参见 CommonJS 源码并未被完整支持 一节对应文档 docs/guide/common-errors.md。与模块类型检测相关的典型修复路径当开启严格模式后出现ReferenceError: __dirname is not defined这类错误时按错误提示可归纳为三条解决路线确认模块本意是 ES 模块改用 ESM 等价语法——import.meta.dirname/import.meta.filename、import/export声明、createRequire(import.meta.url)确认模块本意是 CommonJS将文件重命名为.cjs/.cts或在最近的package.json中显式声明type: commonjs依赖代码无法修改通过server.deps.external将该依赖外部化交由原生运行时按 Node.js 规则处理。小结injectCjsGlobals表面上是要不要注入 CommonJS 变量的开关实质上它控制着一整套与 Node.js 对齐的模块类型判定体系关闭后扩展名 → 最近package.json的type→ ESM 语法检测这三条规则依次生效配合服务端单次检测 缓存复用、文件系统缓存键联动、运行时错误提示增强让测试环境能够真实反映目标运行时的模块语义。无论你是在做 ESM 迁移、编写跨运行时兼容的测试还是排查__dirname is not defined类错误理解该配置的完整语义与底层实现都能让你更精准地定位和解决问题。【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价