@node-cli/comments 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,47 +1,164 @@
1
1
  # @node-cli/comments
2
2
 
3
- CLI tool to reflow, normalize, and optionally merge JavaScript / TypeScript comment blocks (JSDoc and `//` single-line comments).
3
+ CLI tool to reflow, normalize, wrap, and (optionally) merge JavaScript / TypeScript comments (JSDoc blocks and `//` singleline comments) with opinionated heuristics for punctuation, NOTE normalization, and structured content preservation.
4
4
 
5
- ## Features (Planned)
5
+ ## Features
6
6
 
7
- - Reflow JSDoc blocks to a max line width
8
- - Optional wrapping of single-line `//` comments
9
- - Optional merging of consecutive `//` lines into a JSDoc block
10
- - Dry run mode with diff output
11
- - Width / punctuation heuristics and NOTE normalization
7
+ - Reflows JSDoc comment blocks to a target max width while preserving code fences, lists, tags, headings, visually‑indented code, and structured regex / pattern explanation sections.
8
+ - Wraps `//` singleline comments (soft paragraph detection) with smart sentence termination (adds a trailing period only where appropriate).
9
+ - Optional merging of consecutive `//` lines into a synthetically generated multi‑line JSDoc block (preserves structured groups verbatim; converts explanatory paragraphs into wrapped sentences).
10
+ - NOTE normalization (`note:` `NOTE:`) and safe sentence splitting for multiple NOTE sentences in one block.
11
+ - Defensive regex design (linear JSDoc extraction, indentation & size caps) to mitigate pathological input / ReDoS risk.
12
+ - Dry‑run diff mode with minimal, colorized unified style output and deterministic exit codes for CI enforcement.
13
+ - Glob expansion for simple patterns (`*`, `**`, `?`) via internal expander (no shell dependency) – quote globs in shells that expand themselves (e.g. zsh) to ensure consistent behavior.
14
+ - Optional disabling of line comment wrapping, and optional merge of line comment groups.
15
+ - Color output by default, suppressible with `--boring` (useful in test snapshots or plain log collectors).
12
16
 
13
- ## Usage
17
+ ## Install
14
18
 
19
+ Using pnpm (recommended for workspaces):
20
+
21
+ ```sh
22
+ pnpm add -D @node-cli/comments
15
23
  ```
16
- comments [options] <files...>
24
+
25
+ Using npx (no install):
26
+
27
+ ```sh
28
+ npx @node-cli/comments --dry-run src/index.ts
17
29
  ```
18
30
 
19
- Examples:
31
+ ## CLI Usage
20
32
 
21
33
  ```
22
- comments src/**/*.ts
34
+ comments [options] <files...>
35
+ ```
36
+
37
+ ### Examples
38
+
39
+ ```sh
40
+ # Reflow comments in all TS sources
41
+ comments 'src/**/*.ts'
42
+
43
+ # Show diff for a single file (non‑zero exit if changes needed)
23
44
  comments --dry-run src/index.ts
45
+
46
+ # Use custom width and merge explanatory // groups into JSDoc blocks
24
47
  comments --width 100 --merge-line-comments 'src/**/*.ts'
25
- comments --no-line-wrap src/file.ts
48
+
49
+ # Disable wrapping of line comments (only reflow JSDoc blocks)
50
+ comments --no-line-wrap 'src/**/*.ts'
51
+
52
+ # Output transformed content to STDOUT (single file only)
53
+ comments --stdout src/file.ts
26
54
  ```
27
55
 
28
56
  ## Flags
29
57
 
30
- | Flag | Description |
31
- | ----------------------- | --------------------------------------------- |
32
- | `--width <n>` | Max output line width (default 80) |
33
- | `--dry-run` | Show diff only; exit 1 if changes would occur |
34
- | `--stdout` | Print transformed content of a single file |
35
- | `--no-line-wrap` | Disable wrapping of `//` comments |
36
- | `--merge-line-comments` | Merge groups of `//` into JSDoc blocks |
37
- | `-h, --help` | Show help |
38
- | `-v, --version` | Show version |
58
+ | Flag / Short | Default | Description |
59
+ | ----------------------- | ------- | ----------------------------------------------------------------------------------------------------- |
60
+ | `--width <n>` | `80` | Maximum output line width for wrapping/reflow. |
61
+ | `-D, --dry-run` | `false` | Show diff only; exit `1` if any file would change (CI friendly). |
62
+ | `--stdout` | `false` | Print transformed content of a single file to STDOUT (no writes). Requires exactly one file argument. |
63
+ | `--no-line-wrap` | `false` | Do not wrap `//` line comments (still reflows JSDoc blocks). |
64
+ | `--merge-line-comments` | `false` | Merge consecutive `//` comment groups into JSDoc blocks (heuristics skip directives, licenses, URLs). |
65
+ | `-b, --boring` | `false` | Disable colored output. |
66
+ | `-h, --help` | — | Show help and usage examples. |
67
+ | `-v, --version` | — | Show version. |
68
+
69
+ Notes:
70
+
71
+ 1. `--stdout` may not be combined with multiple file/glob inputs.
72
+ 2. `--merge-line-comments` affects grouping heuristics; structured groups (regex / pattern docs) are preserved verbatim line‑by‑line.
39
73
 
40
74
  ## Exit Codes
41
75
 
