Reputation: 1021
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
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
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