我正在更新转换中的源文件 -
export function transformBefore(
absTsconfigPath: string
): ts.TransformerFactory<ts.SourceFile> | ts.CustomTransformerFactory {
return (ctx) => (sf) => {
const add:ts.Statement[]=[];
if (...){
add.push(...createSomeSyntheticStatements(ctx.factory))
}
if (!add.length) return sf;
const flags:ts.NodeFlags = sf.flags;
const sf1 = ctx.factory.updateSourceFile(
sf,ctx.factory.createNodeArray([...add,...sf.statements]));
ts.setSourceMapRange(sf1,
ts.getSourceMapRange(sf)); // no effect on pos,end of added nodes
const sf3 = visitorWalk(sf1,ctx); // no effect on pos,end of added nodes
return sf3;
}
它看起来非常像 Typescript 的 @weswigham here 2017 年的建议。
在调试器中查看,我可以看到开头的新节点
sf3
都具有 pos
和 end
成员等于 -1
。
我想这不一定是问题,但后来,在处理同一文件的 after 转换中,对
node.getText()
的调用会导致 Typescript 中出现异常。调试异常我可以看到 pos
和 end
成员仍然是 -1
,并且在这样的语句上会发生异常。
在没有前变换的情况下运行后变换时,没有异常。
我有预感应该有一种方法可以更新
pos
和 end
成员,但我不知道。这三个人都没有做到:
ctx.factory.createNodeArray
ts.setSourceMapRange
(希望有副作用)ts.forEachChild
也没有(副作用)。pos
和end
成员如何更新?
(同时,避免在稍后的 after 转换中崩溃的解决方法是访问
node.text
而不是 node.getText()
,因为 node.text
具有正确的本地值,而 node.getText()
似乎依赖于 pos
和 end
访问源文件全文,这是不同步的。)
更新:
我通过更新源文件尝试了不同的方法 使用不同的函数
ts.updateSourceFile
,而不是
ctx.factory.updateSourceFile
-
const oldText = sf.getFullText();
const newText = addedText + oldText;
// The function checkChangeRange in typescript.js code-documents the specs for TextChangeRange and newText
const textChangeRange:ts.TextChangeRange = {newLength:addedText.length,span:{start:0,length:0}};
const updatedSf = ts.updateSourceFile(sf,newText,textChangeRange,true);
return updatedSf;
调试器显示
pos
和 end
已正确设置。
然而,在“成功”之后,打字稿继续对生成的源文件执行它的 own before 转换,但在尝试访问符号数据时崩溃了。也许这是因为新的源文件没有经过类型检查,所以没有符号数据。
我在向源文件添加新节点时遇到类似的问题:如果新语句包含对类的引用(在原始源文件中导入,但仅用作类型),则转换后的源文件将返回与转换前相同的文本字符串。并且打字稿看不到导入上的引用并将其标记为未使用(因为它仅在源文件文本字符串中搜索https://github.com/microsoft/TypeScript/blob/main/src/services/findAllReferences.ts #L1799):
源文件:
import { Foo } from './foo;
class Bar {
constructor(foo: Foo) {}
}
如果我尝试添加:
console.log(Foo)
转换后,导入未使用 -> 任何树摇晃都会将其删除 ->
console.log(Foo)
破坏程序(即使我在 Foo 上使用了标识符符号)