我使用typescript编译器api(ts.transform,ts.updateFunctionDeclaration)在现有源文件中的函数开头插入其他语句。这很好,只是当我打印转换后的代码(使用ts.printer)时,原始源代码中的第一条注释会在我注入的语句上方发出。我怎样才能防止这种情况发生?
我已经尝试了各种方法来构造由转换器返回的新节点,但是没有改变输出。这是我的完整代码,我正在使用node执行它。
import * as ts from 'typescript';
const sourceLines = [
"// The list of properties exposed by this script to the user",
"var properties = {'speed': 20};",
"",
"function tick(deltaTime)",
"{",
" var currentDeltaTime = deltaTime;",
"}"
];
var sourceCode = sourceLines.join('\n');
const sourceFile = ts.createSourceFile("originalSource.js", sourceCode, ts.ScriptTarget.Latest, true);
const additionalSource: ts.SourceFile = ts.createSourceFile(
'ownerCheck.js', "var variableToAdd = 5;", ts.ScriptTarget.ES5, false, ts.ScriptKind.JS
);
const transformer = <T extends ts.Node>(context: ts.TransformationContext) =>
(rootNode: T) => {
function visit(node: ts.Node): ts.Node {
if (ts.isFunctionDeclaration(node)) {
var functionDeclaration = <ts.FunctionDeclaration>node;
var functionName: ts.Identifier = functionDeclaration.name;
var updatedFunction = ts.updateFunctionDeclaration(
functionDeclaration,
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*asteriskToken*/ functionDeclaration.asteriskToken,
/*functionName*/ functionName,
/*typeParameters*/ undefined,
/*parameters*/ functionDeclaration.parameters,
/*returnType*/ undefined,
/*body*/ ts.createBlock(ts.createNodeArray([].concat(additionalSource.statements, functionDeclaration.body.statements), false))
);
return updatedFunction;
}
return ts.visitEachChild(node, visit, context);
}
return ts.visitNode(rootNode, visit);
};
const result = ts.transform(
sourceFile, [transformer]
);
const transformedNodes = result.transformed[0];
const printer: ts.Printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed,
removeComments: false
});
console.log(printer.printNode(ts.EmitHint.SourceFile, transformedNodes, sourceFile));
输出:
// The list of properties exposed by this script to the user
var properties = { 'speed': 20 };
function tick(deltaTime) {
// The list of properties exposed by this script to the user
var variableToAdd = 5;
var currentDeltaTime = deltaTime;
}
我希望输出包含附加语句,而不在它们前面添加重复的注释。
更新:
添加此函数:
stripRanges(additionalSource);
function stripRanges(node: ts.Node) {
node.pos = -1;
node.end = -1;
ts.forEachChild(node, stripRanges);
}
剥离
additionalSource
节点中的节点范围在技术上是可行的,但会在这些节点之后删除输出中的所有换行符,从而产生以下输出:// The list of properties exposed by this script to the user
var properties = { 'speed': 20 };
function tick(deltaTime) { var variableToAdd = 5; var currentDeltaTime = deltaTime; }
更新2:
将ts.createBlock()调用更改为将多行设置为true将修复输出:
ts.createBlock(
ts.createNodeArray(
[].concat(
additionalSource.statements,
functionDeclaration.body.statements),
false), /*has trailing comma*/
true) /*multiLine*/
最佳答案
这是因为当打印机打印additionalSource.statements
节点时,它使用sourceFile
节点的文本根据additionalSource
中节点的位置获取注释。
您可以通过运行以下代码看到这一点:
console.log(ts.createPrinter().printNode(
ts.EmitHint.Unspecified,
additionalSource.statements[0],
sourceFile // it's getting the comments from this sourceFile.text
));
输出:
// The list of properties exposed by this script to the user
var variableToAdd = 5;
解决方案
解决方法是在
additionalSource
中使用节点之前,先从中移除节点的位置:stripRanges(additionalSource);
function stripRanges(node: ts.Node) {
node.pos = -1;
node.end = -1;
ts.forEachChild(node, stripRanges);
}
我对
sourceFile
和-1
使用pos
,因为当使用工厂函数生成节点时,编译器就是这样做的。例如:const binaryExpr = ts.createBinary(1, "+", 2);
binaryExpr.pos; // -1
binaryExpr.end; // -1
旁注:在我看来,typescript编译器处理注释的方式并不理想…特别是对转换和打印(我更喜欢巴贝尔如何处理评论,但也有改进,也可以在那里……)。