我使用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编译器处理注释的方式并不理想…特别是对转换和打印(我更喜欢巴贝尔如何处理评论,但也有改进,也可以在那里……)。

09-10 05:13
查看更多