Radu Szasz
Radu Szasz

Reputation: 1021

Add comment before function using TypeScript Compiler API

I have a TypeScript file which I want transpiled to JavaScript. As part of this translation, I want to add a comment before every function and I was hoping to do this using the TypeScript Compiler API.

I tried two different approaches. One of them was to grab the SourceFile and change its statements, like this:

const program = ts.createProgram([args.input], {});
const srcFile = find(program.getSourceFiles(), (sourceFile) => !sourceFile.isDeclarationFile);
srcFile.statements = ts.createNodeArray(srcFile.statements.map((statement) => {
    if (!ts.isFunctionDeclaration(statement)) {
        return statement;
    }
    return ts.addSyntheticLeadingComment(
        statement,
        ts.SyntaxKind.MultiLineCommentTrivia,
        "My long desired comment",
        true,
    );
}));

which gives me the following error:

TypeError: Cannot read property 'emitNode' of undefined
at getOrCreateEmitNode (/Users/.../node_modules/typescript/lib/typescript.js:52792:19)
at getOrCreateEmitNode (/Users/.../node_modules/typescript/lib/typescript.js:52801:17)
at setSyntheticLeadingComments (/Users/.../node_modules/typescript/lib/typescript.js:52918:9)
at Object.addSyntheticLeadingComment (/Users/.../node_modules/typescript/lib/typescript.js:52923:16)
at /Users/.../dist/index.js:26:15
at Array.map (<anonymous>)
at Object.<anonymous> (/Users/.../dist/index.js:21:60)
at Module._compile (internal/modules/cjs/loader.js:654:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:665:10)
at Module.load (internal/modules/cjs/loader.js:566:32)

I tried printing the statement right before the ts.addSyntheticLeadingComment and the statement is a FunctionDeclaration, as expected, albeit missing the emitNode field, which I would expect to be created by the getOrCreateEmitNode function.

The second approach I tried is similar, but it runs into the same issue; rather than overwriting the original srcFile.statement, I'm working with a printer, as follows:

const printer = ts.createPrinter(undefined, {
    substituteNode: (hint, node) => {
        if (ts.isFunctionDeclaration(node)) {
            return ts.addSyntheticLeadingComment(
                node,
                ts.SyntaxKind.MultiLineCommentTrivia,
                "My long desired comment",
                true,
           );
        }
    },
});

console.log(printer.printFile(srcFile));

which gives the same error as the previous code.

The TypeScript file I am trying to change is very simple:

function myFunc(a: number, b: number): number {
    return a + b;
}

Upvotes: 4

Views: 2735

Answers (2)

PMCS
PMCS

Reputation: 21

David Sherret's answer is correct, but I kept running into Cannot read property 'emitNode' of undefined at getOrCreateEmitNode errors, no matter what I tried.

As it turns out I was missing the fourth parameter in ts.createSourceFile called setParentNodes. By setting this parameter to true, I was able to use addSyntheticLeadingComment.

Basically this parameter (setParentNodes) sets each Node's parent property.

getOrCreateEmitNode needs to traverse up the tree and cannot do so without parent references. For more detail on setParentNodes, check this Github issue

Upvotes: 1

David Sherret
David Sherret

Reputation: 106620

Substituting the node is not necessary. Remember that comments aren't part of the AST, so don't add them to the array of statements in the place of the existing function declaration. Instead, just call addSyntheticLeadingComment on the node without using the return value.

For example, the following code works fine:

import * as ts from "typescript";

const file = ts.createSourceFile("test.ts", `function myFunc(a: number, b: number): number {
    return a + b;
}`, ts.ScriptTarget.Latest, true);
const functionDec = file.statements.find(ts.isFunctionDeclaration)!;

ts.addSyntheticLeadingComment(functionDec, ts.SyntaxKind.MultiLineCommentTrivia,
    "My long desired comment", true);

const printer = ts.createPrinter({ removeComments: false });
console.log(printer.printFile(file));

Outputs:

/*My long desired comment*/
function myFunc(a: number, b: number): number {
    return a + b;
}

Upvotes: 8

Related Questions