17

I would like to extract the comments from a typescript source file, preferably with their line numbers. I tried doing it like this:

var program = ts.createProgram(files, {
    target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS, removeComments: false
});
ts.forEachChild(sourceFile, visit);

function visit(node) {
    if (node.kind == ts.SyntaxKind.SingleLineCommentTrivia){
        //print something
    }
    ts.forEachChild(node, visit);
}

In fact, when I printed all the nodes' text, I could see that the comments were discarded entirely. The input source code I used for testing is:

//test comment
declare namespace myLib {
    //another comment
    function makeGreeting(s: string): string;
    let numberOfGreetings: number;
}
Rabee
  • 637
  • 5
  • 19

2 Answers2

16

It's not possible to get comments as nodes but it is still possible to get comments from the source file. The function to use is getLeadingCommentRanges(text: string, pos: number).

I used it like this:

for(var sourceFile of program.getSourceFiles()){
        ts.forEachChild(sourceFile, visit);
    }

function visit(node: ts.Node){
    const commentRanges = ts.getLeadingCommentRanges(
        sourceFile.getFullText(), 
        node.getFullStart());
    if (commentRange?.length)
        const commentStrings:string[] = 
          commentRanges.map(r=>sourceFile.getFullText().slice(r.pos,r.end))
}

NOTE: sourceFile.getFullText() does not skip leading comments, and sourceFile.getText() does skip leading comments. Using .getText in the above usage case is apparently a common source of errors.

Craig Hicks
  • 2,199
  • 20
  • 35
Rabee
  • 637
  • 5
  • 19
  • Note: there is a library that helps you to get the comments easier -- `tsutils.forEachComment()` (just like `ts.getLeadingCommentRanges()`) \ see https://github.com/Microsoft/TypeScript/issues/21049 => https://github.com/ajafff/tsutils – Nor.Z Jul 13 '23 at 12:42
12

In case you use jsDoc-style comments, e.g.

export const myConst = {
  /**
   * My property description
   */
  myProp: 'test prop',
};

it's possible to retrieve a comment content during the tree traverse.

For example extractWithComment will return

{
  name: 'myProp',
  comment: 'My property description',
  type: 'string'
}

import * as ts from 'typescript';

export function extractWithComment(fileNames: string[], options: ts.CompilerOptions): void {
  const program = ts.createProgram(fileNames, options);
  const checker: ts.TypeChecker = program.getTypeChecker();

  for (const sourceFile of program.getSourceFiles()) {
    if (!sourceFile.isDeclarationFile) {
      ts.forEachChild(sourceFile, visit);
    }
  }

  function visit(node: ts.Node) {
    const count = node.getChildCount()

    if (count > 0) {
      ts.forEachChild(node, visit);
    }

    if (ts.isPropertyAssignment(node) && node.name) {
      const symbol = checker.getSymbolAtLocation(node.name);
      if (symbol) {
        return serializeSymbol(symbol)
      }
    }
  }

  function serializeSymbol(symbol: ts.Symbol) {
    return {
      name: symbol.getName(),
      comment: ts.displayPartsToString(symbol.getDocumentationComment(checker)),
      type: checker.typeToString(
        checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!)
      ),
    };
  }
}
Dima
  • 1,455
  • 1
  • 10
  • 6
  • This one doesn't work with a classes. It works with your example but a single class with comments doesn't work. Any idea why? – Antonio Sep 07 '22 at 13:41
  • Found that by removing `if (ts.isPropertyAssignment(node) && node.name)` this works! – Antonio Sep 07 '22 at 13:44