@node-cli/comments 0.1.0 → 0.2.1

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/dist/glob.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/glob.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport interface GlobOptions {\n\tfollowSymlinks?: boolean;\n}\n\ninterface InternalPattern {\n\tpattern: string;\n\tregex: RegExp | null; // null means literal file path\n\troot: string; // directory root to start walking\n}\n\nconst WILDCARD_CHARS = /[!*?]/;\n\nexport function augmentPatterns(patterns: string[]): string[] {\n\t// Add dir/**/*.ext for each dir/*.ext simple pattern.\n\tconst extra: string[] = [];\n\tfor (const p of patterns) {\n\t\tif (p.includes(\"**\")) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst m = /^(.*)\\/([^/]+)$/.exec(p);\n\t\tif (!m) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst [_, dir, file] = m;\n\t\tif (file.includes(\"*\")) {\n\t\t\t// if pattern is like dir/*.ts add deep variant.\n\t\t\tif (!patterns.includes(`${dir}/**/${file}`)) {\n\t\t\t\textra.push(`${dir}/**/${file}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn patterns.concat(extra);\n}\n\nfunction escapeRegex(str: string): string {\n\treturn str.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction toRegex(pattern: string): { regex: RegExp | null; root: string } {\n\tif (!WILDCARD_CHARS.test(pattern)) {\n\t\treturn { regex: null, root: path.dirname(pattern) || \".\" };\n\t}\n\t// Determine walk root: substring up to first wildcard then dirname.\n\tconst firstWildcard = pattern.search(/[*!?]/);\n\tlet rootPart =\n\t\tfirstWildcard === -1 ? pattern : pattern.slice(0, firstWildcard);\n\tif (!rootPart.endsWith(\"/\")) {\n\t\trootPart = path.dirname(rootPart);\n\t}\n\tif (!rootPart.length) {\n\t\trootPart = \".\";\n\t}\n\t// Build regex: convert pattern path separators to '/'.\n\tconst norm = pattern.replace(/\\\\/g, \"/\");\n\tlet rx = \"\";\n\tfor (let i = 0; i < norm.length; ) {\n\t\tif (norm[i] === \"*\") {\n\t\t\tif (norm[i + 1] === \"*\") {\n\t\t\t\t// '**'.\n\t\t\t\ti += 2;\n\t\t\t\t// collapse subsequent /** or trailing /\n\t\t\t\tif (norm[i] === \"/\") {\n\t\t\t\t\t// match zero or more directories.\n\t\t\t\t\trx += \"(?:.*?/)?\";\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\trx += \".*\";\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\trx += \"[^/]*\";\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (norm[i] === \"?\") {\n\t\t\trx += \"[^/]\";\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\trx += escapeRegex(norm[i]);\n\t\ti++;\n\t}\n\treturn { regex: new RegExp(`^${rx}$`), root: rootPart };\n}\n\nfunction walk(root: string, followSymlinks: boolean): string[] {\n\tconst results: string[] = [];\n\tfunction recur(dir: string) {\n\t\tlet entries: fs.Dirent[] = [];\n\t\ttry {\n\t\t\tentries = fs.readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const e of entries) {\n\t\t\tconst full = path.join(dir, e.name);\n\t\t\tif (e.isSymbolicLink()) {\n\t\t\t\tif (!followSymlinks) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet stat;\n\t\t\t\ttry {\n\t\t\t\t\tstat = fs.statSync(full);\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (stat.isDirectory()) {\n\t\t\t\t\trecur(full);\n\t\t\t\t} else if (stat.isFile()) {\n\t\t\t\t\tresults.push(full);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (e.isDirectory()) {\n\t\t\t\trecur(full);\n\t\t\t} else if (e.isFile()) {\n\t\t\t\tresults.push(full);\n\t\t\t}\n\t\t}\n\t}\n\tconst start = root || \".\";\n\tif (!fs.existsSync(start)) {\n\t\treturn [];\n\t}\n\tconst st = fs.statSync(start);\n\tif (st.isFile()) {\n\t\treturn [start];\n\t}\n\trecur(start);\n\treturn results;\n}\n\nexport function expandGlobs(\n\tpatterns: string[],\n\toptions: GlobOptions = {},\n): string[] {\n\tconst augmented = augmentPatterns(patterns);\n\tconst compiled: InternalPattern[] = augmented.map((p) => {\n\t\tconst { regex, root } = toRegex(p);\n\t\treturn { pattern: p, regex, root };\n\t});\n\tconst seen = new Set<string>();\n\tconst out: string[] = [];\n\tfor (const p of compiled) {\n\t\tif (p.regex === null) {\n\t\t\tif (fs.existsSync(p.pattern) && fs.statSync(p.pattern).isFile()) {\n\t\t\t\tif (!seen.has(p.pattern)) {\n\t\t\t\t\tseen.add(p.pattern);\n\t\t\t\t\tout.push(p.pattern);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst files = walk(p.root, options.followSymlinks || false);\n\t\tfor (const f of files) {\n\t\t\tconst rel = f.replace(/\\\\/g, \"/\");\n\t\t\tif (p.regex.test(rel)) {\n\t\t\t\tif (!seen.has(f)) {\n\t\t\t\t\tseen.add(f);\n\t\t\t\t\tout.push(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn out;\n}\n"],"names":["fs","path","WILDCARD_CHARS","augmentPatterns","patterns","extra","p","includes","m","exec","_","dir","file","push","concat","escapeRegex","str","replace","toRegex","pattern","test","regex","root","dirname","firstWildcard","search","rootPart","slice","endsWith","length","norm","rx","i","RegExp","walk","followSymlinks","results","recur","entries","readdirSync","withFileTypes","e","full","join","name","isSymbolicLink","stat","statSync","isDirectory","isFile","start","existsSync","st","expandGlobs","options","augmented","compiled","map","seen","Set","out","has","add","files","f","rel"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/glob.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport micromatch from \"micromatch\";\n\nexport interface GlobOptions {\n\tfollowSymlinks?: boolean;\n}\n\nconst WILDCARD_CHARS = /[!*?]/; // Detects basic glob characters\n\nfunction patternRoot(pattern: string): string {\n\tif (!WILDCARD_CHARS.test(pattern)) {\n\t\treturn path.dirname(pattern) || \".\";\n\t}\n\tconst firstWildcard = pattern.search(/[*!?]/);\n\tlet rootPart =\n\t\tfirstWildcard === -1 ? pattern : pattern.slice(0, firstWildcard);\n\tif (!rootPart.endsWith(\"/\")) {\n\t\trootPart = path.dirname(rootPart);\n\t}\n\tif (!rootPart.length) {\n\t\treturn \".\";\n\t}\n\treturn rootPart;\n}\n\nfunction walk(root: string, followSymlinks: boolean): string[] {\n\tconst results: string[] = [];\n\tfunction recur(dir: string) {\n\t\tlet entries: fs.Dirent[] = [];\n\t\ttry {\n\t\t\tentries = fs.readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const e of entries) {\n\t\t\tconst full = path.join(dir, e.name);\n\t\t\tif (e.isSymbolicLink()) {\n\t\t\t\tif (!followSymlinks) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet stat;\n\t\t\t\ttry {\n\t\t\t\t\tstat = fs.statSync(full);\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (stat.isDirectory()) {\n\t\t\t\t\trecur(full);\n\t\t\t\t} else if (stat.isFile()) {\n\t\t\t\t\tresults.push(full);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (e.isDirectory()) {\n\t\t\t\trecur(full);\n\t\t\t} else if (e.isFile()) {\n\t\t\t\tresults.push(full);\n\t\t\t}\n\t\t}\n\t}\n\tconst start = root || \".\";\n\tif (!fs.existsSync(start)) {\n\t\treturn [];\n\t}\n\tconst st = fs.statSync(start);\n\tif (st.isFile()) {\n\t\treturn [start];\n\t}\n\trecur(start);\n\treturn results;\n}\n\nexport function expandGlobs(\n\tpatterns: string[],\n\toptions: GlobOptions = {},\n): string[] {\n\tconst seen = new Set<string>();\n\tconst out: string[] = [];\n\tfor (const pattern of patterns) {\n\t\t// Literal file (no metachars).\n\t\tif (!WILDCARD_CHARS.test(pattern)) {\n\t\t\tif (\n\t\t\t\tfs.existsSync(pattern) &&\n\t\t\t\tfs.statSync(pattern).isFile() &&\n\t\t\t\t!seen.has(pattern)\n\t\t\t) {\n\t\t\t\tseen.add(pattern);\n\t\t\t\tout.push(pattern);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst root = patternRoot(pattern);\n\t\tconst files = walk(root, options.followSymlinks || false);\n\t\tconst mmPattern = pattern.replace(/\\\\/g, \"/\");\n\t\tfor (const f of files) {\n\t\t\tconst normalized = f.replace(/\\\\/g, \"/\");\n\t\t\tif (micromatch.isMatch(normalized, mmPattern)) {\n\t\t\t\tif (!seen.has(f)) {\n\t\t\t\t\tseen.add(f);\n\t\t\t\t\tout.push(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn out;\n}\n"],"names":["fs","path","micromatch","WILDCARD_CHARS","patternRoot","pattern","test","dirname","firstWildcard","search","rootPart","slice","endsWith","length","walk","root","followSymlinks","results","recur","dir","entries","readdirSync","withFileTypes","e","full","join","name","isSymbolicLink","stat","statSync","isDirectory","isFile","push","start","existsSync","st","expandGlobs","patterns","options","seen","Set","out","has","add","files","mmPattern","replace","f","normalized","isMatch"],"mappings":"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"}
package/dist/lib.js CHANGED
@@ -27,44 +27,48 @@ import { Logger } from "@node-cli/logger";
27
27
  export var logger = new Logger({
28
28
  boring: process.env.NODE_ENV === "test"
29
29
  });
30
- // Safety guards / limits (defense-in-depth vs pathological or malicious input)
31
- // Large indentation sequences (e.g. thousands of tabs) aren't meaningful for real
32
- // source formatting and could be used to inflate processing time if a regex
33
- // exhibited super-linear behavior. Our pattern is already linear (tempered), but
34
- // we still cap accepted indentation length to keep work bounded.
35
- var MAX_JSDOC_INDENT = 256; // characters (tabs + spaces)
36
- // JSDoc block extraction:
37
- // Previous pattern used a lazy dot-all: ([\s\S]*?) which could, under
38
- // pathological inputs, produce excessive backtracking. We replaced it with a
39
- // tempered pattern that advances linearly by never letting the inner part
40
- // consume a closing '*/'. This avoids catastrophic behavior while keeping
41
- // correctness.
42
- //
43
- // Reviewer (PR) concern: potential ReDoS on crafted inputs containing many
44
- // leading tabs then '/**'. Analysis: The inner quantified group
45
- // (?:[^*]|\*(?!/))*
46
- // is unambiguous: on each iteration it consumes exactly one character and can
47
- // never match the closing sentinel '*/' because of the negative lookahead. This
48
- // means the engine proceeds in O(n) time relative to the block body size.
49
- // There is no nested ambiguous quantifier (e.g. (a+)*, (.*)+, etc.). The only
50
- // other quantified part ^[\t ]* is a simple character class that is consumed
51
- // once per line start with no backtracking explosion potential.
52
- //
53
- // Defense-in-depth: we still (1) cap processed body length (see below) and
54
- // (2) cap accepted indentation length (MAX_JSDOC_INDENT) after match to ensure
55
- // we skip absurdly indented constructs.
56
- //
57
- // Pattern explanation:
58
- // (^ [\t ]* ) -> capture indentation at start of line (multiline mode)
59
- // /\*\* -> opening delimiter
60
- // ( -> capture group 2 body
61
- // (?:[^*] -> any non-* char
62
- // |\*(?!/) -> or a * not followed by /
63
- // )* -> repeated greedily (cannot cross closing */)
64
- // )
65
- // \n?[\t ]*\*/ -> optional newline + trailing indent + closing */
66
- // Complexity: linear in length of the matched block.
67
- var JSDOC_REGEX = /(^[\t ]*)\/\*\*((?:[^*]|\*(?!\/))*)\n?[\t ]*\*\//gm;
30
+ /**
31
+ * Safety guards / limits (defense-in-depth vs pathological or malicious input)
32
+ * Large indentation sequences (e.g. thousands of tabs) aren't meaningful for
33
+ * real source formatting and could be used to inflate processing time if a
34
+ * regex exhibited super-linear behavior. Our pattern is already linear
35
+ * (tempered), but we still cap accepted indentation length to keep work
36
+ * bounded.
37
+ */ var MAX_JSDOC_INDENT = 256; // characters (tabs + spaces)
38
+ /**
39
+ * JSDoc block extraction:
40
+ * Previous pattern used a lazy dot-all: ([\s\S]*?) which could, under
41
+ * pathological inputs, produce excessive backtracking. We replaced it with a
42
+ * tempered pattern that advances linearly by never letting the inner part
43
+ * consume a closing '*\/'. This avoids catastrophic behavior while keeping
44
+ * correctness.
45
+ *
46
+ * Reviewer (PR) concern: potential ReDoS on crafted inputs containing many
47
+ * leading tabs then '/**'. Analysis: The inner quantified group
48
+ * (?:[^*]|\*(?!/))*
49
+ * is unambiguous: on each iteration it consumes exactly one character and can
50
+ * never match the closing sentinel '*\/' because of the negative lookahead. This
51
+ * means the engine proceeds in O(n) time relative to the block body size.
52
+ * There is no nested ambiguous quantifier (e.g. (a+)*, (.*)+, etc.). The only
53
+ * other quantified part ^[\t ]* is a simple character class that is consumed
54
+ * once per line start with no backtracking explosion potential.
55
+ *
56
+ * Defense-in-depth: we still (1) cap processed body length (see below) and
57
+ * (2) cap accepted indentation length (MAX_JSDOC_INDENT) after match to ensure
58
+ * we skip absurdly indented constructs.
59
+ *
60
+ * Pattern explanation:
61
+ * (^ [\t ]* ) -> capture indentation at start of line (multiline mode)
62
+ * /\*\* -> opening delimiter
63
+ * ( -> capture group 2 body
64
+ * (?:[^*] -> any non-* char
65
+ * |\*(?!/) -> or a * not followed by /
66
+ * )* -> repeated greedily (cannot cross closing *\/)
67
+ * )
68
+ * \n?[\t ]*\*\/ -> optional newline + trailing indent + closing *\/
69
+ * Complexity: linear in length of the matched block.
70
+ *
71
+ */ var JSDOC_REGEX = /(^[\t ]*)\/\*\*((?:[^*]|\*(?!\/))*)\n?[\t ]*\*\//gm;
68
72
  export function diffLines(a, b) {
69
73
  if (a === b) {
70
74
  return "";
@@ -106,7 +110,16 @@ function isTagLine(line) {
106
110
  }
107
111
  function isHeadingLike(line) {
108
112
  var t = line.trim();
109
- return /:$/.test(t) && !isTagLine(t);
113
+ if (!/:$/.test(t) || isTagLine(t)) {
114
+ return false;
115
+ }
116
+ /**
117
+ * New heuristic: treat as heading only if composed of one or more words that
118
+ * each start with an uppercase letter (allows Internal IDs with
119
+ * dashes/underscores too). Examples considered headings: "Overview:",
120
+ * "Performance Notes:", "API Surface:". Non-headings (treated as sentence
121
+ * continuation): "tested a little bit differently:", "differently:".
122
+ */ return /^[A-Z][A-Za-z0-9_-]*(?: [A-Z][A-Za-z0-9_-]*)*:$/.test(t);
110
123
  }
111
124
  function isCodeFence(line) {
112
125
  return /^```/.test(line.trim());
@@ -158,11 +171,69 @@ function buildJsDoc(indent, rawBody, width) {
158
171
  var lines = rawBody.split(/\n/).map(function(l) {
159
172
  return l.replace(/^\s*\*? ?/, "");
160
173
  });
174
+ // Remove a single leading blank line (artifact of regex capture starting after
175
+ // /**) if content follows.
176
+ while(lines.length > 1 && lines[0].trim() === "" && lines.some(function(l) {
177
+ return l.trim() !== "";
178
+ })){
179
+ lines = lines.slice(1);
180
+ }
181
+ /**
182
+ * Trailing blank handling: keep a single trailing blank only if there are
183
+ * multiple paragraphs (i.e., an internal blank separator exists). If the doc
184
+ * is a single paragraph, drop the trailing blank to avoid an extra standalone
185
+ * '*' line before the closing delimiter.
186
+ */ if (lines.length > 1 && lines[lines.length - 1].trim() === "") {
187
+ var internalBlank = lines.slice(0, -1).some(function(l) {
188
+ return l.trim() === "";
189
+ });
190
+ if (!internalBlank) {
191
+ lines = lines.slice(0, -1);
192
+ }
193
+ }
161
194
  var out = [];
162
195
  var para = [];
163
196
  var inFence = false;
164
197
  var prefix = indent + " * ";
165
198
  var avail = Math.max(10, width - prefix.length);
199
+ /**
200
+ * Detect structured explanatory / regex description blocks where we want to
201
+ * preserve each original line verbatim (no paragraph joining or sentence
202
+ * period insertion) to avoid altering carefully aligned or enumerated lines.
203
+ */ var structured = lines.some(function(l) {
204
+ return /->/.test(l) || /(\(\?:|\*\/)/.test(l) || /Pattern explanation:/i.test(l);
205
+ });
206
+ if (structured) {
207
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
208
+ try {
209
+ for(var _iterator = lines[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
210
+ var raw = _step.value;
211
+ var trimmed = raw.trimEnd();
212
+ if (trimmed === "") {
213
+ // ensure a blank line represented by a lone '*'.
214
+ if (out.length === 0 || /^(?:\s*\*\s*)$/.test(out[out.length - 1]) === false) {
215
+ out.push(prefix.trimEnd());
216
+ }
217
+ continue;
218
+ }
219
+ out.push(prefix + normalizeNote(trimmed));
220
+ }
221
+ } catch (err) {
222
+ _didIteratorError = true;
223
+ _iteratorError = err;
224
+ } finally{
225
+ try {
226
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
227
+ _iterator.return();
228
+ }
229
+ } finally{
230
+ if (_didIteratorError) {
231
+ throw _iteratorError;
232
+ }
233
+ }
234
+ }
235
+ return "".concat(indent, "/**\n").concat(out.join("\n"), "\n").concat(indent, "*/");
236
+ }
166
237
  function flush() {
167
238
  if (!para.length) {
168
239
  return;
@@ -192,50 +263,53 @@ function buildJsDoc(indent, rawBody, width) {
192
263
  }
193
264
  para = [];
194
265
  }
195
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
266
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
196
267
  try {
197
- for(var _iterator = lines[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
198
- var raw = _step.value;
199
- var trimmed = raw.trimEnd();
200
- if (isCodeFence(trimmed)) {
268
+ for(var _iterator1 = lines[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
269
+ var raw1 = _step1.value;
270
+ var trimmed1 = raw1.trimEnd();
271
+ if (isCodeFence(trimmed1)) {
201
272
  flush();
202
273
  inFence = !inFence;
203
- out.push(prefix + trimmed);
274
+ out.push(prefix + trimmed1);
204
275
  continue;
205
276
  }
206
277
  if (inFence) {
207
- out.push(prefix + trimmed);
278
+ out.push(prefix + trimmed1);
208
279
  continue;
209
280
  }
210
- if (trimmed === "" || isListLike(trimmed) || isTagLine(trimmed) || isHeadingLike(trimmed) || isVisuallyIndentedCode(raw)) {
281
+ if (trimmed1 === "" || isListLike(trimmed1) || isTagLine(trimmed1) || isHeadingLike(trimmed1) || isVisuallyIndentedCode(raw1)) {
211
282
  flush();
212
- if (trimmed === "") {
283
+ if (trimmed1 === "") {
213
284
  if (out.length === 0 || /^(?:\s*\*\s*)$/.test(out[out.length - 1]) === false) {
214
285
  out.push(prefix.trimEnd());
215
286
  }
216
287
  } else {
217
- out.push(prefix + normalizeNote(trimmed));
288
+ out.push(prefix + normalizeNote(trimmed1));
218
289
  }
219
290
  continue;
220
291
  }
221
- para.push(trimmed);
292
+ para.push(trimmed1);
222
293
  }
223
294
  } catch (err) {
224
- _didIteratorError = true;
225
- _iteratorError = err;
295
+ _didIteratorError1 = true;
296
+ _iteratorError1 = err;
226
297
  } finally{
227
298
  try {
228
- if (!_iteratorNormalCompletion && _iterator.return != null) {
229
- _iterator.return();
299
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
300
+ _iterator1.return();
230
301
  }
231
302
  } finally{
232
- if (_didIteratorError) {
233
- throw _iteratorError;
303
+ if (_didIteratorError1) {
304
+ throw _iteratorError1;
234
305
  }
235
306
  }
236
307
  }
237
308
  flush();
238
- return "".concat(indent, "/**\n").concat(out.join("\n"), "\n").concat(indent, "*/");
309
+ // Style: ensure a space precedes the closing */ for consistency with blocks
310
+ // generated elsewhere in this tool (merged line comment groups). Previously
311
+ // we emitted `${indent}*/` which produced an off-by-one visual alignment.
312
+ return "".concat(indent, "/**\n").concat(out.join("\n"), "\n").concat(indent, " */");
239
313
  }
240
314
  function reflowJsDocBlocks(content, width) {
241
315
  JSDOC_REGEX.lastIndex = 0;
@@ -294,37 +368,90 @@ function reflowJsDocBlocks(content, width) {
294
368
  };
295
369
  }
296
370
  function wrapLineComments(content, width) {
297
- var _loop = function(i) {
298
- var _out;
371
+ var _loop = function() {
372
+ var _loop = function(k) {
373
+ var _out;
374
+ var _group_k = group[k], indent = _group_k.indent, body = _group_k.body;
375
+ var prefix = indent + "// ";
376
+ var avail = Math.max(10, width - prefix.length);
377
+ var text = body.replace(/\s+/g, " ").trim();
378
+ text = normalizeNote(text);
379
+ if (k === group.length - 1 && !/:$/.test(text.trim())) {
380
+ text = maybeAddPeriod(text);
381
+ }
382
+ var wrapped = wrapWords(text, avail).map(function(w) {
383
+ return prefix + w;
384
+ });
385
+ if (wrapped.join("\n") !== group[k].raw) {
386
+ changed = true;
387
+ }
388
+ (_out = out).push.apply(_out, _to_consumable_array(wrapped));
389
+ };
299
390
  var line = lines[i];
300
391
  var m = /^(\s*)\/\/( ?)(.*)$/.exec(line);
301
392
  if (!m || /^\/\/\//.test(line)) {
302
393
  out.push(line);
394
+ i++;
303
395
  return "continue";
304
396
  }
305
- var indent = m[1];
306
- var body = m[3];
307
- if (/^(@|eslint|ts-ignore)/.test(body) || /https?:\/\//.test(body)) {
308
- out.push(line);
309
- return "continue";
397
+ /**
398
+ * Collect a group of consecutive simple // lines (not triple slash) that are
399
+ * eligible.
400
+ */ var group = [];
401
+ var j = i;
402
+ while(j < lines.length){
403
+ var gm = /^(\s*)\/\/ ?(.*)$/.exec(lines[j]);
404
+ if (!gm || /^\/\/\//.test(lines[j])) {
405
+ break;
406
+ }
407
+ var body = gm[2];
408
+ if (/^(@|eslint|ts-ignore)/.test(body) || /https?:\/\//.test(body)) {
409
+ break; // stop group before directives/URLs; process current line normally
410
+ }
411
+ group.push({
412
+ raw: lines[j],
413
+ indent: gm[1],
414
+ body: body
415
+ });
416
+ j++;
310
417
  }
311
- var prefix = indent + "// ";
312
- var avail = Math.max(10, width - prefix.length);
313
- var text = body.replace(/\s+/g, " ").trim();
314
- text = normalizeNote(text);
315
- text = maybeAddPeriod(text);
316
- var wrapped = wrapWords(text, avail).map(function(w) {
317
- return prefix + w;
318
- });
319
- if (wrapped.join("\n") !== line) {
320
- changed = true;
418
+ if (group.length <= 1) {
419
+ var _out;
420
+ // Single line: existing logic (add period if needed).
421
+ var indent = m[1];
422
+ var body1 = m[3];
423
+ if (/^(@|eslint|ts-ignore)/.test(body1) || /https?:\/\//.test(body1)) {
424
+ out.push(line);
425
+ i++;
426
+ return "continue";
427
+ }
428
+ var prefix = indent + "// ";
429
+ var avail = Math.max(10, width - prefix.length);
430
+ var text = body1.replace(/\s+/g, " ").trim();
431
+ text = normalizeNote(text);
432
+ text = maybeAddPeriod(text);
433
+ var wrapped = wrapWords(text, avail).map(function(w) {
434
+ return prefix + w;
435
+ });
436
+ if (wrapped.join("\n") !== line) {
437
+ changed = true;
438
+ }
439
+ (_out = out).push.apply(_out, _to_consumable_array(wrapped));
440
+ i++;
441
+ return "continue";
321
442
  }
322
- (_out = out).push.apply(_out, _to_consumable_array(wrapped));
443
+ /**
444
+ * Multi-line group: only add terminal punctuation (period) to final line if
445
+ * needed. Other lines are normalized for NOTE but left without forced
446
+ * punctuation.
447
+ */ for(var k = 0; k < group.length; k++)_loop(k);
448
+ i = j;
323
449
  };
324
450
  var lines = content.split(/\n/);
325
451
  var changed = false;
326
452
  var out = [];
327
- for(var i = 0; i < lines.length; i++)_loop(i);
453
+ var i = 0;
454
+ while(i < lines.length)_loop();
328
455
  return {
329
456
  content: out.join("\n"),
330
457
  applied: changed
@@ -335,11 +462,45 @@ function mergeLineCommentGroups(content) {
335
462
  var out = [];
336
463
  var i = 0;
337
464
  var merged = false;
465
+ function qualifiesExplanatoryAfterStatement(start) {
466
+ // Peek ahead to collect consecutive // lines (excluding triple slash).
467
+ var collected = [];
468
+ for(var k = start; k < lines.length; k++){
469
+ var lm = /^(\s*)\/\/( ?)(.*)$/.exec(lines[k]);
470
+ if (!lm || /^\/\/\//.test(lines[k])) {
471
+ break;
472
+ }
473
+ var body = lm[3];
474
+ if (/^(@|eslint|ts-ignore)/.test(body) || /https?:\/\//.test(body)) {
475
+ break;
476
+ }
477
+ collected.push(body.trim());
478
+ }
479
+ if (collected.length < 4) {
480
+ return false; // require minimum size
481
+ }
482
+ if (!/^[A-Z]/.test(collected[0])) {
483
+ return false; // start with capitalized sentence
484
+ }
485
+ /**
486
+ * Avoid matching directive-like or list-lists: require at least one line with
487
+ * a space (a sentence).
488
+ */ return collected.some(function(c) {
489
+ return /\s/.test(c);
490
+ });
491
+ }
338
492
  while(i < lines.length){
339
493
  if (/^\s*\/\//.test(lines[i]) && !/^\s*\/\/\//.test(lines[i])) {
340
494
  var prev = i > 0 ? lines[i - 1] : "";
341
495
  var prevTrim = prev.trim();
342
496
  var contextEligible = prevTrim === "" || /[{}]$/.test(prevTrim);
497
+ /**
498
+ * Additional heuristic: allow large explanatory group after a statement
499
+ * ending with ';' (but not inline trailing comment scenario) when it
500
+ * qualifies as explanatory.
501
+ */ if (!contextEligible && /;\s*$/.test(prevTrim) && qualifiesExplanatoryAfterStatement(i)) {
502
+ contextEligible = true;
503
+ }
343
504
  if (!contextEligible) {
344
505
  out.push(lines[i]);
345
506
  i++;
@@ -363,11 +524,13 @@ function mergeLineCommentGroups(content) {
363
524
  j++;
364
525
  }
365
526
  if (group.length >= 2) {
366
- // Structured explanatory blocks: now we CONVERT them into a multi-line JSDoc block
367
- // while preserving each original line (instead of merging into a single paragraph).
368
- // We must escape any raw '*/' inside the body to avoid premature termination.
369
- // Definition of structured: presence of arrows (->), regex tokens (?:, */), or the phrase 'Pattern explanation:'.
370
- var structured = group.some(function(g) {
527
+ /**
528
+ * Structured explanatory blocks: now we CONVERT them into a multi-line JSDoc block
529
+ * while preserving each original line (instead of merging into a single paragraph).
530
+ * We must escape any raw '*\/' inside the body to avoid premature termination.
531
+ * Definition of structured:
532
+ * presence of arrows (->), regex tokens (?:, *\/), or the phrase 'Pattern explanation:'.
533
+ */ var structured = group.some(function(g) {
371
534
  return /->/.test(g.text) || /(\(\?:|\*\/)/.test(g.text) || /Pattern explanation:/i.test(g.text);
372
535
  });
373
536
  if (structured) {
@@ -377,7 +540,7 @@ function mergeLineCommentGroups(content) {
377
540
  try {
378
541
  for(var _iterator = group[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
379
542
  var ln = _step.value;
380
- // Escape closing sentinel inside content
543
+ // Escape closing sentinel inside content.
381
544
  var safe = ln.text.replace(/\*\//g, "*\\/");
382
545
  out.push("".concat(indent, " * ").concat(safe));
383
546
  }
@@ -402,11 +565,29 @@ function mergeLineCommentGroups(content) {
402
565
  }
403
566
  var indent1 = group[0].indent;
404
567
  merged = true;
405
- var para = group.map(function(g) {
568
+ /**
569
+ * We only want to add terminal punctuation once at the end of the merged
570
+ * paragraph, not after every original line (which can create spurious
571
+ * periods mid-sentence when lines were simple wraps). We also avoid
572
+ * appending a period if the final line ends with a colon introducing a
573
+ * list.
574
+ */ var norm = group.map(function(g) {
406
575
  return normalizeNote(g.text.trim());
407
- }).map(function(g) {
408
- return maybeAddPeriod(g);
409
- }).join(" ");
576
+ });
577
+ // Determine index of last non-empty line.
578
+ var lastIdx = norm.length - 1;
579
+ while(lastIdx > 0 && norm[lastIdx].trim() === ""){
580
+ lastIdx--;
581
+ }
582
+ for(var k = 0; k < norm.length; k++){
583
+ if (k === lastIdx) {
584
+ var ln1 = norm[k];
585
+ if (!/:$/.test(ln1.trim())) {
586
+ norm[k] = maybeAddPeriod(ln1);
587
+ }
588
+ }
589
+ }
590
+ var para = norm.join(" ").replace(/\s+/g, " ").trim();
410
591
  out.push("".concat(indent1, "/**"));
411
592
  out.push("".concat(indent1, " * ").concat(para));
412
593
  out.push("".concat(indent1, " */"));