动态导入读取错误文件路径问题排查及替代方案咨询
问题分析与解决方案
原因:模块缓存导致的读取错误
这肯定是Node.js的模块缓存机制搞的鬼。Node.js(包括ts-node这类工具)会缓存已经加载过的模块,当你后续再次导入相同模块标识符的文件时,直接返回缓存内容,不会重新读取磁盘文件。
你遇到的情况是:第一次导入./translations/nl/[file].ts后,模块被缓存;后续处理其他语言时,虽然打印的路径还是nl目录,但可能因为文件名完全相同导致模块标识符冲突,或是ts-node的编译缓存路径重叠,Node.js直接返回了之前生成的fr版本缓存内容。而readFileSync直接读取磁盘文件,不受模块缓存影响,所以能拿到正确的nl内容。
解决方案
1. 手动清除模块缓存
每次动态导入nl目录文件前,手动删除对应模块的缓存记录,强制Node.js重新读取文件。
示例代码:
import { resolve } from 'path'; import { fileURLToPath } from 'url'; // 生成模块绝对路径,避免相对路径解析歧义 const getModuleAbsolutePath = (fileName: string) => { return resolve(fileURLToPath(import.meta.url), `../translations/nl/${fileName}.ts`); }; // 清除缓存并导入文件 async function loadNlTranslations(fileName: string) { const modulePath = getModuleAbsolutePath(fileName); // 删除CommonJS缓存(ts-node可能混用模块系统) delete require.cache[modulePath]; // 用绝对URL导入,避免相对路径解析问题 const moduleUrl = new URL(`file://${modulePath}`); const translations = await import(moduleUrl.href); return translations.default; // 假设你的导出是default }
2. 用TypeScript编译器API解析文件(无需动态导入)
如果不想依赖动态导入,可以直接用TypeScript的编译器API读取并解析TS文件,提取导出的翻译对象,同时保留类型安全。
示例代码:
import * as ts from 'typescript'; import { readFileSync } from 'fs'; import { resolve } from 'path'; // 定义翻译类型,保持类型安全 type Translations = Record<string, string>; function parseTranslationFile(filePath: string): Translations { const fullPath = resolve(filePath); const fileContent = readFileSync(fullPath, 'utf8'); // 创建TS源文件对象 const sourceFile = ts.createSourceFile( fullPath, fileContent, ts.ScriptTarget.ESNext, true ); let translations: Translations = {}; // 遍历AST找到导出的翻译变量(假设是export const translations = { ... }) ts.forEachChild(sourceFile, (node) => { if (ts.isVariableStatement(node) && node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) { const declaration = node.declarationList.declarations[0]; if (declaration.name.getText() === 'translations' && ts.isObjectLiteralExpression(declaration.initializer)) { translations = parseObjectLiteral(declaration.initializer); } } }); return translations; } // 辅助函数:解析TS对象字面量AST为JS对象 function parseObjectLiteral(node: ts.ObjectLiteralExpression): Translations { const result: Translations = {}; node.properties.forEach((prop) => { if (ts.isPropertyAssignment(prop)) { const key = prop.name.getText().replace(/['"]/g, ''); if (ts.isStringLiteral(prop.initializer)) { result[key] = prop.initializer.text; } } }); return result; } // 使用示例 const nlTranslations = parseTranslationFile('./translations/nl/home.ts');
这个方案直接操作TS抽象语法树,完全避开模块缓存问题,同时通过类型定义保留类型安全,不用转JSON。
3. 临时规避方案(不推荐)
如果上面的方案暂时不好实现,可以尝试在每次处理不同语言前重启进程——但这会降低工具效率,仅适合临时测试。
内容的提问来源于stack exchange,提问作者Joris
相关产品推荐
相关产品推荐

