@node-cli/comments 0.1.0 → 0.2.0

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.
@@ -2,21 +2,29 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { describe, expect, it } from "vitest";
5
- import { augmentPatterns, expandGlobs } from "../glob.js";
5
+ import { expandGlobs } from "../glob.js";
6
6
  import { parseAndTransformComments } from "../lib.js";
7
7
  describe("glob utilities & additional lib branches", function() {
8
- it("augmentPatterns adds deep variant only when needed", function() {
9
- var input = [
10
- "/tmp/dir/*.ts",
11
- "/tmp/dir/**/a.ts",
12
- "/tmp/file.txt"
13
- ]; // second already deep, third no wildcard folder/file split
14
- var out = augmentPatterns(input);
15
- // Should add one deep variant for first pattern
16
- var additions = out.filter(function(p) {
17
- return p.includes("**/*.ts");
18
- });
19
- expect(additions.length).toBe(1);
8
+ it("explicit globstar required for deep matches (no implicit augmentation)", function() {
9
+ // create temp structure.
10
+ var root = fs.mkdtempSync(path.join(os.tmpdir(), "comments-glob-simplify-"));
11
+ fs.mkdirSync(path.join(root, "nested"));
12
+ var a = path.join(root, "a.ts");
13
+ var b = path.join(root, "nested", "b.ts");
14
+ fs.writeFileSync(a, "// a", "utf8");
15
+ fs.writeFileSync(b, "// b", "utf8");
16
+ var shallow = expandGlobs([
17
+ "".concat(root, "/*.ts")
18
+ ]);
19
+ expect(shallow).toContain(a);
20
+ expect(shallow).not.toContain(b); // no implicit deep variant
21
+ var deep = expandGlobs([
22
+ "".concat(root, "/**/*.ts")
23
+ ]);
24
+ expect(deep).toEqual(expect.arrayContaining([
25
+ a,
26
+ b
27
+ ]));
20
28
  });
21
29
  it("expandGlobs matches literals and wildcards including ?", function() {
22
30
  var root = fs.mkdtempSync(path.join(os.tmpdir(), "comments-glob2-"));
@@ -92,7 +100,7 @@ describe("glob utilities & additional lib branches", function() {
92
100
  var root = fs.mkdtempSync(path.join(os.tmpdir(), "comments-glob-lit-"));
93
101
  var file = path.join(root, "only.ts");
94
102
  fs.writeFileSync(file, "// only", "utf8");
95
- // duplicate patterns plus a nonexistent literal
103
+ // duplicate patterns plus a nonexistent literal.
96
104
  var patterns = [
97
105
  file,
98
106
  file,
@@ -120,14 +128,14 @@ describe("glob utilities & additional lib branches", function() {
120
128
  wrapLineComments: true,
121
129
  mergeLineComments: false
122
130
  }).transformed;
123
- // At least one blank line (line with only '*') preserved around fence
131
+ // At least one blank line (line with only '*') preserved around fence.
124
132
  var starBlankCount = out.split(/\n/).filter(function(l) {
125
133
  return /^\s*\*\s*$/.test(l);
126
134
  }).length;
127
135
  expect(starBlankCount).toBeGreaterThanOrEqual(1);
128
- // Fenced code block retained
136
+ // Fenced code block retained.
129
137
  expect(out).toContain("```sh");
130
- // Second paragraph ended with period added
138
+ // Second paragraph ended with period added.
131
139
  expect(/Second paragraph without period\./.test(out)).toBe(true);
132
140
  });
133
141
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/__tests__/glob.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 { augmentPatterns, expandGlobs } from \"../glob.js\";\nimport { parseAndTransformComments } from \"../lib.js\";\n\ndescribe(\"glob utilities & additional lib branches\", () => {\n\tit(\"augmentPatterns adds deep variant only when needed\", () => {\n\t\tconst input = [\"/tmp/dir/*.ts\", \"/tmp/dir/**/a.ts\", \"/tmp/file.txt\"]; // second already deep, third no wildcard folder/file split\n\t\tconst out = augmentPatterns(input);\n\t\t// Should add one deep variant for first pattern\n\t\tconst additions = out.filter((p) => p.includes(\"**/*.ts\"));\n\t\texpect(additions.length).toBe(1);\n\t});\n\n\tit(\"expandGlobs matches literals and wildcards including ?\", () => {\n\t\tconst root = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob2-\"));\n\t\tconst files = [\"a.ts\", \"ab.ts\", \"abc.ts\", \"note.md\"]; // to test ? wildcard and *\n\t\tfor (const f of files) {\n\t\t\tfs.writeFileSync(path.join(root, f), \"// x\", \"utf8\");\n\t\t}\n\t\tconst patterns = [\n\t\t\tpath.join(root, \"a?.ts\"), // should match ab.ts only\n\t\t\tpath.join(root, \"a*.ts\"), // matches a.ts, ab.ts, abc.ts\n\t\t\tpath.join(root, \"note.md\"), // literal\n\t\t];\n\t\tconst expanded = expandGlobs(patterns)\n\t\t\t.map((p) => path.basename(p))\n\t\t\t.sort();\n\t\texpect(expanded).toEqual([\"a.ts\", \"ab.ts\", \"abc.ts\", \"note.md\"].sort());\n\t});\n\n\tit(\"expandGlobs handles symlinks when followSymlinks true\", () => {\n\t\tconst root = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob-sym-\"));\n\t\tconst target = path.join(root, \"target.ts\");\n\t\tfs.writeFileSync(target, \"// target\", \"utf8\");\n\t\tconst link = path.join(root, \"link.ts\");\n\t\ttry {\n\t\t\tfs.symlinkSync(target, link);\n\t\t} catch {\n\t\t\t// Some file systems may not allow symlinks (CI on Windows); skip gracefully.\n\t\t\treturn;\n\t\t}\n\t\tconst pattern = path.join(root, \"*.ts\");\n\t\tconst noFollow = expandGlobs([pattern], { followSymlinks: false });\n\t\texpect(noFollow).toContain(target);\n\t\texpect(noFollow).not.toContain(link);\n\t\tconst withFollow = expandGlobs([pattern], { followSymlinks: true });\n\t\texpect(withFollow).toContain(target);\n\t\texpect(withFollow).toContain(link);\n\t});\n\n\tit(\"expandGlobs handles file literals, duplicate patterns and nonexistent paths\", () => {\n\t\tconst root = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob-lit-\"));\n\t\tconst file = path.join(root, \"only.ts\");\n\t\tfs.writeFileSync(file, \"// only\", \"utf8\");\n\t\t// duplicate patterns plus a nonexistent literal\n\t\tconst patterns = [file, file, path.join(root, \"missing.ts\")];\n\t\tconst out = expandGlobs(patterns);\n\t\texpect(out).toEqual([file]); // only once\n\t});\n\n\tit(\"lib buildJsDoc preserves blank line separation and fences (edge branch)\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * First paragraph line\",\n\t\t\t\" *\",\n\t\t\t\" * ```sh\",\n\t\t\t\" * echo hi\",\n\t\t\t\" * ```\",\n\t\t\t\" *\",\n\t\t\t\" * Second paragraph without period\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\twidth: 60,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: false,\n\t\t}).transformed;\n\t\t// At least one blank line (line with only '*') preserved around fence\n\t\tconst starBlankCount = out\n\t\t\t.split(/\\n/)\n\t\t\t.filter((l) => /^\\s*\\*\\s*$/.test(l)).length;\n\t\texpect(starBlankCount).toBeGreaterThanOrEqual(1);\n\t\t// Fenced code block retained\n\t\texpect(out).toContain(\"```sh\");\n\t\t// Second paragraph ended with period added\n\t\texpect(/Second paragraph without period\\./.test(out)).toBe(true);\n\t});\n});\n"],"names":["fs","os","path","describe","expect","it","augmentPatterns","expandGlobs","parseAndTransformComments","input","out","additions","filter","p","includes","length","toBe","root","mkdtempSync","join","tmpdir","files","f","writeFileSync","patterns","expanded","map","basename","sort","toEqual","target","link","symlinkSync","pattern","noFollow","followSymlinks","toContain","not","withFollow","file","width","wrapLineComments","mergeLineComments","transformed","starBlankCount","split","l","test","toBeGreaterThanOrEqual"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/__tests__/glob.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 { parseAndTransformComments } from \"../lib.js\";\n\ndescribe(\"glob utilities & additional lib branches\", () => {\n\tit(\"explicit globstar required for deep matches (no implicit augmentation)\", () => {\n\t\t// create temp structure.\n\t\tconst root = fs.mkdtempSync(\n\t\t\tpath.join(os.tmpdir(), \"comments-glob-simplify-\"),\n\t\t);\n\t\tfs.mkdirSync(path.join(root, \"nested\"));\n\t\tconst a = path.join(root, \"a.ts\");\n\t\tconst b = path.join(root, \"nested\", \"b.ts\");\n\t\tfs.writeFileSync(a, \"// a\", \"utf8\");\n\t\tfs.writeFileSync(b, \"// b\", \"utf8\");\n\t\tconst shallow = expandGlobs([`${root}/*.ts`]);\n\t\texpect(shallow).toContain(a);\n\t\texpect(shallow).not.toContain(b); // no implicit deep variant\n\t\tconst deep = expandGlobs([`${root}/**/*.ts`]);\n\t\texpect(deep).toEqual(expect.arrayContaining([a, b]));\n\t});\n\n\tit(\"expandGlobs matches literals and wildcards including ?\", () => {\n\t\tconst root = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob2-\"));\n\t\tconst files = [\"a.ts\", \"ab.ts\", \"abc.ts\", \"note.md\"]; // to test ? wildcard and *\n\t\tfor (const f of files) {\n\t\t\tfs.writeFileSync(path.join(root, f), \"// x\", \"utf8\");\n\t\t}\n\t\tconst patterns = [\n\t\t\tpath.join(root, \"a?.ts\"), // should match ab.ts only\n\t\t\tpath.join(root, \"a*.ts\"), // matches a.ts, ab.ts, abc.ts\n\t\t\tpath.join(root, \"note.md\"), // literal\n\t\t];\n\t\tconst expanded = expandGlobs(patterns)\n\t\t\t.map((p) => path.basename(p))\n\t\t\t.sort();\n\t\texpect(expanded).toEqual([\"a.ts\", \"ab.ts\", \"abc.ts\", \"note.md\"].sort());\n\t});\n\n\tit(\"expandGlobs handles symlinks when followSymlinks true\", () => {\n\t\tconst root = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob-sym-\"));\n\t\tconst target = path.join(root, \"target.ts\");\n\t\tfs.writeFileSync(target, \"// target\", \"utf8\");\n\t\tconst link = path.join(root, \"link.ts\");\n\t\ttry {\n\t\t\tfs.symlinkSync(target, link);\n\t\t} catch {\n\t\t\t// Some file systems may not allow symlinks (CI on Windows); skip gracefully.\n\t\t\treturn;\n\t\t}\n\t\tconst pattern = path.join(root, \"*.ts\");\n\t\tconst noFollow = expandGlobs([pattern], { followSymlinks: false });\n\t\texpect(noFollow).toContain(target);\n\t\texpect(noFollow).not.toContain(link);\n\t\tconst withFollow = expandGlobs([pattern], { followSymlinks: true });\n\t\texpect(withFollow).toContain(target);\n\t\texpect(withFollow).toContain(link);\n\t});\n\n\tit(\"expandGlobs handles file literals, duplicate patterns and nonexistent paths\", () => {\n\t\tconst root = fs.mkdtempSync(path.join(os.tmpdir(), \"comments-glob-lit-\"));\n\t\tconst file = path.join(root, \"only.ts\");\n\t\tfs.writeFileSync(file, \"// only\", \"utf8\");\n\t\t// duplicate patterns plus a nonexistent literal.\n\t\tconst patterns = [file, file, path.join(root, \"missing.ts\")];\n\t\tconst out = expandGlobs(patterns);\n\t\texpect(out).toEqual([file]); // only once\n\t});\n\n\tit(\"lib buildJsDoc preserves blank line separation and fences (edge branch)\", () => {\n\t\tconst input = [\n\t\t\t\"/**\",\n\t\t\t\" * First paragraph line\",\n\t\t\t\" *\",\n\t\t\t\" * ```sh\",\n\t\t\t\" * echo hi\",\n\t\t\t\" * ```\",\n\t\t\t\" *\",\n\t\t\t\" * Second paragraph without period\",\n\t\t\t\" */\",\n\t\t].join(\"\\n\");\n\t\tconst out = parseAndTransformComments(input, {\n\t\t\twidth: 60,\n\t\t\twrapLineComments: true,\n\t\t\tmergeLineComments: false,\n\t\t}).transformed;\n\t\t// At least one blank line (line with only '*') preserved around fence.\n\t\tconst starBlankCount = out\n\t\t\t.split(/\\n/)\n\t\t\t.filter((l) => /^\\s*\\*\\s*$/.test(l)).length;\n\t\texpect(starBlankCount).toBeGreaterThanOrEqual(1);\n\t\t// Fenced code block retained.\n\t\texpect(out).toContain(\"```sh\");\n\t\t// Second paragraph ended with period added.\n\t\texpect(/Second paragraph without period\\./.test(out)).toBe(true);\n\t});\n});\n"],"names":["fs","os","path","describe","expect","it","expandGlobs","parseAndTransformComments","root","mkdtempSync","join","tmpdir","mkdirSync","a","b","writeFileSync","shallow","toContain","not","deep","toEqual","arrayContaining","files","f","patterns","expanded","map","p","basename","sort","target","link","symlinkSync","pattern","noFollow","followSymlinks","withFollow","file","out","input","width","wrapLineComments","mergeLineComments","transformed","starBlankCount","split","filter","l","test","length","toBeGreaterThanOrEqual","toBe"],"mappings":"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"}
@@ -54,7 +54,7 @@ import fs from "node:fs";
54
54
  import os from "node:os";
55
55
  import path from "node:path";
56
56
  import { describe, expect, it } from "vitest";
57
- import { augmentPatterns, expandGlobs } from "../glob.js";
57
+ import { expandGlobs } from "../glob.js";
58
58
  import { diffLines, parseAndTransformComments } from "../lib.js";
59
59
  var baseOpts = {
60
60
  width: 60,
@@ -99,7 +99,7 @@ describe("parseAndTransformComments", function() {
99
99
  var input = "/**\n * Before\n * ```js\n * const x= 1; \n * ```\n * After\n */";
100
100
  var res = parseAndTransformComments(input, baseOpts).transformed;
101
101
  expect(/```js/.test(res)).toBe(true);
102
- // ensure spacing inside fence not normalized
102
+ // ensure spacing inside fence not normalized.
103
103
  expect(/const {2}x= {2}1;/.test(res)).toBe(true);
104
104
  });
105
105
  it("does not merge directive or license groups", function() {
@@ -114,13 +114,13 @@ describe("parseAndTransformComments", function() {
114
114
  mergeLineComments: true
115
115
  }));
116
116
  expect(/eslint-disable/.test(res.transformed)).toBe(true);
117
- // Should not have merged into a jsdoc (no /** directly before const)
117
+ // Should not have merged into a jsdoc (no /** directly before const).
118
118
  expect(/\/\*\*/.test(res.transformed)).toBe(false);
119
119
  });
120
120
  it("list items are not reflowed into a paragraph", function() {
121
121
  var input = "/**\n * First line explaining.\n * - item one more words here\n * - item two\n */";
122
122
  var res = parseAndTransformComments(input, baseOpts).transformed;
123
- // each list item remains on its own line with leading dash
123
+ // each list item remains on its own line with leading dash.
124
124
  var lines = res.split(/\n/).filter(function(l) {
125
125
  return /- item/.test(l);
126
126
  });
@@ -144,21 +144,20 @@ describe("parseAndTransformComments", function() {
144
144
  " */"
145
145
  ].join("\n");
146
146
  var out = parseAndTransformComments(input, baseOpts).transformed;
147
- // Heading preserved, not merged into previous paragraph
147
+ // Heading preserved, not merged into previous paragraph.
148
148
  expect(out).toMatch(/Overview:/);
149
- // Visually indented code line kept as-is (no extra wrapping collapse)
149
+ // Visually indented code line kept as-is (no extra wrapping collapse).
150
150
  expect(out).toMatch(/const\s{3}x = 1;/);
151
- // Ensure final blank line before closing preserved (one line ending with ' *')
152
- var trailingBlank = /\n \*\n\*\/$/.test(out.replace(/\r/g, ""));
153
- expect(trailingBlank).toBe(true);
154
- // Numeric list lines preserved
151
+ // Trailing blank before closing is optional after recent trimming change.
152
+ // (we only keep it when multiple paragraphs exist). Assert closing exists.
153
+ expect(/\n\*\/$/.test(out)).toBe(true);
154
+ // Numeric list lines preserved.
155
155
  expect(out).toMatch(/1\. first/);
156
156
  expect(out).toMatch(/2\. second/);
157
- // Trailing paragraph got terminal period
157
+ // Trailing paragraph got terminal period.
158
158
  expect(out).toMatch(/Another paragraph without period\./);
159
159
  });
160
- it("glob expansion matches created files", function() {
161
- // Create a truly temporary isolated directory outside source tree
160
+ it("glob expansion matches deep files only with explicit globstar", function() {
162
161
  var tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "comments-glob-"));
163
162
  var a = path.join(tmpRoot, "a.ts");
164
163
  var nestedDir = path.join(tmpRoot, "nested");
@@ -168,21 +167,26 @@ describe("parseAndTransformComments", function() {
168
167
  var b = path.join(nestedDir, "b.ts");
169
168
  fs.writeFileSync(a, "// file a", "utf8");
170
169
  fs.writeFileSync(b, "// file b", "utf8");
171
- var patterns = [
170
+ // Shallow pattern should only see top-level a.ts.
171
+ var shallow = expandGlobs([
172
172
  "".concat(tmpRoot, "/*.ts")
173
- ];
174
- var augmented = augmentPatterns(patterns);
175
- expect(augmented.some(function(p) {
176
- return p.includes("**/*.ts");
177
- })).toBe(true);
178
- var expanded = expandGlobs(patterns);
179
- expect(expanded.sort()).toEqual([
173
+ ]).sort();
174
+ expect(shallow).toEqual([
175
+ a
176
+ ]);
177
+ // Deep pattern matches both.
178
+ var deep = expandGlobs([
179
+ "".concat(tmpRoot, "/**/*.ts")
180
+ ]).sort();
181
+ expect(deep).toEqual([
180
182
  a,
181
183
  b
182
184
  ].sort());
183
185
  });
184
- // Added test for preserving multi-line // comment groups that should NOT merge when preceded by code.
185
- it("does not merge an inline explanatory multi-line // comment group following code", function() {
186
+ /**
187
+ * Added test for preserving multi-line // comment groups that should NOT merge
188
+ * when preceded by code.
189
+ */ it("does not merge an inline explanatory multi-line // comment group following code", function() {
186
190
  var src = [
187
191
  "function demo() {",
188
192
  " const x = 1; // keep",
@@ -229,19 +233,93 @@ describe("parseAndTransformComments", function() {
229
233
  wrapLineComments: true,
230
234
  mergeLineComments: true
231
235
  }).transformed;
232
- // Should be converted to a JSDoc (first and last lines delimiters)
236
+ // Should be converted to a JSDoc (first and last lines delimiters).
233
237
  var lines = out.split(/\n/);
234
238
  expect(lines[0].trim()).toBe("/**");
235
239
  expect(lines[lines.length - 1].trim()).toBe("*/");
236
- // Ensure representative internal lines are present (now without leading //)
240
+ // Ensure representative internal lines are present (now without leading //).
237
241
  expect(out).toContain("* JSDoc block extraction:");
238
242
  expect(out).toContain("* Pattern explanation:");
239
- // Arrow lines preserved
243
+ // Arrow lines preserved.
240
244
  var arrowLineCount = lines.filter(function(l) {
241
245
  return /->/.test(l);
242
246
  }).length;
243
247
  expect(arrowLineCount).toBeGreaterThanOrEqual(5);
244
248
  });
249
+ it("does not insert period before list introduced by single lowercase word + colon", function() {
250
+ var input = [
251
+ "/**",
252
+ " * Some utilities have logging capabilities that needs to be",
253
+ " * tested a little bit differently:",
254
+ " * - mocking process.exit",
255
+ " * - console.log",
256
+ " * - inquirer.prompt",
257
+ " */"
258
+ ].join("\n");
259
+ var out = parseAndTransformComments(input, baseOpts).transformed;
260
+ // Ensure no stray period after 'be'.
261
+ expect(out).not.toMatch(/needs to be\./);
262
+ // Ensure list items unchanged.
263
+ expect(out).toMatch(/- mocking process\.exit/);
264
+ });
265
+ it("does not add stray period before lowercase colon line inside jsdoc", function() {
266
+ var input = [
267
+ "/**",
268
+ " * Some utilities have logging capabilities that needs to be",
269
+ " * tested a little bit differently:",
270
+ " * - one",
271
+ " * - two",
272
+ " */"
273
+ ].join("\n");
274
+ var out = parseAndTransformComments(input, baseOpts).transformed;
275
+ expect(out).not.toMatch(/needs to be\./);
276
+ });
277
+ it("does not append periods to every line of a merged // comment group", function() {
278
+ var original = [
279
+ "// We only want to add terminal punctuation once at the end of the merged",
280
+ "// paragraph, not after every original line (which can create spurious",
281
+ "// periods mid-sentence when lines were simple wraps). We also avoid",
282
+ "// appending a period if the final line ends with a colon introducing a",
283
+ "// list."
284
+ ].join("\n");
285
+ var out = parseAndTransformComments(original, {
286
+ width: 160,
287
+ wrapLineComments: true,
288
+ mergeLineComments: true
289
+ }).transformed;
290
+ // Ensure it became a JSDoc block.
291
+ expect(out.startsWith("/**")).toBe(true);
292
+ // Should not contain stray periods after former line breaks.
293
+ expect(out).not.toMatch(/merged\./);
294
+ expect(out).not.toMatch(/spurious\./);
295
+ expect(out).not.toMatch(/introducing a\./);
296
+ // Should still retain existing legitimate period after 'wraps).' and final
297
+ // 'list.'
298
+ expect(out).toMatch(/wraps\)\./);
299
+ expect(out).toMatch(/list\./);
300
+ });
301
+ it("merges a large explanatory // group after a statement into JSDoc", function() {
302
+ var src = [
303
+ "const value = compute();",
304
+ "// We only want to add terminal punctuation once at the end of the merged",
305
+ "// paragraph, not after every original line (which can create spurious",
306
+ "// periods mid-sentence when lines were simple wraps). We also avoid",
307
+ "// appending a period if the final line ends with a colon introducing a",
308
+ "// list.",
309
+ "function next() {}"
310
+ ].join("\n");
311
+ var out = parseAndTransformComments(src, {
312
+ width: 160,
313
+ wrapLineComments: true,
314
+ mergeLineComments: true
315
+ }).transformed;
316
+ // Expect merged into a JSDoc immediately after the statement.
317
+ var re = /compute\(\);\n\/\*\*[\s\S]*?\n\*\//;
318
+ expect(re.test(out)).toBe(true);
319
+ // Ensure only one sentence-final period appended (present on final 'list.'
320
+ // already).
321
+ expect(out.match(/merged\./)).toBeNull();
322
+ });
245
323
  });
246
324
 
247
325
  //# 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 { augmentPatterns, 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// Ensure final blank line before closing preserved (one line ending with ' *')\n\t\tconst trailingBlank = /\\n \\*\\n\\*\\/$/.test(out.replace(/\\r/g, \"\"));\n\t\texpect(trailingBlank).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 created files\", () => {\n\t\t// Create a truly temporary isolated directory outside source tree\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\tconst patterns = [`${tmpRoot}/*.ts`];\n\t\tconst augmented = augmentPatterns(patterns);\n\t\texpect(augmented.some((p) => p.includes(\"**/*.ts\"))).toBe(true);\n\t\tconst expanded = expandGlobs(patterns);\n\t\texpect(expanded.sort()).toEqual([a, b].sort());\n\t});\n\n\t// Added test for preserving multi-line // comment groups that should NOT merge when preceded by code.\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"],"names":["fs","os","path","describe","expect","it","augmentPatterns","expandGlobs","diffLines","parseAndTransformComments","baseOpts","width","wrapLineComments","mergeLineComments","input","res","transformed","toContain","test","toBe","split","length","toBeGreaterThan","join","first","second","lines","filter","l","out","toMatch","trailingBlank","replace","tmpRoot","mkdtempSync","tmpdir","a","nestedDir","mkdirSync","recursive","b","writeFileSync","patterns","augmented","some","p","includes","expanded","sort","toEqual","src","not","snippet","trim","arrowLineCount","toBeGreaterThanOrEqual"],"mappings":"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"}
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// Trailing blank before closing is optional after recent trimming change.\n\t\t// (we only keep it when multiple paragraphs exist). Assert closing exists.\n\t\texpect(/\\n\\*\\/$/.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\\*\\//;\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"],"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"],"mappings":"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"}
package/dist/glob.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export interface GlobOptions {
2
2
  followSymlinks?: boolean;
3
3
  }
4
- export declare function augmentPatterns(patterns: string[]): string[];
5
4
  export declare function expandGlobs(patterns: string[], options?: GlobOptions): string[];
package/dist/glob.js CHANGED
@@ -1,143 +1,20 @@
1
- function _array_like_to_array(arr, len) {
2
- if (len == null || len > arr.length) len = arr.length;
3
- for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
- return arr2;
5
- }
6
- function _array_with_holes(arr) {
7
- if (Array.isArray(arr)) return arr;
8
- }
9
- function _iterable_to_array_limit(arr, i) {
10
- var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
11
- if (_i == null) return;
12
- var _arr = [];
13
- var _n = true;
14
- var _d = false;
15
- var _s, _e;
16
- try {
17
- for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
18
- _arr.push(_s.value);
19
- if (i && _arr.length === i) break;
20
- }
21
- } catch (err) {
22
- _d = true;
23
- _e = err;
24
- } finally{
25
- try {
26
- if (!_n && _i["return"] != null) _i["return"]();
27
- } finally{
28
- if (_d) throw _e;
29
- }
30
- }
31
- return _arr;
32
- }
33
- function _non_iterable_rest() {
34
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
35
- }
36
- function _sliced_to_array(arr, i) {
37
- return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
38
- }
39
- function _unsupported_iterable_to_array(o, minLen) {
40
- if (!o) return;
41
- if (typeof o === "string") return _array_like_to_array(o, minLen);
42
- var n = Object.prototype.toString.call(o).slice(8, -1);
43
- if (n === "Object" && o.constructor) n = o.constructor.name;
44
- if (n === "Map" || n === "Set") return Array.from(n);
45
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
46
- }
47
1
  import fs from "node:fs";
48
2
  import path from "node:path";
49
- var WILDCARD_CHARS = /[!*?]/;
50
- export function augmentPatterns(patterns) {
51
- // Add dir/**/*.ext for each dir/*.ext simple pattern.
52
- var extra = [];
53
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
54
- try {
55
- for(var _iterator = patterns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
56
- var p = _step.value;
57
- if (p.includes("**")) {
58
- continue;
59
- }
60
- var m = /^(.*)\/([^/]+)$/.exec(p);
61
- if (!m) {
62
- continue;
63
- }
64
- var _m = _sliced_to_array(m, 3), _ = _m[0], dir = _m[1], file = _m[2];
65
- if (file.includes("*")) {
66
- // if pattern is like dir/*.ts add deep variant.
67
- if (!patterns.includes("".concat(dir, "/**/").concat(file))) {
68
- extra.push("".concat(dir, "/**/").concat(file));
69
- }
70
- }
71
- }
72
- } catch (err) {
73
- _didIteratorError = true;
74
- _iteratorError = err;
75
- } finally{
76
- try {
77
- if (!_iteratorNormalCompletion && _iterator.return != null) {
78
- _iterator.return();
79
- }
80
- } finally{
81
- if (_didIteratorError) {
82
- throw _iteratorError;
83
- }
84
- }
85
- }
86
- return patterns.concat(extra);
87
- }
88
- function escapeRegex(str) {
89
- return str.replace(/[.+^${}()|[\]\\]/g, "\\$&");
90
- }
91
- function toRegex(pattern) {
3
+ import micromatch from "micromatch";
4
+ var WILDCARD_CHARS = /[!*?]/; // Detects basic glob characters
5
+ function patternRoot(pattern) {
92
6
  if (!WILDCARD_CHARS.test(pattern)) {
93
- return {
94
- regex: null,
95
- root: path.dirname(pattern) || "."
96
- };
7
+ return path.dirname(pattern) || ".";
97
8
  }
98
- // Determine walk root: substring up to first wildcard then dirname.
99
9
  var firstWildcard = pattern.search(/[*!?]/);
100
10
  var rootPart = firstWildcard === -1 ? pattern : pattern.slice(0, firstWildcard);
101
11
  if (!rootPart.endsWith("/")) {
102
12
  rootPart = path.dirname(rootPart);
103
13
  }
104
14
  if (!rootPart.length) {
105
- rootPart = ".";
106
- }
107
- // Build regex: convert pattern path separators to '/'.
108
- var norm = pattern.replace(/\\/g, "/");
109
- var rx = "";
110
- for(var i = 0; i < norm.length;){
111
- if (norm[i] === "*") {
112
- if (norm[i + 1] === "*") {
113
- // '**'.
114
- i += 2;
115
- // collapse subsequent /** or trailing /
116
- if (norm[i] === "/") {
117
- // match zero or more directories.
118
- rx += "(?:.*?/)?";
119
- i++;
120
- } else {
121
- rx += ".*";
122
- }
123
- continue;
124
- }
125
- rx += "[^/]*";
126
- i++;
127
- continue;
128
- }
129
- if (norm[i] === "?") {
130
- rx += "[^/]";
131
- i++;
132
- continue;
133
- }
134
- rx += escapeRegex(norm[i]);
135
- i++;
15
+ return ".";
136
16
  }
137
- return {
138
- regex: new RegExp("^".concat(rx, "$")),
139
- root: rootPart
140
- };
17
+ return rootPart;
141
18
  }
142
19
  function walk(root, followSymlinks) {
143
20
  var results = [];
@@ -208,37 +85,29 @@ function walk(root, followSymlinks) {
208
85
  }
209
86
  export function expandGlobs(patterns) {
210
87
  var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
211
- var augmented = augmentPatterns(patterns);
212
- var compiled = augmented.map(function(p) {
213
- var _toRegex = toRegex(p), regex = _toRegex.regex, root = _toRegex.root;
214
- return {
215
- pattern: p,
216
- regex: regex,
217
- root: root
218
- };
219
- });
220
88
  var seen = new Set();
221
89
  var out = [];
222
90
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
223
91
  try {
224
- for(var _iterator = compiled[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
225
- var p = _step.value;
226
- if (p.regex === null) {
227
- if (fs.existsSync(p.pattern) && fs.statSync(p.pattern).isFile()) {
228
- if (!seen.has(p.pattern)) {
229
- seen.add(p.pattern);
230
- out.push(p.pattern);
231
- }
92
+ for(var _iterator = patterns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
93
+ var pattern = _step.value;
94
+ // Literal file (no metachars).
95
+ if (!WILDCARD_CHARS.test(pattern)) {
96
+ if (fs.existsSync(pattern) && fs.statSync(pattern).isFile() && !seen.has(pattern)) {
97
+ seen.add(pattern);
98
+ out.push(pattern);
232
99
  }
233
100
  continue;
234
101
  }
235
- var files = walk(p.root, options.followSymlinks || false);
102
+ var root = patternRoot(pattern);
103
+ var files = walk(root, options.followSymlinks || false);
104
+ var mmPattern = pattern.replace(/\\/g, "/");
236
105
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
237
106
  try {
238
107
  for(var _iterator1 = files[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
239
108
  var f = _step1.value;
240
- var rel = f.replace(/\\/g, "/");
241
- if (p.regex.test(rel)) {
109
+ var normalized = f.replace(/\\/g, "/");
110
+ if (micromatch.isMatch(normalized, mmPattern)) {
242
111
  if (!seen.has(f)) {
243
112
  seen.add(f);
244
113
  out.push(f);