Skip to content

[API] Printer attaches source comments to synthesized nodes because the node encoder sends pos -1 as 0 #64859

Open

Description

@Smrtnyk

🔎 Search Terms

printNode, printer, synthesized node, comments, encodeNode, pos -1, native API, typescript/sync

🕗 Version & Regression Information

  • typescript@7.1.0-dev.20261018.1 (typescript/sync API)
  • This changed between versions 6.0 and 7.1: in 6.0, ts.createPrinter().printFile emits synthesized nodes without source comments.

⏯ Playground Link

N/A (native API, Node.js only)

💻 Code

import { API } from "typescript/sync";
import { ScriptKind, TokenFlags, NodeFlags } from "typescript/ast";
import * as f from "typescript/ast/factory";

const api = new API();
const { sourceFile } = api.createSourceFile("/test.ts", "/** leading comment */\nfoo();\n", { scriptKind: ScriptKind.TS });

const added = f.createExpressionStatement(
    f.createCallExpression(f.createIdentifier("bar"), undefined, undefined, [f.createStringLiteral("x", TokenFlags.None)], NodeFlags.None)
);
const updated = f.updateSourceFile(sourceFile, [added, ...sourceFile.statements], sourceFile.endOfFileToken);

console.log("synthesized pos/end:", added.pos, added.end);
console.log(api.printer.printNode(updated));
api.close();

🙁 Actual behavior

synthesized pos/end: -1 -1
bar(/** leading comment */ "x");
/** leading comment */
foo();
/** leading comment */

🙂 Expected behavior

bar("x");
/** leading comment */
foo();

Synthesized nodes (pos === -1) shouldn't pick up comments from the original source text.

Additional information about the issue

The cause appears to be in dist/api/node/encoder.js, encodeNode:

nodeValues.push(node.kind, node.pos >= 0 ? node.pos : 0, node.end >= 0 ? node.end : 0, ...)

The same clamping happens for the root node and for node lists. The Go side then receives synthesized nodes with range [0, 0]. It can't tell them apart from real nodes at the start of the file, so it emits the leading comments at position 0 for each of them.

Workaround: print the statements inside a new source file with empty text (createSourceFile(statements, eof, "", fileName, path)). This avoids the duplicates but also drops all original comments, which isn't acceptable for codegen that must keep JSDoc or /* @__PURE__ */ annotations.

Metadata

Metadata

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions