news 2026/9/12 4:49:03

Nx 23.2 配置迁移:将 Vite/Vitest 配置文件中的 `__dirname` 替换为 `import.meta.dirname`

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nx 23.2 配置迁移:将 Vite/Vitest 配置文件中的 `__dirname` 替换为 `import.meta.dirname`

Nx 23.2 配置迁移:将 Vite/Vitest 配置文件中的__dirname替换为import.meta.dirname

【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx

本文围绕 Nx 仓库中@nx/vitest插件在 23.2.0 引入的自动迁移(update-23-2-0-use-import-meta-dirname)展开,讲解它为什么存在、覆盖哪些文件、底层如何借助 TypeScript AST 安全改写代码,以及哪些场景会被刻意跳过。读完本文,你将理解 ViteconfigLoader: 'native'对 ESM 语法的新要求,掌握__dirnameimport.meta.dirname的迁移边界,并能判断自己工作区中的配置文件是否还需要手工处理。

迁移背景:Vite 原生配置加载器不认__dirname

Vite 计划在未来主版本中把configLoader: 'native'设为默认的配置加载方式。在这种原生加载模式下,Vite 使用 Node.js 自身的 ESM 机制去加载配置文件,而__dirname是 CommonJS 的运行时全局变量——在 ESM 模块中它并不存在。因此当配置里用到__dirname时,Vite 8 会给出类似如下的警告:

(!) Your Vite config uses features that are unsupported by `configLoader: 'native'`, which is planned to become the default in a future major version of Vite: - `__dirname` (packages/utils/vitest.config.mts:4:9). Use `import.meta.dirname` instead

也就是说:虽然当前默认的configLoader: 'bundle'还能兼容__dirname,但配置一旦迁移到原生加载模式就会失败。Node.js 20.11+ 提供了与__dirname语义等价的 ESM 替代品import.meta.dirname,这就是本次迁移要做的替换。

迁移覆盖范围:只处理 ESM 专属扩展名的配置文件

这条迁移会扫描工作区中所有vite.config.mtsvite.config.mjsvitest.config.mtsvitest.config.mjs文件,把其中的__dirname替换为import.meta.dirname

官方文档给出的替换前后示例:

// Before export default defineConfig(() => ({ root: __dirname, }));
// After export default defineConfig(() => ({ root: import.meta.dirname, }));

实现中对文件匹配的正则定义在 use-import-meta-dirname.ts:

// Only ESM-only extensions. A `.ts`/`.js` config can still be loaded as CJS, // where `import.meta` is a syntax error. const CONFIG_FILE_PATTERN = /(^|\/)(vite|vitest)\.config\.(mts|mjs)$/;

注意两个关键设计:

  • 只匹配.mts/.mjs:只有这两个扩展名能保证文件以 ESM 方式加载,import.meta语法必然合法;
  • 路径匹配不限层级(^|\/)前缀允许packages/utils/vitest.config.mtsapps/web/vite.config.mjs这类子目录中的配置被命中,根目录配置同样覆盖。

迁移通过@nx/devkitvisitNotIgnoredFiles(tree, '.', ...)遍历整个工作区树(尊重.gitignore等忽略规则),并对每个命中的文件先做快速字符串判断(original?.includes('__dirname')),只有确实包含该标识符时才进入 AST 改写流程;全部处理完成后调用formatFiles(tree)统一格式化,保证改动后的文件风格与工作区一致。

如何触发这条迁移

该迁移已注册在@nx/vitest的 migrations.json 中:

"update-23-2-0-use-import-meta-dirname": { "version": "23.2.0-beta.6", "description": "Replace `__dirname` with `import.meta.dirname` in `vite.config.mts`/`vitest.config.mts` files so they work with Vite's `configLoader: 'native'`.", "implementation": "./dist/src/migrations/update-23-2-0/use-import-meta-dirname", "documentation": "./dist/src/migrations/update-23-2-0/use-import-meta-dirname.md" }

它属于标准的 Nx 自动迁移(migration generator),当你把工作区的@nx/vitest从旧版本升级到 23.2.0 时,会随nx migrate流程生成对应的迁移文件并被自动执行。运行迁移后,控制台会输出类似信息:Replaced \__dirname` with `import.meta.dirname` in N Vite config file(s).(来自 [use-import-meta-dirname.ts](https://link.gitcode.com/i/128ef2522e2313fa05da700314db97d7) 的logger.info`),其中 N 是被实际改写的配置文件数量;若一个文件都没改动,则不会输出任何日志。