42
- - `0` success or dry run with no changes
43
- - `1` dry run with changes or usage error
76
+ - `0` Success OR dry run with no required changes.
77
+ - `1` Dry run detected pending changes OR usage / restriction error.
78
+
79
+ These semantics let you add a lint step: `comments --dry-run 'src/**/*.ts'` in CI.
80
+
81
+ ## Programmatic API
82
+
83
+ While the primary interface is the CLI, a small programmatic API is exposed:
84
+
85
+ ```ts
86
+ import { parseAndTransformComments, diffLines } from "@node-cli/comments";
87
+
88
+ const source = `/** example */\n// note: something`;
89
+ const result = parseAndTransformComments(source, {
90
+ width: 100,
91
+ wrapLineComments: true,
92
+ mergeLineComments: false
93
+ });
94
+
95
+ console.log(result.changed); // boolean
96
+ console.log(result.transformed); // new content
97
+ console.log(diffLines(source, result.transformed)); // crude line diff
98
+ ```
99
+
100
+ Types:
101
+
102
+ ```ts
103
+ interface ProcessOptions {
104
+ width: number;
105
+ wrapLineComments: boolean;
106
+ mergeLineComments: boolean;
107
+ }
108
+
109
+ interface FileResult {
110
+ original: string;
111
+ transformed: string;
112
+ changed: boolean;
113
+ }
114
+ ```
115
+
116
+ ## Heuristics Overview
117
+
118
+ - Adds a terminal period to sentences lacking final punctuation (unless ending with a colon, tag line, list marker, or detected continuation).
119
+ - Normalizes `NOTE:` capitalization and splits multiple NOTE sentences safely.
120
+ - Skips wrapping for directive / tool lines (`@tag`, `eslint`, `ts-ignore`) and URLs.
121
+ - Preserves fenced code blocks and visually indented code blocks in JSDoc.
122
+ - Structured explanation groups (regex / pattern walkthroughs) remain line‑stable to avoid unintended semantic changes.
123
+ - Protects against pathological inputs with maximum indentation and comment body size limits.
124
+
125
+ ## Globs
126
+
127
+ Simple glob patterns (`*`, `**`, `?`) are expanded internally. Quote patterns to prevent your shell from pre‑expanding them inconsistently:
128
+
129
+ ```sh
130
+ comments 'src/**/*.ts'
131
+ ```
132
+
133
+ ## Suggested CI Integration
134
+
135
+ Add to your project scripts (example):
136
+
137
+ ```jsonc
138
+ {
139
+ "scripts": {
140
+ "comments:check": "comments --dry-run 'src/**/*.ts'",
141
+ "comments:fix": "comments --merge-line-comments 'src/**/*.ts'"
142
+ }
143
+ }
144
+ ```
145
+
146
+ Then in CI:
147
+
148
+ ```sh
149
+ pnpm comments:check
150
+ ```
151
+
152
+ ## Security & Performance Notes
153
+
154
+ - Regex for JSDoc extraction is linear-time (tempered) and capped by size / indentation limits.
155
+ - No remote network access; operates purely on provided files.
156
+ - Diff output omits file content when unchanged to keep logs concise.
157
+
158
+ ## License
159
+
160
+ MIT © Arno Versini
44
161
 
45
- ## Roadmap
162
+ ---
46
163
 
47
- See project PRD `reflow-jsdoc-PRD.md` for full specification.
164
+ For full design rationale see project PRD (internal) and inline comments in `src/lib.ts` documenting regex safety and heuristics.
@@ -483,6 +483,69 @@ describe("parseAndTransformComments", function() {
483
483
  }
484
484
  }
485
485
  });
486
+ it("merges line comments after lines ending with },", function() {
487
+ var src = [
488
+ "const config = {",
489
+ ' "key": "value",',
490
+ ' "another": "property",',
491
+ "},",
492
+ "// This is a multi-line comment that should be merged",
493
+ "// because it follows a line ending with }, which indicates",
494
+ "// the end of an object or array element",
495
+ "const next = 42;"
496
+ ].join("\n");
497
+ var out = parseAndTransformComments(src, {
498
+ width: 80,
499
+ wrapLineComments: true,
500
+ mergeLineComments: true
501
+ }).transformed;
502
+ expect(out).toMatch(/},\n\/\*\*/);
503
+ expect(out).toMatch(/multi-line comment that should be merged/);
504
+ expect(out).toMatch(/object or array element\./);
505
+ expect(out).not.toMatch(/\/\/ This is a multi-line/);
506
+ });
507
+ it("merges line comments after various comma-ending contexts", function() {
508
+ var src = [
509
+ "const items = [",
510
+ ' "first",',
511
+ ' "second",',
512
+ "];",
513
+ "// Comments after array with trailing comma",
514
+ "// should be merged into JSDoc format",
515
+ "",
516
+ "function demo(a, b,) {",
517
+ " return a + b;",
518
+ "}",
519
+ "// Comments after function ending with }",
520
+ "// should also be merged properly"
521
+ ].join("\n");
522
+ var out = parseAndTransformComments(src, {
523
+ width: 80,
524
+ wrapLineComments: true,
525
+ mergeLineComments: true
526
+ }).transformed;
527
+ // First comment group after array should be merged
528
+ expect(out).toMatch(/];\n\/\*\*/);
529
+ expect(out).toMatch(/Comments after array with trailing comma/);
530
+ // Second comment group after function should be merged
531
+ expect(out).toMatch(/}\n\/\*\*/);
532
+ expect(out).toMatch(/Comments after function ending with }/);
533
+ });
534
+ it("does not merge single line comment when context is not eligible", function() {
535
+ var src = [
536
+ "const value = process()",
537
+ "// single comment that should not be merged",
538
+ "console.log(value);"
539
+ ].join("\n");
540
+ var out = parseAndTransformComments(src, {
541
+ width: 80,
542
+ wrapLineComments: true,
543
+ mergeLineComments: true
544
+ }).transformed;
545
+ // Single comment should remain as line comment, not merged to JSDoc
546
+ expect(out).toMatch(/\/\/ single comment that should not be merged/);
547
+ expect(out).not.toMatch(/\/\*\*/);
548
+ });
486
549
  it("splits consecutive NOTE sentences in a single paragraph", function() {
487
550
  var input = [
488
551
  "/**",
@@ -550,6 +613,47 @@ describe("parseAndTransformComments", function() {
550
613
  // Ensure final sentence has period.
551
614
  expect(/completely random\./.test(out)).toBe(true);
552
615
  });
616
+ it("handles diffLines with different length arrays", function() {
617
+ var a = "line1\nline2";
618
+ var b = "line1\nline2\nline3";
619
+ var diff = diffLines(a, b);
620
+ expect(diff).toContain("+ line3");
621
+ var c = "line1\nline2\nline3";
622
+ var d = "line1\nline2";
623
+ var diff2 = diffLines(c, d);
624
+ expect(diff2).toContain("- line3");
625
+ });
626
+ it("handles complex NOTE sentence boundaries and splitting", function() {
627
+ var input = [
628
+ "/**",
629
+ " * This performs work NOTE: first note without period NOTE: second note. Then continues",
630
+ " */"
631
+ ].join("\n");
632
+ var out = parseAndTransformComments(input, _object_spread_props(_object_spread({}, baseOpts), {
633
+ width: 100
634
+ })).transformed;
635
+ // Test that NOTE content is processed properly (without making assumptions about splitting)
636
+ expect(out).toMatch(/This performs work/);
637
+ expect(out).toMatch(/NOTE: first note without period/);
638
+ expect(out).toMatch(/NOTE: second note/);
639
+ expect(out).toMatch(/Then continues/);
640
+ });
641
+ it("handles line comment merging with NOTE content", function() {
642
+ var src = [
643
+ "// This text has multiple thoughts",
644
+ "// NOTE: important note here",
645
+ "// Final concluding text"
646
+ ].join("\n");
647
+ var out = parseAndTransformComments(src, {
648
+ width: 120,
649
+ wrapLineComments: true,
650
+ mergeLineComments: true
651
+ }).transformed;
652
+ expect(out).toMatch(/\/\*\*/);
653
+ expect(out).toMatch(/This text has multiple thoughts/);
654
+ expect(out).toMatch(/NOTE: important note here/);
655
+ expect(out).toMatch(/Final concluding text/);
656
+ });
553
657
  });
554
658
 
555
659
  //# sourceMappingURL=lib.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/__tests__/lib.test.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { describe, expect, it } from \"vitest\";\nimport { expandGlobs } from \"../glob.js\";\nimport { diffLines, parseAndTransformComments } from \"../lib.js\";\n\nconst baseOpts = {\n\twidth: 60,\n\twrapLineComments: true,\n\tmergeLineComments: false,\n};\n\ndescribe(\"parseAndTransformComments\", () => {\n\tit(\"reflows a simple jsdoc and adds period\", () => {\n\t\tconst input = \"/**\\n * This function does something\\n * important\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts);\n\t\texpect(res.transformed).toContain(\"does something important.\");\n\t});\n\n\tit(\"normalizes NOTE capitalization\", () => {\n\t\tconst input = \"/**\\n * note: edge case\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts);\n\t\texpect(/NOTE: edge case/.test(res.transformed)).toBe(true);\n\t});\n\n\tit(\"wraps single line comments\", () => {\n\t\tconst input =\n\t\t\t\"// this is a very long comment that should be wrapped across multiple lines to satisfy width\";\n\t\tconst res = parseAndTransformComments(input, baseOpts);\n\t\texpect(res.transformed.split(/\\n/).length).toBeGreaterThan(1);\n\t});\n\n\tit(\"merges groups of line comments into jsdoc\", () => {\n\t\tconst input = [\n\t\t\t\"// first line\",\n\t\t\t\"// second line\",\n\t\t\t\"// third line\",\n\t\t\t\"const x = 1;\",\n\t\t].join(\"\\n\");\n\t\tconst res = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\tmergeLineComments: true,\n\t\t});\n\t\texpect(/\\/\\*\\*/.test(res.transformed)).toBe(true);\n\t});\n\n\tit(\"is idempotent (second pass unchanged)\", () => {\n\t\tconst input = \"/**\\n * Example doc that will be normalized\\n */\";\n\t\tconst first = parseAndTransformComments(input, baseOpts).transformed;\n\t\tconst second = parseAndTransformComments(first, baseOpts).transformed;\n\t\texpect(diffLines(first, second)).toBe(\"\");\n\t});\n\n\tit(\"preserves code fence blocks\", () => {\n\t\tconst input =\n\t\t\t\"/**\\n * Before\\n * ```js\\n * const x= 1; \\n * ```\\n * After\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts).transformed;\n\t\texpect(/```js/.test(res)).toBe(true);\n\t\t// ensure spacing inside fence not normalized.\n\t\texpect(/const {2}x= {2}1;/.test(res)).toBe(true);\n\t});\n\n\tit(\"does not merge directive or license groups\", () => {\n\t\tconst input = [\n\t\t\t\"// eslint-disable-next-line\",\n\t\t\t\"// second line should prevent merge due to directive\",\n\t\t\t\"const y = 2;\",\n\t\t\t\"// Copyright 2024 Example\",\n\t\t\t\"// another line\",\n\t\t].join(\"\\n\");\n\t\tconst res = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\tmergeLineComments: true,\n\t\t});\n\t\texpect(/eslint-disable/.test(res.transformed)).toBe(true);\n\t\t// Should not have merged into a jsdoc (no /** directly before const).\n\t\texpect(/\\/\\*\\*/.test(res.transformed)).toBe(false);\n\t});\n\n\tit(\"list items are not reflowed into a paragraph\", () => {\n\t\tconst input =\n\t\t\t\"/**\\n * First line explaining.\\n * - item one more words here\\n * - item two\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// each list item remains on its own line with leading dash.\n\t\tconst lines = res.split(/\\n/).filter((l) => /- item/.test(l));\n\t\texpect(lines.length).toBe(2);\n\t});\n\n\tit(\"tag lines are preserved\", () => {\n\t\tconst input =\n\t\t\t\"/**\\n * short description\\n * @param x value\\n * @returns something\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts).transformed;\n\t\texpect(/@param x value/.test(res)).toBe(true);\n\t\texpect(/@returns something/.test(res)).toBe(true);\n\t});\n\n\tit(\"handles heading-like lines with colon and visually indented code & numeric lists\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Overview:\",\n\t\t\t\" * const x = 1;\",\n\t\t\t\" * 1. first\",\n\t\t\t\" * 2. second\",\n\t\t\t\" *\", // blank separation\n\t\t\t\" * Another paragraph without period\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Heading preserved, not merged into previous paragraph.\n\t\texpect(out).toMatch(/Overview:/);\n\t\t// Visually indented code line kept as-is (no extra wrapping collapse).\n\t\texpect(out).toMatch(/const\\s{3}x = 1;/);\n\t\t/**\n\t\t * Trailing blank before closing is optional after recent trimming change. (we\n\t\t * only keep it when multiple paragraphs exist). Assert closing exists.\n\t\t * Closing delimiter should be on its own line; allow optional leading\n\t\t * space(s).\n\t\t */\n\t\texpect(/\\n\\s*\\*\\/$/.test(out)).toBe(true);\n\t\t// Numeric list lines preserved.\n\t\texpect(out).toMatch(/1\\. first/);\n\t\texpect(out).toMatch(/2\\. second/);\n\t\t// Trailing paragraph got terminal period.\n\t\texpect(out).toMatch(/Another paragraph without period\\./);\n\t});\n\n\tit(\"glob expansion matches deep files only with explicit globstar\", () => {\n\t\tconst tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob-\"));\n\t\tconst a = path.join(tmpRoot, \"a.ts\");\n\t\tconst nestedDir = path.join(tmpRoot, \"nested\");\n\t\tfs.mkdirSync(nestedDir, { recursive: true });\n\t\tconst b = path.join(nestedDir, \"b.ts\");\n\t\tfs.writeFileSync(a, \"// file a\", \"utf8\");\n\t\tfs.writeFileSync(b, \"// file b\", \"utf8\");\n\t\t// Shallow pattern should only see top-level a.ts.\n\t\tconst shallow = expandGlobs([`${tmpRoot}/*.ts`]).sort();\n\t\texpect(shallow).toEqual([a]);\n\t\t// Deep pattern matches both.\n\t\tconst deep = expandGlobs([`${tmpRoot}/**/*.ts`]).sort();\n\t\texpect(deep).toEqual([a, b].sort());\n\t});\n\n\t/**\n\t * Added test for preserving multi-line // comment groups that should NOT merge\n\t * when preceded by code.\n\t */\n\tit(\"does not merge an inline explanatory multi-line // comment group following code\", () => {\n\t\tconst src = [\n\t\t\t\"function demo() {\",\n\t\t\t\" const x = 1; // keep\",\n\t\t\t\" // first line explains next block\",\n\t\t\t\" // still explaining\",\n\t\t\t\" // final line\",\n\t\t\t\" return x; // done\",\n\t\t\t\"}\",\n\t\t\t\"\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\tmergeLineComments: true,\n\t\t\twrapLineComments: true,\n\t\t\twidth: 80,\n\t\t}).transformed;\n\t\texpect(out).toContain(\" // first line explains next block\");\n\t\texpect(out).toContain(\" // still explaining\");\n\t\texpect(out).toContain(\" // final line\");\n\t\texpect(out).not.toContain(\"/**\");\n\t});\n\n\tit(\"converts the multi-line explanatory block from lib.ts into a multi-line JSDoc, preserving lines\", () => {\n\t\tconst snippet = [\n\t\t\t\"// JSDoc block extraction:\",\n\t\t\t\"// Previous pattern used a lazy dot-all: ([\\\\s\\\\S]*?) which could, under\",\n\t\t\t\"// pathological inputs, produce excessive backtracking. We replace it with a\",\n\t\t\t\"// tempered pattern that advances linearly by never letting the inner part\",\n\t\t\t\"// consume a closing '*/'. This avoids catastrophic behavior while keeping\",\n\t\t\t\"// correctness.\",\n\t\t\t\"// Pattern explanation:\",\n\t\t\t\"// (^ [\\\\t ]* ) -> capture indentation at start of line (multiline mode)\",\n\t\t\t\"// /\\\\*\\\\* -> opening delimiter\",\n\t\t\t\"// ( -> capture group 2 body\",\n\t\t\t\"// (?:[^*] -> any non-* char\",\n\t\t\t\"// |\\\\*(?!/) -> or a * not followed by /\",\n\t\t\t\"// )* -> repeated greedily (cannot cross closing */)\",\n\t\t\t\"// )\",\n\t\t\t\"// \\\\n?[\\\\t ]*\\\\*/ -> optional newline + trailing indent + closing */\",\n\t\t\t\"// The greedy repetition is safe because the inner alternatives are mutually\",\n\t\t\t\"// exclusive and each consumes at least one char without overlapping on the\",\n\t\t\t\"// closing sentinel.\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(snippet, {\n\t\t\twidth: 100,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// Should be converted to a JSDoc (first and last lines delimiters).\n\t\tconst lines = out.split(/\\n/);\n\t\texpect(lines[0].trim()).toBe(\"/**\");\n\t\texpect(lines[lines.length - 1].trim()).toBe(\"*/\");\n\t\t// Ensure representative internal lines are present (now without leading //).\n\t\texpect(out).toContain(\"* JSDoc block extraction:\");\n\t\texpect(out).toContain(\"* Pattern explanation:\");\n\t\t// Arrow lines preserved.\n\t\tconst arrowLineCount = lines.filter((l) => /->/.test(l)).length;\n\t\texpect(arrowLineCount).toBeGreaterThanOrEqual(5);\n\t});\n\n\tit(\"does not insert period before list introduced by single lowercase word + colon\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Some utilities have logging capabilities that needs to be\",\n\t\t\t\" * tested a little bit differently:\",\n\t\t\t\" * - mocking process.exit\",\n\t\t\t\" * - console.log\",\n\t\t\t\" * - inquirer.prompt\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Ensure no stray period after 'be'.\n\t\texpect(out).not.toMatch(/needs to be\\./);\n\t\t// Ensure list items unchanged.\n\t\texpect(out).toMatch(/- mocking process\\.exit/);\n\t});\n\n\tit(\"does not add stray period before lowercase colon line inside jsdoc\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Some utilities have logging capabilities that needs to be\",\n\t\t\t\" * tested a little bit differently:\",\n\t\t\t\" * - one\",\n\t\t\t\" * - two\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\texpect(out).not.toMatch(/needs to be\\./);\n\t});\n\n\tit(\"does not append periods to every line of a merged // comment group\", () => {\n\t\tconst original = [\n\t\t\t\"// We only want to add terminal punctuation once at the end of the merged\",\n\t\t\t\"// paragraph, not after every original line (which can create spurious\",\n\t\t\t\"// periods mid-sentence when lines were simple wraps). We also avoid\",\n\t\t\t\"// appending a period if the final line ends with a colon introducing a\",\n\t\t\t\"// list.\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(original, {\n\t\t\twidth: 160, // keep wide to avoid secondary wrapping noise\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// Ensure it became a JSDoc block.\n\t\texpect(out.startsWith(\"/**\")).toBe(true);\n\t\t// Should not contain stray periods after former line breaks.\n\t\texpect(out).not.toMatch(/merged\\./);\n\t\texpect(out).not.toMatch(/spurious\\./);\n\t\texpect(out).not.toMatch(/introducing a\\./);\n\t\t// Should still retain existing legitimate period after 'wraps).' and final\n\t\t// 'list.'\n\t\texpect(out).toMatch(/wraps\\)\\./);\n\t\texpect(out).toMatch(/list\\./);\n\t});\n\n\tit(\"merges a large explanatory // group after a statement into JSDoc\", () => {\n\t\tconst src = [\n\t\t\t\"const value = compute();\",\n\t\t\t\"// We only want to add terminal punctuation once at the end of the merged\",\n\t\t\t\"// paragraph, not after every original line (which can create spurious\",\n\t\t\t\"// periods mid-sentence when lines were simple wraps). We also avoid\",\n\t\t\t\"// appending a period if the final line ends with a colon introducing a\",\n\t\t\t\"// list.\",\n\t\t\t\"function next() {}\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 160,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// Expect merged into a JSDoc immediately after the statement.\n\t\tconst re = /compute\\(\\);\\n\\/\\*\\*[\\s\\S]*?\\n\\s*\\*\\//;\n\t\texpect(re.test(out)).toBe(true);\n\t\t// Ensure only one sentence-final period appended (present on final 'list.'\n\t\t// already).\n\t\texpect(out.match(/merged\\./)).toBeNull();\n\t});\n\n\tit(\"merges a 3-line explanatory // group after a statement into JSDoc (threshold lowered)\", () => {\n\t\tconst src = [\n\t\t\t\"const value = compute();\",\n\t\t\t\"// First explanatory line that starts with a capital\",\n\t\t\t\"// second line continues the explanation\",\n\t\t\t\"// third line completes it\",\n\t\t\t\"next();\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 120,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// The 3-line group should have been converted to a JSDoc block.\n\t\texpect(/compute\\(\\);\\n\\/\\*\\*/.test(out)).toBe(true);\n\t\t// Ensure original lines no longer start with // (inside block now) and period\n\t\t// appended once.\n\t\texpect(out).toMatch(/First explanatory line that starts with a capital/);\n\t\texpect(out).not.toMatch(/third line completes it\\n\\/\\//); // no stray // after\n\t});\n\n\tit(\"keeps a space before closing delimiter for complex multi-paragraph example\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Extract the average from a list of numbers.\",\n\t\t\t\" *\",\n\t\t\t\" * @example\",\n\t\t\t\" * ```js\",\n\t\t\t\" * const res = extractAverage({ data: [11, 22, 33, 44] });\",\n\t\t\t\" * console.log(res); // 27.5 -> (11 + 22 + 33 + 44) / 4\",\n\t\t\t\" * ```\",\n\t\t\t\" *\",\n\t\t\t\" * Any value that is not a number or is less than 0 will be ignored.\",\n\t\t\t\" *\",\n\t\t\t\" * A formatter function can be passed to format the output. If no formatter is\",\n\t\t\t\" * provided, the default behavior is to cast the number to the generic Output\",\n\t\t\t\" * type.\",\n\t\t\t\" *\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Closing line should contain a leading space before */ (style consistency).\n\t\texpect(/\\n \\*\\/$/.test(out)).toBe(true);\n\t\t// Should not regress by emitting no-space variant.\n\t\texpect(/\\n\\*\\/$/.test(out)).toBe(false);\n\t});\n\n\tit(\"does not add trailing space on blank '*' lines inside code fences\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Function to format a number with commas as thousands separators.\",\n\t\t\t\" *\",\n\t\t\t\" * @example\",\n\t\t\t\" * ```js\",\n\t\t\t\" * const formattedNumber = numberFormatter.format(1000);\",\n\t\t\t' * console.log(formattedNumber); // \"1,000\"',\n\t\t\t\" *\", // blank line inside fence we want ' *' (no trailing space)\n\t\t\t\" * const roundedNumber = numberFormatter.format(1234.56);\",\n\t\t\t' * console.log(roundedNumber); // \"1,235\"',\n\t\t\t\" * ```\",\n\t\t\t\" *\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Collect lines that are just star forms.\n\t\tconst starLines = out.split(/\\n/).filter((l) => /^ \\* ?$/.test(l));\n\t\t// None of them should have a trailing space after the star.\n\t\tfor (const l of starLines) {\n\t\t\texpect(l).toBe(\" *\");\n\t\t}\n\t});\n\n\tit(\"isolates mid-paragraph NOTE sentence inside a JSDoc block\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * This operation performs work and may take time NOTE: use with caution if running on production systems. It returns a result\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 120,\n\t\t}).transformed;\n\t\t// Ensure NOTE is on its own line.\n\t\texpect(\n\t\t\t/\\n \\* NOTE: use with caution if running on production systems\\.\\n/.test(\n\t\t\t\tout,\n\t\t\t),\n\t\t).toBe(true);\n\t\t// Preceding sentence fragments present and end with period somewhere before\n\t\t// NOTE line.\n\t\texpect(out).toMatch(/This operation performs work and may take time\\n/);\n\t\t// Trailing sentence appears after NOTE line with terminal period.\n\t\texpect(/NOTE: use with caution[\\s\\S]*It returns a result\\./.test(out)).toBe(\n\t\t\ttrue,\n\t\t);\n\t});\n\n\tit(\"keeps NOTE at start of JSDoc on its own line with added period\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * note: must initialize the runtime before calling any methods\",\n\t\t\t\" * subsequent calls depend on global state\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\tconst noteLine = out\n\t\t\t.split(/\\n/)\n\t\t\t.find((l) => /NOTE: must initialize/.test(l));\n\t\texpect(noteLine).toBeDefined();\n\t\texpect(/\\.$/.test(noteLine || \"\")).toBe(true);\n\t\texpect(out).toMatch(/subsequent calls depend on global state\\./);\n\t});\n\n\tit(\"isolates multiple NOTE sentences within merged // comment group\", () => {\n\t\tconst src = [\n\t\t\t\"// This performs the main task.\",\n\t\t\t\"// NOTE: side effects may occur.\",\n\t\t\t\"// It then produces output.\",\n\t\t\t\"// NOTE: results are cached for 5 minutes.\",\n\t\t\t\"function run() {}\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 160,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\tconst noteLines = out.split(/\\n/).filter((l) => /NOTE: /.test(l));\n\t\texpect(noteLines.length).toBe(2);\n\t\tfor (const l of noteLines) {\n\t\t\texpect(l.trim()).toMatch(/\\.$/);\n\t\t}\n\t});\n\n\tit(\"splits consecutive NOTE sentences in a single paragraph\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * This does work. NOTE: first caveat. NOTE: second caveat. Done\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\tconst lines = out.split(/\\n/).filter((l) => /^ \\* /.test(l));\n\t\tconst notes = lines.filter((l) => /NOTE: /.test(l));\n\t\texpect(notes.length).toBe(2);\n\t\tfor (const l of notes) {\n\t\t\texpect(l).toMatch(/\\.$/);\n\t\t}\n\t\texpect(out).toMatch(/Done\\./);\n\t});\n\n\tit(\"detects NOTE after exclamation boundary\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Success! NOTE: edge case follows. Continue processing\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 120,\n\t\t}).transformed;\n\t\texpect(out).toMatch(/Success!\\n \\* NOTE: edge case follows\\./);\n\t\texpect(out).toMatch(/Continue processing\\./);\n\t});\n\n\tit(\"does not force period on NOTE line ending with continuation word when followed by continuation line\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * NOTE: we are still using the good old lodash uniqueId when the code is not in\",\n\t\t\t\" * production, so that the results are a little bit more consistent tests after\",\n\t\t\t\" * test instead of being completely random.\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\t// New structural policy: first NOTE line always gets period even if followed\n\t\t// by prose.\n\t\texpect(out).toMatch(/when the code is not in\\./);\n\t\t// Ensure final sentence has period.\n\t\texpect(/completely random\\./.test(out)).toBe(true);\n\t});\n});\n"],"names":["fs","os","path","describe","expect","it","expandGlobs","diffLines","parseAndTransformComments","baseOpts","width","wrapLineComments","mergeLineComments","input","res","transformed","toContain","test","toBe","split","length","toBeGreaterThan","join","first","second","lines","filter","l","out","toMatch","tmpRoot","mkdtempSync","tmpdir","a","nestedDir","mkdirSync","recursive","b","writeFileSync","shallow","sort","toEqual","deep","src","not","snippet","trim","arrowLineCount","toBeGreaterThanOrEqual","original","startsWith","re","match","toBeNull","starLines","noteLine","find","toBeDefined","noteLines","notes"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAOA,QAAQ,UAAU;AACzB,OAAOC,QAAQ,UAAU;AACzB,OAAOC,UAAU,YAAY;AAC7B,SAASC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAC9C,SAASC,WAAW,QAAQ,aAAa;AACzC,SAASC,SAAS,EAAEC,yBAAyB,QAAQ,YAAY;AAEjE,IAAMC,WAAW;IAChBC,OAAO;IACPC,kBAAkB;IAClBC,mBAAmB;AACpB;AAEAT,SAAS,6BAA6B;IACrCE,GAAG,0CAA0C;QAC5C,IAAMQ,QAAQ;QACd,IAAMC,MAAMN,0BAA0BK,OAAOJ;QAC7CL,OAAOU,IAAIC,WAAW,EAAEC,SAAS,CAAC;IACnC;IAEAX,GAAG,kCAAkC;QACpC,IAAMQ,QAAQ;QACd,IAAMC,MAAMN,0BAA0BK,OAAOJ;QAC7CL,OAAO,kBAAkBa,IAAI,CAACH,IAAIC,WAAW,GAAGG,IAAI,CAAC;IACtD;IAEAb,GAAG,8BAA8B;QAChC,IAAMQ,QACL;QACD,IAAMC,MAAMN,0BAA0BK,OAAOJ;QAC7CL,OAAOU,IAAIC,WAAW,CAACI,KAAK,CAAC,MAAMC,MAAM,EAAEC,eAAe,CAAC;IAC5D;IAEAhB,GAAG,6CAA6C;QAC/C,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMR,MAAMN,0BAA0BK,OAAO,wCACzCJ;YACHG,mBAAmB;;QAEpBR,OAAO,SAASa,IAAI,CAACH,IAAIC,WAAW,GAAGG,IAAI,CAAC;IAC7C;IAEAb,GAAG,yCAAyC;QAC3C,IAAMQ,QAAQ;QACd,IAAMU,QAAQf,0BAA0BK,OAAOJ,UAAUM,WAAW;QACpE,IAAMS,SAAShB,0BAA0Be,OAAOd,UAAUM,WAAW;QACrEX,OAAOG,UAAUgB,OAAOC,SAASN,IAAI,CAAC;IACvC;IAEAb,GAAG,+BAA+B;QACjC,IAAMQ,QACL;QACD,IAAMC,MAAMN,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClEX,OAAO,QAAQa,IAAI,CAACH,MAAMI,IAAI,CAAC;QAC/B,8CAA8C;QAC9Cd,OAAO,oBAAoBa,IAAI,CAACH,MAAMI,IAAI,CAAC;IAC5C;IAEAb,GAAG,8CAA8C;QAChD,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMR,MAAMN,0BAA0BK,OAAO,wCACzCJ;YACHG,mBAAmB;;QAEpBR,OAAO,iBAAiBa,IAAI,CAACH,IAAIC,WAAW,GAAGG,IAAI,CAAC;QACpD,sEAAsE;QACtEd,OAAO,SAASa,IAAI,CAACH,IAAIC,WAAW,GAAGG,IAAI,CAAC;IAC7C;IAEAb,GAAG,gDAAgD;QAClD,IAAMQ,QACL;QACD,IAAMC,MAAMN,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClE,4DAA4D;QAC5D,IAAMU,QAAQX,IAAIK,KAAK,CAAC,MAAMO,MAAM,CAAC,SAACC;mBAAM,SAASV,IAAI,CAACU;;QAC1DvB,OAAOqB,MAAML,MAAM,EAAEF,IAAI,CAAC;IAC3B;IAEAb,GAAG,2BAA2B;QAC7B,IAAMQ,QACL;QACD,IAAMC,MAAMN,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClEX,OAAO,iBAAiBa,IAAI,CAACH,MAAMI,IAAI,CAAC;QACxCd,OAAO,qBAAqBa,IAAI,CAACH,MAAMI,IAAI,CAAC;IAC7C;IAEAb,GAAG,oFAAoF;QACtF,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClE,yDAAyD;QACzDX,OAAOwB,KAAKC,OAAO,CAAC;QACpB,uEAAuE;QACvEzB,OAAOwB,KAAKC,OAAO,CAAC;QACpB;;;;;GAKC,GACDzB,OAAO,aAAaa,IAAI,CAACW,MAAMV,IAAI,CAAC;QACpC,gCAAgC;QAChCd,OAAOwB,KAAKC,OAAO,CAAC;QACpBzB,OAAOwB,KAAKC,OAAO,CAAC;QACpB,0CAA0C;QAC1CzB,OAAOwB,KAAKC,OAAO,CAAC;IACrB;IAEAxB,GAAG,iEAAiE;QACnE,IAAMyB,UAAU9B,GAAG+B,WAAW,CAAC7B,KAAKoB,IAAI,CAACrB,GAAG+B,MAAM,IAAI;QACtD,IAAMC,IAAI/B,KAAKoB,IAAI,CAACQ,SAAS;QAC7B,IAAMI,YAAYhC,KAAKoB,IAAI,CAACQ,SAAS;QACrC9B,GAAGmC,SAAS,CAACD,WAAW;YAAEE,WAAW;QAAK;QAC1C,IAAMC,IAAInC,KAAKoB,IAAI,CAACY,WAAW;QAC/BlC,GAAGsC,aAAa,CAACL,GAAG,aAAa;QACjCjC,GAAGsC,aAAa,CAACD,GAAG,aAAa;QACjC,kDAAkD;QAClD,IAAME,UAAUjC,YAAY;YAAE,GAAU,OAARwB,SAAQ;SAAO,EAAEU,IAAI;QACrDpC,OAAOmC,SAASE,OAAO,CAAC;YAACR;SAAE;QAC3B,6BAA6B;QAC7B,IAAMS,OAAOpC,YAAY;YAAE,GAAU,OAARwB,SAAQ;SAAU,EAAEU,IAAI;QACrDpC,OAAOsC,MAAMD,OAAO,CAAC;YAACR;YAAGI;SAAE,CAACG,IAAI;IACjC;IAEA;;;EAGC,GACDnC,GAAG,mFAAmF;QACrF,IAAMsC,MAAM;YACX;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACrB,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BmC,KAAK;YAC1C/B,mBAAmB;YACnBD,kBAAkB;YAClBD,OAAO;QACR,GAAGK,WAAW;QACdX,OAAOwB,KAAKZ,SAAS,CAAC;QACtBZ,OAAOwB,KAAKZ,SAAS,CAAC;QACtBZ,OAAOwB,KAAKZ,SAAS,CAAC;QACtBZ,OAAOwB,KAAKgB,GAAG,CAAC5B,SAAS,CAAC;IAC3B;IAEAX,GAAG,mGAAmG;QACrG,IAAMwC,UAAU;YACf;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACvB,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BqC,SAAS;YAC9CnC,OAAO;YACPC,kBAAkB;YAClBC,mBAAmB;QACpB,GAAGG,WAAW;QACd,oEAAoE;QACpE,IAAMU,QAAQG,IAAIT,KAAK,CAAC;QACxBf,OAAOqB,KAAK,CAAC,EAAE,CAACqB,IAAI,IAAI5B,IAAI,CAAC;QAC7Bd,OAAOqB,KAAK,CAACA,MAAML,MAAM,GAAG,EAAE,CAAC0B,IAAI,IAAI5B,IAAI,CAAC;QAC5C,6EAA6E;QAC7Ed,OAAOwB,KAAKZ,SAAS,CAAC;QACtBZ,OAAOwB,KAAKZ,SAAS,CAAC;QACtB,yBAAyB;QACzB,IAAM+B,iBAAiBtB,MAAMC,MAAM,CAAC,SAACC;mBAAM,KAAKV,IAAI,CAACU;WAAIP,MAAM;QAC/DhB,OAAO2C,gBAAgBC,sBAAsB,CAAC;IAC/C;IAEA3C,GAAG,kFAAkF;QACpF,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClE,qCAAqC;QACrCX,OAAOwB,KAAKgB,GAAG,CAACf,OAAO,CAAC;QACxB,+BAA+B;QAC/BzB,OAAOwB,KAAKC,OAAO,CAAC;IACrB;IAEAxB,GAAG,sEAAsE;QACxE,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClEX,OAAOwB,KAAKgB,GAAG,CAACf,OAAO,CAAC;IACzB;IAEAxB,GAAG,sEAAsE;QACxE,IAAM4C,WAAW;YAChB;YACA;YACA;YACA;YACA;SACA,CAAC3B,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0ByC,UAAU;YAC/CvC,OAAO;YACPC,kBAAkB;YAClBC,mBAAmB;QACpB,GAAGG,WAAW;QACd,kCAAkC;QAClCX,OAAOwB,IAAIsB,UAAU,CAAC,QAAQhC,IAAI,CAAC;QACnC,6DAA6D;QAC7Dd,OAAOwB,KAAKgB,GAAG,CAACf,OAAO,CAAC;QACxBzB,OAAOwB,KAAKgB,GAAG,CAACf,OAAO,CAAC;QACxBzB,OAAOwB,KAAKgB,GAAG,CAACf,OAAO,CAAC;QACxB,2EAA2E;QAC3E,UAAU;QACVzB,OAAOwB,KAAKC,OAAO,CAAC;QACpBzB,OAAOwB,KAAKC,OAAO,CAAC;IACrB;IAEAxB,GAAG,oEAAoE;QACtE,IAAMsC,MAAM;YACX;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACrB,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BmC,KAAK;YAC1CjC,OAAO;YACPC,kBAAkB;YAClBC,mBAAmB;QACpB,GAAGG,WAAW;QACd,8DAA8D;QAC9D,IAAMoC,KAAK;QACX/C,OAAO+C,GAAGlC,IAAI,CAACW,MAAMV,IAAI,CAAC;QAC1B,2EAA2E;QAC3E,YAAY;QACZd,OAAOwB,IAAIwB,KAAK,CAAC,aAAaC,QAAQ;IACvC;IAEAhD,GAAG,yFAAyF;QAC3F,IAAMsC,MAAM;YACX;YACA;YACA;YACA;YACA;SACA,CAACrB,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BmC,KAAK;YAC1CjC,OAAO;YACPC,kBAAkB;YAClBC,mBAAmB;QACpB,GAAGG,WAAW;QACd,gEAAgE;QAChEX,OAAO,uBAAuBa,IAAI,CAACW,MAAMV,IAAI,CAAC;QAC9C,8EAA8E;QAC9E,iBAAiB;QACjBd,OAAOwB,KAAKC,OAAO,CAAC;QACpBzB,OAAOwB,KAAKgB,GAAG,CAACf,OAAO,CAAC,kCAAkC,oBAAoB;IAC/E;IAEAxB,GAAG,8EAA8E;QAChF,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClE,6EAA6E;QAC7EX,OAAO,WAAWa,IAAI,CAACW,MAAMV,IAAI,CAAC;QAClC,mDAAmD;QACnDd,OAAO,UAAUa,IAAI,CAACW,MAAMV,IAAI,CAAC;IAClC;IAEAb,GAAG,qEAAqE;QACvE,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAOJ,UAAUM,WAAW;QAClE,0CAA0C;QAC1C,IAAMuC,YAAY1B,IAAIT,KAAK,CAAC,MAAMO,MAAM,CAAC,SAACC;mBAAM,UAAUV,IAAI,CAACU;;YAE1D,kCAAA,2BAAA;;YADL,4DAA4D;YAC5D,QAAK,YAAW2B,8BAAX,SAAA,6BAAA,QAAA,yBAAA,iCAAsB;gBAAtB,IAAM3B,IAAN;gBACJvB,OAAOuB,GAAGT,IAAI,CAAC;YAChB;;YAFK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAGN;IAEAb,GAAG,6DAA6D;QAC/D,IAAMQ,QAAQ;YACb;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAO,wCACzCJ;YACHC,OAAO;YACLK,WAAW;QACd,kCAAkC;QAClCX,OACC,oEAAoEa,IAAI,CACvEW,MAEAV,IAAI,CAAC;QACP,4EAA4E;QAC5E,aAAa;QACbd,OAAOwB,KAAKC,OAAO,CAAC;QACpB,kEAAkE;QAClEzB,OAAO,qDAAqDa,IAAI,CAACW,MAAMV,IAAI,CAC1E;IAEF;IAEAb,GAAG,kEAAkE;QACpE,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAO,wCACzCJ;YACHC,OAAO;YACLK,WAAW;QACd,IAAMwC,WAAW3B,IACfT,KAAK,CAAC,MACNqC,IAAI,CAAC,SAAC7B;mBAAM,wBAAwBV,IAAI,CAACU;;QAC3CvB,OAAOmD,UAAUE,WAAW;QAC5BrD,OAAO,MAAMa,IAAI,CAACsC,YAAY,KAAKrC,IAAI,CAAC;QACxCd,OAAOwB,KAAKC,OAAO,CAAC;IACrB;IAEAxB,GAAG,mEAAmE;QACrE,IAAMsC,MAAM;YACX;YACA;YACA;YACA;YACA;SACA,CAACrB,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BmC,KAAK;YAC1CjC,OAAO;YACPC,kBAAkB;YAClBC,mBAAmB;QACpB,GAAGG,WAAW;QACd,IAAM2C,YAAY9B,IAAIT,KAAK,CAAC,MAAMO,MAAM,CAAC,SAACC;mBAAM,SAASV,IAAI,CAACU;;QAC9DvB,OAAOsD,UAAUtC,MAAM,EAAEF,IAAI,CAAC;YACzB,kCAAA,2BAAA;;YAAL,QAAK,YAAWwC,8BAAX,SAAA,6BAAA,QAAA,yBAAA,iCAAsB;gBAAtB,IAAM/B,IAAN;gBACJvB,OAAOuB,EAAEmB,IAAI,IAAIjB,OAAO,CAAC;YAC1B;;YAFK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAGN;IAEAxB,GAAG,2DAA2D;QAC7D,IAAMQ,QAAQ;YACb;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAO,wCACzCJ;YACHC,OAAO;YACLK,WAAW;QACd,IAAMU,QAAQG,IAAIT,KAAK,CAAC,MAAMO,MAAM,CAAC,SAACC;mBAAM,QAAQV,IAAI,CAACU;;QACzD,IAAMgC,QAAQlC,MAAMC,MAAM,CAAC,SAACC;mBAAM,SAASV,IAAI,CAACU;;QAChDvB,OAAOuD,MAAMvC,MAAM,EAAEF,IAAI,CAAC;YACrB,kCAAA,2BAAA;;YAAL,QAAK,YAAWyC,0BAAX,SAAA,6BAAA,QAAA,yBAAA,iCAAkB;gBAAlB,IAAMhC,IAAN;gBACJvB,OAAOuB,GAAGE,OAAO,CAAC;YACnB;;YAFK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;QAGLzB,OAAOwB,KAAKC,OAAO,CAAC;IACrB;IAEAxB,GAAG,2CAA2C;QAC7C,IAAMQ,QAAQ;YACb;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAO,wCACzCJ;YACHC,OAAO;YACLK,WAAW;QACdX,OAAOwB,KAAKC,OAAO,CAAC;QACpBzB,OAAOwB,KAAKC,OAAO,CAAC;IACrB;IAEAxB,GAAG,uGAAuG;QACzG,IAAMQ,QAAQ;YACb;YACA;YACA;YACA;YACA;SACA,CAACS,IAAI,CAAC;QACP,IAAMM,MAAMpB,0BAA0BK,OAAO,wCACzCJ;YACHC,OAAO;YACLK,WAAW;QACd,6EAA6E;QAC7E,YAAY;QACZX,OAAOwB,KAAKC,OAAO,CAAC;QACpB,oCAAoC;QACpCzB,OAAO,sBAAsBa,IAAI,CAACW,MAAMV,IAAI,CAAC;IAC9C;AACD"}
