From 3baa3bd9cff2a0bb9792d99828575aa010cd5035 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sat, 6 Sep 2025 20:26:55 +0200 Subject: [PATCH 01/11] feat: add additionalComments option for programmatic comment insertion --- .changeset/pretty-spiders-hear.md | 5 ++ README.md | 7 +- src/languages/ts/index.js | 41 +++++++++++- src/languages/types.d.ts | 12 +++- test/additional-comments.test.js | 105 ++++++++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 .changeset/pretty-spiders-hear.md create mode 100644 test/additional-comments.test.js diff --git a/.changeset/pretty-spiders-hear.md b/.changeset/pretty-spiders-hear.md new file mode 100644 index 0000000..812f4d5 --- /dev/null +++ b/.changeset/pretty-spiders-hear.md @@ -0,0 +1,5 @@ +--- +'esrap': minor +--- + +feat: add additionalComments option for programmatic comment insertion diff --git a/README.md b/README.md index 3c7f62f..ad13df2 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,12 @@ const { code, map } = print( quotes: 'single', // an array of `{ type: 'Line' | 'Block', value: string, loc: { start, end } }` objects - comments: [] + comments: [], + + // a WeakMap of AST nodes to additional comments to insert at specific nodes + // useful for programmatically adding comments during code transformation, + // especially for nodes that were added programmatically + additionalComments: new WeakMap() }) ); ``` diff --git a/src/languages/ts/index.js b/src/languages/ts/index.js index 7bb4f7c..9543123 100644 --- a/src/languages/ts/index.js +++ b/src/languages/ts/index.js @@ -1,6 +1,6 @@ /** @import { TSESTree } from '@typescript-eslint/types' */ /** @import { Visitors } from '../../types.js' */ -/** @import { TSOptions, Comment } from '../types.js' */ +/** @import { TSOptions, BaseComment, AdditionalComment } from '../types.js' */ import { Context } from 'esrap'; /** @typedef {TSESTree.Node} Node */ @@ -74,7 +74,7 @@ const OPERATOR_PRECEDENCE = { }; /** - * @param {Comment} comment + * @param {BaseComment} comment * @param {Context} context */ function write_comment(comment, context) { @@ -101,9 +101,42 @@ export default (options = {}) => { const quote_char = options.quotes === 'double' ? '"' : "'"; const comments = options.comments ?? []; + /** @type {WeakMap} */ + const additionalComments = options.additionalComments ?? new WeakMap(); let comment_index = 0; + /** + * Write additional comments for a node + * @param {Context} context + * @param {TSESTree.Node} node + * @param {('leading' | 'trailing')} position + */ + function write_additional_comments(context, node, position) { + const nodeComments = additionalComments.get(node); + if (!nodeComments) return; + + const relevantComments = nodeComments.filter((comment) => comment.position === position); + + for (let i = 0; i < relevantComments.length; i += 1) { + const comment = relevantComments[i]; + + if (position === 'trailing' && i === 0) { + context.write(' '); + } + + write_comment(comment, context); + + if (position === 'leading') { + if (comment.type === 'Line') { + context.newline(); + } else { + context.write(' '); + } + } + } + } + /** * Set `comment_index` to be the first comment after `start`. * Most of the time this is already correct, but if nodes @@ -586,11 +619,15 @@ export default (options = {}) => { return { _(node, context, visit) { + write_additional_comments(context, node, 'leading'); + if (node.loc) { flush_comments_until(context, null, node.loc.start, true); } visit(node); + + write_additional_comments(context, node, 'trailing'); }, ArrayExpression: shared['ArrayExpression|ArrayPattern'], diff --git a/src/languages/types.d.ts b/src/languages/types.d.ts index 1229d88..3aa92d4 100644 --- a/src/languages/types.d.ts +++ b/src/languages/types.d.ts @@ -1,6 +1,9 @@ +import { TSESTree } from '@typescript-eslint/types'; + export type TSOptions = { quotes?: 'double' | 'single'; comments?: Comment[]; + additionalComments?: WeakMap; }; interface Position { @@ -10,13 +13,20 @@ interface Position { // this exists in TSESTree but because of the inanity around enums // it's easier to do this ourselves -export interface Comment { +export interface BaseComment { type: 'Line' | 'Block'; value: string; start?: number; end?: number; +} + +export interface Comment extends BaseComment { loc: { start: Position; end: Position; }; } + +export interface AdditionalComment extends BaseComment { + position: 'leading' | 'trailing'; +} diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js new file mode 100644 index 0000000..511ea71 --- /dev/null +++ b/test/additional-comments.test.js @@ -0,0 +1,105 @@ +// @ts-check +/** @import { TSESTree } from '@typescript-eslint/types' */ +/** @import { AdditionalComment } from '../src/languages/types.js' */ +import { expect, test } from 'vitest'; +import { print } from '../src/index.js'; +import { load } from './common.js'; +import ts from '../src/languages/ts/index.js'; + +/** + * Helper to create additional comments and print code + * @param {TSESTree.Program} ast - Parsed AST + * @param {TSESTree.Node} node - AST node to attach comments to + * @param {AdditionalComment[]} comments - Comments to attach + * @returns {string} Generated code + */ +function printWithComments(ast, node, comments) { + const additionalComments = new WeakMap(); + additionalComments.set(node, comments); + + const output = print(ast, ts({ additionalComments })); + return output.code; +} + +/** + * Helper to get return statement from a simple function + * @param {TSESTree.Program} ast - Parsed AST + * @returns {TSESTree.Node} The return statement + */ +function getReturnStatement(ast) { + const functionDecl = ast.body[0]; + // @ts-expect-error accessing function body + const statements = functionDecl.body.body; + // Find the return statement (could be first or second depending on function structure) + return statements.find(/** @param {any} stmt */ (stmt) => stmt.type === 'ReturnStatement'); +} + +test('additional comments are inserted correctly', () => { + const input = `function example() { + const x = 1; + return x; +}`; + + const { ast } = load(input); + const returnStatement = getReturnStatement(ast); + expect(returnStatement.type).toBe('ReturnStatement'); + + /** @type {AdditionalComment[]} */ + const comments = [ + { + type: 'Line', + value: ' This is a leading comment', + position: 'leading' + }, + { + type: 'Block', + value: ' This is a trailing comment ', + position: 'trailing' + } + ]; + + const code = printWithComments(ast, returnStatement, comments); + + expect(code).toContain('// This is a leading comment'); + expect(code).toContain('/* This is a trailing comment */'); +}); + +test('only leading comments are inserted when specified', () => { + const input = `function test() { return 42; }`; + const { ast } = load(input); + const returnStatement = getReturnStatement(ast); + + /** @type {AdditionalComment[]} */ + const comments = [ + { + type: 'Line', + value: ' Leading only', + position: 'leading' + } + ]; + + const code = printWithComments(ast, returnStatement, comments); + + expect(code).toContain('// Leading only'); + expect(code).not.toContain('trailing'); +}); + +test('only trailing comments are inserted when specified', () => { + const input = `function test() { return 42; }`; + const { ast } = load(input); + const returnStatement = getReturnStatement(ast); + + /** @type {AdditionalComment[]} */ + const comments = [ + { + type: 'Block', + value: ' Trailing only ', + position: 'trailing' + } + ]; + + const code = printWithComments(ast, returnStatement, comments); + + expect(code).toContain('/* Trailing only */'); + expect(code).not.toContain('//'); +}); From de0ef3560386b30074f630c78f4e0660f78ed377 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sat, 6 Sep 2025 20:36:46 +0200 Subject: [PATCH 02/11] fix casing --- test/additional-comments.test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js index 511ea71..fa72357 100644 --- a/test/additional-comments.test.js +++ b/test/additional-comments.test.js @@ -13,7 +13,7 @@ import ts from '../src/languages/ts/index.js'; * @param {AdditionalComment[]} comments - Comments to attach * @returns {string} Generated code */ -function printWithComments(ast, node, comments) { +function print_with_comments(ast, node, comments) { const additionalComments = new WeakMap(); additionalComments.set(node, comments); @@ -26,7 +26,7 @@ function printWithComments(ast, node, comments) { * @param {TSESTree.Program} ast - Parsed AST * @returns {TSESTree.Node} The return statement */ -function getReturnStatement(ast) { +function get_return_statement(ast) { const functionDecl = ast.body[0]; // @ts-expect-error accessing function body const statements = functionDecl.body.body; @@ -41,7 +41,7 @@ test('additional comments are inserted correctly', () => { }`; const { ast } = load(input); - const returnStatement = getReturnStatement(ast); + const returnStatement = get_return_statement(ast); expect(returnStatement.type).toBe('ReturnStatement'); /** @type {AdditionalComment[]} */ @@ -58,7 +58,7 @@ test('additional comments are inserted correctly', () => { } ]; - const code = printWithComments(ast, returnStatement, comments); + const code = print_with_comments(ast, returnStatement, comments); expect(code).toContain('// This is a leading comment'); expect(code).toContain('/* This is a trailing comment */'); @@ -67,7 +67,7 @@ test('additional comments are inserted correctly', () => { test('only leading comments are inserted when specified', () => { const input = `function test() { return 42; }`; const { ast } = load(input); - const returnStatement = getReturnStatement(ast); + const returnStatement = get_return_statement(ast); /** @type {AdditionalComment[]} */ const comments = [ @@ -78,7 +78,7 @@ test('only leading comments are inserted when specified', () => { } ]; - const code = printWithComments(ast, returnStatement, comments); + const code = print_with_comments(ast, returnStatement, comments); expect(code).toContain('// Leading only'); expect(code).not.toContain('trailing'); @@ -87,7 +87,7 @@ test('only leading comments are inserted when specified', () => { test('only trailing comments are inserted when specified', () => { const input = `function test() { return 42; }`; const { ast } = load(input); - const returnStatement = getReturnStatement(ast); + const returnStatement = get_return_statement(ast); /** @type {AdditionalComment[]} */ const comments = [ @@ -98,7 +98,7 @@ test('only trailing comments are inserted when specified', () => { } ]; - const code = printWithComments(ast, returnStatement, comments); + const code = print_with_comments(ast, returnStatement, comments); expect(code).toContain('/* Trailing only */'); expect(code).not.toContain('//'); From 718afce9c3f9296a0733bdc5e87f44adf42c2275 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 7 Sep 2025 10:36:33 +0200 Subject: [PATCH 03/11] add type export --- src/languages/ts/public.d.ts | 2 +- src/languages/tsx/public.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/ts/public.d.ts b/src/languages/ts/public.d.ts index 06fa86b..f76dcc2 100644 --- a/src/languages/ts/public.d.ts +++ b/src/languages/ts/public.d.ts @@ -1,2 +1,2 @@ export * from './index'; -export { Comment } from '../types'; +export { Comment, AdditionalComment } from '../types'; diff --git a/src/languages/tsx/public.d.ts b/src/languages/tsx/public.d.ts index 06fa86b..f76dcc2 100644 --- a/src/languages/tsx/public.d.ts +++ b/src/languages/tsx/public.d.ts @@ -1,2 +1,2 @@ export * from './index'; -export { Comment } from '../types'; +export { Comment, AdditionalComment } from '../types'; From af12b38de515a7f5ad3e5832f706684d9f58a9ef Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 08:24:11 +0100 Subject: [PATCH 04/11] fix multi-line comments --- src/languages/ts/index.js | 3 ++- test/additional-comments.test.js | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/languages/ts/index.js b/src/languages/ts/index.js index 9543123..4ed8081 100644 --- a/src/languages/ts/index.js +++ b/src/languages/ts/index.js @@ -90,6 +90,7 @@ function write_comment(comment, context) { } context.write('*/'); + if (lines.length > 1) context.newline(); } } @@ -130,7 +131,7 @@ export default (options = {}) => { if (position === 'leading') { if (comment.type === 'Line') { context.newline(); - } else { + } else if (comment.type === 'Block' && !comment.value.includes('\n')) { context.write(' '); } } diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js index fa72357..07958bb 100644 --- a/test/additional-comments.test.js +++ b/test/additional-comments.test.js @@ -103,3 +103,36 @@ test('only trailing comments are inserted when specified', () => { expect(code).toContain('/* Trailing only */'); expect(code).not.toContain('//'); }); + +test('additional comments multi-line comments have new line', () => { + const input = `function example() { + const x = 1; + return x; +}`; + + const { ast } = load(input); + const returnStatement = get_return_statement(ast); + expect(returnStatement.type).toBe('ReturnStatement'); + + /** @type {AdditionalComment[]} */ + const comments = [ + { + type: 'Block', + value: '*\n * This is a trailing comment\n ', + position: 'leading' + } + ]; + + const code = print_with_comments(ast, returnStatement, comments); + + expect(code).toMatchInlineSnapshot(` + "function example() { + const x = 1; + + /** + * This is a trailing comment + */ + return x; + }" + `); +}); From 4214cd32a5706a0a2766ced0f66994c18bf022e9 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sun, 26 Oct 2025 14:44:53 +0100 Subject: [PATCH 05/11] update `load` to `acornParse` --- test/additional-comments.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js index 07958bb..302e57b 100644 --- a/test/additional-comments.test.js +++ b/test/additional-comments.test.js @@ -3,7 +3,7 @@ /** @import { AdditionalComment } from '../src/languages/types.js' */ import { expect, test } from 'vitest'; import { print } from '../src/index.js'; -import { load } from './common.js'; +import { acornParse } from './common.js'; import ts from '../src/languages/ts/index.js'; /** @@ -40,7 +40,7 @@ test('additional comments are inserted correctly', () => { return x; }`; - const { ast } = load(input); + const { ast } = acornParse(input); const returnStatement = get_return_statement(ast); expect(returnStatement.type).toBe('ReturnStatement'); @@ -66,7 +66,7 @@ test('additional comments are inserted correctly', () => { test('only leading comments are inserted when specified', () => { const input = `function test() { return 42; }`; - const { ast } = load(input); + const { ast } = acornParse(input); const returnStatement = get_return_statement(ast); /** @type {AdditionalComment[]} */ @@ -86,7 +86,7 @@ test('only leading comments are inserted when specified', () => { test('only trailing comments are inserted when specified', () => { const input = `function test() { return 42; }`; - const { ast } = load(input); + const { ast } = acornParse(input); const returnStatement = get_return_statement(ast); /** @type {AdditionalComment[]} */ @@ -110,7 +110,7 @@ test('additional comments multi-line comments have new line', () => { return x; }`; - const { ast } = load(input); + const { ast } = acornParse(input); const returnStatement = get_return_statement(ast); expect(returnStatement.type).toBe('ReturnStatement'); From 005e1c382bac75652bab6b1f5d1bcd4f53ad319e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Nov 2025 18:37:55 -0500 Subject: [PATCH 06/11] feat: `getLeadingComments`/`getTrailingComments` --- src/languages/ts/index.js | 23 +++---- src/languages/ts/public.d.ts | 2 +- src/languages/tsx/public.d.ts | 2 +- src/languages/types.d.ts | 7 +- test/additional-comments.test.js | 111 ++++++++++++++++--------------- 5 files changed, 71 insertions(+), 74 deletions(-) diff --git a/src/languages/ts/index.js b/src/languages/ts/index.js index 77ef86f..34df1e0 100644 --- a/src/languages/ts/index.js +++ b/src/languages/ts/index.js @@ -1,6 +1,6 @@ /** @import { TSESTree } from '@typescript-eslint/types' */ /** @import { Visitors } from '../../types.js' */ -/** @import { TSOptions, BaseComment, AdditionalComment } from '../types.js' */ +/** @import { TSOptions, BaseComment } from '../types.js' */ import { Context } from 'esrap'; /** @typedef {TSESTree.Node} Node */ @@ -102,25 +102,22 @@ export default (options = {}) => { const quote_char = options.quotes === 'double' ? '"' : "'"; const comments = options.comments ?? []; - /** @type {WeakMap} */ - const additionalComments = options.additionalComments ?? new WeakMap(); let comment_index = 0; /** * Write additional comments for a node * @param {Context} context - * @param {TSESTree.Node} node + * @param {BaseComment[] | undefined} comments * @param {('leading' | 'trailing')} position */ - function write_additional_comments(context, node, position) { - const nodeComments = additionalComments.get(node); - if (!nodeComments) return; - - const relevantComments = nodeComments.filter((comment) => comment.position === position); + function write_additional_comments(context, comments, position) { + if (!comments) { + return; + } - for (let i = 0; i < relevantComments.length; i += 1) { - const comment = relevantComments[i]; + for (let i = 0; i < comments.length; i += 1) { + const comment = comments[i]; if (position === 'trailing' && i === 0) { context.write(' '); @@ -809,7 +806,7 @@ export default (options = {}) => { return { _(node, context, visit) { - write_additional_comments(context, node, 'leading'); + write_additional_comments(context, options.getLeadingComments?.(node), 'leading'); if (node.loc) { flush_comments_until(context, null, node.loc.start, true); @@ -817,7 +814,7 @@ export default (options = {}) => { visit(node); - write_additional_comments(context, node, 'trailing'); + write_additional_comments(context, options.getTrailingComments?.(node), 'trailing'); }, AccessorProperty: diff --git a/src/languages/ts/public.d.ts b/src/languages/ts/public.d.ts index f76dcc2..06fa86b 100644 --- a/src/languages/ts/public.d.ts +++ b/src/languages/ts/public.d.ts @@ -1,2 +1,2 @@ export * from './index'; -export { Comment, AdditionalComment } from '../types'; +export { Comment } from '../types'; diff --git a/src/languages/tsx/public.d.ts b/src/languages/tsx/public.d.ts index f76dcc2..06fa86b 100644 --- a/src/languages/tsx/public.d.ts +++ b/src/languages/tsx/public.d.ts @@ -1,2 +1,2 @@ export * from './index'; -export { Comment, AdditionalComment } from '../types'; +export { Comment } from '../types'; diff --git a/src/languages/types.d.ts b/src/languages/types.d.ts index 3aa92d4..7e60a07 100644 --- a/src/languages/types.d.ts +++ b/src/languages/types.d.ts @@ -3,7 +3,8 @@ import { TSESTree } from '@typescript-eslint/types'; export type TSOptions = { quotes?: 'double' | 'single'; comments?: Comment[]; - additionalComments?: WeakMap; + getLeadingComments?: (node: TSESTree.Node) => BaseComment[] | undefined; + getTrailingComments?: (node: TSESTree.Node) => BaseComment[] | undefined; }; interface Position { @@ -26,7 +27,3 @@ export interface Comment extends BaseComment { end: Position; }; } - -export interface AdditionalComment extends BaseComment { - position: 'leading' | 'trailing'; -} diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js index 302e57b..ef5a70a 100644 --- a/test/additional-comments.test.js +++ b/test/additional-comments.test.js @@ -1,6 +1,6 @@ // @ts-check /** @import { TSESTree } from '@typescript-eslint/types' */ -/** @import { AdditionalComment } from '../src/languages/types.js' */ +/** @import { BaseComment } from '../src/languages/types.js' */ import { expect, test } from 'vitest'; import { print } from '../src/index.js'; import { acornParse } from './common.js'; @@ -10,17 +10,37 @@ import ts from '../src/languages/ts/index.js'; * Helper to create additional comments and print code * @param {TSESTree.Program} ast - Parsed AST * @param {TSESTree.Node} node - AST node to attach comments to - * @param {AdditionalComment[]} comments - Comments to attach + * @param {BaseComment[]} leading - Comments to attach before the node + * @param {BaseComment[]} trailing - Comments to attach after the node * @returns {string} Generated code */ -function print_with_comments(ast, node, comments) { - const additionalComments = new WeakMap(); - additionalComments.set(node, comments); - - const output = print(ast, ts({ additionalComments })); +function print_with_comments(ast, node, leading, trailing) { + const output = print( + ast, + ts({ + getLeadingComments: (n) => (n === node ? leading : []), + getTrailingComments: (n) => (n === node ? trailing : []) + }) + ); return output.code; } +/** + * @param {string} value + * @returns {BaseComment} + */ +function line(value) { + return { type: 'Line', value }; +} + +/** + * @param {string} value + * @returns {BaseComment} + */ +function block(value) { + return { type: 'Block', value }; +} + /** * Helper to get return statement from a simple function * @param {TSESTree.Program} ast - Parsed AST @@ -34,7 +54,7 @@ function get_return_statement(ast) { return statements.find(/** @param {any} stmt */ (stmt) => stmt.type === 'ReturnStatement'); } -test('additional comments are inserted correctly', () => { +test.only('additional comments are inserted correctly', () => { const input = `function example() { const x = 1; return x; @@ -44,21 +64,15 @@ test('additional comments are inserted correctly', () => { const returnStatement = get_return_statement(ast); expect(returnStatement.type).toBe('ReturnStatement'); - /** @type {AdditionalComment[]} */ - const comments = [ - { - type: 'Line', - value: ' This is a leading comment', - position: 'leading' - }, - { - type: 'Block', - value: ' This is a trailing comment ', - position: 'trailing' - } - ]; - - const code = print_with_comments(ast, returnStatement, comments); + const { code } = print( + ast, + ts({ + getLeadingComments: (n) => + n === returnStatement ? [line(' This is a leading comment')] : undefined, + getTrailingComments: (n) => + n === returnStatement ? [block(' This is a trailing comment ')] : undefined + }) + ); expect(code).toContain('// This is a leading comment'); expect(code).toContain('/* This is a trailing comment */'); @@ -69,16 +83,12 @@ test('only leading comments are inserted when specified', () => { const { ast } = acornParse(input); const returnStatement = get_return_statement(ast); - /** @type {AdditionalComment[]} */ - const comments = [ - { - type: 'Line', - value: ' Leading only', - position: 'leading' - } - ]; - - const code = print_with_comments(ast, returnStatement, comments); + const { code } = print( + ast, + ts({ + getLeadingComments: (n) => (n === returnStatement ? [line(' Leading only ')] : undefined) + }) + ); expect(code).toContain('// Leading only'); expect(code).not.toContain('trailing'); @@ -89,16 +99,12 @@ test('only trailing comments are inserted when specified', () => { const { ast } = acornParse(input); const returnStatement = get_return_statement(ast); - /** @type {AdditionalComment[]} */ - const comments = [ - { - type: 'Block', - value: ' Trailing only ', - position: 'trailing' - } - ]; - - const code = print_with_comments(ast, returnStatement, comments); + const { code } = print( + ast, + ts({ + getTrailingComments: (n) => (n === returnStatement ? [block(' Trailing only ')] : undefined) + }) + ); expect(code).toContain('/* Trailing only */'); expect(code).not.toContain('//'); @@ -114,23 +120,20 @@ test('additional comments multi-line comments have new line', () => { const returnStatement = get_return_statement(ast); expect(returnStatement.type).toBe('ReturnStatement'); - /** @type {AdditionalComment[]} */ - const comments = [ - { - type: 'Block', - value: '*\n * This is a trailing comment\n ', - position: 'leading' - } - ]; - - const code = print_with_comments(ast, returnStatement, comments); + const { code } = print( + ast, + ts({ + getLeadingComments: (n) => + n === returnStatement ? [block('*\n * This is a leading comment\n ')] : undefined + }) + ); expect(code).toMatchInlineSnapshot(` "function example() { const x = 1; /** - * This is a trailing comment + * This is a leading comment */ return x; }" From 867dce3d2817b3495d0ebeaeddcff842f34fa2dd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Nov 2025 18:42:30 -0500 Subject: [PATCH 07/11] unused --- test/additional-comments.test.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js index ef5a70a..0140357 100644 --- a/test/additional-comments.test.js +++ b/test/additional-comments.test.js @@ -6,25 +6,6 @@ import { print } from '../src/index.js'; import { acornParse } from './common.js'; import ts from '../src/languages/ts/index.js'; -/** - * Helper to create additional comments and print code - * @param {TSESTree.Program} ast - Parsed AST - * @param {TSESTree.Node} node - AST node to attach comments to - * @param {BaseComment[]} leading - Comments to attach before the node - * @param {BaseComment[]} trailing - Comments to attach after the node - * @returns {string} Generated code - */ -function print_with_comments(ast, node, leading, trailing) { - const output = print( - ast, - ts({ - getLeadingComments: (n) => (n === node ? leading : []), - getTrailingComments: (n) => (n === node ? trailing : []) - }) - ); - return output.code; -} - /** * @param {string} value * @returns {BaseComment} From 7cd55e508de9d862719cd3b1943558f2706ca780 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Nov 2025 18:44:17 -0500 Subject: [PATCH 08/11] expose BaseComment --- src/languages/ts/public.d.ts | 2 +- src/languages/tsx/public.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/ts/public.d.ts b/src/languages/ts/public.d.ts index 06fa86b..4009459 100644 --- a/src/languages/ts/public.d.ts +++ b/src/languages/ts/public.d.ts @@ -1,2 +1,2 @@ export * from './index'; -export { Comment } from '../types'; +export { BaseComment, Comment } from '../types'; diff --git a/src/languages/tsx/public.d.ts b/src/languages/tsx/public.d.ts index 06fa86b..4009459 100644 --- a/src/languages/tsx/public.d.ts +++ b/src/languages/tsx/public.d.ts @@ -1,2 +1,2 @@ export * from './index'; -export { Comment } from '../types'; +export { BaseComment, Comment } from '../types'; From e781fb98f544719af74683dd2addb88c06db7574 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 24 Nov 2025 18:44:49 -0500 Subject: [PATCH 09/11] oops --- test/additional-comments.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/additional-comments.test.js b/test/additional-comments.test.js index 0140357..1b3d32f 100644 --- a/test/additional-comments.test.js +++ b/test/additional-comments.test.js @@ -35,7 +35,7 @@ function get_return_statement(ast) { return statements.find(/** @param {any} stmt */ (stmt) => stmt.type === 'ReturnStatement'); } -test.only('additional comments are inserted correctly', () => { +test('additional comments are inserted correctly', () => { const input = `function example() { const x = 1; return x; From ed526332c9bf0ba98f101cb4a7fbfc8685904100 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 25 Nov 2025 13:25:49 -0500 Subject: [PATCH 10/11] Update .changeset/pretty-spiders-hear.md Co-authored-by: Manuel <30698007+manuel3108@users.noreply.github.com> --- .changeset/pretty-spiders-hear.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/pretty-spiders-hear.md b/.changeset/pretty-spiders-hear.md index 812f4d5..5778e9f 100644 --- a/.changeset/pretty-spiders-hear.md +++ b/.changeset/pretty-spiders-hear.md @@ -2,4 +2,4 @@ 'esrap': minor --- -feat: add additionalComments option for programmatic comment insertion +feat: add `getLeadingComments` & `getTrailingComments` option for programmatic comment insertion From 00c0e6c61daf7e055b9357076dc80bf1cdde76f6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 25 Nov 2025 13:59:44 -0500 Subject: [PATCH 11/11] update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ad13df2..0fa7c29 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,10 @@ const { code, map } = print( // an array of `{ type: 'Line' | 'Block', value: string, loc: { start, end } }` objects comments: [], - // a WeakMap of AST nodes to additional comments to insert at specific nodes - // useful for programmatically adding comments during code transformation, - // especially for nodes that were added programmatically - additionalComments: new WeakMap() + // a pair of functions for inserting additional comments before or after a given node. + // returns `Array<{ type: 'Line' | 'Block', value: string }>` or `undefined` + getLeadingComments: (node) => [{ type: 'Line', value: ' a comment before the node' }], + getTrailingComments: (node) => [{ type: 'Block', value: ' a comment after the node' }] }) ); ```