pkgprn 0.5.1 → 0.5.2
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/extract-references.js +4 -23
- package/index.d.ts.map +14 -14
- package/index.js +3 -20
- package/package.json +1 -1
- package/prune.js +38 -214
- package/strip-comments.js +81 -316
package/strip-comments.js
CHANGED
|
@@ -1,36 +1,11 @@
|
|
|
1
1
|
import { decode, encode } from '@jridgewell/sourcemap-codec';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {'jsdoc' | 'license' | 'regular'} CommentType
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} CommentRange
|
|
9
|
-
* @property {number} start - Start index in source (inclusive)
|
|
10
|
-
* @property {number} end - End index in source (exclusive)
|
|
11
|
-
* @property {CommentType} type - Classification of the comment
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {Object} StripResult
|
|
16
|
-
* @property {string} result - The stripped source text
|
|
17
|
-
* @property {Int32Array | null} lineMap - Maps 0-based original line → 0-based new line (-1 if removed). null when nothing changed.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
3
|
const jsExtensions = ['.js', '.mjs', '.cjs'];
|
|
21
4
|
|
|
22
|
-
/**
|
|
23
|
-
* Check if a file path has a JS extension that may contain comments.
|
|
24
|
-
* @param {string} file
|
|
25
|
-
* @returns {boolean}
|
|
26
|
-
*/
|
|
27
5
|
export function isStrippableFile(file) {
|
|
28
6
|
return jsExtensions.some(ext => file.endsWith(ext));
|
|
29
7
|
}
|
|
30
8
|
|
|
31
|
-
/**
|
|
32
|
-
* Keywords after which a `/` token begins a regex literal rather than division.
|
|
33
|
-
*/
|
|
34
9
|
const regexPrecedingKeywords = new Set([
|
|
35
10
|
'return',
|
|
36
11
|
'throw',
|
|
@@ -51,28 +26,17 @@ const regexPrecedingKeywords = new Set([
|
|
|
51
26
|
'else',
|
|
52
27
|
]);
|
|
53
28
|
|
|
54
|
-
/**
|
|
55
|
-
* Classify a block comment based on its content.
|
|
56
|
-
* Priority: license > jsdoc > regular.
|
|
57
|
-
*
|
|
58
|
-
* @param {string} source - Full source text
|
|
59
|
-
* @param {number} start - Start index of the comment (at `/`)
|
|
60
|
-
* @param {number} end - End index of the comment (after `*/`)
|
|
61
|
-
* @returns {CommentType}
|
|
62
|
-
*/
|
|
63
29
|
function classifyBlockComment(source, start, end) {
|
|
64
|
-
|
|
30
|
+
|
|
65
31
|
if (source[start + 2] === '!') {
|
|
66
32
|
return 'license';
|
|
67
33
|
}
|
|
68
34
|
|
|
69
|
-
// Check for @license or @preserve inside the comment body
|
|
70
35
|
const body = source.slice(start + 2, end - 2);
|
|
71
36
|
if (body.includes('@license') || body.includes('@preserve')) {
|
|
72
37
|
return 'license';
|
|
73
38
|
}
|
|
74
39
|
|
|
75
|
-
// JSDoc: starts with /** (and is not the degenerate /**/ which is length 4)
|
|
76
40
|
if (source[start + 2] === '*' && end - start > 4) {
|
|
77
41
|
return 'jsdoc';
|
|
78
42
|
}
|
|
@@ -80,113 +44,80 @@ function classifyBlockComment(source, start, end) {
|
|
|
80
44
|
return 'regular';
|
|
81
45
|
}
|
|
82
46
|
|
|
83
|
-
/**
|
|
84
|
-
* Scan source code and return an array of comment ranges with their types.
|
|
85
|
-
* Correctly handles:
|
|
86
|
-
* - Single and double quoted strings (with escapes)
|
|
87
|
-
* - Template literals (with nested `${…}` expressions, arbitrarily deep)
|
|
88
|
-
* - Regular expression literals (with character classes `[…]`)
|
|
89
|
-
* - Hashbang lines (`#!/…`)
|
|
90
|
-
* - Single-line comments (`// …`)
|
|
91
|
-
* - Block comments (`/* … */`)
|
|
92
|
-
*
|
|
93
|
-
* @param {string} source
|
|
94
|
-
* @returns {CommentRange[]}
|
|
95
|
-
*/
|
|
96
47
|
export function scanComments(source) {
|
|
97
|
-
|
|
48
|
+
|
|
98
49
|
const comments = [];
|
|
99
50
|
const len = source.length;
|
|
100
51
|
let i = 0;
|
|
101
52
|
|
|
102
|
-
// Stack for template literal nesting.
|
|
103
|
-
// Each entry holds the brace depth inside a `${…}` expression.
|
|
104
|
-
// When the stack is non-empty the main loop is inside a template expression.
|
|
105
|
-
/** @type {number[]} */
|
|
106
53
|
const templateStack = [];
|
|
107
54
|
|
|
108
|
-
// For regex-vs-division disambiguation we track whether the last
|
|
109
|
-
// *significant* (non-whitespace, non-comment) token could be the end
|
|
110
|
-
// of an expression. If it could, `/` is the division operator;
|
|
111
|
-
// otherwise `/` starts a regex literal.
|
|
112
55
|
let exprEnd = false;
|
|
113
56
|
|
|
114
|
-
// --- Hashbang ----------------------------------------------------------
|
|
115
57
|
if (len >= 2 && source[0] === '#' && source[1] === '!') {
|
|
116
|
-
|
|
58
|
+
|
|
117
59
|
while (i < len && source[i] !== '\n') i++;
|
|
118
|
-
|
|
60
|
+
|
|
119
61
|
}
|
|
120
62
|
|
|
121
63
|
while (i < len) {
|
|
122
64
|
const ch = source.charCodeAt(i);
|
|
123
65
|
|
|
124
|
-
// ---- whitespace (skip, preserve exprEnd) --------------------------
|
|
125
|
-
// space, tab, newline, carriage return, vertical tab, form feed,
|
|
126
|
-
// BOM / NBSP (0xFEFF, 0x00A0) – we keep it simple: anything ≤ 0x20
|
|
127
|
-
// plus the two common Unicode whitespace chars.
|
|
128
66
|
if (ch <= 0x20 || ch === 0xfeff || ch === 0xa0) {
|
|
129
67
|
i++;
|
|
130
68
|
continue;
|
|
131
69
|
}
|
|
132
70
|
|
|
133
|
-
|
|
134
|
-
if (ch === 0x2f /* / */ && i + 1 < len && source.charCodeAt(i + 1) === 0x2f /* / */) {
|
|
71
|
+
if (ch === 0x2f && i + 1 < len && source.charCodeAt(i + 1) === 0x2f ) {
|
|
135
72
|
const start = i;
|
|
136
73
|
i += 2;
|
|
137
|
-
while (i < len && source.charCodeAt(i) !== 0x0a
|
|
74
|
+
while (i < len && source.charCodeAt(i) !== 0x0a ) i++;
|
|
138
75
|
comments.push({ start, end: i, type: 'regular' });
|
|
139
|
-
|
|
76
|
+
|
|
140
77
|
continue;
|
|
141
78
|
}
|
|
142
79
|
|
|
143
|
-
|
|
144
|
-
if (ch === 0x2f /* / */ && i + 1 < len && source.charCodeAt(i + 1) === 0x2a /* * */) {
|
|
80
|
+
if (ch === 0x2f && i + 1 < len && source.charCodeAt(i + 1) === 0x2a ) {
|
|
145
81
|
const start = i;
|
|
146
82
|
i += 2;
|
|
147
|
-
while (i < len && !((source.charCodeAt(i) === 0x2a
|
|
83
|
+
while (i < len && !((source.charCodeAt(i) === 0x2a && i + 1 < len && source.charCodeAt(i + 1) === 0x2f) )) {
|
|
148
84
|
i++;
|
|
149
85
|
}
|
|
150
|
-
if (i < len) i += 2;
|
|
86
|
+
if (i < len) i += 2;
|
|
151
87
|
comments.push({ start, end: i, type: classifyBlockComment(source, start, i) });
|
|
152
|
-
|
|
88
|
+
|
|
153
89
|
continue;
|
|
154
90
|
}
|
|
155
91
|
|
|
156
|
-
|
|
157
|
-
if (ch === 0x2f /* / */ && !exprEnd) {
|
|
92
|
+
if (ch === 0x2f && !exprEnd) {
|
|
158
93
|
i = skipRegex(source, i, len);
|
|
159
|
-
exprEnd = true;
|
|
94
|
+
exprEnd = true;
|
|
160
95
|
continue;
|
|
161
96
|
}
|
|
162
97
|
|
|
163
|
-
|
|
164
|
-
if (ch === 0x27 /* ' */) {
|
|
98
|
+
if (ch === 0x27 ) {
|
|
165
99
|
i = skipSingleString(source, i, len);
|
|
166
100
|
exprEnd = true;
|
|
167
101
|
continue;
|
|
168
102
|
}
|
|
169
103
|
|
|
170
|
-
|
|
171
|
-
if (ch === 0x22 /* " */) {
|
|
104
|
+
if (ch === 0x22 ) {
|
|
172
105
|
i = skipDoubleString(source, i, len);
|
|
173
106
|
exprEnd = true;
|
|
174
107
|
continue;
|
|
175
108
|
}
|
|
176
109
|
|
|
177
|
-
|
|
178
|
-
if (ch === 0x60 /* ` */) {
|
|
110
|
+
if (ch === 0x60 ) {
|
|
179
111
|
i = scanTemplateTail(source, i + 1, len, templateStack, comments);
|
|
180
112
|
exprEnd = true;
|
|
181
113
|
continue;
|
|
182
114
|
}
|
|
183
115
|
|
|
184
|
-
|
|
185
|
-
if (ch === 0x7d /* } */) {
|
|
116
|
+
if (ch === 0x7d ) {
|
|
186
117
|
if (templateStack.length > 0) {
|
|
187
118
|
const depth = templateStack[templateStack.length - 1];
|
|
188
119
|
if (depth === 0) {
|
|
189
|
-
|
|
120
|
+
|
|
190
121
|
templateStack.pop();
|
|
191
122
|
i = scanTemplateTail(source, i + 1, len, templateStack, comments);
|
|
192
123
|
exprEnd = true;
|
|
@@ -195,17 +126,12 @@ export function scanComments(source) {
|
|
|
195
126
|
templateStack[templateStack.length - 1] = depth - 1;
|
|
196
127
|
}
|
|
197
128
|
i++;
|
|
198
|
-
|
|
199
|
-
// This is correct for block statements, if/for/while bodies,
|
|
200
|
-
// class bodies, etc. For the rare `({}) / x` pattern it would
|
|
201
|
-
// misidentify division as regex, but that is harmless for
|
|
202
|
-
// comment detection (we just skip over the "regex" body).
|
|
129
|
+
|
|
203
130
|
exprEnd = false;
|
|
204
131
|
continue;
|
|
205
132
|
}
|
|
206
133
|
|
|
207
|
-
|
|
208
|
-
if (ch === 0x7b /* { */) {
|
|
134
|
+
if (ch === 0x7b ) {
|
|
209
135
|
if (templateStack.length > 0) {
|
|
210
136
|
templateStack[templateStack.length - 1]++;
|
|
211
137
|
}
|
|
@@ -214,7 +140,6 @@ export function scanComments(source) {
|
|
|
214
140
|
continue;
|
|
215
141
|
}
|
|
216
142
|
|
|
217
|
-
// ---- identifier / keyword / number --------------------------------
|
|
218
143
|
if (isIdentStart(ch) || isDigit(ch)) {
|
|
219
144
|
const wordStart = i;
|
|
220
145
|
i++;
|
|
@@ -224,21 +149,18 @@ export function scanComments(source) {
|
|
|
224
149
|
continue;
|
|
225
150
|
}
|
|
226
151
|
|
|
227
|
-
|
|
228
|
-
if ((ch === 0x2b /* + */ || ch === 0x2d) /* - */ && i + 1 < len && source.charCodeAt(i + 1) === ch) {
|
|
152
|
+
if ((ch === 0x2b || ch === 0x2d) && i + 1 < len && source.charCodeAt(i + 1) === ch) {
|
|
229
153
|
i += 2;
|
|
230
|
-
exprEnd = true;
|
|
154
|
+
exprEnd = true;
|
|
231
155
|
continue;
|
|
232
156
|
}
|
|
233
157
|
|
|
234
|
-
|
|
235
|
-
if (ch === 0x29 /* ) */ || ch === 0x5d /* ] */) {
|
|
158
|
+
if (ch === 0x29 || ch === 0x5d ) {
|
|
236
159
|
i++;
|
|
237
160
|
exprEnd = true;
|
|
238
161
|
continue;
|
|
239
162
|
}
|
|
240
163
|
|
|
241
|
-
// ---- everything else: operators, punctuation ----------------------
|
|
242
164
|
i++;
|
|
243
165
|
exprEnd = false;
|
|
244
166
|
}
|
|
@@ -246,266 +168,168 @@ export function scanComments(source) {
|
|
|
246
168
|
return comments;
|
|
247
169
|
}
|
|
248
170
|
|
|
249
|
-
// ---------------------------------------------------------------------------
|
|
250
|
-
// Character classification helpers
|
|
251
|
-
// ---------------------------------------------------------------------------
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* @param {number} ch - char code
|
|
255
|
-
* @returns {boolean}
|
|
256
|
-
*/
|
|
257
171
|
function isDigit(ch) {
|
|
258
|
-
return ch >= 0x30 && ch <= 0x39;
|
|
172
|
+
return ch >= 0x30 && ch <= 0x39;
|
|
259
173
|
}
|
|
260
174
|
|
|
261
|
-
/**
|
|
262
|
-
* @param {number} ch - char code
|
|
263
|
-
* @returns {boolean}
|
|
264
|
-
*/
|
|
265
175
|
function isIdentStart(ch) {
|
|
266
176
|
return (
|
|
267
|
-
(ch >= 0x41 && ch <= 0x5a) ||
|
|
268
|
-
(ch >= 0x61 && ch <= 0x7a) ||
|
|
269
|
-
ch === 0x5f ||
|
|
270
|
-
ch === 0x24 ||
|
|
271
|
-
ch === 0x5c ||
|
|
272
|
-
ch > 0x7f
|
|
177
|
+
(ch >= 0x41 && ch <= 0x5a) ||
|
|
178
|
+
(ch >= 0x61 && ch <= 0x7a) ||
|
|
179
|
+
ch === 0x5f ||
|
|
180
|
+
ch === 0x24 ||
|
|
181
|
+
ch === 0x5c ||
|
|
182
|
+
ch > 0x7f
|
|
273
183
|
);
|
|
274
184
|
}
|
|
275
185
|
|
|
276
|
-
/**
|
|
277
|
-
* @param {number} ch - char code
|
|
278
|
-
* @returns {boolean}
|
|
279
|
-
*/
|
|
280
186
|
function isIdentPart(ch) {
|
|
281
187
|
return isIdentStart(ch) || isDigit(ch);
|
|
282
188
|
}
|
|
283
189
|
|
|
284
|
-
// ---------------------------------------------------------------------------
|
|
285
|
-
// Skip helpers — each returns the new index *after* the construct.
|
|
286
|
-
// ---------------------------------------------------------------------------
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Skip a single-quoted string starting at index `i` (which points at the
|
|
290
|
-
* opening `'`). Returns the index after the closing `'`.
|
|
291
|
-
* @param {string} s
|
|
292
|
-
* @param {number} i
|
|
293
|
-
* @param {number} len
|
|
294
|
-
* @returns {number}
|
|
295
|
-
*/
|
|
296
190
|
function skipSingleString(s, i, len) {
|
|
297
|
-
i++;
|
|
191
|
+
i++;
|
|
298
192
|
while (i < len) {
|
|
299
193
|
const ch = s.charCodeAt(i);
|
|
300
|
-
if (ch === 0x27
|
|
194
|
+
if (ch === 0x27 ) {
|
|
301
195
|
i++;
|
|
302
196
|
break;
|
|
303
197
|
}
|
|
304
|
-
if (ch === 0x5c
|
|
198
|
+
if (ch === 0x5c ) {
|
|
305
199
|
i += 2;
|
|
306
200
|
continue;
|
|
307
|
-
}
|
|
308
|
-
if (ch === 0x0a
|
|
201
|
+
}
|
|
202
|
+
if (ch === 0x0a || ch === 0x0d ) break;
|
|
309
203
|
i++;
|
|
310
204
|
}
|
|
311
205
|
return i;
|
|
312
206
|
}
|
|
313
207
|
|
|
314
|
-
/**
|
|
315
|
-
* Skip a double-quoted string starting at index `i` (which points at the
|
|
316
|
-
* opening `"`). Returns the index after the closing `"`.
|
|
317
|
-
* @param {string} s
|
|
318
|
-
* @param {number} i
|
|
319
|
-
* @param {number} len
|
|
320
|
-
* @returns {number}
|
|
321
|
-
*/
|
|
322
208
|
function skipDoubleString(s, i, len) {
|
|
323
|
-
i++;
|
|
209
|
+
i++;
|
|
324
210
|
while (i < len) {
|
|
325
211
|
const ch = s.charCodeAt(i);
|
|
326
|
-
if (ch === 0x22
|
|
212
|
+
if (ch === 0x22 ) {
|
|
327
213
|
i++;
|
|
328
214
|
break;
|
|
329
215
|
}
|
|
330
|
-
if (ch === 0x5c
|
|
216
|
+
if (ch === 0x5c ) {
|
|
331
217
|
i += 2;
|
|
332
218
|
continue;
|
|
333
219
|
}
|
|
334
|
-
if (ch === 0x0a || ch === 0x0d) break;
|
|
220
|
+
if (ch === 0x0a || ch === 0x0d) break;
|
|
335
221
|
i++;
|
|
336
222
|
}
|
|
337
223
|
return i;
|
|
338
224
|
}
|
|
339
225
|
|
|
340
|
-
/**
|
|
341
|
-
* Skip a regex literal starting at index `i` (which points at the opening `/`).
|
|
342
|
-
* Handles character classes `[…]` and escape sequences.
|
|
343
|
-
* Returns the index after the closing `/` and any flags.
|
|
344
|
-
* @param {string} s
|
|
345
|
-
* @param {number} i
|
|
346
|
-
* @param {number} len
|
|
347
|
-
* @returns {number}
|
|
348
|
-
*/
|
|
349
226
|
function skipRegex(s, i, len) {
|
|
350
|
-
i++;
|
|
227
|
+
i++;
|
|
351
228
|
while (i < len) {
|
|
352
229
|
const ch = s.charCodeAt(i);
|
|
353
|
-
if (ch === 0x5c
|
|
354
|
-
i += 2;
|
|
230
|
+
if (ch === 0x5c ) {
|
|
231
|
+
i += 2;
|
|
355
232
|
continue;
|
|
356
233
|
}
|
|
357
|
-
if (ch === 0x5b
|
|
358
|
-
|
|
234
|
+
if (ch === 0x5b ) {
|
|
235
|
+
|
|
359
236
|
i++;
|
|
360
237
|
while (i < len) {
|
|
361
238
|
const cc = s.charCodeAt(i);
|
|
362
|
-
if (cc === 0x5c
|
|
239
|
+
if (cc === 0x5c ) {
|
|
363
240
|
i += 2;
|
|
364
241
|
continue;
|
|
365
242
|
}
|
|
366
|
-
if (cc === 0x5d
|
|
243
|
+
if (cc === 0x5d ) {
|
|
367
244
|
i++;
|
|
368
245
|
break;
|
|
369
246
|
}
|
|
370
|
-
if (cc === 0x0a || cc === 0x0d) break;
|
|
247
|
+
if (cc === 0x0a || cc === 0x0d) break;
|
|
371
248
|
i++;
|
|
372
249
|
}
|
|
373
250
|
continue;
|
|
374
251
|
}
|
|
375
|
-
if (ch === 0x2f
|
|
376
|
-
i++;
|
|
377
|
-
|
|
252
|
+
if (ch === 0x2f ) {
|
|
253
|
+
i++;
|
|
254
|
+
|
|
378
255
|
while (i < len && isRegexFlag(s.charCodeAt(i))) i++;
|
|
379
256
|
break;
|
|
380
257
|
}
|
|
381
|
-
if (ch === 0x0a || ch === 0x0d) break;
|
|
258
|
+
if (ch === 0x0a || ch === 0x0d) break;
|
|
382
259
|
i++;
|
|
383
260
|
}
|
|
384
261
|
return i;
|
|
385
262
|
}
|
|
386
263
|
|
|
387
|
-
/**
|
|
388
|
-
* @param {number} ch
|
|
389
|
-
* @returns {boolean}
|
|
390
|
-
*/
|
|
391
264
|
function isRegexFlag(ch) {
|
|
392
|
-
return ch >= 0x61 && ch <= 0x7a;
|
|
265
|
+
return ch >= 0x61 && ch <= 0x7a;
|
|
393
266
|
}
|
|
394
267
|
|
|
395
|
-
/**
|
|
396
|
-
* Scan the body of a template literal starting *after* the opening `` ` ``
|
|
397
|
-
* (or after the `}` that closes a template expression).
|
|
398
|
-
*
|
|
399
|
-
* If we hit `${`, we push onto `templateStack` and return to the main loop
|
|
400
|
-
* so that the expression is parsed as normal code (which may contain
|
|
401
|
-
* comments, nested templates, etc.).
|
|
402
|
-
*
|
|
403
|
-
* If we hit the closing `` ` ``, we return and the template is done.
|
|
404
|
-
*
|
|
405
|
-
* @param {string} s
|
|
406
|
-
* @param {number} i - index right after the `` ` `` or `}`
|
|
407
|
-
* @param {number} len
|
|
408
|
-
* @param {number[]} templateStack
|
|
409
|
-
* @param {CommentRange[]} comments - passed through so inner comments are recorded
|
|
410
|
-
* @returns {number} new index
|
|
411
|
-
*/
|
|
412
268
|
function scanTemplateTail(s, i, len, templateStack, comments) {
|
|
413
|
-
void comments;
|
|
269
|
+
void comments;
|
|
414
270
|
while (i < len) {
|
|
415
271
|
const ch = s.charCodeAt(i);
|
|
416
|
-
if (ch === 0x5c
|
|
417
|
-
i += 2;
|
|
272
|
+
if (ch === 0x5c ) {
|
|
273
|
+
i += 2;
|
|
418
274
|
continue;
|
|
419
275
|
}
|
|
420
|
-
if (ch === 0x60
|
|
421
|
-
i++;
|
|
276
|
+
if (ch === 0x60 ) {
|
|
277
|
+
i++;
|
|
422
278
|
return i;
|
|
423
279
|
}
|
|
424
|
-
if (ch === 0x24
|
|
425
|
-
i += 2;
|
|
426
|
-
templateStack.push(0);
|
|
427
|
-
return i;
|
|
280
|
+
if (ch === 0x24 && i + 1 < len && s.charCodeAt(i + 1) === 0x7b ) {
|
|
281
|
+
i += 2;
|
|
282
|
+
templateStack.push(0);
|
|
283
|
+
return i;
|
|
428
284
|
}
|
|
429
285
|
i++;
|
|
430
286
|
}
|
|
431
287
|
return i;
|
|
432
288
|
}
|
|
433
289
|
|
|
434
|
-
// ---------------------------------------------------------------------------
|
|
435
|
-
// Public API
|
|
436
|
-
// ---------------------------------------------------------------------------
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Parse the `--strip-comments` flag value into a `Set` of comment types.
|
|
440
|
-
*
|
|
441
|
-
* - `'all'` or `true` → `{'jsdoc', 'license', 'regular'}`
|
|
442
|
-
* - `'jsdoc,regular'` → `{'jsdoc', 'regular'}`
|
|
443
|
-
*
|
|
444
|
-
* @param {string | true} value
|
|
445
|
-
* @returns {Set<CommentType>}
|
|
446
|
-
*/
|
|
447
290
|
export function parseCommentTypes(value) {
|
|
448
291
|
if (value === true || value === 'all') {
|
|
449
|
-
return new Set(
|
|
292
|
+
return new Set( (['jsdoc', 'license', 'regular']));
|
|
450
293
|
}
|
|
451
294
|
|
|
452
|
-
const valid =
|
|
295
|
+
const valid = (['jsdoc', 'license', 'regular']);
|
|
453
296
|
const parts = String(value)
|
|
454
297
|
.split(',')
|
|
455
298
|
.map(s => s.trim())
|
|
456
299
|
.filter(Boolean);
|
|
457
300
|
|
|
458
|
-
/** @type {Set<CommentType>} */
|
|
459
301
|
const result = new Set();
|
|
460
302
|
|
|
461
303
|
for (const part of parts) {
|
|
462
304
|
if (part === 'all') {
|
|
463
305
|
return new Set(valid);
|
|
464
306
|
}
|
|
465
|
-
if (!valid.includes(
|
|
307
|
+
if (!valid.includes( (part))) {
|
|
466
308
|
throw new Error(`unknown comment type "${part}" (expected: ${valid.join(', ')}, all)`);
|
|
467
309
|
}
|
|
468
|
-
result.add(
|
|
310
|
+
result.add( (part));
|
|
469
311
|
}
|
|
470
312
|
|
|
471
313
|
if (result.size === 0) {
|
|
472
|
-
return new Set(valid);
|
|
314
|
+
return new Set(valid);
|
|
473
315
|
}
|
|
474
316
|
|
|
475
317
|
return result;
|
|
476
318
|
}
|
|
477
319
|
|
|
478
|
-
/**
|
|
479
|
-
* Strip comments from `source` whose type is in `typesToStrip`.
|
|
480
|
-
*
|
|
481
|
-
* @param {string} source
|
|
482
|
-
* @param {Set<CommentType>} typesToStrip
|
|
483
|
-
* @returns {string}
|
|
484
|
-
*/
|
|
485
320
|
export function stripComments(source, typesToStrip) {
|
|
486
321
|
return stripCommentsWithLineMap(source, typesToStrip).result;
|
|
487
322
|
}
|
|
488
323
|
|
|
489
|
-
/**
|
|
490
|
-
* Strip comments and return both the stripped source and a line map that
|
|
491
|
-
* tracks where each original line ended up in the output.
|
|
492
|
-
*
|
|
493
|
-
* @param {string} source
|
|
494
|
-
* @param {Set<CommentType>} typesToStrip
|
|
495
|
-
* @returns {StripResult}
|
|
496
|
-
*/
|
|
497
324
|
export function stripCommentsWithLineMap(source, typesToStrip) {
|
|
498
325
|
const comments = scanComments(source);
|
|
499
326
|
|
|
500
327
|
if (comments.length === 0) return { result: source, lineMap: null };
|
|
501
328
|
|
|
502
|
-
// Filter to only the comments we want to remove.
|
|
503
329
|
const toRemove = comments.filter(c => typesToStrip.has(c.type));
|
|
504
330
|
|
|
505
331
|
if (toRemove.length === 0) return { result: source, lineMap: null };
|
|
506
332
|
|
|
507
|
-
// Build output by copying non-removed ranges.
|
|
508
|
-
/** @type {string[]} */
|
|
509
333
|
const parts = [];
|
|
510
334
|
let pos = 0;
|
|
511
335
|
|
|
@@ -522,58 +346,36 @@ export function stripCommentsWithLineMap(source, typesToStrip) {
|
|
|
522
346
|
|
|
523
347
|
let intermediate = parts.join('');
|
|
524
348
|
|
|
525
|
-
// --- Build original-line → intermediate-line mapping -------------------
|
|
526
|
-
// For every original offset, compute how many bytes were removed before it.
|
|
527
|
-
// Then convert original line-start offsets to intermediate offsets and
|
|
528
|
-
// derive intermediate line numbers.
|
|
529
|
-
|
|
530
349
|
const origLines = source.split('\n');
|
|
531
350
|
const origLineCount = origLines.length;
|
|
532
351
|
|
|
533
|
-
// Build sorted prefix-sum of removed byte counts for fast lookup.
|
|
534
|
-
// removedBefore(offset) = total chars removed in ranges fully before offset.
|
|
535
|
-
// We also detect if an offset falls inside a removed range.
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Translate an original offset to an intermediate offset.
|
|
539
|
-
* Returns -1 if the offset is inside a removed range.
|
|
540
|
-
* @param {number} offset
|
|
541
|
-
* @returns {number}
|
|
542
|
-
*/
|
|
543
352
|
function translateOffset(offset) {
|
|
544
353
|
let removed = 0;
|
|
545
354
|
for (const { start, end } of toRemove) {
|
|
546
355
|
if (offset < start) break;
|
|
547
|
-
if (offset < end) return -1;
|
|
356
|
+
if (offset < end) return -1;
|
|
548
357
|
removed += end - start;
|
|
549
358
|
}
|
|
550
359
|
return offset - removed;
|
|
551
360
|
}
|
|
552
361
|
|
|
553
|
-
// For each original line, figure out which intermediate line it maps to.
|
|
554
|
-
// An original line maps to -1 if its entire non-whitespace content was
|
|
555
|
-
// inside removed ranges (i.e. the line becomes blank/whitespace-only).
|
|
556
362
|
const intermediateText = intermediate;
|
|
557
363
|
const intermediateLineStarts = buildLineStarts(intermediateText);
|
|
558
364
|
|
|
559
|
-
/** @type {Int32Array} */
|
|
560
365
|
const origToIntermediate = new Int32Array(origLineCount).fill(-1);
|
|
561
366
|
let origOffset = 0;
|
|
562
367
|
for (let oi = 0; oi < origLineCount; oi++) {
|
|
563
368
|
const lineLen = origLines[oi].length;
|
|
564
|
-
|
|
565
|
-
// We try the line-start offset; if it's inside a removed range
|
|
566
|
-
// the whole beginning is gone, but content may survive later.
|
|
567
|
-
// The most reliable way: translate the offset of each non-WS char.
|
|
369
|
+
|
|
568
370
|
let survived = false;
|
|
569
371
|
for (let ci = 0; ci < lineLen; ci++) {
|
|
570
372
|
const ch = source.charCodeAt(origOffset + ci);
|
|
571
|
-
|
|
373
|
+
|
|
572
374
|
if (ch === 0x20 || ch === 0x09 || ch === 0x0d) continue;
|
|
573
375
|
const mapped = translateOffset(origOffset + ci);
|
|
574
376
|
if (mapped !== -1) {
|
|
575
377
|
survived = true;
|
|
576
|
-
|
|
378
|
+
|
|
577
379
|
origToIntermediate[oi] = offsetToLine(intermediateLineStarts, mapped);
|
|
578
380
|
break;
|
|
579
381
|
}
|
|
@@ -581,18 +383,13 @@ export function stripCommentsWithLineMap(source, typesToStrip) {
|
|
|
581
383
|
if (!survived) {
|
|
582
384
|
origToIntermediate[oi] = -1;
|
|
583
385
|
}
|
|
584
|
-
origOffset += lineLen + 1;
|
|
386
|
+
origOffset += lineLen + 1;
|
|
585
387
|
}
|
|
586
388
|
|
|
587
|
-
// --- Apply cleanup (same logic as before) ------------------------------
|
|
588
|
-
|
|
589
|
-
// Trim trailing whitespace from every line.
|
|
590
389
|
intermediate = intermediate.replace(/[ \t]+$/gm, '');
|
|
591
390
|
|
|
592
|
-
// Collapse 3+ consecutive newlines into 2 newlines.
|
|
593
391
|
intermediate = intermediate.replace(/\n{3,}/g, '\n\n');
|
|
594
392
|
|
|
595
|
-
// Remove leading blank lines (preserve hashbang).
|
|
596
393
|
if (intermediate.startsWith('#!')) {
|
|
597
394
|
const hashbangEnd = intermediate.indexOf('\n');
|
|
598
395
|
if (hashbangEnd !== -1) {
|
|
@@ -604,23 +401,17 @@ export function stripCommentsWithLineMap(source, typesToStrip) {
|
|
|
604
401
|
intermediate = intermediate.replace(/^\n+/, '');
|
|
605
402
|
}
|
|
606
403
|
|
|
607
|
-
// Ensure the file ends with exactly one newline (if it originally did).
|
|
608
404
|
if (source.endsWith('\n') && intermediate.length > 0) {
|
|
609
405
|
intermediate = intermediate.replace(/\n*$/, '\n');
|
|
610
406
|
}
|
|
611
407
|
|
|
612
408
|
const result = intermediate;
|
|
613
409
|
|
|
614
|
-
// --- Build intermediate-line → final-line mapping ----------------------
|
|
615
|
-
// The cleanup may have removed/collapsed lines from the intermediateText.
|
|
616
|
-
// We line up intermediateText lines with final lines by content matching.
|
|
617
410
|
const finalLines = result.split('\n');
|
|
618
411
|
const intLines = intermediateText.split('\n');
|
|
619
412
|
|
|
620
|
-
// Trim trailing WS from intermediate lines to match what cleanup did.
|
|
621
413
|
const intLinesTrimmed = intLines.map(l => l.replace(/[ \t]+$/, ''));
|
|
622
414
|
|
|
623
|
-
/** @type {Int32Array} */
|
|
624
415
|
const intermediateToFinal = new Int32Array(intLines.length).fill(-1);
|
|
625
416
|
let fi = 0;
|
|
626
417
|
for (let ii = 0; ii < intLinesTrimmed.length && fi < finalLines.length; ii++) {
|
|
@@ -628,11 +419,9 @@ export function stripCommentsWithLineMap(source, typesToStrip) {
|
|
|
628
419
|
intermediateToFinal[ii] = fi;
|
|
629
420
|
fi++;
|
|
630
421
|
}
|
|
631
|
-
|
|
422
|
+
|
|
632
423
|
}
|
|
633
424
|
|
|
634
|
-
// --- Compose: original → intermediate → final --------------------------
|
|
635
|
-
/** @type {Int32Array} */
|
|
636
425
|
const lineMap = new Int32Array(origLineCount).fill(-1);
|
|
637
426
|
for (let oi = 0; oi < origLineCount; oi++) {
|
|
638
427
|
const il = origToIntermediate[oi];
|
|
@@ -644,14 +433,8 @@ export function stripCommentsWithLineMap(source, typesToStrip) {
|
|
|
644
433
|
return { result, lineMap };
|
|
645
434
|
}
|
|
646
435
|
|
|
647
|
-
/**
|
|
648
|
-
* Build an array of line-start offsets for the given text.
|
|
649
|
-
* `result[i]` is the char offset where line `i` begins (0-based lines).
|
|
650
|
-
* @param {string} text
|
|
651
|
-
* @returns {number[]}
|
|
652
|
-
*/
|
|
653
436
|
function buildLineStarts(text) {
|
|
654
|
-
|
|
437
|
+
|
|
655
438
|
const starts = [0];
|
|
656
439
|
for (let i = 0; i < text.length; i++) {
|
|
657
440
|
if (text.charCodeAt(i) === 0x0a) {
|
|
@@ -661,14 +444,8 @@ function buildLineStarts(text) {
|
|
|
661
444
|
return starts;
|
|
662
445
|
}
|
|
663
446
|
|
|
664
|
-
/**
|
|
665
|
-
* Given sorted line-start offsets, find which line a char offset falls on.
|
|
666
|
-
* @param {number[]} lineStarts
|
|
667
|
-
* @param {number} offset
|
|
668
|
-
* @returns {number} 0-based line number
|
|
669
|
-
*/
|
|
670
447
|
function offsetToLine(lineStarts, offset) {
|
|
671
|
-
|
|
448
|
+
|
|
672
449
|
let lo = 0;
|
|
673
450
|
let hi = lineStarts.length - 1;
|
|
674
451
|
while (lo < hi) {
|
|
@@ -682,43 +459,31 @@ function offsetToLine(lineStarts, offset) {
|
|
|
682
459
|
return lo;
|
|
683
460
|
}
|
|
684
461
|
|
|
685
|
-
/**
|
|
686
|
-
* Adjust a parsed sourcemap (v3) whose `sources` reference a file that had
|
|
687
|
-
* comments stripped. Updates the original-line numbers in `mappings` for
|
|
688
|
-
* segments that point at the given source index.
|
|
689
|
-
*
|
|
690
|
-
* Segments whose original line maps to -1 (i.e. the line was removed) are
|
|
691
|
-
* dropped from the output.
|
|
692
|
-
*
|
|
693
|
-
* @param {{ version: number, mappings: string, sources?: string[], names?: string[], [k: string]: unknown }} map - Parsed sourcemap object (mutated in place).
|
|
694
|
-
* @param {number} sourceIndex - Index in `map.sources` of the stripped file.
|
|
695
|
-
* @param {Int32Array} lineMap - 0-based original line → 0-based new line (-1 if removed).
|
|
696
|
-
*/
|
|
697
462
|
export function adjustSourcemapLineMappings(map, sourceIndex, lineMap) {
|
|
698
463
|
if (map.version !== 3 || typeof map.mappings !== 'string') return;
|
|
699
464
|
|
|
700
465
|
const decoded = decode(map.mappings);
|
|
701
466
|
|
|
702
467
|
for (const line of decoded) {
|
|
703
|
-
|
|
468
|
+
|
|
704
469
|
for (let si = line.length - 1; si >= 0; si--) {
|
|
705
470
|
const seg = line[si];
|
|
706
|
-
|
|
471
|
+
|
|
707
472
|
if (seg.length < 4) continue;
|
|
708
|
-
const seg4 =
|
|
709
|
-
|
|
473
|
+
const seg4 = (seg);
|
|
474
|
+
|
|
710
475
|
if (seg4[1] !== sourceIndex) continue;
|
|
711
476
|
|
|
712
|
-
const origLine = seg4[2];
|
|
477
|
+
const origLine = seg4[2];
|
|
713
478
|
if (origLine < 0 || origLine >= lineMap.length) {
|
|
714
|
-
|
|
479
|
+
|
|
715
480
|
line.splice(si, 1);
|
|
716
481
|
continue;
|
|
717
482
|
}
|
|
718
483
|
|
|
719
484
|
const newLine = lineMap[origLine];
|
|
720
485
|
if (newLine === -1) {
|
|
721
|
-
|
|
486
|
+
|
|
722
487
|
line.splice(si, 1);
|
|
723
488
|
continue;
|
|
724
489
|
}
|