@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.
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,45 +263,45 @@ 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
  }
@@ -294,37 +365,90 @@ function reflowJsDocBlocks(content, width) {
294
365
  };
295
366
  }
296
367
  function wrapLineComments(content, width) {
297
- var _loop = function(i) {
298
- var _out;
368
+ var _loop = function() {
369
+ var _loop = function(k) {
370
+ var _out;
371
+ var _group_k = group[k], indent = _group_k.indent, body = _group_k.body;
372
+ var prefix = indent + "// ";
373
+ var avail = Math.max(10, width - prefix.length);
374
+ var text = body.replace(/\s+/g, " ").trim();
375
+ text = normalizeNote(text);
376
+ if (k === group.length - 1 && !/:$/.test(text.trim())) {
377
+ text = maybeAddPeriod(text);
378
+ }
379
+ var wrapped = wrapWords(text, avail).map(function(w) {
380
+ return prefix + w;
381
+ });
382
+ if (wrapped.join("\n") !== group[k].raw) {
383
+ changed = true;
384
+ }
385
+ (_out = out).push.apply(_out, _to_consumable_array(wrapped));
386
+ };
299
387
  var line = lines[i];
300
388
  var m = /^(\s*)\/\/( ?)(.*)$/.exec(line);
301
389
  if (!m || /^\/\/\//.test(line)) {
302
390
  out.push(line);
391
+ i++;
303
392
  return "continue";
304
393
  }
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";
394
+ /**
395
+ * Collect a group of consecutive simple // lines (not triple slash) that are
396
+ * eligible.
397
+ */ var group = [];
398
+ var j = i;
399
+ while(j < lines.length){
400
+ var gm = /^(\s*)\/\/ ?(.*)$/.exec(lines[j]);
401
+ if (!gm || /^\/\/\//.test(lines[j])) {
402
+ break;
403
+ }
404
+ var body = gm[2];
405
+ if (/^(@|eslint|ts-ignore)/.test(body) || /https?:\/\//.test(body)) {
406
+ break; // stop group before directives/URLs; process current line normally
407
+ }
408
+ group.push({
409
+ raw: lines[j],
410
+ indent: gm[1],
411
+ body: body
412
+ });
413
+ j++;
310
414
  }
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;
415
+ if (group.length <= 1) {
416
+ var _out;
417
+ // Single line: existing logic (add period if needed).
418
+ var indent = m[1];
419
+ var body1 = m[3];
420
+ if (/^(@|eslint|ts-ignore)/.test(body1) || /https?:\/\//.test(body1)) {
421
+ out.push(line);
422
+ i++;
423
+ return "continue";
424
+ }
425
+ var prefix = indent + "// ";
426
+ var avail = Math.max(10, width - prefix.length);
427
+ var text = body1.replace(/\s+/g, " ").trim();
428
+ text = normalizeNote(text);
429
+ text = maybeAddPeriod(text);
430
+ var wrapped = wrapWords(text, avail).map(function(w) {
431
+ return prefix + w;
432
+ });
433
+ if (wrapped.join("\n") !== line) {
434
+ changed = true;
435
+ }
436
+ (_out = out).push.apply(_out, _to_consumable_array(wrapped));
437
+ i++;
438
+ return "continue";
321
439
  }
322
- (_out = out).push.apply(_out, _to_consumable_array(wrapped));
440
+ /**
441
+ * Multi-line group: only add terminal punctuation (period) to final line if
442
+ * needed. Other lines are normalized for NOTE but left without forced
443
+ * punctuation.
444
+ */ for(var k = 0; k < group.length; k++)_loop(k);
445
+ i = j;
323
446
  };
324
447
  var lines = content.split(/\n/);
325
448
  var changed = false;
326
449
  var out = [];
327
- for(var i = 0; i < lines.length; i++)_loop(i);
450
+ var i = 0;
451
+ while(i < lines.length)_loop();
328
452
  return {
329
453
  content: out.join("\n"),
330
454
  applied: changed
@@ -335,11 +459,45 @@ function mergeLineCommentGroups(content) {
335
459
  var out = [];
336
460
  var i = 0;
337
461
  var merged = false;
462
+ function qualifiesExplanatoryAfterStatement(start) {
463
+ // Peek ahead to collect consecutive // lines (excluding triple slash).
464
+ var collected = [];
465
+ for(var k = start; k < lines.length; k++){
466
+ var lm = /^(\s*)\/\/( ?)(.*)$/.exec(lines[k]);
467
+ if (!lm || /^\/\/\//.test(lines[k])) {
468
+ break;
469
+ }
470
+ var body = lm[3];
471
+ if (/^(@|eslint|ts-ignore)/.test(body) || /https?:\/\//.test(body)) {
472
+ break;
473
+ }
474
+ collected.push(body.trim());
475
+ }
476
+ if (collected.length < 4) {
477
+ return false; // require minimum size
478
+ }
479
+ if (!/^[A-Z]/.test(collected[0])) {
480
+ return false; // start with capitalized sentence
481
+ }
482
+ /**
483
+ * Avoid matching directive-like or list-lists: require at least one line with
484
+ * a space (a sentence).
485
+ */ return collected.some(function(c) {
486
+ return /\s/.test(c);
487
+ });
488
+ }
338
489
  while(i < lines.length){
339
490
  if (/^\s*\/\//.test(lines[i]) && !/^\s*\/\/\//.test(lines[i])) {
340
491
  var prev = i > 0 ? lines[i - 1] : "";
341
492
  var prevTrim = prev.trim();
342
493
  var contextEligible = prevTrim === "" || /[{}]$/.test(prevTrim);
494
+ /**
495
+ * Additional heuristic: allow large explanatory group after a statement
496
+ * ending with ';' (but not inline trailing comment scenario) when it
497
+ * qualifies as explanatory.
498
+ */ if (!contextEligible && /;\s*$/.test(prevTrim) && qualifiesExplanatoryAfterStatement(i)) {
499
+ contextEligible = true;
500
+ }
343
501
  if (!contextEligible) {
344
502
  out.push(lines[i]);
345
503
  i++;
@@ -363,11 +521,13 @@ function mergeLineCommentGroups(content) {
363
521
  j++;
364
522
  }
365
523
  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) {
524
+ /**
525
+ * Structured explanatory blocks: now we CONVERT them into a multi-line JSDoc block
526
+ * while preserving each original line (instead of merging into a single paragraph).
527
+ * We must escape any raw '*\/' inside the body to avoid premature termination.
528
+ * Definition of structured:
529
+ * presence of arrows (->), regex tokens (?:, *\/), or the phrase 'Pattern explanation:'.
530
+ */ var structured = group.some(function(g) {
371
531
  return /->/.test(g.text) || /(\(\?:|\*\/)/.test(g.text) || /Pattern explanation:/i.test(g.text);
372
532
  });
373
533
  if (structured) {
@@ -377,7 +537,7 @@ function mergeLineCommentGroups(content) {
377
537
  try {
378
538
  for(var _iterator = group[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
379
539
  var ln = _step.value;
380
- // Escape closing sentinel inside content
540
+ // Escape closing sentinel inside content.
381
541
  var safe = ln.text.replace(/\*\//g, "*\\/");
382
542
  out.push("".concat(indent, " * ").concat(safe));
383
543
  }
@@ -402,11 +562,29 @@ function mergeLineCommentGroups(content) {
402
562
  }
403
563
  var indent1 = group[0].indent;
404
564
  merged = true;
405
- var para = group.map(function(g) {
565
+ /**
566
+ * We only want to add terminal punctuation once at the end of the merged
567
+ * paragraph, not after every original line (which can create spurious
568
+ * periods mid-sentence when lines were simple wraps). We also avoid
569
+ * appending a period if the final line ends with a colon introducing a
570
+ * list.
571
+ */ var norm = group.map(function(g) {
406
572
  return normalizeNote(g.text.trim());
407
- }).map(function(g) {
408
- return maybeAddPeriod(g);
409
- }).join(" ");
573
+ });
574
+ // Determine index of last non-empty line.
575
+ var lastIdx = norm.length - 1;
576
+ while(lastIdx > 0 && norm[lastIdx].trim() === ""){
577
+ lastIdx--;
578
+ }
579
+ for(var k = 0; k < norm.length; k++){
580
+ if (k === lastIdx) {
581
+ var ln1 = norm[k];
582
+ if (!/:$/.test(ln1.trim())) {
583
+ norm[k] = maybeAddPeriod(ln1);
584
+ }
585
+ }
586
+ }
587
+ var para = norm.join(" ").replace(/\s+/g, " ").trim();
410
588
  out.push("".concat(indent1, "/**"));
411
589
  out.push("".concat(indent1, " * ").concat(para));
412
590
  out.push("".concat(indent1, " */"));