🌐 Correct TypeScript Specifiers
将导入说明符从旧的 tsc(TypeScript 的编译器)要求使用 .js 文件扩展名来导入实际上是 TypeScript 的文件,转换为新的格式;修复后的说明符允许源代码被像 Node.js 这样的标准兼容软件运行。这是一次性操作,更新后的源代码应该提交到版本控制(例如 git);之后,源代码的导入语句应该按照 ECMAScript(JavaScript)标准来编写。
🌐 Transforms import specifiers from the old tsc (TypeScript's compiler) requirement of using .js file extensions in source-code to import files that are actually TypeScript; the corrected specifiers enable source-code to be runnable by standards-compliant software like Node.js. This is a one-and-done process, and the updated source-code should be committed to your version control (eg git); thereafter, source-code import statements should be authored compliant with the ECMAScript (JavaScript) standard.
支持的情况:
🌐 Supported cases:
- 没有文件扩展名 →
.cts、.mts、.js、.ts、.d.cts、.d.mts或.d.ts .cjs→.cts,.mjs→.mts,.js→.ts.js→.d.cts、.d.mts或.d.ts- Package.json 子路径导入
- tsconfig 路径(通过
@nodejs-loaders/alias)- 为了随后通过 node 运行代码,你需要把这个(或其他)加载器添加到你自己的项目中。或者,切换到 subimports 。
- 类似 Commonjs 的目录指示符
🌐 Usage
[!小心] 这会更改你的源代码。在运行此包之前,请提交任何未保存的更改。
[!重要]
--experimental-import-meta-resolve必须启用;这个功能并不是真的实验性——它之所以非标准,是因为与浏览器无关。
使用这个 codemod 运行:
🌐 Run this codemod with:
NODE_OPTIONS="--experimental-import-meta-resolve" \
npx codemod @nodejs/correct-ts-specifiers🌐 Monorepos
为了获得最佳效果,请在 monorepo 的每个工作区 内部 运行此操作。
🌐 For best results, run this within each workspace of the monorepo.
project-root/
├ workspaces/
├ foo/ ←--------- RUN HERE
├ …
├ package.json
└ tsconfig.json
└ bar/ ←--------- RUN HERE
├ …
├ package.json
└ tsconfig.json
└ utils/ ←--------- RUN HERE
├ qux.js
└ zed.js🌐 Examples
import { URL } from 'node:url';
import { bar } from '@dep/bar';
import { foo } from 'foo';
-import { Bird } from './Bird';
+import { Bird } from './Bird/index.ts';
import { Cat } from './Cat.ts';
-import { Dog } from '…/Dog/index.mjs';
+import { Dog } from '…/Dog/index.mts';
import { baseUrl } from '#config.js';
-import { qux } from './qux.js';
+import { qux } from './qux.js/index.ts';
-export { Zed } from './zed';
+export type { Zed } from './zed.d.ts';
-const nil = await import('./nil.js');
+const nil = await import('./nil.ts');[!提示] 使用
tsc进行编译的用户需要启用rewriteRelativeImportExtensions;仅使用tsc进行类型检查(例如通过像npm run test:types这样的 lint/测试步骤)则需要allowImportingTsExtensions(以及一些额外的编译选项——详见引用文档);
🌐 Notes
这个包并不是盲目地在指定符中查找并替换文件扩展名:它会确认替换后的指定符实际上存在;在有歧义的情况下(比如在同一位置有两个同名但关联文件扩展名不同的文件,比如 /tmp/foo.js 和 /tmp/foo.ts),它会记录一个错误,跳过该指定符,然后继续处理。
🌐 This package does not just blindly find & replace file extensions within specifiers: It confirms that the replacement specifier actually exists; in ambiguous cases (such as two files with the same basename in the same location but different relevant file extensions like /tmp/foo.js and /tmp/foo.ts), it logs an error, skips that specifier, and continues processing.
[!小心] 这个包不能确认导入的模块是否包含所需的导出项。其实这通常不会造成问题,因为模糊的情况会被跳过(所以如果有问题,那在迁移开始之前就已经存在了)。迁移完成后只要运行你的源代码,就能确认一切是否正常(如果有问题,node 会报错,并指出问题所在)。
[!提示] Node.js 要求类型导入中必须存在
type关键字。对于自己的代码,这个包通常会处理这个问题。不过,在某些情况下以及对于 node 模块,它不会处理。已经有现成的强大工具可以自动修复,比如
use-import-type通过 biometypescript/no-import-type-side-effects通过 oxlintconsistent-type-imports通过 typescript-lint如果你的源代码需要这个,先运行这个 codemod,然后再使用其中一个修复工具。
🌐 Limitations
当在同一路径下既存在 .js 文件又存在对应的 .ts 文件时,codemod 无法确定说明符指的是哪一个。此时它会记录一个错误,保持说明符不变,并继续处理文件的其他部分。
🌐 When both a .js file and a corresponding .ts file exist at the same path, the codemod cannot determine which one the specifier refers to. In that case it logs an error, leaves the specifier unchanged, and continues processing the rest of the file.