@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/__tests__/glob.test.js +25 -17
- package/dist/__tests__/glob.test.js.map +1 -1
- package/dist/__tests__/lib.test.js +107 -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 +267 -86
- 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":"AAAA,OAAOA,QAAQ,UAAU;AACzB,OAAOC,UAAU,YAAY;AAC7B,OAAOC,gBAAgB,aAAa;AAMpC,IAAMC,iBAAiB,SAAS,gCAAgC;AAEhE,SAASC,YAAYC,OAAe;IACnC,IAAI,CAACF,eAAeG,IAAI,CAACD,UAAU;QAClC,OAAOJ,KAAKM,OAAO,CAACF,YAAY;IACjC;IACA,IAAMG,gBAAgBH,QAAQI,MAAM,CAAC;IACrC,IAAIC,WACHF,kBAAkB,CAAC,IAAIH,UAAUA,QAAQM,KAAK,CAAC,GAAGH;IACnD,IAAI,CAACE,SAASE,QAAQ,CAAC,MAAM;QAC5BF,WAAWT,KAAKM,OAAO,CAACG;IACzB;IACA,IAAI,CAACA,SAASG,MAAM,EAAE;QACrB,OAAO;IACR;IACA,OAAOH;AACR;AAEA,SAASI,KAAKC,IAAY,EAAEC,cAAuB;IAClD,IAAMC,UAAoB,EAAE;IAC5B,SAASC,MAAMC,GAAW;QACzB,IAAIC,UAAuB,EAAE;QAC7B,IAAI;YACHA,UAAUpB,GAAGqB,WAAW,CAACF,KAAK;gBAAEG,eAAe;YAAK;QACrD,EAAE,UAAM;YACP;QACD;YACK,kCAAA,2BAAA;;YAAL,QAAK,YAAWF,4BAAX,SAAA,6BAAA,QAAA,yBAAA,iCAAoB;gBAApB,IAAMG,IAAN;gBACJ,IAAMC,OAAOvB,KAAKwB,IAAI,CAACN,KAAKI,EAAEG,IAAI;gBAClC,IAAIH,EAAEI,cAAc,IAAI;oBACvB,IAAI,CAACX,gBAAgB;wBACpB;oBACD;oBACA,IAAIY,OAAAA,KAAAA;oBACJ,IAAI;wBACHA,OAAO5B,GAAG6B,QAAQ,CAACL;oBACpB,EAAE,UAAM;wBACP;oBACD;oBACA,IAAII,KAAKE,WAAW,IAAI;wBACvBZ,MAAMM;oBACP,OAAO,IAAII,KAAKG,MAAM,IAAI;wBACzBd,QAAQe,IAAI,CAACR;oBACd;oBACA;gBACD;gBACA,IAAID,EAAEO,WAAW,IAAI;oBACpBZ,MAAMM;gBACP,OAAO,IAAID,EAAEQ,MAAM,IAAI;oBACtBd,QAAQe,IAAI,CAACR;gBACd;YACD;;YAxBK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAyBN;IACA,IAAMS,QAAQlB,QAAQ;IACtB,IAAI,CAACf,GAAGkC,UAAU,CAACD,QAAQ;QAC1B,OAAO,EAAE;IACV;IACA,IAAME,KAAKnC,GAAG6B,QAAQ,CAACI;IACvB,IAAIE,GAAGJ,MAAM,IAAI;QAChB,OAAO;YAACE;SAAM;IACf;IACAf,MAAMe;IACN,OAAOhB;AACR;AAEA,OAAO,SAASmB,YACfC,QAAkB;QAClBC,UAAAA,iEAAuB,CAAC;IAExB,IAAMC,OAAO,IAAIC;IACjB,IAAMC,MAAgB,EAAE;QACnB,kCAAA,2BAAA;;QAAL,QAAK,YAAiBJ,6BAAjB,SAAA,6BAAA,QAAA,yBAAA,iCAA2B;YAA3B,IAAMhC,UAAN;YACJ,+BAA+B;YAC/B,IAAI,CAACF,eAAeG,IAAI,CAACD,UAAU;gBAClC,IACCL,GAAGkC,UAAU,CAAC7B,YACdL,GAAG6B,QAAQ,CAACxB,SAAS0B,MAAM,MAC3B,CAACQ,KAAKG,GAAG,CAACrC,UACT;oBACDkC,KAAKI,GAAG,CAACtC;oBACToC,IAAIT,IAAI,CAAC3B;gBACV;gBACA;YACD;YACA,IAAMU,OAAOX,YAAYC;YACzB,IAAMuC,QAAQ9B,KAAKC,MAAMuB,QAAQtB,cAAc,IAAI;YACnD,IAAM6B,YAAYxC,QAAQyC,OAAO,CAAC,OAAO;gBACpC,mCAAA,4BAAA;;gBAAL,QAAK,aAAWF,0BAAX,UAAA,8BAAA,SAAA,0BAAA,kCAAkB;oBAAlB,IAAMG,IAAN;oBACJ,IAAMC,aAAaD,EAAED,OAAO,CAAC,OAAO;oBACpC,IAAI5C,WAAW+C,OAAO,CAACD,YAAYH,YAAY;wBAC9C,IAAI,CAACN,KAAKG,GAAG,CAACK,IAAI;4BACjBR,KAAKI,GAAG,CAACI;4BACTN,IAAIT,IAAI,CAACe;wBACV;oBACD;gBACD;;gBARK;gBAAA;;;yBAAA,8BAAA;wBAAA;;;wBAAA;8BAAA;;;;QASN;;QAzBK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IA0BL,OAAON;AACR"}
|
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,50 +263,53 @@ 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
|
}
|
|
237
308
|
flush();
|
|
238
|
-
|
|
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(
|
|
298
|
-
var
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
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
|
-
})
|
|
408
|
-
|
|
409
|
-
|
|
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, " */"));
|