源码级原理:AST 分类与三种处理策略

迁移的核心是 rewriteDirname。它不是粗暴的字符串替换,而是先用 TypeScript 编译器把文件内容解析成 AST,再遍历每一个__dirname标识符并为其分类,根据分类决定动作:

  • bail(整体放弃):只要文件里出现一个bail类型的__dirname,整个文件都不做任何改写;
  • reference(改写):确认是引用 CommonJS 全局__dirname,收集起来统一替换;
  • skip(跳过单个):该标识符只是某个声明里的名字位,不是真正的引用,仅跳过它本身。

分类逻辑在 classify 函数 中实现,同时配套 bindsDirname 函数 判断一个声明是否把新的__dirname引入了作用域(即发生了遮蔽,此时__dirname不再是 CJS 全局变量):

  • 出现在变量声明、函数参数、解构绑定元素、函数声明、类声明、import/export 子句的名字位时 → 判定为遮蔽声明,bail整个文件;
  • 出现在简写属性赋值{ __dirname })中 →bail,因为此时 key 和 value 是同一个 token,改写会破坏语义;
  • 出现在父节点的name/propertyName槽位 →skip。如源码注释所述,这个“反向”的名字位测试是刻意设计的:凡是没被显式枚举到的节点类型(类字段、访问器、枚举成员等),默认都进不了reference,从而不可能被改写成非法语法;
  • 其余情况 →reference,即真正的全局__dirname引用。

改写时按 AST 节点位置从后往前逐个替换文本(避免位置偏移相互影响),每个引用点被精确替换为import.meta.dirname。另外,如果文件本身存在 TypeScript解析错误parseDiagnostics非空),迁移会直接原样返回、绝不盲目改写——源码注释明确说明:一个 TypeScript 都只能错误恢复解析的配置,其 AST 表达的含义不可信,应当留给人工处理。

什么不会被重写:五类边界情况

这是本次迁移设计上最值得注意的部分,也是避免迁移引入新 bug 的关键。

1..ts.js配置文件一律不动

vite.config.tsvitest.config.js这类文件依然可以被当作 CommonJS 加载(Vite 默认的 bundle 模式可以处理),而在 CJS 中import.meta是语法错误。所以迁移对它们保持原样。相关断言见 use-import-meta-dirname.spec.ts。

2. 自行声明了__dirname的配置

如果配置里自己定义了__dirname(通常是const __dirname = path.dirname(fileURLToPath(import.meta.url))这个惯用写法),那么它已经与import.meta语义等价,在原生配置加载器下也能正常工作,迁移不会画蛇添足。对应的测试用例如 use-import-meta-dirname.spec.ts。

3. 解构重命名、import 别名、re-export

以下场景中__dirname都不是全局引用,迁移全部跳过:

// 解构重命名:只改后面的真实引用,不解构键名 const { __dirname: dir } = someOptions; export default { root: __dirname, dir }; // import 别名:整体不动 import { __dirname as dir } from './paths.mjs'; export default { root: dir }; // 再导出:整体不动 export { __dirname } from './paths.mjs';

这三个用例分别对应 use-import-meta-dirname.spec.ts 与第 L171-L178 行的测试。

4. 类成员、访问器、枚举成员

即使类字段、getter、枚举成员恰好也叫__dirname,它们也不会被误改,而同一文件里真正的全局引用仍会正常替换——这得益于classify中“名字位默认排除”的反向测试设计,具体见测试 use-import-meta-dirname.spec.ts。

5. 无法解析的配置文件

语法不完整的配置(例如缺少闭合括号)会被整体跳过,等待人工修复,见 use-import-meta-dirname.spec.ts。

此外,非配置类文件(如src/other.mts)不在匹配范围内,天然不会被触碰(见 use-import-meta-dirname.spec.ts);迁移也是幂等的——对已改写过的文件再次运行不会产生任何变化(见 use-import-meta-dirname.spec.ts)。

未被迁移的.ts配置:仍有一个 CJS/ESM 警告需要你处理

迁移不会把现有配置重命名成.mts,因为其他工具链可能通过路径引用这些配置文件,擅自改名会破坏引用关系。因此,一个.ts配置会继续保留“ESM 语法出现在 CommonJS 加载文件里”的伴随警告。要消除它,有两种方式(按 use-import-meta-dirname.md 的说明):

  1. 手动把配置重命名为.mtsvite.config.tsvite.config.mts),同时记得同步更新任何按路径引用它的工具配置;
  2. 在最近的package.json中设置"type": "module",让该目录下的文件默认按 ESM 解析。

