Insert a comment prior to a function with the TypeScript Compiler API

I have a TypeScript file that needs to be transpiled into JavaScript. As part of this conversion process, I want to include a comment before every function using the TypeScript Compiler API.

I experimented with two different methods. One involved accessing the SourceFile and modifying its statements like so:

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 desired comment",
        true,
    );
}));

This approach resulted in 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)

Prior to the ts.addSyntheticLeadingComment, when I printed the statement, it was indeed a

FunctionDeclaration</code as expected, but lacked the <code>emitNode
field that should have been created by the getOrCreateEmitNode.

The second method I attempted followed a similar path, encountering the same issue. Rather than replacing the original srcFile.statement, I used a printer instead:

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

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

However, this also produced the same error as seen in previous code attempts.

The TypeScript file I am attempting to modify is quite simple:

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

Answer №1

Remember, when working with the AST, it's important to understand that comments are not part of it. Therefore, when modifying nodes, avoid substituting comments and instead utilize addSyntheticLeadingComment directly on the node without worrying about the return value.

To illustrate this concept, consider the code snippet below:

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));

When executed, the output will be as follows:

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

Answer №2

Sarah Johnson's response is accurate, however, I encountered a recurring issue of

TypeError: Cannot read property 'emitNode' of undefined when attempting to use getOrCreateEmitNode
, despite various attempted solutions.

It was eventually discovered that the absence of the fourth parameter in ts.createSourceFile named setParentNodes was causing the problem. By enabling this parameter as true, the utilization of addSyntheticLeadingComment became possible.

Essentially, the setParentNodes parameter assigns each Node's parent attribute.

getOrCreateEmitNode requires access to parent references within the tree structure. For more insights on the significance of setParentNodes, refer to this pertinent Github discussion

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

MongoDB TypeScript issue: "The type 'ObjectId' cannot be assigned to type 'never'."

One of my GraphQL resolvers includes a function that adds an Artist ID to a user's Liked Artists Object ID array. The code for this function is provided below: async likeArtist(_parent, args, _context, _info) { await User.findOneAndUpdate( ...

There was an issue thrown during the afterAll function: Unable to access properties of undefined

Recently, I upgraded my Angular project from version 15 to 15.1 and encountered an error while running tests. To replicate the issue, I created a new Angular 15.1 project using the CLI and generated a service with similar semantics to the one causing probl ...

Is emitting a side effect event acceptable within an RxJS pipe?

Currently, I am utilizing RxJS within the context of an Angular application. Within my service, there is functionality to reinitialize the entire application with different settings as needed. @Injectable() class BootstrapService{ public initApplicatio ...

Find a specific string within an array where both the keys and values are subject to change

Below is an array that I have: var Array = [{id:100,name:'N1',state:'delhi',country:'india',status:'active'}, {id:101,name:'N2',state:'kenya',country:'africa',status:'suspended&a ...

Utilize the composition API for importing modules

Let's say I have a file named index.ts import { defineComponent, onMounted } from '@nuxtjs/composition-api' const Todo = defineComponent({ setup() { onMounted(() => { test() }) function test(){ return ' ...

Detonating the second-level element in a PHP/Typescript array

My PHP code is currently formatting an array for me with the following structure: $data = $dataQuery->fetchAll(PDO::FETCH_ASSOC); if(count($data)){ $data_arr=array(); $data_arr["records"]=array(); $data_arr["records"] = ...

The type 'typeof globalThis' does not have an index signature, therefore the element is implicitly of type 'any'. Error code: ts(7017) in TypeScript

I'm encountering an issue with my input handleChange function. Specifically, I am receiving the following error message: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.ts(7017) when att ...

Using Angular with Google Maps: Learn how to retrieve a list of markers from a map and implement onClick events for each one

I am currently using the AGM angular module for Angular 2+ to integrate the Google Map API. In my project, I have a directive that displays waypoints as markers on the map using the Google Map Directions Service. Now, I am looking for a way to handle the ...

Retrieve and showcase information from Firebase using Angular

I need to showcase some data stored in firebase on my HTML page with a specific database structure. I want to present the years as a clickable array and upon selecting a year, retrieve the corresponding records in my code. Although I managed to display a ...

Error: Typescript error at line 18 in app.ts - Cannot locate the 'server' namespace

Check out this straightforward code snippet: "use strict"; import * as express from "express"; class Server { public app: express.Application; public static start(): Server { return new Server(); } constructor() { this. ...

Broaden material-ui component functionality with forwardRef and typescript

To enhance a material-ui component with typescript, I have the javascript code provided in this link. import Button from "@material-ui/core/Button"; const RegularButton = React.forwardRef((props, ref) => { return ( <B ...

An Axios error message indicates ERR_NETWORK and ERR_EMPTY_RESPONSE

When I initiate a Patch Request from the frontend, it takes approximately 30-40 seconds for the backend to resolve. const handleSendClick = (data: any) => { const requiredLanguages = Array.isArray(data.required_languages) ? data.required_langu ...

Is there a way to determine the height of the ion-footer?

Is there a way to obtain the height of the ion-footer element in Ionic2? I want to calculate the initial window height minus the ion-footer height, but I am currently only able to get the overall window height. I'm not interested in retrieving the ...

Ensure that TypeScript compiled files are set to read-only mode

There is a suggestion on GitHub to implement a feature in tsc that would mark compiled files as readonly. However, it has been deemed not feasible and will not be pursued. As someone who tends to accidentally modify compiled files instead of the source fil ...

Retrieve today's bookings using a Firebase query and store them in an array

I am attempting to retrieve all bookings from firebase that match today's date. Using an *ngFor loop in my HTML, I am displaying all returned orders. In the firebase database, there are 7 saved bookings, with 2 of them being for today's date. T ...

Is the blur effect on the navbar not visible when there is an animated element positioned behind it?

My brain is tapped out at 2 am, so I'm reaching out to see if anyone has a solution to this issue on my hands. While using TypeScript, Framer Motion, and Tailwind to create my personal portfolio, I've encountered a problem with the fixed bottom n ...

Using Typescript in React to render font colors with specific styling

Attempting to utilize a variable to set the font color within a react component, encountering an error with my <span>: Type '{ style: "color:yellow"; }' is not assignable to type 'HTMLProps<HTMLSpanElement>' The use of yel ...

What is the best method for showcasing information within an Angular Material table?

When using Angular material to display a list, I am facing an issue where the content of the list is not being displayed but the header is. Component: export class CompaniesComponent implements OnInit { displayedColumns: string[] = ['id']; ...

This error message in AngularJS indicates that the argument 'fn' is not being recognized as a function

I am currently working with angularjs and typescript and I am attempting to create a directive in the following manner: Below is my controller : export const test1 = { template: require('./app.html'), controller($scope, $http) { ...

How can one effectively utilize TypeScript with native JavaScript methods and calls?

After a long break from TypeScript, I am struggling to override implementations or use native methods in the .ts file. The ones highlighted in red, excluding this, are causing errors. https://i.sstatic.net/WhcRM.png I'm not getting autocomplete sup ...