1
+ {"version":3,"sources":["../../src/__tests__/lib.test.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { describe, expect, it } from \"vitest\";\nimport { expandGlobs } from \"../glob.js\";\nimport { diffLines, parseAndTransformComments } from \"../lib.js\";\n\nconst baseOpts = {\n\twidth: 60,\n\twrapLineComments: true,\n\tmergeLineComments: false,\n};\n\ndescribe(\"parseAndTransformComments\", () => {\n\tit(\"reflows a simple jsdoc and adds period\", () => {\n\t\tconst input = \"/**\\n * This function does something\\n * important\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts);\n\t\texpect(res.transformed).toContain(\"does something important.\");\n\t});\n\n\tit(\"normalizes NOTE capitalization\", () => {\n\t\tconst input = \"/**\\n * note: edge case\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts);\n\t\texpect(/NOTE: edge case/.test(res.transformed)).toBe(true);\n\t});\n\n\tit(\"wraps single line comments\", () => {\n\t\tconst input =\n\t\t\t\"// this is a very long comment that should be wrapped across multiple lines to satisfy width\";\n\t\tconst res = parseAndTransformComments(input, baseOpts);\n\t\texpect(res.transformed.split(/\\n/).length).toBeGreaterThan(1);\n\t});\n\n\tit(\"merges groups of line comments into jsdoc\", () => {\n\t\tconst input = [\n\t\t\t\"// first line\",\n\t\t\t\"// second line\",\n\t\t\t\"// third line\",\n\t\t\t\"const x = 1;\",\n\t\t].join(\"\\n\");\n\t\tconst res = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\tmergeLineComments: true,\n\t\t});\n\t\texpect(/\\/\\*\\*/.test(res.transformed)).toBe(true);\n\t});\n\n\tit(\"is idempotent (second pass unchanged)\", () => {\n\t\tconst input = \"/**\\n * Example doc that will be normalized\\n */\";\n\t\tconst first = parseAndTransformComments(input, baseOpts).transformed;\n\t\tconst second = parseAndTransformComments(first, baseOpts).transformed;\n\t\texpect(diffLines(first, second)).toBe(\"\");\n\t});\n\n\tit(\"preserves code fence blocks\", () => {\n\t\tconst input =\n\t\t\t\"/**\\n * Before\\n * ```js\\n * const x= 1; \\n * ```\\n * After\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts).transformed;\n\t\texpect(/```js/.test(res)).toBe(true);\n\t\t// ensure spacing inside fence not normalized.\n\t\texpect(/const {2}x= {2}1;/.test(res)).toBe(true);\n\t});\n\n\tit(\"does not merge directive or license groups\", () => {\n\t\tconst input = [\n\t\t\t\"// eslint-disable-next-line\",\n\t\t\t\"// second line should prevent merge due to directive\",\n\t\t\t\"const y = 2;\",\n\t\t\t\"// Copyright 2024 Example\",\n\t\t\t\"// another line\",\n\t\t].join(\"\\n\");\n\t\tconst res = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\tmergeLineComments: true,\n\t\t});\n\t\texpect(/eslint-disable/.test(res.transformed)).toBe(true);\n\t\t// Should not have merged into a jsdoc (no /** directly before const).\n\t\texpect(/\\/\\*\\*/.test(res.transformed)).toBe(false);\n\t});\n\n\tit(\"list items are not reflowed into a paragraph\", () => {\n\t\tconst input =\n\t\t\t\"/**\\n * First line explaining.\\n * - item one more words here\\n * - item two\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// each list item remains on its own line with leading dash.\n\t\tconst lines = res.split(/\\n/).filter((l) => /- item/.test(l));\n\t\texpect(lines.length).toBe(2);\n\t});\n\n\tit(\"tag lines are preserved\", () => {\n\t\tconst input =\n\t\t\t\"/**\\n * short description\\n * @param x value\\n * @returns something\\n */\";\n\t\tconst res = parseAndTransformComments(input, baseOpts).transformed;\n\t\texpect(/@param x value/.test(res)).toBe(true);\n\t\texpect(/@returns something/.test(res)).toBe(true);\n\t});\n\n\tit(\"handles heading-like lines with colon and visually indented code & numeric lists\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Overview:\",\n\t\t\t\" * const x = 1;\",\n\t\t\t\" * 1. first\",\n\t\t\t\" * 2. second\",\n\t\t\t\" *\", // blank separation\n\t\t\t\" * Another paragraph without period\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Heading preserved, not merged into previous paragraph.\n\t\texpect(out).toMatch(/Overview:/);\n\t\t// Visually indented code line kept as-is (no extra wrapping collapse).\n\t\texpect(out).toMatch(/const\\s{3}x = 1;/);\n\t\t/**\n\t\t * Trailing blank before closing is optional after recent trimming change. (we\n\t\t * only keep it when multiple paragraphs exist). Assert closing exists.\n\t\t * Closing delimiter should be on its own line; allow optional leading\n\t\t * space(s).\n\t\t */\n\t\texpect(/\\n\\s*\\*\\/$/.test(out)).toBe(true);\n\t\t// Numeric list lines preserved.\n\t\texpect(out).toMatch(/1\\. first/);\n\t\texpect(out).toMatch(/2\\. second/);\n\t\t// Trailing paragraph got terminal period.\n\t\texpect(out).toMatch(/Another paragraph without period\\./);\n\t});\n\n\tit(\"glob expansion matches deep files only with explicit globstar\", () => {\n\t\tconst tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob-\"));\n\t\tconst a = path.join(tmpRoot, \"a.ts\");\n\t\tconst nestedDir = path.join(tmpRoot, \"nested\");\n\t\tfs.mkdirSync(nestedDir, { recursive: true });\n\t\tconst b = path.join(nestedDir, \"b.ts\");\n\t\tfs.writeFileSync(a, \"// file a\", \"utf8\");\n\t\tfs.writeFileSync(b, \"// file b\", \"utf8\");\n\t\t// Shallow pattern should only see top-level a.ts.\n\t\tconst shallow = expandGlobs([`${tmpRoot}/*.ts`]).sort();\n\t\texpect(shallow).toEqual([a]);\n\t\t// Deep pattern matches both.\n\t\tconst deep = expandGlobs([`${tmpRoot}/**/*.ts`]).sort();\n\t\texpect(deep).toEqual([a, b].sort());\n\t});\n\n\t/**\n\t * Added test for preserving multi-line // comment groups that should NOT merge\n\t * when preceded by code.\n\t */\n\tit(\"does not merge an inline explanatory multi-line // comment group following code\", () => {\n\t\tconst src = [\n\t\t\t\"function demo() {\",\n\t\t\t\" const x = 1; // keep\",\n\t\t\t\" // first line explains next block\",\n\t\t\t\" // still explaining\",\n\t\t\t\" // final line\",\n\t\t\t\" return x; // done\",\n\t\t\t\"}\",\n\t\t\t\"\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\tmergeLineComments: true,\n\t\t\twrapLineComments: true,\n\t\t\twidth: 80,\n\t\t}).transformed;\n\t\texpect(out).toContain(\" // first line explains next block\");\n\t\texpect(out).toContain(\" // still explaining\");\n\t\texpect(out).toContain(\" // final line\");\n\t\texpect(out).not.toContain(\"/**\");\n\t});\n\n\tit(\"converts the multi-line explanatory block from lib.ts into a multi-line JSDoc, preserving lines\", () => {\n\t\tconst snippet = [\n\t\t\t\"// JSDoc block extraction:\",\n\t\t\t\"// Previous pattern used a lazy dot-all: ([\\\\s\\\\S]*?) which could, under\",\n\t\t\t\"// pathological inputs, produce excessive backtracking. We replace it with a\",\n\t\t\t\"// tempered pattern that advances linearly by never letting the inner part\",\n\t\t\t\"// consume a closing '*/'. This avoids catastrophic behavior while keeping\",\n\t\t\t\"// correctness.\",\n\t\t\t\"// Pattern explanation:\",\n\t\t\t\"// (^ [\\\\t ]* ) -> capture indentation at start of line (multiline mode)\",\n\t\t\t\"// /\\\\*\\\\* -> opening delimiter\",\n\t\t\t\"// ( -> capture group 2 body\",\n\t\t\t\"// (?:[^*] -> any non-* char\",\n\t\t\t\"// |\\\\*(?!/) -> or a * not followed by /\",\n\t\t\t\"// )* -> repeated greedily (cannot cross closing */)\",\n\t\t\t\"// )\",\n\t\t\t\"// \\\\n?[\\\\t ]*\\\\*/ -> optional newline + trailing indent + closing */\",\n\t\t\t\"// The greedy repetition is safe because the inner alternatives are mutually\",\n\t\t\t\"// exclusive and each consumes at least one char without overlapping on the\",\n\t\t\t\"// closing sentinel.\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(snippet, {\n\t\t\twidth: 100,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// Should be converted to a JSDoc (first and last lines delimiters).\n\t\tconst lines = out.split(/\\n/);\n\t\texpect(lines[0].trim()).toBe(\"/**\");\n\t\texpect(lines[lines.length - 1].trim()).toBe(\"*/\");\n\t\t// Ensure representative internal lines are present (now without leading //).\n\t\texpect(out).toContain(\"* JSDoc block extraction:\");\n\t\texpect(out).toContain(\"* Pattern explanation:\");\n\t\t// Arrow lines preserved.\n\t\tconst arrowLineCount = lines.filter((l) => /->/.test(l)).length;\n\t\texpect(arrowLineCount).toBeGreaterThanOrEqual(5);\n\t});\n\n\tit(\"does not insert period before list introduced by single lowercase word + colon\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Some utilities have logging capabilities that needs to be\",\n\t\t\t\" * tested a little bit differently:\",\n\t\t\t\" * - mocking process.exit\",\n\t\t\t\" * - console.log\",\n\t\t\t\" * - inquirer.prompt\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Ensure no stray period after 'be'.\n\t\texpect(out).not.toMatch(/needs to be\\./);\n\t\t// Ensure list items unchanged.\n\t\texpect(out).toMatch(/- mocking process\\.exit/);\n\t});\n\n\tit(\"does not add stray period before lowercase colon line inside jsdoc\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Some utilities have logging capabilities that needs to be\",\n\t\t\t\" * tested a little bit differently:\",\n\t\t\t\" * - one\",\n\t\t\t\" * - two\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\texpect(out).not.toMatch(/needs to be\\./);\n\t});\n\n\tit(\"does not append periods to every line of a merged // comment group\", () => {\n\t\tconst original = [\n\t\t\t\"// We only want to add terminal punctuation once at the end of the merged\",\n\t\t\t\"// paragraph, not after every original line (which can create spurious\",\n\t\t\t\"// periods mid-sentence when lines were simple wraps). We also avoid\",\n\t\t\t\"// appending a period if the final line ends with a colon introducing a\",\n\t\t\t\"// list.\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(original, {\n\t\t\twidth: 160, // keep wide to avoid secondary wrapping noise\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// Ensure it became a JSDoc block.\n\t\texpect(out.startsWith(\"/**\")).toBe(true);\n\t\t// Should not contain stray periods after former line breaks.\n\t\texpect(out).not.toMatch(/merged\\./);\n\t\texpect(out).not.toMatch(/spurious\\./);\n\t\texpect(out).not.toMatch(/introducing a\\./);\n\t\t// Should still retain existing legitimate period after 'wraps).' and final\n\t\t// 'list.'\n\t\texpect(out).toMatch(/wraps\\)\\./);\n\t\texpect(out).toMatch(/list\\./);\n\t});\n\n\tit(\"merges a large explanatory // group after a statement into JSDoc\", () => {\n\t\tconst src = [\n\t\t\t\"const value = compute();\",\n\t\t\t\"// We only want to add terminal punctuation once at the end of the merged\",\n\t\t\t\"// paragraph, not after every original line (which can create spurious\",\n\t\t\t\"// periods mid-sentence when lines were simple wraps). We also avoid\",\n\t\t\t\"// appending a period if the final line ends with a colon introducing a\",\n\t\t\t\"// list.\",\n\t\t\t\"function next() {}\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 160,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// Expect merged into a JSDoc immediately after the statement.\n\t\tconst re = /compute\\(\\);\\n\\/\\*\\*[\\s\\S]*?\\n\\s*\\*\\//;\n\t\texpect(re.test(out)).toBe(true);\n\t\t// Ensure only one sentence-final period appended (present on final 'list.'\n\t\t// already).\n\t\texpect(out.match(/merged\\./)).toBeNull();\n\t});\n\n\tit(\"merges a 3-line explanatory // group after a statement into JSDoc (threshold lowered)\", () => {\n\t\tconst src = [\n\t\t\t\"const value = compute();\",\n\t\t\t\"// First explanatory line that starts with a capital\",\n\t\t\t\"// second line continues the explanation\",\n\t\t\t\"// third line completes it\",\n\t\t\t\"next();\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 120,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\t// The 3-line group should have been converted to a JSDoc block.\n\t\texpect(/compute\\(\\);\\n\\/\\*\\*/.test(out)).toBe(true);\n\t\t// Ensure original lines no longer start with // (inside block now) and period\n\t\t// appended once.\n\t\texpect(out).toMatch(/First explanatory line that starts with a capital/);\n\t\texpect(out).not.toMatch(/third line completes it\\n\\/\\//); // no stray // after\n\t});\n\n\tit(\"keeps a space before closing delimiter for complex multi-paragraph example\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Extract the average from a list of numbers.\",\n\t\t\t\" *\",\n\t\t\t\" * @example\",\n\t\t\t\" * ```js\",\n\t\t\t\" * const res = extractAverage({ data: [11, 22, 33, 44] });\",\n\t\t\t\" * console.log(res); // 27.5 -> (11 + 22 + 33 + 44) / 4\",\n\t\t\t\" * ```\",\n\t\t\t\" *\",\n\t\t\t\" * Any value that is not a number or is less than 0 will be ignored.\",\n\t\t\t\" *\",\n\t\t\t\" * A formatter function can be passed to format the output. If no formatter is\",\n\t\t\t\" * provided, the default behavior is to cast the number to the generic Output\",\n\t\t\t\" * type.\",\n\t\t\t\" *\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Closing line should contain a leading space before */ (style consistency).\n\t\texpect(/\\n \\*\\/$/.test(out)).toBe(true);\n\t\t// Should not regress by emitting no-space variant.\n\t\texpect(/\\n\\*\\/$/.test(out)).toBe(false);\n\t});\n\n\tit(\"does not add trailing space on blank '*' lines inside code fences\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Function to format a number with commas as thousands separators.\",\n\t\t\t\" *\",\n\t\t\t\" * @example\",\n\t\t\t\" * ```js\",\n\t\t\t\" * const formattedNumber = numberFormatter.format(1000);\",\n\t\t\t' * console.log(formattedNumber); // \"1,000\"',\n\t\t\t\" *\", // blank line inside fence we want ' *' (no trailing space)\n\t\t\t\" * const roundedNumber = numberFormatter.format(1234.56);\",\n\t\t\t' * console.log(roundedNumber); // \"1,235\"',\n\t\t\t\" * ```\",\n\t\t\t\" *\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, baseOpts).transformed;\n\t\t// Collect lines that are just star forms.\n\t\tconst starLines = out.split(/\\n/).filter((l) => /^ \\* ?$/.test(l));\n\t\t// None of them should have a trailing space after the star.\n\t\tfor (const l of starLines) {\n\t\t\texpect(l).toBe(\" *\");\n\t\t}\n\t});\n\n\tit(\"isolates mid-paragraph NOTE sentence inside a JSDoc block\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * This operation performs work and may take time NOTE: use with caution if running on production systems. It returns a result\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 120,\n\t\t}).transformed;\n\t\t// Ensure NOTE is on its own line.\n\t\texpect(\n\t\t\t/\\n \\* NOTE: use with caution if running on production systems\\.\\n/.test(\n\t\t\t\tout,\n\t\t\t),\n\t\t).toBe(true);\n\t\t// Preceding sentence fragments present and end with period somewhere before\n\t\t// NOTE line.\n\t\texpect(out).toMatch(/This operation performs work and may take time\\n/);\n\t\t// Trailing sentence appears after NOTE line with terminal period.\n\t\texpect(/NOTE: use with caution[\\s\\S]*It returns a result\\./.test(out)).toBe(\n\t\t\ttrue,\n\t\t);\n\t});\n\n\tit(\"keeps NOTE at start of JSDoc on its own line with added period\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * note: must initialize the runtime before calling any methods\",\n\t\t\t\" * subsequent calls depend on global state\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\tconst noteLine = out\n\t\t\t.split(/\\n/)\n\t\t\t.find((l) => /NOTE: must initialize/.test(l));\n\t\texpect(noteLine).toBeDefined();\n\t\texpect(/\\.$/.test(noteLine || \"\")).toBe(true);\n\t\texpect(out).toMatch(/subsequent calls depend on global state\\./);\n\t});\n\n\tit(\"isolates multiple NOTE sentences within merged // comment group\", () => {\n\t\tconst src = [\n\t\t\t\"// This performs the main task.\",\n\t\t\t\"// NOTE: side effects may occur.\",\n\t\t\t\"// It then produces output.\",\n\t\t\t\"// NOTE: results are cached for 5 minutes.\",\n\t\t\t\"function run() {}\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 160,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\tconst noteLines = out.split(/\\n/).filter((l) => /NOTE: /.test(l));\n\t\texpect(noteLines.length).toBe(2);\n\t\tfor (const l of noteLines) {\n\t\t\texpect(l.trim()).toMatch(/\\.$/);\n\t\t}\n\t});\n\n\tit(\"merges line comments after lines ending with },\", () => {\n\t\tconst src = [\n\t\t\t\"const config = {\",\n\t\t\t'\t\"key\": \"value\",',\n\t\t\t'\t\"another\": \"property\",',\n\t\t\t\"},\",\n\t\t\t\"// This is a multi-line comment that should be merged\",\n\t\t\t\"// because it follows a line ending with }, which indicates\",\n\t\t\t\"// the end of an object or array element\",\n\t\t\t\"const next = 42;\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 80,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\texpect(out).toMatch(/},\\n\\/\\*\\*/);\n\t\texpect(out).toMatch(/multi-line comment that should be merged/);\n\t\texpect(out).toMatch(/object or array element\\./);\n\t\texpect(out).not.toMatch(/\\/\\/ This is a multi-line/);\n\t});\n\n\tit(\"merges line comments after various comma-ending contexts\", () => {\n\t\tconst src = [\n\t\t\t\"const items = [\",\n\t\t\t'\t\"first\",',\n\t\t\t'\t\"second\",',\n\t\t\t\"];\",\n\t\t\t\"// Comments after array with trailing comma\",\n\t\t\t\"// should be merged into JSDoc format\",\n\t\t\t\"\",\n\t\t\t\"function demo(a, b,) {\",\n\t\t\t\"\treturn a + b;\",\n\t\t\t\"}\",\n\t\t\t\"// Comments after function ending with }\",\n\t\t\t\"// should also be merged properly\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 80,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\n\t\t// First comment group after array should be merged\n\t\texpect(out).toMatch(/];\\n\\/\\*\\*/);\n\t\texpect(out).toMatch(/Comments after array with trailing comma/);\n\n\t\t// Second comment group after function should be merged\n\t\texpect(out).toMatch(/}\\n\\/\\*\\*/);\n\t\texpect(out).toMatch(/Comments after function ending with }/);\n\t});\n\n\tit(\"does not merge single line comment when context is not eligible\", () => {\n\t\tconst src = [\n\t\t\t\"const value = process()\", // doesn't end with eligible punctuation, no semicolon\n\t\t\t\"// single comment that should not be merged\",\n\t\t\t\"console.log(value);\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 80,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\n\t\t// Single comment should remain as line comment, not merged to JSDoc\n\t\texpect(out).toMatch(/\\/\\/ single comment that should not be merged/);\n\t\texpect(out).not.toMatch(/\\/\\*\\*/);\n\t});\n\n\tit(\"splits consecutive NOTE sentences in a single paragraph\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * This does work. NOTE: first caveat. NOTE: second caveat. Done\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\tconst lines = out.split(/\\n/).filter((l) => /^ \\* /.test(l));\n\t\tconst notes = lines.filter((l) => /NOTE: /.test(l));\n\t\texpect(notes.length).toBe(2);\n\t\tfor (const l of notes) {\n\t\t\texpect(l).toMatch(/\\.$/);\n\t\t}\n\t\texpect(out).toMatch(/Done\\./);\n\t});\n\n\tit(\"detects NOTE after exclamation boundary\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * Success! NOTE: edge case follows. Continue processing\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 120,\n\t\t}).transformed;\n\t\texpect(out).toMatch(/Success!\\n \\* NOTE: edge case follows\\./);\n\t\texpect(out).toMatch(/Continue processing\\./);\n\t});\n\n\tit(\"does not force period on NOTE line ending with continuation word when followed by continuation line\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * NOTE: we are still using the good old lodash uniqueId when the code is not in\",\n\t\t\t\" * production, so that the results are a little bit more consistent tests after\",\n\t\t\t\" * test instead of being completely random.\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\t// New structural policy: first NOTE line always gets period even if followed\n\t\t// by prose.\n\t\texpect(out).toMatch(/when the code is not in\\./);\n\t\t// Ensure final sentence has period.\n\t\texpect(/completely random\\./.test(out)).toBe(true);\n\t});\n\n\tit(\"handles diffLines with different length arrays\", () => {\n\t\tconst a = \"line1\\nline2\";\n\t\tconst b = \"line1\\nline2\\nline3\";\n\t\tconst diff = diffLines(a, b);\n\t\texpect(diff).toContain(\"+ line3\");\n\n\t\tconst c = \"line1\\nline2\\nline3\";\n\t\tconst d = \"line1\\nline2\";\n\t\tconst diff2 = diffLines(c, d);\n\t\texpect(diff2).toContain(\"- line3\");\n\t});\n\n\tit(\"handles complex NOTE sentence boundaries and splitting\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * This performs work NOTE: first note without period NOTE: second note. Then continues\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\t...baseOpts,\n\t\t\twidth: 100,\n\t\t}).transformed;\n\t\t// Test that NOTE content is processed properly (without making assumptions about splitting)\n\t\texpect(out).toMatch(/This performs work/);\n\t\texpect(out).toMatch(/NOTE: first note without period/);\n\t\texpect(out).toMatch(/NOTE: second note/);\n\t\texpect(out).toMatch(/Then continues/);\n\t});\n\n\tit(\"handles line comment merging with NOTE content\", () => {\n\t\tconst src = [\n\t\t\t\"// This text has multiple thoughts\",\n\t\t\t\"// NOTE: important note here\",\n\t\t\t\"// Final concluding text\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(src, {\n\t\t\twidth: 120,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: true,\n\t\t}).transformed;\n\t\texpect(out).toMatch(/\\/\\*\\*/);\n\t\texpect(out).toMatch(/This text has multiple thoughts/);\n\t\texpect(out).toMatch(/NOTE: important note here/);\n\t\texpect(out).toMatch(/Final concluding text/);\n\t});\n});\n"],"names":["fs","os","path","describe","expect","it","expandGlobs","diffLines","parseAndTransformComments","baseOpts","width","wrapLineComments","mergeLineComments","input","res","transformed","toContain","test","toBe","split","length","toBeGreaterThan","join","first","second","lines","filter","l","out","toMatch","tmpRoot","mkdtempSync","tmpdir","a","nestedDir","mkdirSync","recursive","b","writeFileSync","shallow","sort","toEqual","deep","src","not","snippet","trim","arrowLineCount","toBeGreaterThanOrEqual","original","startsWith","re","match","toBeNull","starLines","noteLine","find","toBeDefined","noteLines","notes","diff","c","d","diff2"],"mappings":"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"}
package/dist/lib.js CHANGED
@@ -619,7 +619,7 @@ function mergeLineCommentGroups(content) {
619
619
  if (/^\s*\/\//.test(lines[i]) && !/^\s*\/\/\//.test(lines[i])) {
620
620
  var prev = i > 0 ? lines[i - 1] : "";
621
621
  var prevTrim = prev.trim();
622
- var contextEligible = prevTrim === "" || /[{}]$/.test(prevTrim);
622
+ var contextEligible = prevTrim === "" || /[{}\]),;]$/.test(prevTrim);
623
623
  /**
624
624
  * Heuristic: also allow large explanatory group after a statement ending
625
625
  * with ';' when it qualifies as explanatory so it can be elevated to a block
package/dist/lib.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib.ts"],"sourcesContent":["import { Logger } from \"@node-cli/logger\";\n\nexport const logger = new Logger({ boring: process.env.NODE_ENV === \"test\" });\n\nexport interface ProcessOptions {\n\twidth: number;\n\twrapLineComments: boolean;\n\tmergeLineComments: boolean;\n}\n\nexport interface FileResult {\n\toriginal: string;\n\ttransformed: string;\n\tchanged: boolean;\n}\n\ninterface JsDocMatch {\n\tindent: string;\n\tbody: string;\n\tstart: number;\n\tend: number;\n}\n\n/**\n * Safety guards / limits (defense-in-depth vs pathological or malicious input)\n * Large indentation sequences (e.g. thousands of tabs) aren't meaningful for\n * real source formatting and could be used to inflate processing time if a\n * regex exhibited super-linear behavior. Our pattern is already linear\n * (tempered), but we still cap accepted indentation length to keep work\n * bounded.\n */\nconst MAX_JSDOC_INDENT = 256; // characters (tabs + spaces)\n\n/**\n * JSDoc block extraction:\n * Previous pattern used a lazy dot-all: ([\\s\\S]*?) which could, under\n * pathological inputs, produce excessive backtracking. We replaced it with a\n * tempered pattern that advances linearly by never letting the inner part\n * consume a closing '*\\/'. This avoids catastrophic behavior while keeping\n * correctness.\n *\n * Reviewer (PR) concern: potential ReDoS on crafted inputs containing many\n * leading tabs then '/**'. Analysis: The inner quantified group\n * (?:[^*]|\\*(?!/))*\n * is unambiguous: on each iteration it consumes exactly one character and can\n * never match the closing sentinel '*\\/' because of the negative lookahead. This\n * means the engine proceeds in O(n) time relative to the block body size.\n * There is no nested ambiguous quantifier (e.g. (a+)*, (.*)+, etc.). The only\n * other quantified part ^[\\t ]* is a simple character class that is consumed\n * once per line start with no backtracking explosion potential.\n *\n * Defense-in-depth: we still (1) cap processed body length (see below) and\n * (2) cap accepted indentation length (MAX_JSDOC_INDENT) after match to ensure\n * we skip absurdly indented constructs.\n *\n * Pattern explanation:\n * (^ [\\t ]* ) -> capture indentation at start of line (multiline mode)\n * /\\*\\* -> opening delimiter\n * ( -> capture group 2 body\n * (?:[^*] -> any non-* char\n * |\\*(?!/) -> or a * not followed by /\n * )* -> repeated greedily (cannot cross closing *\\/)\n * )\n * \\n?[\\t ]*\\*\\/ -> optional newline + trailing indent + closing *\\/\n * Complexity: linear in length of the matched block.\n *\n */\nconst JSDOC_REGEX = /(^[\\t ]*)\\/\\*\\*((?:[^*]|\\*(?!\\/))*)\\n?[\\t ]*\\*\\//gm;\n\nexport function diffLines(a: string, b: string): string {\n\tif (a === b) {\n\t\treturn \"\";\n\t}\n\tconst A = a.split(/\\n/);\n\tconst B = b.split(/\\n/);\n\tconst out: string[] = [];\n\tconst m = Math.max(A.length, B.length);\n\tfor (let i = 0; i < m; i++) {\n\t\tif (A[i] === B[i]) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (A[i] !== undefined) {\n\t\t\tout.push(`- ${A[i]}`);\n\t\t}\n\t\tif (B[i] !== undefined) {\n\t\t\tout.push(`+ ${B[i]}`);\n\t\t}\n\t}\n\treturn out.join(\"\\n\");\n}\n\nfunction endsSentence(line: string): boolean {\n\treturn /[.!?](?:['\")\\]]*)$/.test(line.trim());\n}\n\nfunction needsTerminalPunctuation(line: string): boolean {\n\treturn /[A-Za-z0-9\")\\]']$/.test(line) && !endsSentence(line);\n}\n\nfunction maybeAddPeriod(line: string): string {\n\treturn needsTerminalPunctuation(line) ? line + \".\" : line;\n}\n\nfunction normalizeNote(line: string): string {\n\treturn line.replace(/^note:/i, \"NOTE:\");\n}\n\n/**\n * ' at start or immediately after a sentence terminator + space.\n */\nfunction splitNoteSentences(text: string): string[] {\n\tif (!/NOTE: /.test(text)) {\n\t\treturn [text];\n\t}\n\tconst segments: string[] = [];\n\tlet i = 0;\n\twhile (i < text.length) {\n\t\tconst idx = text.indexOf(\"NOTE: \", i);\n\t\tif (idx === -1) {\n\t\t\tconst tail = text.slice(i).trim();\n\t\t\tif (tail) {\n\t\t\t\tsegments.push(tail);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t/**\n\t\t * Boundary heuristics: start of text, sentence punctuation + space, OR a\n\t\t * plain space (allows splitting even when author forgot to terminate the\n\t\t * previous sentence before starting a NOTE clause).\n\t\t */\n\t\tconst boundary =\n\t\t\tidx === 0 ||\n\t\t\t/[.!?] /.test(text.slice(Math.max(0, idx - 2), idx + 1)) ||\n\t\t\ttext[idx - 1] === \" \";\n\t\tif (!boundary) {\n\t\t\ti = idx + 6; // skip this occurrence\n\t\t\tcontinue;\n\t\t}\n\t\tconst before = text.slice(i, idx).trim();\n\t\tif (before) {\n\t\t\tsegments.push(before);\n\t\t}\n\t\tlet end = idx;\n\t\twhile (end < text.length && !/[.!?]/.test(text[end])) {\n\t\t\tend++;\n\t\t}\n\t\tif (end < text.length && /[.!?]/.test(text[end])) {\n\t\t\tend++;\n\t\t}\n\t\tlet note = text.slice(idx, end).trim();\n\t\tif (!/[.!?]$/.test(note)) {\n\t\t\tnote += \".\";\n\t\t}\n\t\tsegments.push(note);\n\t\ti = end;\n\t\tif (text[i] === \" \") {\n\t\t\ti++;\n\t\t}\n\t}\n\treturn segments.filter(Boolean);\n}\n\nfunction isListLike(line: string): boolean {\n\treturn /^(?:[-*+] |\\d+\\. )/.test(line.trim());\n}\n\nfunction isTagLine(line: string): boolean {\n\treturn /^@/.test(line.trim());\n}\n\nfunction isHeadingLike(line: string): boolean {\n\tconst t = line.trim();\n\tif (!/:$/.test(t) || isTagLine(t)) {\n\t\treturn false;\n\t}\n\t/**\n\t * New heuristic: treat as heading only if composed of one or more words that\n\t * each start with an uppercase letter (allows Internal IDs with\n\t * dashes/underscores too). Examples considered headings: \"Overview:\",\n\t * \"Performance Notes:\", \"API Surface:\". Non-headings (treated as sentence\n\t * continuation): \"tested a little bit differently:\", \"differently:\".\n\t */\n\treturn /^[A-Z][A-Za-z0-9_-]*(?: [A-Z][A-Za-z0-9_-]*)*:$/.test(t);\n}\n\nfunction isCodeFence(line: string): boolean {\n\treturn /^```/.test(line.trim());\n}\n\nfunction isVisuallyIndentedCode(line: string): boolean {\n\treturn /^\\s{2,}\\S/.test(line);\n}\n\nfunction wrapWords(text: string, width: number): string[] {\n\tconst words = text.split(/\\s+/).filter(Boolean);\n\tconst lines: string[] = [];\n\tlet cur = \"\";\n\tfor (const w of words) {\n\t\tif (!cur.length) {\n\t\t\tcur = w;\n\t\t\tcontinue;\n\t\t}\n\t\tif (cur.length + 1 + w.length <= width) {\n\t\t\tcur += \" \" + w;\n\t\t} else {\n\t\t\tlines.push(cur);\n\t\t\tcur = w;\n\t\t}\n\t}\n\tif (cur) {\n\t\tlines.push(cur);\n\t}\n\treturn lines.length ? lines : [\"\"];\n}\n\nfunction buildJsDoc(indent: string, rawBody: string, width: number): string {\n\tlet lines = rawBody.split(/\\n/).map((l) => l.replace(/^\\s*\\*? ?/, \"\"));\n\t// Remove a single leading blank line (artifact of regex capture starting after\n\t// /**) if content follows.\n\twhile (\n\t\tlines.length > 1 &&\n\t\tlines[0].trim() === \"\" &&\n\t\tlines.some((l) => l.trim() !== \"\")\n\t) {\n\t\tlines = lines.slice(1);\n\t}\n\t/**\n\t * Trailing blank handling: keep a single trailing blank only if there are\n\t * multiple paragraphs (i.e., an internal blank separator exists). If the doc\n\t * is a single paragraph, drop the trailing blank to avoid an extra standalone\n\t * '*' line before the closing delimiter.\n\t */\n\tif (lines.length > 1 && lines[lines.length - 1].trim() === \"\") {\n\t\tconst internalBlank = lines.slice(0, -1).some((l) => l.trim() === \"\");\n\t\tif (!internalBlank) {\n\t\t\tlines = lines.slice(0, -1);\n\t\t}\n\t}\n\tconst out: string[] = [];\n\tlet para: string[] = [];\n\tlet inFence = false;\n\tconst prefix = indent + \" * \";\n\tconst avail = Math.max(10, width - prefix.length);\n\n\t/**\n\t * Detect structured explanatory / regex description blocks where we want to\n\t * preserve each original line verbatim (no paragraph joining or sentence\n\t * period insertion) to avoid altering carefully aligned or enumerated lines.\n\t */\n\tconst structured = lines.some(\n\t\t(l) =>\n\t\t\t/->/.test(l) ||\n\t\t\t/(\\(?:\\?|\\*\\/)/.test(l) ||\n\t\t\t/Pattern explanation:/i.test(l),\n\t);\n\tif (structured) {\n\t\tfor (const raw of lines) {\n\t\t\tconst trimmed = raw.trimEnd();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\t// ensure a blank line represented by a lone '*'.\n\t\t\t\tif (\n\t\t\t\t\tout.length === 0 ||\n\t\t\t\t\t/^(?:\\s*\\*\\s*)$/.test(out[out.length - 1]) === false\n\t\t\t\t) {\n\t\t\t\t\tout.push(prefix.trimEnd());\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout.push(prefix + normalizeNote(trimmed));\n\t\t}\n\t\t// Consistent style: space before closing */\n\t\treturn `${indent}/**\\n${out.join(\"\\n\")}\\n${indent} */`;\n\t}\n\n\tfunction flush(): void {\n\t\tif (!para.length) {\n\t\t\treturn;\n\t\t}\n\t\tlet text = para.join(\" \").replace(/\\s+/g, \" \").trim();\n\t\ttext = normalizeNote(text);\n\t\ttext = maybeAddPeriod(text);\n\t\tconst pieces = splitNoteSentences(text);\n\t\tfor (const piece of pieces) {\n\t\t\tfor (const l of wrapWords(piece, avail)) {\n\t\t\t\tout.push(prefix + l);\n\t\t\t}\n\t\t}\n\t\tpara = [];\n\t}\n\n\tlet lineIndex = -1;\n\tfor (const raw of lines) {\n\t\tlineIndex++;\n\t\tconst trimmed = raw.trimEnd();\n\t\tif (isCodeFence(trimmed)) {\n\t\t\tflush();\n\t\t\tinFence = !inFence;\n\t\t\tout.push(prefix + trimmed);\n\t\t\tcontinue;\n\t\t}\n\t\tif (inFence) {\n\t\t\t/**\n\t\t\t * Inside a code fence we preserve content verbatim, but for a blank line we\n\t\t\t * still prefer the canonical ' *' (no trailing space after the star) instead\n\t\t\t * of ' * '.\n\t\t\t */\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tout.push(prefix.trimEnd());\n\t\t\t} else {\n\t\t\t\tout.push(prefix + trimmed);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\ttrimmed === \"\" ||\n\t\t\tisListLike(trimmed) ||\n\t\t\tisTagLine(trimmed) ||\n\t\t\tisHeadingLike(trimmed) ||\n\t\t\tisVisuallyIndentedCode(raw) ||\n\t\t\t/^NOTE: /.test(normalizeNote(trimmed))\n\t\t) {\n\t\t\tflush();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tif (\n\t\t\t\t\tout.length === 0 ||\n\t\t\t\t\t/^(?:\\s*\\*\\s*)$/.test(out[out.length - 1]) === false\n\t\t\t\t) {\n\t\t\t\t\tout.push(prefix.trimEnd());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlet noteLine = normalizeNote(trimmed);\n\t\t\t\tif (/^NOTE: /.test(noteLine) && !/[.!?]$/.test(noteLine)) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Structural lookahead: add period only if the NOTE line is followed by a\n\t\t\t\t\t * boundary (blank line, tag line, list item, heading, code fence, visually\n\t\t\t\t\t * indented code, another NOTE line, or end-of-block). If next non-blank\n\t\t\t\t\t * line is plain prose, skip.\n\t\t\t\t\t */\n\t\t\t\t\tlet addPeriod = true; // assume boundary until proven prose continuation\n\t\t\t\t\tconst look = lineIndex + 1;\n\t\t\t\t\twhile (look < lines.length) {\n\t\t\t\t\t\tconst rawNext = lines[look];\n\t\t\t\t\t\tconst cleaned = rawNext.replace(/^\\s*\\*? ?/, \"\").trimEnd();\n\t\t\t\t\t\tconst trimmedNext = cleaned.trim();\n\t\t\t\t\t\tif (trimmedNext === \"\") {\n\t\t\t\t\t\t\t// Blank line: definite boundary.\n\t\t\t\t\t\t\taddPeriod = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t/^NOTE: /i.test(trimmedNext) ||\n\t\t\t\t\t\t\tisTagLine(trimmedNext) ||\n\t\t\t\t\t\t\tisListLike(trimmedNext) ||\n\t\t\t\t\t\t\tisHeadingLike(trimmedNext) ||\n\t\t\t\t\t\t\tisCodeFence(trimmedNext) ||\n\t\t\t\t\t\t\tisVisuallyIndentedCode(rawNext)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\taddPeriod = true; // boundary structure\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Anything else: treat as prose continuation.\n\t\t\t\t\t\taddPeriod = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/**\n\t\t\t\t\t * If this is the first NOTE line emitted in the block (no prior non-blank\n\t\t\t\t\t * paragraph lines), we still want a period for consistency even if a prose\n\t\t\t\t\t * continuation follows; treat it as a standalone NOTE sentence introducing\n\t\t\t\t\t * subsequent content.\n\t\t\t\t\t */\n\t\t\t\t\tconst hasPriorContent = out.some(\n\t\t\t\t\t\t(l) => /\\* [^ ]/.test(l) && !/\\* NOTE: /.test(l),\n\t\t\t\t\t);\n\t\t\t\t\tif (addPeriod || !hasPriorContent) {\n\t\t\t\t\t\tnoteLine += \".\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tout.push(prefix + noteLine);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tpara.push(trimmed);\n\t}\n\tflush();\n\t/**\n\t * Style: ensure a space precedes the closing *\\/ for consistency with blocks\n\t * generated elsewhere in this tool (merged line comment groups). Previously we\n\t * emitted `${indent}*\\/` which produced an off-by-one visual alignment.\n\t */\n\treturn `${indent}/**\\n${out.join(\"\\n\")}\\n${indent} */`;\n}\n\nfunction reflowJsDocBlocks(\n\tcontent: string,\n\twidth: number,\n): { content: string; blocks: number } {\n\tJSDOC_REGEX.lastIndex = 0;\n\tconst blocks: JsDocMatch[] = [];\n\tlet m: RegExpExecArray | null = JSDOC_REGEX.exec(content);\n\twhile (m) {\n\t\tconst indent = m[1] || \"\";\n\t\tconst body = m[2] || \"\";\n\t\t// Body length guard protects against extremely large comment blocks.\n\t\tif (body.length <= 500_000 && indent.length <= MAX_JSDOC_INDENT) {\n\t\t\tblocks.push({\n\t\t\t\tindent,\n\t\t\t\tbody,\n\t\t\t\tstart: m.index,\n\t\t\t\tend: m.index + m[0].length,\n\t\t\t});\n\t\t}\n\t\tm = JSDOC_REGEX.exec(content);\n\t}\n\tif (!blocks.length) {\n\t\treturn { content, blocks: 0 };\n\t}\n\tlet delta = 0;\n\tlet out = content;\n\tfor (const b of blocks) {\n\t\tconst original = out.slice(b.start + delta, b.end + delta);\n\t\tconst built = buildJsDoc(b.indent, b.body, width);\n\t\tif (original !== built) {\n\t\t\tout = out.slice(0, b.start + delta) + built + out.slice(b.end + delta);\n\t\t\tdelta += built.length - original.length;\n\t\t}\n\t}\n\treturn { content: out, blocks: blocks.length };\n}\n\nfunction wrapLineComments(\n\tcontent: string,\n\twidth: number,\n): { content: string; applied: boolean } {\n\tconst lines = content.split(/\\n/);\n\tlet changed = false;\n\tconst out: string[] = [];\n\tlet i = 0;\n\twhile (i < lines.length) {\n\t\tconst line = lines[i];\n\t\tconst m = /^(\\s*)\\/\\/( ?)(.*)$/.exec(line);\n\t\tif (!m || /^\\/\\/\\//.test(line)) {\n\t\t\tout.push(line);\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\t/**\n\t\t * Collect a group of consecutive simple // lines (not triple slash) that are\n\t\t * eligible to be wrapped/merged. We stop before directive lines, URLs, or\n\t\t * triple-slash (reference) comments to avoid altering those semantics.\n\t\t */\n\t\tconst group: { raw: string; indent: string; body: string }[] = [];\n\t\tlet j = i;\n\t\twhile (j < lines.length) {\n\t\t\tconst gm = /^(\\s*)\\/\\/ ?(.*)$/.exec(lines[j]);\n\t\t\tif (!gm || /^\\/\\/\\//.test(lines[j])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst body = gm[2];\n\t\t\tif (/^(@|eslint|ts-ignore)/.test(body) || /https?:\\/\\//.test(body)) {\n\t\t\t\tbreak; // stop group before directives/URLs; process current line normally\n\t\t\t}\n\t\t\tgroup.push({ raw: lines[j], indent: gm[1], body });\n\t\t\tj++;\n\t\t}\n\t\tif (group.length <= 1) {\n\t\t\t// Single line: existing logic (add period if needed).\n\t\t\tconst indent = m[1];\n\t\t\tconst body = m[3];\n\t\t\tif (/^(@|eslint|ts-ignore)/.test(body) || /https?:\\/\\//.test(body)) {\n\t\t\t\tout.push(line);\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst prefix = indent + \"// \";\n\t\t\tconst avail = Math.max(10, width - prefix.length);\n\t\t\tlet text = body.replace(/\\s+/g, \" \").trim();\n\t\t\ttext = normalizeNote(text);\n\t\t\ttext = maybeAddPeriod(text);\n\t\t\tconst wrapped = wrapWords(text, avail).map((w) => prefix + w);\n\t\t\tif (wrapped.join(\"\\n\") !== line) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tout.push(...wrapped);\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\t/**\n\t\t * Multi-line group: only add terminal punctuation (a single period) to the\n\t\t * final logical sentence if needed. Earlier lines are treated as soft wraps\n\t\t * of the same paragraph; adding periods to each would create spurious\n\t\t * sentence boundaries.\n\t\t */\n\t\tfor (let k = 0; k < group.length; k++) {\n\t\t\tconst { indent, body } = group[k];\n\t\t\tconst prefix = indent + \"// \";\n\t\t\tconst avail = Math.max(10, width - prefix.length);\n\t\t\tlet text = body.replace(/\\s+/g, \" \").trim();\n\t\t\ttext = normalizeNote(text);\n\t\t\tif (k === group.length - 1 && !/:$/.test(text.trim())) {\n\t\t\t\ttext = maybeAddPeriod(text);\n\t\t\t}\n\t\t\tconst wrapped = wrapWords(text, avail).map((w) => prefix + w);\n\t\t\tif (wrapped.join(\"\\n\") !== group[k].raw) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tout.push(...wrapped);\n\t\t}\n\t\ti = j;\n\t}\n\treturn { content: out.join(\"\\n\"), applied: changed };\n}\n\nfunction mergeLineCommentGroups(content: string): {\n\tcontent: string;\n\tmerged: boolean;\n} {\n\tconst lines = content.split(/\\n/);\n\tconst out: string[] = [];\n\tlet i = 0;\n\tlet merged = false;\n\n\tfunction qualifiesExplanatoryAfterStatement(start: number): boolean {\n\t\t/**\n\t\t * Peek ahead to collect consecutive // lines (excluding triple slash) to\n\t\t * decide if they form a sufficiently large explanatory paragraph that\n\t\t * deserves merging directly after a terminated statement.\n\t\t */\n\t\tconst collected: string[] = [];\n\t\tfor (let k = start; k < lines.length; k++) {\n\t\t\tconst lm = /^(\\s*)\\/\\/( ?)(.*)$/.exec(lines[k]);\n\t\t\tif (!lm || /^\\/\\/\\//.test(lines[k])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst body = lm[3];\n\t\t\tif (/^(@|eslint|ts-ignore)/.test(body) || /https?:\\/\\//.test(body)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcollected.push(body.trim());\n\t\t}\n\t\tif (collected.length < 3) {\n\t\t\treturn false; // lowered threshold from 4 -> 3 to permit shorter explanatory groups\n\t\t}\n\t\tif (!/^[A-Z]/.test(collected[0])) {\n\t\t\treturn false; // start with capitalized sentence\n\t\t}\n\t\t/**\n\t\t * Avoid matching directive-like or list-lists: require at least one line\n\t\t * containing a space (a sentence fragment vs a single token).\n\t\t */\n\t\treturn collected.some((c) => /\\s/.test(c));\n\t}\n\twhile (i < lines.length) {\n\t\tif (/^\\s*\\/\\//.test(lines[i]) && !/^\\s*\\/\\/\\//.test(lines[i])) {\n\t\t\tconst prev = i > 0 ? lines[i - 1] : \"\";\n\t\t\tconst prevTrim = prev.trim();\n\t\t\tlet contextEligible = prevTrim === \"\" || /[{}]$/.test(prevTrim);\n\t\t\t/**\n\t\t\t * Heuristic: also allow large explanatory group after a statement ending\n\t\t\t * with ';' when it qualifies as explanatory so it can be elevated to a block\n\t\t\t * comment rather than remaining a run of // lines.\n\t\t\t */\n\t\t\tif (\n\t\t\t\t!contextEligible &&\n\t\t\t\t/;\\s*$/.test(prevTrim) &&\n\t\t\t\tqualifiesExplanatoryAfterStatement(i)\n\t\t\t) {\n\t\t\t\tcontextEligible = true;\n\t\t\t}\n\t\t\tif (!contextEligible) {\n\t\t\t\tout.push(lines[i]);\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst group: { indent: string; text: string }[] = [];\n\t\t\tlet j = i;\n\t\t\twhile (j < lines.length) {\n\t\t\t\tconst lm = /^(\\s*)\\/\\/ ?(.*)$/.exec(lines[j]);\n\t\t\t\tif (!lm || /^\\s*\\/\\/\\//.test(lines[j])) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst txt = lm[2];\n\t\t\t\tif (\n\t\t\t\t\t/license|copyright/i.test(txt) ||\n\t\t\t\t\t/https?:\\/\\//.test(txt) ||\n\t\t\t\t\t/eslint|ts-ignore/.test(txt)\n\t\t\t\t) {\n\t\t\t\t\tbreak; // do not merge directive / license groups\n\t\t\t\t}\n\t\t\t\tgroup.push({ indent: lm[1], text: txt });\n\t\t\t\tj++;\n\t\t\t}\n\t\t\tif (group.length >= 2) {\n\t\t\t\t/**\n\t\t\t\t * Structured explanatory blocks: now we CONVERT them into a multi-line JSDoc block\n\t\t\t\t * while preserving each original line (instead of merging into a single paragraph).\n\t\t\t\t * We must escape any raw '*\\/' inside the body to avoid premature termination.\n\t\t\t\t * Definition of structured:\n\t\t\t\t * presence of arrows (->), regex tokens (?:, *\\/), or the phrase 'Pattern explanation:'.\n\t\t\t\t */\n\t\t\t\tconst structured = group.some(\n\t\t\t\t\t(g) =>\n\t\t\t\t\t\t/->/.test(g.text) ||\n\t\t\t\t\t\t/(\\(\\?:|\\*\\/)/.test(g.text) ||\n\t\t\t\t\t\t/Pattern explanation:/i.test(g.text),\n\t\t\t\t);\n\t\t\t\tif (structured) {\n\t\t\t\t\tconst indent = group[0].indent;\n\t\t\t\t\tout.push(`${indent}/**`);\n\t\t\t\t\tfor (const ln of group) {\n\t\t\t\t\t\t// Escape closing sentinel inside content.\n\t\t\t\t\t\tconst safe = ln.text.replace(/\\*\\//g, \"*\\\\/\");\n\t\t\t\t\t\tout.push(`${indent} * ${safe}`);\n\t\t\t\t\t}\n\t\t\t\t\tout.push(`${indent} */`);\n\t\t\t\t\tmerged = true;\n\t\t\t\t\ti = j;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst indent = group[0].indent;\n\t\t\t\tmerged = true;\n\t\t\t\t/**\n\t\t\t\t * We only want to add terminal punctuation once at the end of the merged\n\t\t\t\t * paragraph, not after every original line (which can create spurious\n\t\t\t\t * periods mid-sentence when lines were simple wraps). We also avoid\n\t\t\t\t * appending a period if the final line ends with a colon introducing a\n\t\t\t\t * list.\n\t\t\t\t */\n\t\t\t\tconst norm = group.map((g) => normalizeNote(g.text.trim()));\n\t\t\t\t/**\n\t\t\t\t * We only want to add terminal punctuation once at the end of the merged\n\t\t\t\t * paragraph, not after every original line (which can create spurious\n\t\t\t\t * periods mid-sentence when lines were simple wraps). We also avoid\n\t\t\t\t * appending a period if the final line ends with a colon introducing a\n\t\t\t\t * list.\n\t\t\t\t */\n\t\t\t\tlet lastIdx = norm.length - 1;\n\t\t\t\twhile (lastIdx > 0 && norm[lastIdx].trim() === \"\") {\n\t\t\t\t\tlastIdx--;\n\t\t\t\t}\n\t\t\t\tfor (let k = 0; k < norm.length; k++) {\n\t\t\t\t\tif (k === lastIdx) {\n\t\t\t\t\t\tconst ln = norm[k];\n\t\t\t\t\t\tif (!/:$/.test(ln.trim())) {\n\t\t\t\t\t\t\tnorm[k] = maybeAddPeriod(ln);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst para = norm.join(\" \").replace(/\\s+/g, \" \").trim();\n\t\t\t\tout.push(`${indent}/**`);\n\t\t\t\tfor (const seg of splitNoteSentences(para)) {\n\t\t\t\t\tout.push(`${indent} * ${seg}`);\n\t\t\t\t}\n\t\t\t\tout.push(`${indent} */`);\n\t\t\t\ti = j;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tout.push(lines[i]);\n\t\ti++;\n\t}\n\treturn { content: out.join(\"\\n\"), merged };\n}\n\nexport function parseAndTransformComments(\n\tinput: string,\n\toptions: ProcessOptions,\n): FileResult {\n\tlet working = input;\n\tif (options.mergeLineComments) {\n\t\tconst m = mergeLineCommentGroups(working);\n\t\tworking = m.content;\n\t}\n\tconst js = reflowJsDocBlocks(working, options.width);\n\tworking = js.content;\n\tif (options.wrapLineComments) {\n\t\tconst w = wrapLineComments(working, options.width);\n\t\tworking = w.content;\n\t}\n\treturn { original: input, transformed: working, changed: working !== input };\n}\n"],"names":["Logger","logger","boring","process","env","NODE_ENV","MAX_JSDOC_INDENT","JSDOC_REGEX","diffLines","a","b","A","split","B","out","m","Math","max","length","i","undefined","push","join","endsSentence","line","test","trim","needsTerminalPunctuation","maybeAddPeriod","normalizeNote","replace","splitNoteSentences","text","segments","idx","indexOf","tail","slice","boundary","before","end","note","filter","Boolean","isListLike","isTagLine","isHeadingLike","t","isCodeFence","isVisuallyIndentedCode","wrapWords","width","words","lines","cur","w","buildJsDoc","indent","rawBody","map","l","some","internalBlank","para","inFence","prefix","avail","structured","raw","trimmed","trimEnd","flush","pieces","piece","lineIndex","noteLine","addPeriod","look","rawNext","cleaned","trimmedNext","hasPriorContent","reflowJsDocBlocks","content","lastIndex","blocks","exec","body","start","index","delta","original","built","wrapLineComments","group","k","wrapped","changed","j","gm","applied","mergeLineCommentGroups","merged","qualifiesExplanatoryAfterStatement","collected","lm","c","prev","prevTrim","contextEligible","txt","g","ln","safe","norm","lastIdx","seg","parseAndTransformComments","input","options","working","mergeLineComments","js","transformed"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/lib.ts"],"sourcesContent":["import { Logger } from \"@node-cli/logger\";\n\nexport const logger = new Logger({ boring: process.env.NODE_ENV === \"test\" });\n\nexport interface ProcessOptions {\n\twidth: number;\n\twrapLineComments: boolean;\n\tmergeLineComments: boolean;\n}\n\nexport interface FileResult {\n\toriginal: string;\n\ttransformed: string;\n\tchanged: boolean;\n}\n\ninterface JsDocMatch {\n\tindent: string;\n\tbody: string;\n\tstart: number;\n\tend: number;\n}\n\n/**\n * Safety guards / limits (defense-in-depth vs pathological or malicious input)\n * Large indentation sequences (e.g. thousands of tabs) aren't meaningful for\n * real source formatting and could be used to inflate processing time if a\n * regex exhibited super-linear behavior. Our pattern is already linear\n * (tempered), but we still cap accepted indentation length to keep work\n * bounded.\n */\nconst MAX_JSDOC_INDENT = 256; // characters (tabs + spaces)\n\n/**\n * JSDoc block extraction:\n * Previous pattern used a lazy dot-all: ([\\s\\S]*?) which could, under\n * pathological inputs, produce excessive backtracking. We replaced it with a\n * tempered pattern that advances linearly by never letting the inner part\n * consume a closing '*\\/'. This avoids catastrophic behavior while keeping\n * correctness.\n *\n * Reviewer (PR) concern: potential ReDoS on crafted inputs containing many\n * leading tabs then '/**'. Analysis: The inner quantified group\n * (?:[^*]|\\*(?!/))*\n * is unambiguous: on each iteration it consumes exactly one character and can\n * never match the closing sentinel '*\\/' because of the negative lookahead. This\n * means the engine proceeds in O(n) time relative to the block body size.\n * There is no nested ambiguous quantifier (e.g. (a+)*, (.*)+, etc.). The only\n * other quantified part ^[\\t ]* is a simple character class that is consumed\n * once per line start with no backtracking explosion potential.\n *\n * Defense-in-depth: we still (1) cap processed body length (see below) and\n * (2) cap accepted indentation length (MAX_JSDOC_INDENT) after match to ensure\n * we skip absurdly indented constructs.\n *\n * Pattern explanation:\n * (^ [\\t ]* ) -> capture indentation at start of line (multiline mode)\n * /\\*\\* -> opening delimiter\n * ( -> capture group 2 body\n * (?:[^*] -> any non-* char\n * |\\*(?!/) -> or a * not followed by /\n * )* -> repeated greedily (cannot cross closing *\\/)\n * )\n * \\n?[\\t ]*\\*\\/ -> optional newline + trailing indent + closing *\\/\n * Complexity: linear in length of the matched block.\n *\n */\nconst JSDOC_REGEX = /(^[\\t ]*)\\/\\*\\*((?:[^*]|\\*(?!\\/))*)\\n?[\\t ]*\\*\\//gm;\n\nexport function diffLines(a: string, b: string): string {\n\tif (a === b) {\n\t\treturn \"\";\n\t}\n\tconst A = a.split(/\\n/);\n\tconst B = b.split(/\\n/);\n\tconst out: string[] = [];\n\tconst m = Math.max(A.length, B.length);\n\tfor (let i = 0; i < m; i++) {\n\t\tif (A[i] === B[i]) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (A[i] !== undefined) {\n\t\t\tout.push(`- ${A[i]}`);\n\t\t}\n\t\tif (B[i] !== undefined) {\n\t\t\tout.push(`+ ${B[i]}`);\n\t\t}\n\t}\n\treturn out.join(\"\\n\");\n}\n\nfunction endsSentence(line: string): boolean {\n\treturn /[.!?](?:['\")\\]]*)$/.test(line.trim());\n}\n\nfunction needsTerminalPunctuation(line: string): boolean {\n\treturn /[A-Za-z0-9\")\\]']$/.test(line) && !endsSentence(line);\n}\n\nfunction maybeAddPeriod(line: string): string {\n\treturn needsTerminalPunctuation(line) ? line + \".\" : line;\n}\n\nfunction normalizeNote(line: string): string {\n\treturn line.replace(/^note:/i, \"NOTE:\");\n}\n\n/**\n * ' at start or immediately after a sentence terminator + space.\n */\nfunction splitNoteSentences(text: string): string[] {\n\tif (!/NOTE: /.test(text)) {\n\t\treturn [text];\n\t}\n\tconst segments: string[] = [];\n\tlet i = 0;\n\twhile (i < text.length) {\n\t\tconst idx = text.indexOf(\"NOTE: \", i);\n\t\tif (idx === -1) {\n\t\t\tconst tail = text.slice(i).trim();\n\t\t\tif (tail) {\n\t\t\t\tsegments.push(tail);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t/**\n\t\t * Boundary heuristics: start of text, sentence punctuation + space, OR a\n\t\t * plain space (allows splitting even when author forgot to terminate the\n\t\t * previous sentence before starting a NOTE clause).\n\t\t */\n\t\tconst boundary =\n\t\t\tidx === 0 ||\n\t\t\t/[.!?] /.test(text.slice(Math.max(0, idx - 2), idx + 1)) ||\n\t\t\ttext[idx - 1] === \" \";\n\t\tif (!boundary) {\n\t\t\ti = idx + 6; // skip this occurrence\n\t\t\tcontinue;\n\t\t}\n\t\tconst before = text.slice(i, idx).trim();\n\t\tif (before) {\n\t\t\tsegments.push(before);\n\t\t}\n\t\tlet end = idx;\n\t\twhile (end < text.length && !/[.!?]/.test(text[end])) {\n\t\t\tend++;\n\t\t}\n\t\tif (end < text.length && /[.!?]/.test(text[end])) {\n\t\t\tend++;\n\t\t}\n\t\tlet note = text.slice(idx, end).trim();\n\t\tif (!/[.!?]$/.test(note)) {\n\t\t\tnote += \".\";\n\t\t}\n\t\tsegments.push(note);\n\t\ti = end;\n\t\tif (text[i] === \" \") {\n\t\t\ti++;\n\t\t}\n\t}\n\treturn segments.filter(Boolean);\n}\n\nfunction isListLike(line: string): boolean {\n\treturn /^(?:[-*+] |\\d+\\. )/.test(line.trim());\n}\n\nfunction isTagLine(line: string): boolean {\n\treturn /^@/.test(line.trim());\n}\n\nfunction isHeadingLike(line: string): boolean {\n\tconst t = line.trim();\n\tif (!/:$/.test(t) || isTagLine(t)) {\n\t\treturn false;\n\t}\n\t/**\n\t * New heuristic: treat as heading only if composed of one or more words that\n\t * each start with an uppercase letter (allows Internal IDs with\n\t * dashes/underscores too). Examples considered headings: \"Overview:\",\n\t * \"Performance Notes:\", \"API Surface:\". Non-headings (treated as sentence\n\t * continuation): \"tested a little bit differently:\", \"differently:\".\n\t */\n\treturn /^[A-Z][A-Za-z0-9_-]*(?: [A-Z][A-Za-z0-9_-]*)*:$/.test(t);\n}\n\nfunction isCodeFence(line: string): boolean {\n\treturn /^```/.test(line.trim());\n}\n\nfunction isVisuallyIndentedCode(line: string): boolean {\n\treturn /^\\s{2,}\\S/.test(line);\n}\n\nfunction wrapWords(text: string, width: number): string[] {\n\tconst words = text.split(/\\s+/).filter(Boolean);\n\tconst lines: string[] = [];\n\tlet cur = \"\";\n\tfor (const w of words) {\n\t\tif (!cur.length) {\n\t\t\tcur = w;\n\t\t\tcontinue;\n\t\t}\n\t\tif (cur.length + 1 + w.length <= width) {\n\t\t\tcur += \" \" + w;\n\t\t} else {\n\t\t\tlines.push(cur);\n\t\t\tcur = w;\n\t\t}\n\t}\n\tif (cur) {\n\t\tlines.push(cur);\n\t}\n\treturn lines.length ? lines : [\"\"];\n}\n\nfunction buildJsDoc(indent: string, rawBody: string, width: number): string {\n\tlet lines = rawBody.split(/\\n/).map((l) => l.replace(/^\\s*\\*? ?/, \"\"));\n\t// Remove a single leading blank line (artifact of regex capture starting after\n\t// /**) if content follows.\n\twhile (\n\t\tlines.length > 1 &&\n\t\tlines[0].trim() === \"\" &&\n\t\tlines.some((l) => l.trim() !== \"\")\n\t) {\n\t\tlines = lines.slice(1);\n\t}\n\t/**\n\t * Trailing blank handling: keep a single trailing blank only if there are\n\t * multiple paragraphs (i.e., an internal blank separator exists). If the doc\n\t * is a single paragraph, drop the trailing blank to avoid an extra standalone\n\t * '*' line before the closing delimiter.\n\t */\n\tif (lines.length > 1 && lines[lines.length - 1].trim() === \"\") {\n\t\tconst internalBlank = lines.slice(0, -1).some((l) => l.trim() === \"\");\n\t\tif (!internalBlank) {\n\t\t\tlines = lines.slice(0, -1);\n\t\t}\n\t}\n\tconst out: string[] = [];\n\tlet para: string[] = [];\n\tlet inFence = false;\n\tconst prefix = indent + \" * \";\n\tconst avail = Math.max(10, width - prefix.length);\n\n\t/**\n\t * Detect structured explanatory / regex description blocks where we want to\n\t * preserve each original line verbatim (no paragraph joining or sentence\n\t * period insertion) to avoid altering carefully aligned or enumerated lines.\n\t */\n\tconst structured = lines.some(\n\t\t(l) =>\n\t\t\t/->/.test(l) ||\n\t\t\t/(\\(?:\\?|\\*\\/)/.test(l) ||\n\t\t\t/Pattern explanation:/i.test(l),\n\t);\n\tif (structured) {\n\t\tfor (const raw of lines) {\n\t\t\tconst trimmed = raw.trimEnd();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\t// ensure a blank line represented by a lone '*'.\n\t\t\t\tif (\n\t\t\t\t\tout.length === 0 ||\n\t\t\t\t\t/^(?:\\s*\\*\\s*)$/.test(out[out.length - 1]) === false\n\t\t\t\t) {\n\t\t\t\t\tout.push(prefix.trimEnd());\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout.push(prefix + normalizeNote(trimmed));\n\t\t}\n\t\t// Consistent style: space before closing */\n\t\treturn `${indent}/**\\n${out.join(\"\\n\")}\\n${indent} */`;\n\t}\n\n\tfunction flush(): void {\n\t\tif (!para.length) {\n\t\t\treturn;\n\t\t}\n\t\tlet text = para.join(\" \").replace(/\\s+/g, \" \").trim();\n\t\ttext = normalizeNote(text);\n\t\ttext = maybeAddPeriod(text);\n\t\tconst pieces = splitNoteSentences(text);\n\t\tfor (const piece of pieces) {\n\t\t\tfor (const l of wrapWords(piece, avail)) {\n\t\t\t\tout.push(prefix + l);\n\t\t\t}\n\t\t}\n\t\tpara = [];\n\t}\n\n\tlet lineIndex = -1;\n\tfor (const raw of lines) {\n\t\tlineIndex++;\n\t\tconst trimmed = raw.trimEnd();\n\t\tif (isCodeFence(trimmed)) {\n\t\t\tflush();\n\t\t\tinFence = !inFence;\n\t\t\tout.push(prefix + trimmed);\n\t\t\tcontinue;\n\t\t}\n\t\tif (inFence) {\n\t\t\t/**\n\t\t\t * Inside a code fence we preserve content verbatim, but for a blank line we\n\t\t\t * still prefer the canonical ' *' (no trailing space after the star) instead\n\t\t\t * of ' * '.\n\t\t\t */\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tout.push(prefix.trimEnd());\n\t\t\t} else {\n\t\t\t\tout.push(prefix + trimmed);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\ttrimmed === \"\" ||\n\t\t\tisListLike(trimmed) ||\n\t\t\tisTagLine(trimmed) ||\n\t\t\tisHeadingLike(trimmed) ||\n\t\t\tisVisuallyIndentedCode(raw) ||\n\t\t\t/^NOTE: /.test(normalizeNote(trimmed))\n\t\t) {\n\t\t\tflush();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tif (\n\t\t\t\t\tout.length === 0 ||\n\t\t\t\t\t/^(?:\\s*\\*\\s*)$/.test(out[out.length - 1]) === false\n\t\t\t\t) {\n\t\t\t\t\tout.push(prefix.trimEnd());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlet noteLine = normalizeNote(trimmed);\n\t\t\t\tif (/^NOTE: /.test(noteLine) && !/[.!?]$/.test(noteLine)) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Structural lookahead: add period only if the NOTE line is followed by a\n\t\t\t\t\t * boundary (blank line, tag line, list item, heading, code fence, visually\n\t\t\t\t\t * indented code, another NOTE line, or end-of-block). If next non-blank\n\t\t\t\t\t * line is plain prose, skip.\n\t\t\t\t\t */\n\t\t\t\t\tlet addPeriod = true; // assume boundary until proven prose continuation\n\t\t\t\t\tconst look = lineIndex + 1;\n\t\t\t\t\twhile (look < lines.length) {\n\t\t\t\t\t\tconst rawNext = lines[look];\n\t\t\t\t\t\tconst cleaned = rawNext.replace(/^\\s*\\*? ?/, \"\").trimEnd();\n\t\t\t\t\t\tconst trimmedNext = cleaned.trim();\n\t\t\t\t\t\tif (trimmedNext === \"\") {\n\t\t\t\t\t\t\t// Blank line: definite boundary.\n\t\t\t\t\t\t\taddPeriod = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t/^NOTE: /i.test(trimmedNext) ||\n\t\t\t\t\t\t\tisTagLine(trimmedNext) ||\n\t\t\t\t\t\t\tisListLike(trimmedNext) ||\n\t\t\t\t\t\t\tisHeadingLike(trimmedNext) ||\n\t\t\t\t\t\t\tisCodeFence(trimmedNext) ||\n\t\t\t\t\t\t\tisVisuallyIndentedCode(rawNext)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\taddPeriod = true; // boundary structure\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Anything else: treat as prose continuation.\n\t\t\t\t\t\taddPeriod = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/**\n\t\t\t\t\t * If this is the first NOTE line emitted in the block (no prior non-blank\n\t\t\t\t\t * paragraph lines), we still want a period for consistency even if a prose\n\t\t\t\t\t * continuation follows; treat it as a standalone NOTE sentence introducing\n\t\t\t\t\t * subsequent content.\n\t\t\t\t\t */\n\t\t\t\t\tconst hasPriorContent = out.some(\n\t\t\t\t\t\t(l) => /\\* [^ ]/.test(l) && !/\\* NOTE: /.test(l),\n\t\t\t\t\t);\n\t\t\t\t\tif (addPeriod || !hasPriorContent) {\n\t\t\t\t\t\tnoteLine += \".\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tout.push(prefix + noteLine);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tpara.push(trimmed);\n\t}\n\tflush();\n\t/**\n\t * Style: ensure a space precedes the closing *\\/ for consistency with blocks\n\t * generated elsewhere in this tool (merged line comment groups). Previously we\n\t * emitted `${indent}*\\/` which produced an off-by-one visual alignment.\n\t */\n\treturn `${indent}/**\\n${out.join(\"\\n\")}\\n${indent} */`;\n}\n\nfunction reflowJsDocBlocks(\n\tcontent: string,\n\twidth: number,\n): { content: string; blocks: number } {\n\tJSDOC_REGEX.lastIndex = 0;\n\tconst blocks: JsDocMatch[] = [];\n\tlet m: RegExpExecArray | null = JSDOC_REGEX.exec(content);\n\twhile (m) {\n\t\tconst indent = m[1] || \"\";\n\t\tconst body = m[2] || \"\";\n\t\t// Body length guard protects against extremely large comment blocks.\n\t\tif (body.length <= 500_000 && indent.length <= MAX_JSDOC_INDENT) {\n\t\t\tblocks.push({\n\t\t\t\tindent,\n\t\t\t\tbody,\n\t\t\t\tstart: m.index,\n\t\t\t\tend: m.index + m[0].length,\n\t\t\t});\n\t\t}\n\t\tm = JSDOC_REGEX.exec(content);\n\t}\n\tif (!blocks.length) {\n\t\treturn { content, blocks: 0 };\n\t}\n\tlet delta = 0;\n\tlet out = content;\n\tfor (const b of blocks) {\n\t\tconst original = out.slice(b.start + delta, b.end + delta);\n\t\tconst built = buildJsDoc(b.indent, b.body, width);\n\t\tif (original !== built) {\n\t\t\tout = out.slice(0, b.start + delta) + built + out.slice(b.end + delta);\n\t\t\tdelta += built.length - original.length;\n\t\t}\n\t}\n\treturn { content: out, blocks: blocks.length };\n}\n\nfunction wrapLineComments(\n\tcontent: string,\n\twidth: number,\n): { content: string; applied: boolean } {\n\tconst lines = content.split(/\\n/);\n\tlet changed = false;\n\tconst out: string[] = [];\n\tlet i = 0;\n\twhile (i < lines.length) {\n\t\tconst line = lines[i];\n\t\tconst m = /^(\\s*)\\/\\/( ?)(.*)$/.exec(line);\n\t\tif (!m || /^\\/\\/\\//.test(line)) {\n\t\t\tout.push(line);\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\t/**\n\t\t * Collect a group of consecutive simple // lines (not triple slash) that are\n\t\t * eligible to be wrapped/merged. We stop before directive lines, URLs, or\n\t\t * triple-slash (reference) comments to avoid altering those semantics.\n\t\t */\n\t\tconst group: { raw: string; indent: string; body: string }[] = [];\n\t\tlet j = i;\n\t\twhile (j < lines.length) {\n\t\t\tconst gm = /^(\\s*)\\/\\/ ?(.*)$/.exec(lines[j]);\n\t\t\tif (!gm || /^\\/\\/\\//.test(lines[j])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst body = gm[2];\n\t\t\tif (/^(@|eslint|ts-ignore)/.test(body) || /https?:\\/\\//.test(body)) {\n\t\t\t\tbreak; // stop group before directives/URLs; process current line normally\n\t\t\t}\n\t\t\tgroup.push({ raw: lines[j], indent: gm[1], body });\n\t\t\tj++;\n\t\t}\n\t\tif (group.length <= 1) {\n\t\t\t// Single line: existing logic (add period if needed).\n\t\t\tconst indent = m[1];\n\t\t\tconst body = m[3];\n\t\t\tif (/^(@|eslint|ts-ignore)/.test(body) || /https?:\\/\\//.test(body)) {\n\t\t\t\tout.push(line);\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst prefix = indent + \"// \";\n\t\t\tconst avail = Math.max(10, width - prefix.length);\n\t\t\tlet text = body.replace(/\\s+/g, \" \").trim();\n\t\t\ttext = normalizeNote(text);\n\t\t\ttext = maybeAddPeriod(text);\n\t\t\tconst wrapped = wrapWords(text, avail).map((w) => prefix + w);\n\t\t\tif (wrapped.join(\"\\n\") !== line) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tout.push(...wrapped);\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\t/**\n\t\t * Multi-line group: only add terminal punctuation (a single period) to the\n\t\t * final logical sentence if needed. Earlier lines are treated as soft wraps\n\t\t * of the same paragraph; adding periods to each would create spurious\n\t\t * sentence boundaries.\n\t\t */\n\t\tfor (let k = 0; k < group.length; k++) {\n\t\t\tconst { indent, body } = group[k];\n\t\t\tconst prefix = indent + \"// \";\n\t\t\tconst avail = Math.max(10, width - prefix.length);\n\t\t\tlet text = body.replace(/\\s+/g, \" \").trim();\n\t\t\ttext = normalizeNote(text);\n\t\t\tif (k === group.length - 1 && !/:$/.test(text.trim())) {\n\t\t\t\ttext = maybeAddPeriod(text);\n\t\t\t}\n\t\t\tconst wrapped = wrapWords(text, avail).map((w) => prefix + w);\n\t\t\tif (wrapped.join(\"\\n\") !== group[k].raw) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tout.push(...wrapped);\n\t\t}\n\t\ti = j;\n\t}\n\treturn { content: out.join(\"\\n\"), applied: changed };\n}\n\nfunction mergeLineCommentGroups(content: string): {\n\tcontent: string;\n\tmerged: boolean;\n} {\n\tconst lines = content.split(/\\n/);\n\tconst out: string[] = [];\n\tlet i = 0;\n\tlet merged = false;\n\n\tfunction qualifiesExplanatoryAfterStatement(start: number): boolean {\n\t\t/**\n\t\t * Peek ahead to collect consecutive // lines (excluding triple slash) to\n\t\t * decide if they form a sufficiently large explanatory paragraph that\n\t\t * deserves merging directly after a terminated statement.\n\t\t */\n\t\tconst collected: string[] = [];\n\t\tfor (let k = start; k < lines.length; k++) {\n\t\t\tconst lm = /^(\\s*)\\/\\/( ?)(.*)$/.exec(lines[k]);\n\t\t\tif (!lm || /^\\/\\/\\//.test(lines[k])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst body = lm[3];\n\t\t\tif (/^(@|eslint|ts-ignore)/.test(body) || /https?:\\/\\//.test(body)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcollected.push(body.trim());\n\t\t}\n\t\tif (collected.length < 3) {\n\t\t\treturn false; // lowered threshold from 4 -> 3 to permit shorter explanatory groups\n\t\t}\n\t\tif (!/^[A-Z]/.test(collected[0])) {\n\t\t\treturn false; // start with capitalized sentence\n\t\t}\n\t\t/**\n\t\t * Avoid matching directive-like or list-lists: require at least one line\n\t\t * containing a space (a sentence fragment vs a single token).\n\t\t */\n\t\treturn collected.some((c) => /\\s/.test(c));\n\t}\n\twhile (i < lines.length) {\n\t\tif (/^\\s*\\/\\//.test(lines[i]) && !/^\\s*\\/\\/\\//.test(lines[i])) {\n\t\t\tconst prev = i > 0 ? lines[i - 1] : \"\";\n\t\t\tconst prevTrim = prev.trim();\n\t\t\tlet contextEligible = prevTrim === \"\" || /[{}\\]),;]$/.test(prevTrim);\n\t\t\t/**\n\t\t\t * Heuristic: also allow large explanatory group after a statement ending\n\t\t\t * with ';' when it qualifies as explanatory so it can be elevated to a block\n\t\t\t * comment rather than remaining a run of // lines.\n\t\t\t */\n\t\t\tif (\n\t\t\t\t!contextEligible &&\n\t\t\t\t/;\\s*$/.test(prevTrim) &&\n\t\t\t\tqualifiesExplanatoryAfterStatement(i)\n\t\t\t) {\n\t\t\t\tcontextEligible = true;\n\t\t\t}\n\t\t\tif (!contextEligible) {\n\t\t\t\tout.push(lines[i]);\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst group: { indent: string; text: string }[] = [];\n\t\t\tlet j = i;\n\t\t\twhile (j < lines.length) {\n\t\t\t\tconst lm = /^(\\s*)\\/\\/ ?(.*)$/.exec(lines[j]);\n\t\t\t\tif (!lm || /^\\s*\\/\\/\\//.test(lines[j])) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst txt = lm[2];\n\t\t\t\tif (\n\t\t\t\t\t/license|copyright/i.test(txt) ||\n\t\t\t\t\t/https?:\\/\\//.test(txt) ||\n\t\t\t\t\t/eslint|ts-ignore/.test(txt)\n\t\t\t\t) {\n\t\t\t\t\tbreak; // do not merge directive / license groups\n\t\t\t\t}\n\t\t\t\tgroup.push({ indent: lm[1], text: txt });\n\t\t\t\tj++;\n\t\t\t}\n\t\t\tif (group.length >= 2) {\n\t\t\t\t/**\n\t\t\t\t * Structured explanatory blocks: now we CONVERT them into a multi-line JSDoc block\n\t\t\t\t * while preserving each original line (instead of merging into a single paragraph).\n\t\t\t\t * We must escape any raw '*\\/' inside the body to avoid premature termination.\n\t\t\t\t * Definition of structured:\n\t\t\t\t * presence of arrows (->), regex tokens (?:, *\\/), or the phrase 'Pattern explanation:'.\n\t\t\t\t */\n\t\t\t\tconst structured = group.some(\n\t\t\t\t\t(g) =>\n\t\t\t\t\t\t/->/.test(g.text) ||\n\t\t\t\t\t\t/(\\(\\?:|\\*\\/)/.test(g.text) ||\n\t\t\t\t\t\t/Pattern explanation:/i.test(g.text),\n\t\t\t\t);\n\t\t\t\tif (structured) {\n\t\t\t\t\tconst indent = group[0].indent;\n\t\t\t\t\tout.push(`${indent}/**`);\n\t\t\t\t\tfor (const ln of group) {\n\t\t\t\t\t\t// Escape closing sentinel inside content.\n\t\t\t\t\t\tconst safe = ln.text.replace(/\\*\\//g, \"*\\\\/\");\n\t\t\t\t\t\tout.push(`${indent} * ${safe}`);\n\t\t\t\t\t}\n\t\t\t\t\tout.push(`${indent} */`);\n\t\t\t\t\tmerged = true;\n\t\t\t\t\ti = j;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst indent = group[0].indent;\n\t\t\t\tmerged = true;\n\t\t\t\t/**\n\t\t\t\t * We only want to add terminal punctuation once at the end of the merged\n\t\t\t\t * paragraph, not after every original line (which can create spurious\n\t\t\t\t * periods mid-sentence when lines were simple wraps). We also avoid\n\t\t\t\t * appending a period if the final line ends with a colon introducing a\n\t\t\t\t * list.\n\t\t\t\t */\n\t\t\t\tconst norm = group.map((g) => normalizeNote(g.text.trim()));\n\t\t\t\t/**\n\t\t\t\t * We only want to add terminal punctuation once at the end of the merged\n\t\t\t\t * paragraph, not after every original line (which can create spurious\n\t\t\t\t * periods mid-sentence when lines were simple wraps). We also avoid\n\t\t\t\t * appending a period if the final line ends with a colon introducing a\n\t\t\t\t * list.\n\t\t\t\t */\n\t\t\t\tlet lastIdx = norm.length - 1;\n\t\t\t\twhile (lastIdx > 0 && norm[lastIdx].trim() === \"\") {\n\t\t\t\t\tlastIdx--;\n\t\t\t\t}\n\t\t\t\tfor (let k = 0; k < norm.length; k++) {\n\t\t\t\t\tif (k === lastIdx) {\n\t\t\t\t\t\tconst ln = norm[k];\n\t\t\t\t\t\tif (!/:$/.test(ln.trim())) {\n\t\t\t\t\t\t\tnorm[k] = maybeAddPeriod(ln);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst para = norm.join(\" \").replace(/\\s+/g, \" \").trim();\n\t\t\t\tout.push(`${indent}/**`);\n\t\t\t\tfor (const seg of splitNoteSentences(para)) {\n\t\t\t\t\tout.push(`${indent} * ${seg}`);\n\t\t\t\t}\n\t\t\t\tout.push(`${indent} */`);\n\t\t\t\ti = j;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tout.push(lines[i]);\n\t\ti++;\n\t}\n\treturn { content: out.join(\"\\n\"), merged };\n}\n\nexport function parseAndTransformComments(\n\tinput: string,\n\toptions: ProcessOptions,\n): FileResult {\n\tlet working = input;\n\tif (options.mergeLineComments) {\n\t\tconst m = mergeLineCommentGroups(working);\n\t\tworking = m.content;\n\t}\n\tconst js = reflowJsDocBlocks(working, options.width);\n\tworking = js.content;\n\tif (options.wrapLineComments) {\n\t\tconst w = wrapLineComments(working, options.width);\n\t\tworking = w.content;\n\t}\n\treturn { original: input, transformed: working, changed: working !== input };\n}\n"],"names":["Logger","logger","boring","process","env","NODE_ENV","MAX_JSDOC_INDENT","JSDOC_REGEX","diffLines","a","b","A","split","B","out","m","Math","max","length","i","undefined","push","join","endsSentence","line","test","trim","needsTerminalPunctuation","maybeAddPeriod","normalizeNote","replace","splitNoteSentences","text","segments","idx","indexOf","tail","slice","boundary","before","end","note","filter","Boolean","isListLike","isTagLine","isHeadingLike","t","isCodeFence","isVisuallyIndentedCode","wrapWords","width","words","lines","cur","w","buildJsDoc","indent","rawBody","map","l","some","internalBlank","para","inFence","prefix","avail","structured","raw","trimmed","trimEnd","flush","pieces","piece","lineIndex","noteLine","addPeriod","look","rawNext","cleaned","trimmedNext","hasPriorContent","reflowJsDocBlocks","content","lastIndex","blocks","exec","body","start","index","delta","original","built","wrapLineComments","group","k","wrapped","changed","j","gm","applied","mergeLineCommentGroups","merged","qualifiesExplanatoryAfterStatement","collected","lm","c","prev","prevTrim","contextEligible","txt","g","ln","safe","norm","lastIdx","seg","parseAndTransformComments","input","options","working","mergeLineComments","js","transformed"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-cli/comments",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "description": "CLI tool to reflow and normalize JSDoc and line comments in source files",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@node-cli/logger": "1.3.2",
32
- "@node-cli/parser": "2.4.2",
32
+ "@node-cli/parser": "2.4.3",
33
33
  "@node-cli/utilities": "1.0.4",
34
34
  "micromatch": "4.0.8"
35
35
  },
@@ -40,5 +40,5 @@
40
40
  "@vitest/coverage-v8": "3.2.4",
41
41
  "vitest": "3.2.4"
42
42
  },
43
- "gitHead": "786eceb6816c7927c27239a5cf585ccae16888a1"
43
+ "gitHead": "3701646df3232463baf2867ee961ca3417804efe"
44
44
  }