'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;
}


Solution 1:[1]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 PMCS