or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

builders.mdcloning.mdcomments.mdconstants.mdconverters.mdindex.mdmodifications.mdreact.mdretrievers.mdtraversal.mdvalidators.md
tile.json

comments.mddocs/

Comments

Utilities for managing comments on AST nodes. Comments in JavaScript/TypeScript can be leading, trailing, or inner comments, and these functions help manipulate and inherit comment attachments during AST transformations.

Capabilities

Adding Comments

Add Comment

Adds a single comment to a node in the specified position.

/**
 * Add a comment to an AST node
 * @param node - Node to add comment to
 * @param type - Comment position type
 * @param content - Comment text content
 * @param line - Whether this is a line comment (default: true)
 * @returns The node with added comment
 */
function addComment<T extends t.Node>(
  node: T,
  type: "leading" | "inner" | "trailing",
  content: string,
  line?: boolean
): T;

Add Comments

Adds multiple comments to a node in the specified position.

/**
 * Add multiple comments to an AST node
 * @param node - Node to add comments to
 * @param type - Comment position type
 * @param comments - Array of comment objects
 * @returns The node with added comments
 */
function addComments<T extends t.Node>(
  node: T,
  type: "leading" | "inner" | "trailing",
  comments: Array<t.Comment>
): T;

Comment Inheritance

Inherit Leading Comments

Transfers leading comments from a parent node to a child node.

/**
 * Inherit leading comments from parent to child node
 * @param child - Node to receive comments
 * @param parent - Node to copy comments from
 */
function inheritLeadingComments<T extends t.Node>(
  child: T,
  parent: t.Node
): void;

Inherit Trailing Comments

Transfers trailing comments from a parent node to a child node.

/**
 * Inherit trailing comments from parent to child node
 * @param child - Node to receive comments
 * @param parent - Node to copy comments from
 */
function inheritTrailingComments<T extends t.Node>(
  child: T,
  parent: t.Node
): void;

Inherit Inner Comments

Transfers inner comments from a parent node to a child node.

/**
 * Inherit inner comments from parent to child node
 * @param child - Node to receive comments
 * @param parent - Node to copy comments from
 */
function inheritInnerComments<T extends t.Node>(
  child: T,
  parent: t.Node
): void;

Inherits Comments

Transfers all comments (leading, trailing, and inner) from parent to child.

/**
 * Inherit all comments from parent to child node
 * @param child - Node to receive comments
 * @param parent - Node to copy comments from
 */
function inheritsComments<T extends t.Node>(
  child: T,
  parent: t.Node
): void;

Comment Removal

Remove Comments

Removes all comments from a node.

/**
 * Remove all comments from an AST node
 * @param node - Node to remove comments from
 * @returns The node with comments removed
 */
function removeComments<T extends t.Node>(node: T): T;

Comment Types

Comment Object Structure

/**
 * Comment object structure
 */
interface Comment {
  type: "CommentBlock" | "CommentLine";
  value: string;
  start?: number;
  end?: number;
  loc?: {
    start: { line: number; column: number };
    end: { line: number; column: number };
  };
}

Comment Position Types

  • Leading Comments: Comments that appear before the node
  • Trailing Comments: Comments that appear after the node
  • Inner Comments: Comments that appear inside the node (for compound statements)

Usage Examples

Adding Comments

import * as t from "@babel/types";

// Create a function declaration
const funcDecl = t.functionDeclaration(
  t.identifier("myFunction"),
  [],
  t.blockStatement([])
);

// Add leading comment (appears before function)
t.addComment(funcDecl, "leading", " This is an important function", true);

// Add trailing comment (appears after function)
t.addComment(funcDecl, "trailing", " End of function", true);

// Add block comment
t.addComment(funcDecl, "leading", "* This is a block comment *", false);

console.log(funcDecl.leadingComments);
// [
//   { type: "CommentLine", value: " This is an important function" },
//   { type: "CommentBlock", value: "* This is a block comment *" }
// ]

Adding Multiple Comments

const comments = [
  { type: "CommentLine", value: " First comment" },
  { type: "CommentLine", value: " Second comment" },
  { type: "CommentBlock", value: "* Block comment *" }
] as t.Comment[];

const variable = t.variableDeclaration("const", [
  t.variableDeclarator(t.identifier("x"), t.numericLiteral(42))
]);

t.addComments(variable, "leading", comments);

console.log(variable.leadingComments?.length); // 3

Comment Inheritance During Transformation

// Original node with comments
const original = t.identifier("oldName");
t.addComment(original, "leading", " Original variable");
t.addComment(original, "trailing", " End of original");

// New node to replace it
const replacement = t.identifier("newName");

// Inherit all comments
t.inheritsComments(replacement, original);

console.log(replacement.leadingComments); // Comments from original
console.log(replacement.trailingComments); // Comments from original

Selective Comment Inheritance

const sourceNode = t.binaryExpression(
  "+",
  t.identifier("a"),
  t.identifier("b")
);

t.addComment(sourceNode, "leading", " Start of expression");
t.addComment(sourceNode, "trailing", " End of expression");
t.addComment(sourceNode, "inner", " Inside expression");

const targetNode = t.callExpression(
  t.identifier("add"),
  [t.identifier("a"), t.identifier("b")]
);

