@depup/webpack-sources 3.3.4-depup.0 → 3.6.0-depup.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/README.md +2 -2
- package/changes.json +1 -1
- package/lib/CachedSource.js +257 -63
- package/lib/CompatSource.js +42 -1
- package/lib/ConcatSource.js +100 -28
- package/lib/OriginalSource.js +111 -45
- package/lib/PrefixSource.js +60 -11
- package/lib/RawSource.js +85 -29
- package/lib/ReplaceSource.js +269 -73
- package/lib/SizeOnlySource.js +10 -1
- package/lib/Source.js +39 -0
- package/lib/SourceMapSource.js +92 -10
- package/lib/helpers/createMappingsSerializer.js +307 -54
- package/lib/helpers/getFromStreamChunks.js +165 -73
- package/lib/helpers/getGeneratedSourceInfo.js +7 -4
- package/lib/helpers/readMappings.js +6 -3
- package/lib/helpers/scopes.js +438 -0
- package/lib/helpers/splitIntoLines.js +10 -9
- package/lib/helpers/splitIntoPotentialTokens.js +99 -26
- package/lib/helpers/streamAndGetSourceAndMap.js +61 -22
- package/lib/helpers/streamChunks.js +3 -2
- package/lib/helpers/streamChunksOfCombinedSourceMap.js +7 -3
- package/lib/helpers/streamChunksOfRawSource.js +23 -15
- package/lib/helpers/streamChunksOfSourceMap.js +50 -42
- package/lib/index.js +3 -0
- package/package.json +34 -23
- package/types/CachedSource.d.ts +192 -0
- package/types/CompatSource.d.ts +87 -0
- package/types/ConcatSource.d.ts +75 -0
- package/types/OriginalSource.d.ts +87 -0
- package/types/PrefixSource.d.ts +59 -0
- package/types/RawSource.d.ts +75 -0
- package/types/ReplaceSource.d.ts +107 -0
- package/types/SizeOnlySource.d.ts +24 -0
- package/types/Source.d.ts +189 -0
- package/types/SourceMapSource.d.ts +136 -0
- package/types/helpers/createMappingsSerializer.d.ts +60 -0
- package/types/helpers/getFromStreamChunks.d.ts +16 -0
- package/types/helpers/getGeneratedSourceInfo.d.ts +31 -0
- package/types/helpers/getName.d.ts +15 -0
- package/types/helpers/getSource.d.ts +15 -0
- package/types/helpers/readMappings.d.ts +19 -0
- package/types/helpers/scopes.d.ts +119 -0
- package/types/helpers/splitIntoLines.d.ts +6 -0
- package/types/helpers/splitIntoPotentialTokens.d.ts +37 -0
- package/types/helpers/streamAndGetSourceAndMap.d.ts +39 -0
- package/types/helpers/streamChunks.d.ts +56 -0
- package/types/helpers/streamChunksOfCombinedSourceMap.d.ts +42 -0
- package/types/helpers/streamChunksOfRawSource.d.ts +16 -0
- package/types/helpers/streamChunksOfSourceMap.d.ts +19 -0
- package/types/helpers/stringBufferUtils.d.ts +55 -0
- package/types/index.d.ts +50 -0
- package/types.d.ts +42 -439
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
3
|
+
Author Alexander Akait @alexander-akait
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
/** @typedef {import("../Source").RawSourceMap} RawSourceMap */
|
|
9
|
+
/** @typedef {import("./streamChunks").ScopeBindings} ScopeBindings */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A generated or original position, both counted from zero.
|
|
13
|
+
* @typedef {object} ScopePosition
|
|
14
|
+
* @property {number} line line
|
|
15
|
+
* @property {number} column column
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One source's scope, and the span of generated code it explains.
|
|
20
|
+
* @typedef {object} SourceScope
|
|
21
|
+
* @property {number} sourceIndex index into the map's `sources`
|
|
22
|
+
* @property {string[]} variables the names the source declares
|
|
23
|
+
* @property {string[]} values the generated expression each name evaluates to
|
|
24
|
+
* @property {ScopePosition} originalEnd end of the original scope, exclusive
|
|
25
|
+
* @property {ScopePosition[]} rangeStarts start of each generated range, inclusive
|
|
26
|
+
* @property {ScopePosition[]} rangeEnds end of each generated range, exclusive
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const BASE64_CHARS =
|
|
30
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
31
|
+
|
|
32
|
+
const BASE64_VALUES = new Int8Array(128).fill(-1);
|
|
33
|
+
for (let index = 0; index < BASE64_CHARS.length; index++) {
|
|
34
|
+
BASE64_VALUES[BASE64_CHARS.charCodeAt(index)] = index;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const VLQ_BASE_SHIFT = 5;
|
|
38
|
+
const VLQ_BASE_MASK = (1 << VLQ_BASE_SHIFT) - 1;
|
|
39
|
+
const VLQ_CONTINUATION_BIT = 1 << VLQ_BASE_SHIFT;
|
|
40
|
+
|
|
41
|
+
// Item tags of the "Scopes" proposal, base64 digits of their numeric values.
|
|
42
|
+
const TAG_EMPTY = "A";
|
|
43
|
+
const TAG_ORIGINAL_SCOPE_START = "B";
|
|
44
|
+
const TAG_ORIGINAL_SCOPE_END = "C";
|
|
45
|
+
const TAG_ORIGINAL_SCOPE_VARIABLES = "D";
|
|
46
|
+
const TAG_GENERATED_RANGE_START = "E";
|
|
47
|
+
const TAG_GENERATED_RANGE_END = "F";
|
|
48
|
+
const TAG_GENERATED_RANGE_BINDINGS = "G";
|
|
49
|
+
|
|
50
|
+
const ORIGINAL_SCOPE_FLAG_HAS_KIND = 0x2;
|
|
51
|
+
const GENERATED_RANGE_FLAG_HAS_LINE = 0x1;
|
|
52
|
+
const GENERATED_RANGE_FLAG_HAS_DEFINITION = 0x2;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Every scope webpack emits describes one module, which the proposal's
|
|
56
|
+
* JavaScript vocabulary calls a module scope.
|
|
57
|
+
*/
|
|
58
|
+
const MODULE_SCOPE_KIND = "Module";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {number} value non-negative value
|
|
62
|
+
* @returns {string} base64 VLQ digits
|
|
63
|
+
*/
|
|
64
|
+
const encodeUnsigned = (value) => {
|
|
65
|
+
let result = "";
|
|
66
|
+
let rest = value;
|
|
67
|
+
for (;;) {
|
|
68
|
+
const digit = rest & VLQ_BASE_MASK;
|
|
69
|
+
rest >>>= VLQ_BASE_SHIFT;
|
|
70
|
+
if (rest === 0) return result + BASE64_CHARS[digit];
|
|
71
|
+
result += BASE64_CHARS[VLQ_CONTINUATION_BIT + digit];
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {number} value value of either sign
|
|
77
|
+
* @returns {string} base64 VLQ digits
|
|
78
|
+
*/
|
|
79
|
+
const encodeSigned = (value) =>
|
|
80
|
+
encodeUnsigned(value >= 0 ? 2 * value : 1 - 2 * value);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Walks the map's segments in generated order, reporting each one's source and
|
|
84
|
+
* original line. A segment with no source position reports `sourceIndex` -1,
|
|
85
|
+
* which ends whatever run precedes it.
|
|
86
|
+
* @param {string} mappings the map's `mappings` field
|
|
87
|
+
* @param {(generatedLine: number, generatedColumn: number, sourceIndex: number, originalLine: number) => void} onSegment called per segment
|
|
88
|
+
* @returns {number} the last generated line the mappings reach
|
|
89
|
+
*/
|
|
90
|
+
const forEachMapping = (mappings, onSegment) => {
|
|
91
|
+
let generatedLine = 0;
|
|
92
|
+
let generatedColumn = 0;
|
|
93
|
+
let sourceIndex = 0;
|
|
94
|
+
let originalLine = 0;
|
|
95
|
+
let position = 0;
|
|
96
|
+
const { length } = mappings;
|
|
97
|
+
/** @type {number[]} */
|
|
98
|
+
const fields = [];
|
|
99
|
+
while (position < length) {
|
|
100
|
+
const char = mappings.charCodeAt(position);
|
|
101
|
+
if (char === 59 /* ; */) {
|
|
102
|
+
generatedLine++;
|
|
103
|
+
generatedColumn = 0;
|
|
104
|
+
position++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (char === 44 /* , */) {
|
|
108
|
+
position++;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
fields.length = 0;
|
|
112
|
+
while (position < length) {
|
|
113
|
+
const next = mappings.charCodeAt(position);
|
|
114
|
+
if (next === 59 || next === 44) break;
|
|
115
|
+
let value = 0;
|
|
116
|
+
let shift = 0;
|
|
117
|
+
let digit;
|
|
118
|
+
do {
|
|
119
|
+
digit = BASE64_VALUES[mappings.charCodeAt(position++)];
|
|
120
|
+
// A digit outside the alphabet cannot be recovered from, and
|
|
121
|
+
// pretending otherwise would misplace every later segment.
|
|
122
|
+
if (digit < 0) return generatedLine;
|
|
123
|
+
value += (digit & VLQ_BASE_MASK) << shift;
|
|
124
|
+
shift += VLQ_BASE_SHIFT;
|
|
125
|
+
} while (digit & VLQ_CONTINUATION_BIT);
|
|
126
|
+
fields.push(value & 1 ? -(value >>> 1) : value >>> 1);
|
|
127
|
+
}
|
|
128
|
+
if (fields.length === 0) continue;
|
|
129
|
+
generatedColumn += fields[0];
|
|
130
|
+
if (fields.length >= 4) {
|
|
131
|
+
sourceIndex += fields[1];
|
|
132
|
+
originalLine += fields[2];
|
|
133
|
+
onSegment(generatedLine, generatedColumn, sourceIndex, originalLine);
|
|
134
|
+
} else {
|
|
135
|
+
onSegment(generatedLine, generatedColumn, -1, -1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return generatedLine;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Collects, segment by segment, the generated runs each source explains and how
|
|
143
|
+
* far into it the map reaches. Both lines are counted from zero, so a caller
|
|
144
|
+
* whose lines start at one subtracts before feeding a segment in.
|
|
145
|
+
* @param {number=} sourceCount number of entries in the map's `sources`, when known
|
|
146
|
+
* @returns {{ add: (generatedLine: number, generatedColumn: number, sourceIndex: number, originalLine: number) => void, finish: (lastLine: number, lastColumn?: number) => SourceScope[] }} collector
|
|
147
|
+
*/
|
|
148
|
+
const createScopeCollector = (sourceCount) => {
|
|
149
|
+
/** @type {Map<number, SourceScope>} */
|
|
150
|
+
const scopes = new Map();
|
|
151
|
+
let openSource = -1;
|
|
152
|
+
/** @type {SourceScope | undefined} */
|
|
153
|
+
let openScope;
|
|
154
|
+
/**
|
|
155
|
+
* @param {number} line generated line the run ends at
|
|
156
|
+
* @param {number} column generated column the run ends at
|
|
157
|
+
* @returns {void}
|
|
158
|
+
*/
|
|
159
|
+
const closeRun = (line, column) => {
|
|
160
|
+
if (openScope === undefined) return;
|
|
161
|
+
openScope.rangeEnds.push({ line, column });
|
|
162
|
+
openScope = undefined;
|
|
163
|
+
openSource = -1;
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
add(generatedLine, generatedColumn, sourceIndex, originalLine) {
|
|
167
|
+
// A segment naming no source reports -1, which is also what no open
|
|
168
|
+
// run reports, so the run has to be there before it is extended.
|
|
169
|
+
if (openScope !== undefined && sourceIndex === openSource) {
|
|
170
|
+
openScope.originalEnd.line = Math.max(
|
|
171
|
+
openScope.originalEnd.line,
|
|
172
|
+
originalLine + 1,
|
|
173
|
+
);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
closeRun(generatedLine, generatedColumn);
|
|
177
|
+
if (
|
|
178
|
+
sourceIndex < 0 ||
|
|
179
|
+
(sourceCount !== undefined && sourceIndex >= sourceCount)
|
|
180
|
+
) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
let scope = scopes.get(sourceIndex);
|
|
184
|
+
if (scope === undefined) {
|
|
185
|
+
scope = {
|
|
186
|
+
sourceIndex,
|
|
187
|
+
variables: [],
|
|
188
|
+
values: [],
|
|
189
|
+
originalEnd: { line: originalLine + 1, column: 0 },
|
|
190
|
+
rangeStarts: [],
|
|
191
|
+
rangeEnds: [],
|
|
192
|
+
};
|
|
193
|
+
scopes.set(sourceIndex, scope);
|
|
194
|
+
} else {
|
|
195
|
+
scope.originalEnd.line = Math.max(
|
|
196
|
+
scope.originalEnd.line,
|
|
197
|
+
originalLine + 1,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
scope.rangeStarts.push({ line: generatedLine, column: generatedColumn });
|
|
201
|
+
openScope = scope;
|
|
202
|
+
openSource = sourceIndex;
|
|
203
|
+
},
|
|
204
|
+
finish(lastLine, lastColumn = 0) {
|
|
205
|
+
closeRun(lastLine, lastColumn);
|
|
206
|
+
return [...scopes.values()].sort((a, b) => a.sourceIndex - b.sourceIndex);
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Groups a finished map's segments into one entry per source. Prefer the
|
|
213
|
+
* collector when the segments are still being written, which saves decoding
|
|
214
|
+
* back what was just encoded.
|
|
215
|
+
* @param {string} mappings the map's `mappings` field
|
|
216
|
+
* @param {number} sourceCount number of entries in the map's `sources`
|
|
217
|
+
* @returns {SourceScope[]} one entry per source that the mappings reach, in source order
|
|
218
|
+
*/
|
|
219
|
+
const collectSourceScopes = (mappings, sourceCount) => {
|
|
220
|
+
const collector = createScopeCollector(sourceCount);
|
|
221
|
+
const lastLine = forEachMapping(mappings, collector.add);
|
|
222
|
+
return collector.finish(lastLine + 1);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Encodes the scope tree into the `scopes` field of the proposal, appending any
|
|
227
|
+
* name it needs to the map's `names`.
|
|
228
|
+
* @param {SourceScope[]} scopes the scopes to encode, in source order
|
|
229
|
+
* @param {number} sourceCount number of entries in the map's `sources`
|
|
230
|
+
* @param {string[]} names the map's `names`, extended in place
|
|
231
|
+
* @returns {string} the encoded `scopes` field
|
|
232
|
+
*/
|
|
233
|
+
const encodeScopes = (scopes, sourceCount, names) => {
|
|
234
|
+
/** @type {Map<string, number>} */
|
|
235
|
+
const nameToIndex = new Map();
|
|
236
|
+
for (let index = 0; index < names.length; index++) {
|
|
237
|
+
if (!nameToIndex.has(names[index])) nameToIndex.set(names[index], index);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* @param {string} name the name to resolve
|
|
241
|
+
* @returns {number} its index in `names`
|
|
242
|
+
*/
|
|
243
|
+
const nameIndex = (name) => {
|
|
244
|
+
const existing = nameToIndex.get(name);
|
|
245
|
+
if (existing !== undefined) return existing;
|
|
246
|
+
const added = names.length;
|
|
247
|
+
names.push(name);
|
|
248
|
+
nameToIndex.set(name, added);
|
|
249
|
+
return added;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/** @type {Map<number, SourceScope>} */
|
|
253
|
+
const scopeBySource = new Map();
|
|
254
|
+
for (const scope of scopes) scopeBySource.set(scope.sourceIndex, scope);
|
|
255
|
+
|
|
256
|
+
/** @type {string[]} */
|
|
257
|
+
const items = [];
|
|
258
|
+
// The three running indices the proposal encodes names and definitions
|
|
259
|
+
// against. Only the position pair restarts per source.
|
|
260
|
+
let lastKind = 0;
|
|
261
|
+
let lastVariable = 0;
|
|
262
|
+
let lastDefinition = 0;
|
|
263
|
+
let scopeCount = 0;
|
|
264
|
+
/** @type {Map<number, number>} */
|
|
265
|
+
const scopeIndexBySource = new Map();
|
|
266
|
+
|
|
267
|
+
for (let sourceIndex = 0; sourceIndex < sourceCount; sourceIndex++) {
|
|
268
|
+
const scope = scopeBySource.get(sourceIndex);
|
|
269
|
+
if (scope === undefined || scope.variables.length === 0) {
|
|
270
|
+
items.push(TAG_EMPTY);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const kind = nameIndex(MODULE_SCOPE_KIND);
|
|
274
|
+
items.push(
|
|
275
|
+
TAG_ORIGINAL_SCOPE_START +
|
|
276
|
+
encodeUnsigned(ORIGINAL_SCOPE_FLAG_HAS_KIND) +
|
|
277
|
+
encodeUnsigned(0) +
|
|
278
|
+
encodeUnsigned(0) +
|
|
279
|
+
encodeSigned(kind - lastKind),
|
|
280
|
+
);
|
|
281
|
+
lastKind = kind;
|
|
282
|
+
let variables = TAG_ORIGINAL_SCOPE_VARIABLES;
|
|
283
|
+
for (const variable of scope.variables) {
|
|
284
|
+
const index = nameIndex(variable);
|
|
285
|
+
variables += encodeSigned(index - lastVariable);
|
|
286
|
+
lastVariable = index;
|
|
287
|
+
}
|
|
288
|
+
items.push(variables);
|
|
289
|
+
items.push(
|
|
290
|
+
TAG_ORIGINAL_SCOPE_END +
|
|
291
|
+
encodeUnsigned(scope.originalEnd.line) +
|
|
292
|
+
encodeUnsigned(scope.originalEnd.column),
|
|
293
|
+
);
|
|
294
|
+
scopeIndexBySource.set(sourceIndex, scopeCount++);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** @type {{ start: ScopePosition, end: ScopePosition, scope: SourceScope }[]} */
|
|
298
|
+
const ranges = [];
|
|
299
|
+
for (const scope of scopes) {
|
|
300
|
+
if (!scopeIndexBySource.has(scope.sourceIndex)) continue;
|
|
301
|
+
for (let index = 0; index < scope.rangeStarts.length; index++) {
|
|
302
|
+
ranges.push({
|
|
303
|
+
start: scope.rangeStarts[index],
|
|
304
|
+
end: scope.rangeEnds[index],
|
|
305
|
+
scope,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
ranges.sort(
|
|
310
|
+
(a, b) => a.start.line - b.start.line || a.start.column - b.start.column,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
let lastLine = 0;
|
|
314
|
+
let lastColumn = 0;
|
|
315
|
+
/**
|
|
316
|
+
* Encodes a position against the running one, reporting whether it needed a
|
|
317
|
+
* line of its own. A line is only carried when it moved forward, and then
|
|
318
|
+
* the column is absolute rather than relative.
|
|
319
|
+
* @param {ScopePosition} position the position to encode
|
|
320
|
+
* @returns {[boolean, string]} whether a line is carried, and the digits
|
|
321
|
+
*/
|
|
322
|
+
const encodePosition = (position) => {
|
|
323
|
+
const line = position.line - lastLine;
|
|
324
|
+
const carriesLine = line > 0;
|
|
325
|
+
const column = carriesLine ? position.column : position.column - lastColumn;
|
|
326
|
+
lastLine = position.line;
|
|
327
|
+
lastColumn = position.column;
|
|
328
|
+
return [
|
|
329
|
+
carriesLine,
|
|
330
|
+
(carriesLine ? encodeUnsigned(line) : "") + encodeUnsigned(column),
|
|
331
|
+
];
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
for (const { start, end, scope } of ranges) {
|
|
335
|
+
const definition =
|
|
336
|
+
/** @type {number} */
|
|
337
|
+
(scopeIndexBySource.get(scope.sourceIndex));
|
|
338
|
+
const [startCarriesLine, startDigits] = encodePosition(start);
|
|
339
|
+
items.push(
|
|
340
|
+
TAG_GENERATED_RANGE_START +
|
|
341
|
+
encodeUnsigned(
|
|
342
|
+
GENERATED_RANGE_FLAG_HAS_DEFINITION |
|
|
343
|
+
(startCarriesLine ? GENERATED_RANGE_FLAG_HAS_LINE : 0),
|
|
344
|
+
) +
|
|
345
|
+
startDigits +
|
|
346
|
+
encodeSigned(definition - lastDefinition),
|
|
347
|
+
);
|
|
348
|
+
lastDefinition = definition;
|
|
349
|
+
let bindings = TAG_GENERATED_RANGE_BINDINGS;
|
|
350
|
+
for (const value of scope.values) {
|
|
351
|
+
bindings += encodeUnsigned(value === "" ? 0 : nameIndex(value) + 1);
|
|
352
|
+
}
|
|
353
|
+
items.push(bindings);
|
|
354
|
+
// An end item carries no flags: one digit is a column, two a line and a
|
|
355
|
+
// column, which is what tells the reader whether the line moved.
|
|
356
|
+
items.push(TAG_GENERATED_RANGE_END + encodePosition(end)[1]);
|
|
357
|
+
}
|
|
358
|
+
return items.join(",");
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Adds the `scopes` field of the "Scopes" proposal to a source map, naming for
|
|
363
|
+
* each source the bindings a debugger cannot resolve on its own and the
|
|
364
|
+
* generated expression each one evaluates to. Does nothing when no source
|
|
365
|
+
* contributes a binding.
|
|
366
|
+
* @param {RawSourceMap} sourceMap the map to extend in place
|
|
367
|
+
* @param {(sourceIndex: number) => Map<string, string> | undefined} getBindings binding expressions per source
|
|
368
|
+
* @returns {void}
|
|
369
|
+
*/
|
|
370
|
+
const addScopesToSourceMap = (sourceMap, getBindings) => {
|
|
371
|
+
if (!sourceMap.mappings || !sourceMap.sources) return;
|
|
372
|
+
const sourceCount = sourceMap.sources.length;
|
|
373
|
+
const scopes = collectSourceScopes(sourceMap.mappings, sourceCount);
|
|
374
|
+
let any = false;
|
|
375
|
+
for (const scope of scopes) {
|
|
376
|
+
const bindings = getBindings(scope.sourceIndex);
|
|
377
|
+
if (bindings === undefined || bindings.size === 0) continue;
|
|
378
|
+
for (const [name, value] of bindings) {
|
|
379
|
+
scope.variables.push(name);
|
|
380
|
+
scope.values.push(value);
|
|
381
|
+
}
|
|
382
|
+
any = true;
|
|
383
|
+
}
|
|
384
|
+
if (!any) return;
|
|
385
|
+
const names = sourceMap.names || (sourceMap.names = []);
|
|
386
|
+
sourceMap.scopes = encodeScopes(scopes, sourceCount, names);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Builds the `scopes` field while a map is being written, so the segments are
|
|
391
|
+
* read as they are produced rather than decoded back out of `mappings`. Lines
|
|
392
|
+
* are the one-based ones the chunk stream reports; `finish` takes the position
|
|
393
|
+
* the generated code ends at, where the last range closes.
|
|
394
|
+
* @returns {{ add: (generatedLine: number, generatedColumn: number, sourceIndex: number, originalLine: number) => void, addSource: (sourceIndex: number, scopeBindings?: ScopeBindings) => void, finish: (map: RawSourceMap, generatedLine: number, generatedColumn?: number) => void }} writer
|
|
395
|
+
*/
|
|
396
|
+
const createScopesWriter = () => {
|
|
397
|
+
const collector = createScopeCollector();
|
|
398
|
+
/** @type {Map<number, ScopeBindings>} */
|
|
399
|
+
const bindingsBySource = new Map();
|
|
400
|
+
return {
|
|
401
|
+
add(generatedLine, generatedColumn, sourceIndex, originalLine) {
|
|
402
|
+
collector.add(
|
|
403
|
+
generatedLine - 1,
|
|
404
|
+
generatedColumn,
|
|
405
|
+
sourceIndex,
|
|
406
|
+
originalLine - 1,
|
|
407
|
+
);
|
|
408
|
+
},
|
|
409
|
+
addSource(sourceIndex, scopeBindings) {
|
|
410
|
+
if (scopeBindings !== undefined && scopeBindings.size > 0) {
|
|
411
|
+
bindingsBySource.set(sourceIndex, scopeBindings);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
finish(map, generatedLine, generatedColumn = 0) {
|
|
415
|
+
if (bindingsBySource.size === 0) return;
|
|
416
|
+
const scopes = collector.finish(generatedLine - 1, generatedColumn);
|
|
417
|
+
let any = false;
|
|
418
|
+
for (const scope of scopes) {
|
|
419
|
+
const bindings = bindingsBySource.get(scope.sourceIndex);
|
|
420
|
+
if (bindings === undefined) continue;
|
|
421
|
+
for (const [name, value] of bindings) {
|
|
422
|
+
scope.variables.push(name);
|
|
423
|
+
scope.values.push(value);
|
|
424
|
+
}
|
|
425
|
+
any = true;
|
|
426
|
+
}
|
|
427
|
+
if (!any) return;
|
|
428
|
+
const names = map.names || (map.names = []);
|
|
429
|
+
map.scopes = encodeScopes(scopes, map.sources.length, names);
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
module.exports.addScopesToSourceMap = addScopesToSourceMap;
|
|
435
|
+
module.exports.collectSourceScopes = collectSourceScopes;
|
|
436
|
+
module.exports.createScopeCollector = createScopeCollector;
|
|
437
|
+
module.exports.createScopesWriter = createScopesWriter;
|
|
438
|
+
module.exports.encodeScopes = encodeScopes;
|
|
@@ -14,18 +14,19 @@ const splitIntoLines = (str) => {
|
|
|
14
14
|
const len = str.length;
|
|
15
15
|
let i = 0;
|
|
16
16
|
while (i < len) {
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
17
|
+
// indexOf is implemented natively and is significantly faster than
|
|
18
|
+
// scanning char-by-char with charCodeAt for long lines.
|
|
19
|
+
const n = str.indexOf("\n", i);
|
|
20
|
+
if (n === -1) {
|
|
21
|
+
results.push(i === 0 ? str : str.slice(i));
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
if (n === i) {
|
|
20
25
|
results.push("\n");
|
|
21
|
-
i++;
|
|
22
26
|
} else {
|
|
23
|
-
|
|
24
|
-
// 10 is "\n".charCodeAt(0)
|
|
25
|
-
while (j < len && str.charCodeAt(j) !== 10) j++;
|
|
26
|
-
results.push(str.slice(i, j + 1));
|
|
27
|
-
i = j + 1;
|
|
27
|
+
results.push(str.slice(i, n + 1));
|
|
28
28
|
}
|
|
29
|
+
i = n + 1;
|
|
29
30
|
}
|
|
30
31
|
return results;
|
|
31
32
|
};
|
|
@@ -5,15 +5,87 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// \r
|
|
14
|
-
//
|
|
8
|
+
// Character classification via a lookup table. A single bitmask test
|
|
9
|
+
// replaces the multi-comparison chains in each inner loop phase.
|
|
10
|
+
//
|
|
11
|
+
// BIT layout per character:
|
|
12
|
+
// bit 0 (STOP1 = 1): stops phase-1 scan (\n ; { })
|
|
13
|
+
// bit 1 (CONT2 = 2): continues phase-2 scan (; { } space \r \t)
|
|
14
|
+
//
|
|
15
|
+
// Phase 1: scan regular source chars that are NOT a phase-1 stop.
|
|
16
|
+
// Phase 2: consume runs of statement-boundary / whitespace chars.
|
|
17
|
+
// Phase 3: consume a trailing \n if present.
|
|
18
|
+
|
|
19
|
+
const STOP1 = 1;
|
|
20
|
+
const CONT2 = 2;
|
|
21
|
+
|
|
22
|
+
/** @type {Uint8Array} */
|
|
23
|
+
const CF = new Uint8Array(128);
|
|
24
|
+
CF[10] = STOP1; // \n – stops phase 1, NOT consumed in phase 2
|
|
25
|
+
CF[59] = STOP1 | CONT2; // ;
|
|
26
|
+
CF[123] = STOP1 | CONT2; // {
|
|
27
|
+
CF[125] = STOP1 | CONT2; // }
|
|
28
|
+
CF[32] = CONT2; // space
|
|
29
|
+
CF[13] = CONT2; // \r
|
|
30
|
+
CF[9] = CONT2; // \t
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @callback OnPotentialToken
|
|
34
|
+
* @param {number} start start offset (inclusive)
|
|
35
|
+
* @param {number} end end offset (exclusive)
|
|
36
|
+
* @param {boolean} newline whether the token ends with a `\n`
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Streaming core: report each potential token by its `[start, end)` bounds
|
|
42
|
+
* instead of materialising substrings. The single real consumer
|
|
43
|
+
* (`OriginalSource.streamChunks`) slices on demand — and skips slicing
|
|
44
|
+
* entirely when emitting the final source (the `map()` / `sourceAndMap()`
|
|
45
|
+
* paths, which discard the chunk text) — so this avoids both the
|
|
46
|
+
* intermediate results array and every per-token `String.slice` allocation
|
|
47
|
+
* in the dominant case.
|
|
48
|
+
* @param {string} str string
|
|
49
|
+
* @param {OnPotentialToken} onToken called for each token
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
const eachPotentialToken = (str, onToken) => {
|
|
53
|
+
const len = str.length;
|
|
54
|
+
let i = 0;
|
|
55
|
+
outer: while (i < len) {
|
|
56
|
+
const start = i;
|
|
57
|
+
// Phase 1 – skip regular (non-stop) characters
|
|
58
|
+
let cc = str.charCodeAt(i);
|
|
59
|
+
while (cc > 127 || !(CF[cc] & STOP1)) {
|
|
60
|
+
if (++i >= len) {
|
|
61
|
+
onToken(start, i, false);
|
|
62
|
+
break outer;
|
|
63
|
+
}
|
|
64
|
+
cc = str.charCodeAt(i);
|
|
65
|
+
}
|
|
66
|
+
// Phase 2 – consume delimiter / whitespace run (; { } space \r \t)
|
|
67
|
+
while (cc < 128 && CF[cc] & CONT2) {
|
|
68
|
+
if (++i >= len) {
|
|
69
|
+
onToken(start, i, false);
|
|
70
|
+
break outer;
|
|
71
|
+
}
|
|
72
|
+
cc = str.charCodeAt(i);
|
|
73
|
+
}
|
|
74
|
+
// Phase 3 – consume trailing newline
|
|
75
|
+
if (cc === 10) {
|
|
76
|
+
i++;
|
|
77
|
+
onToken(start, i, true);
|
|
78
|
+
} else {
|
|
79
|
+
onToken(start, i, false);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
15
83
|
|
|
16
84
|
/**
|
|
85
|
+
* Array-returning variant. Kept as a standalone loop rather than wrapping
|
|
86
|
+
* `eachPotentialToken` with a per-token callback: the callback indirection
|
|
87
|
+
* measurably slows this hot scan (V8 can no longer inline the slice/push),
|
|
88
|
+
* and the two only share the same small, well-tested classification table.
|
|
17
89
|
* @param {string} str string
|
|
18
90
|
* @returns {string[] | null} array of string separated by potential tokens
|
|
19
91
|
*/
|
|
@@ -22,28 +94,28 @@ const splitIntoPotentialTokens = (str) => {
|
|
|
22
94
|
if (len === 0) return null;
|
|
23
95
|
const results = [];
|
|
24
96
|
let i = 0;
|
|
25
|
-
while (i < len) {
|
|
97
|
+
outer: while (i < len) {
|
|
26
98
|
const start = i;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
while (
|
|
34
|
-
cc === 59 ||
|
|
35
|
-
cc === 32 ||
|
|
36
|
-
cc === 123 ||
|
|
37
|
-
cc === 125 ||
|
|
38
|
-
cc === 13 ||
|
|
39
|
-
cc === 9
|
|
40
|
-
) {
|
|
41
|
-
if (++i >= len) break block;
|
|
42
|
-
cc = str.charCodeAt(i);
|
|
99
|
+
// Phase 1 – skip regular (non-stop) characters
|
|
100
|
+
let cc = str.charCodeAt(i);
|
|
101
|
+
while (cc > 127 || !(CF[cc] & STOP1)) {
|
|
102
|
+
if (++i >= len) {
|
|
103
|
+
results.push(str.slice(start, i));
|
|
104
|
+
break outer;
|
|
43
105
|
}
|
|
44
|
-
|
|
45
|
-
|
|
106
|
+
cc = str.charCodeAt(i);
|
|
107
|
+
}
|
|
108
|
+
// Phase 2 – consume delimiter / whitespace run (; { } space \r \t)
|
|
109
|
+
while (cc < 128 && CF[cc] & CONT2) {
|
|
110
|
+
if (++i >= len) {
|
|
111
|
+
results.push(str.slice(start, i));
|
|
112
|
+
break outer;
|
|
46
113
|
}
|
|
114
|
+
cc = str.charCodeAt(i);
|
|
115
|
+
}
|
|
116
|
+
// Phase 3 – consume trailing newline
|
|
117
|
+
if (cc === 10) {
|
|
118
|
+
i++;
|
|
47
119
|
}
|
|
48
120
|
results.push(str.slice(start, i));
|
|
49
121
|
}
|
|
@@ -51,3 +123,4 @@ const splitIntoPotentialTokens = (str) => {
|
|
|
51
123
|
};
|
|
52
124
|
|
|
53
125
|
module.exports = splitIntoPotentialTokens;
|
|
126
|
+
module.exports.eachPotentialToken = eachPotentialToken;
|