如何使用ts-morph克隆/分离TypeScript AST节点且不影响源文件?
在ts-morph中跨Project克隆并修改AST节点的解决方案
完全可行,你可以通过创建节点独立副本的方式,在不改动源Project节点的前提下,将修改后的节点添加到目标Project中。核心思路是脱离源节点的上下文,操作独立的克隆副本,具体实现有两种常用方式:
方式1:基于节点文本重新生成(适合简单场景)
提取源节点的文本内容,修改后在目标Project中重新解析生成节点,完全隔离源节点和目标节点的上下文:
import { Project } from "ts-morph"; // 初始化两个独立Project实例 const fooProject = new Project(); const barProject = new Project(); // 创建源文件foo.ts const fooSourceFile = fooProject.createSourceFile("foo.ts", ` export const config = { id: 1, name: "foo" }; `); // 创建目标空文件bar.ts const barSourceFile = barProject.createSourceFile("bar.ts", ""); // 获取源文件中的id属性节点 const idProperty = fooSourceFile.getVariableDeclaration("config") ?.getInitializer() ?.getProperty("id"); if (idProperty) { // 获取id属性的原始文本 const idText = idProperty.getText(); // 将id值加1,替换文本中的数字 const updatedIdText = idText.replace(/\d+/, match => String(Number(match) + 1)); // 在目标文件中添加包含更新后id的对象 barSourceFile.addVariableDeclaration({ name: "config", initializer: `{ ${updatedIdText}, name: "bar" }` }); } // 保存两个Project,源文件foo.ts不会被修改 fooProject.saveSync(); barProject.saveSync();
方式2:使用copy()方法克隆节点(适合复杂节点)
利用ts-morph提供的copy()方法直接克隆节点,将副本关联到目标Project的节点上下文,修改副本不会影响源节点:
import { Project } from "ts-morph"; const fooProject = new Project(); const barProject = new Project(); const fooSourceFile = fooProject.createSourceFile("foo.ts", ` export const config = { id: 1, name: "foo" }; `); const barSourceFile = barProject.createSourceFile("bar.ts", ""); const idProperty = fooSourceFile.getVariableDeclaration("config") ?.getInitializer() ?.getProperty("id"); if (idProperty) { // 在目标文件中创建空对象变量 const barConfigObj = barSourceFile.addVariableDeclaration({ name: "config", initializer: "{}" }).getInitializer(); // 克隆源id属性节点到目标对象中,生成独立副本 const clonedIdProp = idProperty.copy(barConfigObj); // 修改副本的id值(此操作仅影响目标文件,与源文件无关) const currentId = Number(clonedIdProp.getInitializer()?.getLiteralValue()); clonedIdProp.getInitializer()?.setLiteralValue(currentId + 1); // 可按需添加其他属性 barConfigObj?.addProperty({ name: "name", initializer: `"bar"` }); } fooProject.saveSync(); barProject.saveSync();
关键注意事项
- 禁止直接修改源节点后添加到目标Project:节点属于源Project的上下文,修改会直接同步到源文件
forget()/forgetDescendants()是移除节点的方法,会导致源节点失效,不适合克隆场景- 两种方式都能保证源Project的节点不受影响,最终执行
saveSync()时,源文件foo.ts保持原始状态,目标文件bar.ts包含更新后的id值
内容的提问来源于stack exchange,提问作者Jon Sud
相关产品推荐
相关产品推荐