// Inherit only leading comments
t.inheritLeadingComments(targetNode, sourceNode);
console.log(targetNode.leadingComments); // Leading comments only
console.log(targetNode.trailingComments); // undefined

// Inherit trailing comments separately
t.inheritTrailingComments(targetNode, sourceNode);
console.log(targetNode.trailingComments); // Trailing comments now present

Block Statement Inner Comments

const blockStmt = t.blockStatement([
  t.expressionStatement(t.identifier("first")),
  t.expressionStatement(t.identifier("second"))
]);

// Inner comments appear inside the block but not attached to specific statements
t.addComment(blockStmt, "inner", " This comment is inside the block");

console.log(blockStmt.innerComments);
// [{ type: "CommentLine", value: " This comment is inside the block" }]

// When transforming, inner comments can be inherited
const newBlock = t.blockStatement([
  t.expressionStatement(t.identifier("transformed"))
]);

t.inheritInnerComments(newBlock, blockStmt);
console.log(newBlock.innerComments); // Inherited inner comments

Documentation Generation

// Add JSDoc-style comments
function addJSDocComment(node: t.Node, description: string, params?: string[], returns?: string) {
  let jsdoc = `*\n * ${description}\n`;
  
  if (params) {
    params.forEach(param => {
      jsdoc += ` * @param ${param}\n`;
    });
  }
  
  if (returns) {
    jsdoc += ` * @returns ${returns}\n`;
  }
  
  jsdoc += " ";
  
  t.addComment(node, "leading", jsdoc, false);
}

const myFunction = t.functionDeclaration(
  t.identifier("calculateSum"),
  [t.identifier("a"), t.identifier("b")],
  t.blockStatement([
    t.returnStatement(
      t.binaryExpression("+", t.identifier("a"), t.identifier("b"))
    )
  ])
);

addJSDocComment(
  myFunction,
  "Calculates the sum of two numbers",
  ["a - First number", "b - Second number"],
  "The sum of a and b"
);

Comment Preservation During AST Transformations

import { traverse } from "@babel/types";

// Transform AST while preserving comments
traverse(ast, {
  BinaryExpression(path) {
    // Transform binary expression to function call
    const callExpr = t.callExpression(
      t.identifier("operate"),
      [
        t.stringLiteral(path.node.operator),
        path.node.left,
        path.node.right
      ]
    );
    
    // Preserve all comments from original node
    t.inheritsComments(callExpr, path.node);
    
    path.replaceWith(callExpr);
  }
});

Comment Cleanup

// Remove all comments from a node tree
function stripComments(node: t.Node): void {
  t.traverse(node, {
    enter(path) {
      t.removeComments(path.node);
    }
  });
}

// Usage
const nodeWithComments = /* node with many comments */;
stripComments(nodeWithComments);
console.log(nodeWithComments.leadingComments); // undefined

Conditional Comment Handling

// Add comments based on conditions
function addConditionalComments(node: t.Node, debug: boolean, minify: boolean) {
  if (debug) {
    t.addComment(node, "leading", " DEBUG: Generated node", true);
  }
  
  if (!minify) {
    t.addComment(node, "trailing", " End of generated code", true);
  }
}

// Copy comments selectively
function copyRelevantComments(target: t.Node, source: t.Node, includeInner: boolean) {
  t.inheritLeadingComments(target, source);
  t.inheritTrailingComments(target, source);
  
  if (includeInner && t.isBlockStatement(target) && t.isBlockStatement(source)) {
    t.inheritInnerComments(target, source);
  }
}

Template Generation with Comments

// Generate code templates with embedded comments
function createFunctionTemplate(name: string, withComments: boolean = true) {
  const func = t.functionDeclaration(
    t.identifier(name),
    [t.identifier("args")],
    t.blockStatement([
      t.expressionStatement(t.identifier("TODO"))
    ])
  );
  
  if (withComments) {
    t.addComment(func, "leading", ` TODO: Implement ${name} function`, true);
    t.addComment(func.body, "inner", " Function implementation goes here", true);
    t.addComment(func, "trailing", ` End of ${name}`, true);
  }
  
  return func;
}

const template = createFunctionTemplate("processData");

Comment-Based Code Analysis

// Analyze comments for specific patterns
function findTodoComments(node: t.Node): string[] {
  const todos: string[] = [];
  
  t.traverse(node, {
    enter(path) {
      const allComments = [
        ...(path.node.leadingComments || []),
        ...(path.node.trailingComments || []),
        ...(path.node.innerComments || [])
      ];
      
      allComments.forEach(comment => {
        if (comment.value.includes("TODO") || comment.value.includes("FIXME")) {
          todos.push(comment.value.trim());
        }
      });
    }
  });
  
  return todos;
}

// Find all documentation comments
function extractDocComments(node: t.Node): Array<{ node: t.Node; comment: string }> {
  const docs: Array<{ node: t.Node; comment: string }> = [];
  
  t.traverse(node, {
    enter(path) {
      if (path.node.leadingComments) {
        path.node.leadingComments.forEach(comment => {
          if (comment.type === "CommentBlock" && comment.value.startsWith("*")) {
            docs.push({ node: path.node, comment: comment.value });
          }
        });
      }
    }
  });
  
  return docs;
}