@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/__tests__/glob.test.js +25 -17
- package/dist/__tests__/glob.test.js.map +1 -1
- package/dist/__tests__/lib.test.js +104 -26
- package/dist/__tests__/lib.test.js.map +1 -1
- package/dist/glob.d.ts +0 -1
- package/dist/glob.js +18 -149
- package/dist/glob.js.map +1 -1
- package/dist/lib.js +263 -85
- package/dist/lib.js.map +1 -1
- package/package.json +6 -3
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\
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
266
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
196
267
|
try {
|
|
197
|
-
for(var
|
|
198
|
-
var
|
|
199
|
-
var
|
|
200
|
-
if (isCodeFence(
|
|
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 +
|
|
274
|
+
out.push(prefix + trimmed1);
|
|
204
275
|
continue;
|
|
205
276
|
}
|
|
206
277
|
if (inFence) {
|
|
207
|
-
out.push(prefix +
|
|
278
|
+
out.push(prefix + trimmed1);
|
|
208
279
|
continue;
|
|
209
280
|
}
|
|
210
|
-
if (
|
|
281
|
+
if (trimmed1 === "" || isListLike(trimmed1) || isTagLine(trimmed1) || isHeadingLike(trimmed1) || isVisuallyIndentedCode(raw1)) {
|
|
211
282
|
flush();
|
|
212
|
-
if (
|
|
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(
|
|
288
|
+
out.push(prefix + normalizeNote(trimmed1));
|
|
218
289
|
}
|
|
219
290
|
continue;
|
|
220
291
|
}
|
|
221
|
-
para.push(
|
|
292
|
+
para.push(trimmed1);
|
|
222
293
|
}
|
|
223
294
|
} catch (err) {
|
|
224
|
-
|
|
225
|
-
|
|
295
|
+
_didIteratorError1 = true;
|
|
296
|
+
_iteratorError1 = err;
|
|
226
297
|
} finally{
|
|
227
298
|
try {
|
|
228
|
-
if (!
|
|
229
|
-
|
|
299
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
300
|
+
_iterator1.return();
|
|
230
301
|
}
|
|
231
302
|
} finally{
|
|
232
|
-
if (
|
|
233
|
-
throw
|
|
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(
|
|
298
|
-
var
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
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
|
-
})
|
|
408
|
-
|
|
409
|
-
|
|
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, " */"));
|