新生成的配置不受影响——@nx/vitest的 configuration 生成器 在生成根级聚合配置时明确选择.mts扩展名,源码注释指出:.mts保证无论根package.jsontype字段是什么都按 ESM 加载,而 CommonJS 加载的配置会触发 ViteconfigLoader: 'native'警告。

一个特殊的例外:@nx/nuxt在 eslintrc 工作区中的.ts回退

有一个自动生成的场景确实会把import.meta.dirname放进.ts配置:@nx/nuxt的 Vitest 配置生成器在仍使用旧版 eslintrc(.eslintrc.json)的工作区中会回退到vitest.config.ts,原因是旧版@nuxt/eslint-config(约~0.5.6)的解析器配置不认识.mts文件。相关逻辑见 packages/nuxt/src/generators/application/lib/add-vitest.ts:

// Only the legacy @nuxt/eslint-config (~0.5.6) leaves .mts out of its // parser configuration, so fall back to .ts just for it. A .ts config in // a CommonJS package trips Vite's `configLoader: 'native'` warning. useEsmExtension: options.linter !== 'eslint' || useFlatConfig(tree),

这种情况下生成的vitest.config.ts今天还能通过 Vite 默认配置加载器(bundle 模式)正常工作——它自己的import语句本来就依赖该模式。但该文件在configLoader: 'native'下是不可加载的。换句话说,如果你正处于这个例外场景,升级 Vite 主版本前需要先迁移到 ESLint flat config,或按上一节的方式处理该.ts配置。

结语

use-import-meta-dirname是一条小而精的 Nx 自动迁移:它精准锁定.mts/.mjs配置、以 AST 级粒度区分“真正的__dirname引用”与“同名但无关的标识符”,并主动放弃所有存在歧义或解析异常的文件。理解它的边界设计,不仅能帮你顺利度过 Vite 原生配置加载器的过渡期,也能为你在自己工具链中编写“安全优先”的代码改写迁移提供一个可参考的范本。

如果想深入验证或学习,可以继续阅读仓库内的三份关键文件:迁移说明 use-import-meta-dirname.md、迁移实现 use-import-meta-dirname.ts、以及覆盖全部边界场景的测试 use-import-meta-dirname.spec.ts。

【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 4:48:20

DDoS实时检测实战:随机森林+孤立森林工业级部署

简介:本资源是一套基于Python实现的DDoS网络入侵检测完整实践方案,面向网络安全初学者、机器学习入门者及高校课程设计学生,聚焦于利用逻辑回归等经典算法构建可运行的流量异常识别模型。压缩包共5个文件(3个Python源码、1份Markd…

作者头像 李华
网站建设 2026/9/12 4:47:54

Qt 5.14.2 aarch64静态交叉编译完整指南:从sysroot到可执行文件

接手的项目是个典型的嵌入式活儿:手里一块aarch64开发板,要在上面跑一个带界面的Qt程序,但开发机和构建机都是x86_64的服务器。刚开始走的是动态编译,生成的可执行文件倒是能跑,可一到目标板上就发现牵一发动全身——Q…

作者头像 李华
网站建设 2026/9/12 4:47:39

SRS 怎么配置 logrotate 自动轮转日志文件

SRS 怎么配置 logrotate 自动轮转日志文件 【免费下载链接】srs SRS is a simple, high-performance, AI-driven real-time media server supporting RTMP, WebRTC, HLS, HTTP-FLV, HTTP-TS, SRT, MPEG-DASH, and GB28181, with codec support for H.264, H.265, AV1, VP9, AAC…

作者头像 李华
网站建设 2026/9/12 4:47:15

开源提示词模板库实战:从结构化设计到跨模型复用

1. 从到处CtrlC到自建提示词库:我为什么要做这个开源项目 先交代下背景。过去一年里,我几乎每天都在和提示词打交道。无论是日常的内容创作、代码调试,还是团队内部的项目协作,提示词都成了绕不开的入口。但真正让我暴躁到想骂人的…

作者头像 李华
网站建设 2026/9/12 4:46:46

RetroArch 缩略图不显示?沿 3 条故障线一次修好

RetroArch 缩略图不显示?沿 3 条故障线一次修好 【免费下载链接】RetroArch Cross-platform, sophisticated frontend for the libretro API. Licensed GPLv3. 项目地址: https://gitcode.com/GitHub_Trending/re/RetroArch 你打开 RetroArch,ROM…

作者头像 李华