@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
package/lib/RawSource.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
isDualStringBufferCachingEnabled,
|
|
13
13
|
} = require("./helpers/stringBufferUtils");
|
|
14
14
|
|
|
15
|
+
/** @typedef {import("./Source").ClearCacheOptions} ClearCacheOptions */
|
|
15
16
|
/** @typedef {import("./Source").HashLike} HashLike */
|
|
16
17
|
/** @typedef {import("./Source").MapOptions} MapOptions */
|
|
17
18
|
/** @typedef {import("./Source").RawSourceMap} RawSourceMap */
|
|
@@ -30,36 +31,32 @@ class RawSource extends Source {
|
|
|
30
31
|
constructor(value, convertToString = false) {
|
|
31
32
|
super();
|
|
32
33
|
const isBuffer = Buffer.isBuffer(value);
|
|
33
|
-
if (
|
|
34
|
+
if (isBuffer) {
|
|
35
|
+
/**
|
|
36
|
+
* @type {boolean}
|
|
37
|
+
*/
|
|
38
|
+
this._valueIsBuffer = !convertToString;
|
|
39
|
+
/**
|
|
40
|
+
* @type {undefined | string | Buffer}
|
|
41
|
+
*/
|
|
42
|
+
this._value = convertToString ? undefined : value;
|
|
43
|
+
/**
|
|
44
|
+
* @type {undefined | Buffer}
|
|
45
|
+
*/
|
|
46
|
+
this._valueAsBuffer = value;
|
|
47
|
+
/**
|
|
48
|
+
* @type {undefined | string}
|
|
49
|
+
*/
|
|
50
|
+
this._valueAsString = undefined;
|
|
51
|
+
} else if (typeof value === "string") {
|
|
52
|
+
const interned = internString(value);
|
|
53
|
+
this._valueIsBuffer = false;
|
|
54
|
+
this._value = interned;
|
|
55
|
+
this._valueAsBuffer = undefined;
|
|
56
|
+
this._valueAsString = interned;
|
|
57
|
+
} else {
|
|
34
58
|
throw new TypeError("argument 'value' must be either string or Buffer");
|
|
35
59
|
}
|
|
36
|
-
/**
|
|
37
|
-
* @private
|
|
38
|
-
* @type {boolean}
|
|
39
|
-
*/
|
|
40
|
-
this._valueIsBuffer = !convertToString && isBuffer;
|
|
41
|
-
const internedString =
|
|
42
|
-
typeof value === "string" ? internString(value) : undefined;
|
|
43
|
-
/**
|
|
44
|
-
* @private
|
|
45
|
-
* @type {undefined | string | Buffer}
|
|
46
|
-
*/
|
|
47
|
-
this._value =
|
|
48
|
-
convertToString && isBuffer
|
|
49
|
-
? undefined
|
|
50
|
-
: typeof value === "string"
|
|
51
|
-
? internedString
|
|
52
|
-
: value;
|
|
53
|
-
/**
|
|
54
|
-
* @private
|
|
55
|
-
* @type {undefined | Buffer}
|
|
56
|
-
*/
|
|
57
|
-
this._valueAsBuffer = isBuffer ? value : undefined;
|
|
58
|
-
/**
|
|
59
|
-
* @private
|
|
60
|
-
* @type {undefined | string}
|
|
61
|
-
*/
|
|
62
|
-
this._valueAsString = isBuffer ? undefined : internedString;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
isBuffer() {
|
|
@@ -82,6 +79,9 @@ class RawSource extends Source {
|
|
|
82
79
|
return this._value;
|
|
83
80
|
}
|
|
84
81
|
|
|
82
|
+
/**
|
|
83
|
+
* @returns {Buffer} buffer
|
|
84
|
+
*/
|
|
85
85
|
buffer() {
|
|
86
86
|
if (this._valueAsBuffer === undefined) {
|
|
87
87
|
const value = Buffer.from(/** @type {string} */ (this._value), "utf8");
|
|
@@ -93,6 +93,20 @@ class RawSource extends Source {
|
|
|
93
93
|
return this._valueAsBuffer;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @returns {number} size
|
|
98
|
+
*/
|
|
99
|
+
size() {
|
|
100
|
+
if (this._cachedSize !== undefined) return this._cachedSize;
|
|
101
|
+
if (this._valueAsBuffer !== undefined) {
|
|
102
|
+
return (this._cachedSize = this._valueAsBuffer.length);
|
|
103
|
+
}
|
|
104
|
+
return (this._cachedSize = Buffer.byteLength(
|
|
105
|
+
/** @type {string} */ (this._valueAsString),
|
|
106
|
+
"utf8",
|
|
107
|
+
));
|
|
108
|
+
}
|
|
109
|
+
|
|
96
110
|
/**
|
|
97
111
|
* @param {MapOptions=} options map options
|
|
98
112
|
* @returns {RawSourceMap | null} map
|
|
@@ -127,13 +141,55 @@ class RawSource extends Source {
|
|
|
127
141
|
);
|
|
128
142
|
}
|
|
129
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Release cached data held by this source. clearCache is a memory
|
|
146
|
+
* hint: it never affects correctness or output, only how expensive
|
|
147
|
+
* the next read is. Subclasses override; the base is a no-op so
|
|
148
|
+
* every Source supports the call. Composite sources always recurse
|
|
149
|
+
* into wrapped sources. When the same child is reachable via several
|
|
150
|
+
* parents (e.g. modules shared across webpack chunks), pass a shared
|
|
151
|
+
* `visited` WeakSet so each subtree is walked at most once.
|
|
152
|
+
* Not safe to call concurrently with source/map/sourceAndMap/
|
|
153
|
+
* streamChunks/updateHash on the same instance.
|
|
154
|
+
* @param {ClearCacheOptions=} options selectors
|
|
155
|
+
* @param {WeakSet<Source>=} visited de-duplication set shared across calls
|
|
156
|
+
* @returns {void}
|
|
157
|
+
*/
|
|
158
|
+
clearCache(options, visited) {
|
|
159
|
+
if (visited !== undefined) {
|
|
160
|
+
if (visited.has(this)) return;
|
|
161
|
+
visited.add(this);
|
|
162
|
+
}
|
|
163
|
+
if (options !== undefined && options.source === false) return;
|
|
164
|
+
if (this._valueIsBuffer) {
|
|
165
|
+
// Buffer is the primary representation (and lives in `_value`);
|
|
166
|
+
// only the string form, populated lazily by streamChunks, is
|
|
167
|
+
// safe to drop.
|
|
168
|
+
this._valueAsString = undefined;
|
|
169
|
+
} else if (this._valueAsBuffer !== undefined && this._value !== undefined) {
|
|
170
|
+
// The source was constructed from a string (so `_value` and
|
|
171
|
+
// `_valueAsString` reference the same interned string) and the
|
|
172
|
+
// buffer form was materialized later by `buffer()` /
|
|
173
|
+
// `updateHash()`. The buffer is therefore safe to drop.
|
|
174
|
+
this._valueAsBuffer = undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
130
178
|
/**
|
|
131
179
|
* @param {HashLike} hash hash
|
|
132
180
|
* @returns {void}
|
|
133
181
|
*/
|
|
134
182
|
updateHash(hash) {
|
|
135
183
|
hash.update("RawSource");
|
|
136
|
-
|
|
184
|
+
// A string is hashed as utf8, which is what `buffer()` would have
|
|
185
|
+
// encoded it to, so hashing never has to materialize the buffer.
|
|
186
|
+
// One of the two forms is always set: the constructor sets whichever
|
|
187
|
+
// matches its input, and `clearCache` only drops the derived one.
|
|
188
|
+
if (this._valueAsBuffer === undefined) {
|
|
189
|
+
hash.update(/** @type {string} */ (this._valueAsString));
|
|
190
|
+
} else {
|
|
191
|
+
hash.update(this._valueAsBuffer);
|
|
192
|
+
}
|
|
137
193
|
}
|
|
138
194
|
}
|
|
139
195
|
|
package/lib/ReplaceSource.js
CHANGED
|
@@ -10,6 +10,7 @@ const { getMap, getSourceAndMap } = require("./helpers/getFromStreamChunks");
|
|
|
10
10
|
const splitIntoLines = require("./helpers/splitIntoLines");
|
|
11
11
|
const streamChunks = require("./helpers/streamChunks");
|
|
12
12
|
|
|
13
|
+
/** @typedef {import("./Source").ClearCacheOptions} ClearCacheOptions */
|
|
13
14
|
/** @typedef {import("./Source").HashLike} HashLike */
|
|
14
15
|
/** @typedef {import("./Source").MapOptions} MapOptions */
|
|
15
16
|
/** @typedef {import("./Source").RawSourceMap} RawSourceMap */
|
|
@@ -31,6 +32,38 @@ const hasStableSort =
|
|
|
31
32
|
// This is larger than max string length
|
|
32
33
|
const MAX_SOURCE_POSITION = 0x20000000;
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Stable comparator hoisted to module scope so each `_sortReplacements()`
|
|
37
|
+
* call doesn't allocate a fresh closure.
|
|
38
|
+
* @param {Replacement} a a
|
|
39
|
+
* @param {Replacement} b b
|
|
40
|
+
* @returns {number} order
|
|
41
|
+
*/
|
|
42
|
+
const compareStable = (a, b) => {
|
|
43
|
+
const diff1 = a.start - b.start;
|
|
44
|
+
if (diff1 !== 0) return diff1;
|
|
45
|
+
const diff2 = a.end - b.end;
|
|
46
|
+
if (diff2 !== 0) return diff2;
|
|
47
|
+
return 0;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Index-stabilising comparator for v8 < 7.0 (pre-stable Array.prototype.sort).
|
|
52
|
+
* Unreachable on any supported Node — the `hasStableSort` guard always
|
|
53
|
+
* wins so coverage tools never see this execute.
|
|
54
|
+
* @param {Replacement} a a
|
|
55
|
+
* @param {Replacement} b b
|
|
56
|
+
* @returns {number} order
|
|
57
|
+
*/
|
|
58
|
+
/* istanbul ignore next */
|
|
59
|
+
const compareUnstableFallback = (a, b) => {
|
|
60
|
+
const diff1 = a.start - b.start;
|
|
61
|
+
if (diff1 !== 0) return diff1;
|
|
62
|
+
const diff2 = a.end - b.end;
|
|
63
|
+
if (diff2 !== 0) return diff2;
|
|
64
|
+
return /** @type {number} */ (a.index) - /** @type {number} */ (b.index);
|
|
65
|
+
};
|
|
66
|
+
|
|
34
67
|
class Replacement {
|
|
35
68
|
/**
|
|
36
69
|
* @param {number} start start
|
|
@@ -43,6 +76,8 @@ class Replacement {
|
|
|
43
76
|
this.end = end;
|
|
44
77
|
this.content = content;
|
|
45
78
|
this.name = name;
|
|
79
|
+
// V8 < 7.0 only — unreachable on any supported Node.
|
|
80
|
+
/* istanbul ignore if */
|
|
46
81
|
if (!hasStableSort) {
|
|
47
82
|
this.index = -1;
|
|
48
83
|
}
|
|
@@ -57,19 +92,16 @@ class ReplaceSource extends Source {
|
|
|
57
92
|
constructor(source, name) {
|
|
58
93
|
super();
|
|
59
94
|
/**
|
|
60
|
-
* @private
|
|
61
95
|
* @type {Source}
|
|
62
96
|
*/
|
|
63
97
|
this._source = source;
|
|
64
98
|
/**
|
|
65
|
-
* @private
|
|
66
99
|
* @type {string | undefined}
|
|
67
100
|
*/
|
|
68
101
|
this._name = name;
|
|
69
102
|
/** @type {Replacement[]} */
|
|
70
103
|
this._replacements = [];
|
|
71
104
|
/**
|
|
72
|
-
* @private
|
|
73
105
|
* @type {boolean}
|
|
74
106
|
*/
|
|
75
107
|
this._isSorted = true;
|
|
@@ -124,7 +156,7 @@ class ReplaceSource extends Source {
|
|
|
124
156
|
if (this._replacements.length === 0) {
|
|
125
157
|
return this._source.source();
|
|
126
158
|
}
|
|
127
|
-
|
|
159
|
+
const current = /** @type {string} */ (this._source.source());
|
|
128
160
|
let pos = 0;
|
|
129
161
|
const result = [];
|
|
130
162
|
|
|
@@ -133,22 +165,45 @@ class ReplaceSource extends Source {
|
|
|
133
165
|
const start = Math.floor(replacement.start);
|
|
134
166
|
const end = Math.floor(replacement.end + 1);
|
|
135
167
|
if (pos < start) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
current
|
|
168
|
+
// slice directly from the original string rather than repeatedly
|
|
169
|
+
// producing smaller intermediate strings, which avoids O(n) copies.
|
|
170
|
+
result.push(current.slice(pos, start));
|
|
139
171
|
pos = start;
|
|
140
172
|
}
|
|
141
173
|
result.push(replacement.content);
|
|
142
174
|
if (pos < end) {
|
|
143
|
-
const offset = end - pos;
|
|
144
|
-
current = current.slice(offset);
|
|
145
175
|
pos = end;
|
|
146
176
|
}
|
|
147
177
|
}
|
|
148
|
-
|
|
178
|
+
if (pos < current.length) {
|
|
179
|
+
result.push(pos === 0 ? current : current.slice(pos));
|
|
180
|
+
}
|
|
149
181
|
return result.join("");
|
|
150
182
|
}
|
|
151
183
|
|
|
184
|
+
/**
|
|
185
|
+
* @returns {Buffer} buffer
|
|
186
|
+
*/
|
|
187
|
+
buffer() {
|
|
188
|
+
if (this._replacements.length === 0) {
|
|
189
|
+
return this._source.buffer();
|
|
190
|
+
}
|
|
191
|
+
return super.buffer();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @returns {Buffer[]} buffers
|
|
196
|
+
*/
|
|
197
|
+
buffers() {
|
|
198
|
+
if (this._replacements.length === 0) {
|
|
199
|
+
// TODO remove in the next major release
|
|
200
|
+
return typeof this._source.buffers === "function"
|
|
201
|
+
? this._source.buffers()
|
|
202
|
+
: [this._source.buffer()];
|
|
203
|
+
}
|
|
204
|
+
return [this.buffer()];
|
|
205
|
+
}
|
|
206
|
+
|
|
152
207
|
/**
|
|
153
208
|
* @param {MapOptions=} options map options
|
|
154
209
|
* @returns {RawSourceMap | null} map
|
|
@@ -177,25 +232,34 @@ class ReplaceSource extends Source {
|
|
|
177
232
|
|
|
178
233
|
_sortReplacements() {
|
|
179
234
|
if (this._isSorted) return;
|
|
235
|
+
const replacements = this._replacements;
|
|
236
|
+
// Replacements are usually appended in source order (ties keep
|
|
237
|
+
// insertion order, matching a stable sort), so an O(n) pre-scan
|
|
238
|
+
// often lets us skip the sort and its per-element comparator calls.
|
|
239
|
+
let isPresorted = true;
|
|
240
|
+
for (let i = 1; i < replacements.length; i++) {
|
|
241
|
+
const prev = replacements[i - 1];
|
|
242
|
+
const repl = replacements[i];
|
|
243
|
+
if (
|
|
244
|
+
repl.start < prev.start ||
|
|
245
|
+
(repl.start === prev.start && repl.end < prev.end)
|
|
246
|
+
) {
|
|
247
|
+
isPresorted = false;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (isPresorted) {
|
|
252
|
+
this._isSorted = true;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
180
255
|
if (hasStableSort) {
|
|
181
|
-
this._replacements.sort(
|
|
182
|
-
const diff1 = a.start - b.start;
|
|
183
|
-
if (diff1 !== 0) return diff1;
|
|
184
|
-
const diff2 = a.end - b.end;
|
|
185
|
-
if (diff2 !== 0) return diff2;
|
|
186
|
-
return 0;
|
|
187
|
-
});
|
|
256
|
+
this._replacements.sort(compareStable);
|
|
188
257
|
} else {
|
|
258
|
+
// V8 < 7.0 only — unreachable on any supported Node.
|
|
259
|
+
/* istanbul ignore next */
|
|
189
260
|
for (const [i, repl] of this._replacements.entries()) repl.index = i;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (diff1 !== 0) return diff1;
|
|
193
|
-
const diff2 = a.end - b.end;
|
|
194
|
-
if (diff2 !== 0) return diff2;
|
|
195
|
-
return (
|
|
196
|
-
/** @type {number} */ (a.index) - /** @type {number} */ (b.index)
|
|
197
|
-
);
|
|
198
|
-
});
|
|
261
|
+
/* istanbul ignore next */
|
|
262
|
+
this._replacements.sort(compareUnstableFallback);
|
|
199
263
|
}
|
|
200
264
|
this._isSorted = true;
|
|
201
265
|
}
|
|
@@ -209,6 +273,18 @@ class ReplaceSource extends Source {
|
|
|
209
273
|
*/
|
|
210
274
|
streamChunks(options, onChunk, onSource, onName) {
|
|
211
275
|
this._sortReplacements();
|
|
276
|
+
// When the consumer only wants the final source (map() /
|
|
277
|
+
// sourceAndMap()), emit position-only chunks (chunk === undefined,
|
|
278
|
+
// like OriginalSource and RawSource do) and hand back the whole
|
|
279
|
+
// replaced source once at the end. This avoids allocating boundary
|
|
280
|
+
// slices for emission and — more importantly — the per-chunk
|
|
281
|
+
// `code += chunk` cons-string chain in every enclosing consumer.
|
|
282
|
+
// With `source: false` the caller (getMap) additionally promises not
|
|
283
|
+
// to read the returned source, so its assembly is skipped entirely;
|
|
284
|
+
// streamAndGetSourceAndMap overrides that flag because it caches the
|
|
285
|
+
// text.
|
|
286
|
+
const finalSource = Boolean(options && options.finalSource);
|
|
287
|
+
const needSource = !options || options.source !== false;
|
|
212
288
|
const replacements = this._replacements;
|
|
213
289
|
let pos = 0;
|
|
214
290
|
let i = 0;
|
|
@@ -220,8 +296,15 @@ class ReplaceSource extends Source {
|
|
|
220
296
|
let generatedLineOffset = 0;
|
|
221
297
|
let generatedColumnOffset = 0;
|
|
222
298
|
let generatedColumnOffsetLine = 0;
|
|
223
|
-
/** @type {(string |
|
|
299
|
+
/** @type {(string | undefined)[]} */
|
|
224
300
|
const sourceContents = [];
|
|
301
|
+
/**
|
|
302
|
+
* Lazily-built line-start offsets per source content. One number per
|
|
303
|
+
* line instead of one substring per line (`splitIntoLines`), and the
|
|
304
|
+
* chunk comparison below runs allocation-free via `startsWith`.
|
|
305
|
+
* @type {(number[] | undefined)[]}
|
|
306
|
+
*/
|
|
307
|
+
const sourceContentLineStarts = [];
|
|
225
308
|
/** @type {Map<string, number>} */
|
|
226
309
|
const nameMapping = new Map();
|
|
227
310
|
/** @type {number[]} */
|
|
@@ -234,22 +317,34 @@ class ReplaceSource extends Source {
|
|
|
234
317
|
* @returns {boolean} result
|
|
235
318
|
*/
|
|
236
319
|
const checkOriginalContent = (sourceIndex, line, column, expectedChunk) => {
|
|
237
|
-
|
|
238
|
-
let content =
|
|
320
|
+
const content =
|
|
239
321
|
sourceIndex < sourceContents.length
|
|
240
322
|
? sourceContents[sourceIndex]
|
|
241
323
|
: undefined;
|
|
242
324
|
if (content === undefined) return false;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
325
|
+
let lineStarts = sourceContentLineStarts[sourceIndex];
|
|
326
|
+
if (lineStarts === undefined) {
|
|
327
|
+
// Line boundaries mirror `splitIntoLines`: every line includes
|
|
328
|
+
// its trailing "\n"; a final line without "\n" still counts.
|
|
329
|
+
lineStarts = [];
|
|
330
|
+
const { length } = content;
|
|
331
|
+
let offset = 0;
|
|
332
|
+
while (offset < length) {
|
|
333
|
+
lineStarts.push(offset);
|
|
334
|
+
const newline = content.indexOf("\n", offset);
|
|
335
|
+
if (newline === -1) break;
|
|
336
|
+
offset = newline + 1;
|
|
337
|
+
}
|
|
338
|
+
sourceContentLineStarts[sourceIndex] = lineStarts;
|
|
246
339
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
)
|
|
340
|
+
if (line > lineStarts.length) return false;
|
|
341
|
+
const lineStart = lineStarts[line - 1];
|
|
342
|
+
const lineEnd =
|
|
343
|
+
line < lineStarts.length ? lineStarts[line] : content.length;
|
|
344
|
+
// The expected chunk never spans lines, so a match must fit into
|
|
345
|
+
// the current line (mirrors the old per-line slice comparison).
|
|
346
|
+
if (column + expectedChunk.length > lineEnd - lineStart) return false;
|
|
347
|
+
return content.startsWith(expectedChunk, lineStart + column);
|
|
253
348
|
};
|
|
254
349
|
const { generatedLine, generatedColumn } = streamChunks(
|
|
255
350
|
this._source,
|
|
@@ -281,7 +376,9 @@ class ReplaceSource extends Source {
|
|
|
281
376
|
} else if (generatedColumnOffsetLine === line) {
|
|
282
377
|
generatedColumnOffset -= chunk.length;
|
|
283
378
|
} else {
|
|
379
|
+
/* istanbul ignore next: pre-existing chunk-skipping cross-line case (also untested on main) */
|
|
284
380
|
generatedColumnOffset = -chunk.length;
|
|
381
|
+
/* istanbul ignore next: pre-existing chunk-skipping cross-line case (also untested on main) */
|
|
285
382
|
generatedColumnOffsetLine = line;
|
|
286
383
|
}
|
|
287
384
|
pos = endPos;
|
|
@@ -302,6 +399,7 @@ class ReplaceSource extends Source {
|
|
|
302
399
|
}
|
|
303
400
|
pos += chunkPos;
|
|
304
401
|
const line = generatedLine + generatedLineOffset;
|
|
402
|
+
/* istanbul ignore else: pre-existing chunk-skipping cross-line case (also untested on main) */
|
|
305
403
|
if (generatedColumnOffsetLine === line) {
|
|
306
404
|
generatedColumnOffset -= chunkPos;
|
|
307
405
|
} else {
|
|
@@ -320,7 +418,7 @@ class ReplaceSource extends Source {
|
|
|
320
418
|
const offset = nextReplacement - pos;
|
|
321
419
|
const chunkSlice = chunk.slice(chunkPos, chunkPos + offset);
|
|
322
420
|
onChunk(
|
|
323
|
-
chunkSlice,
|
|
421
|
+
finalSource ? undefined : chunkSlice,
|
|
324
422
|
line,
|
|
325
423
|
generatedColumn +
|
|
326
424
|
(line === generatedColumnOffsetLine
|
|
@@ -350,7 +448,6 @@ class ReplaceSource extends Source {
|
|
|
350
448
|
|
|
351
449
|
// Insert replacement content splitted into chunks by lines
|
|
352
450
|
const { content, name } = replacements[i];
|
|
353
|
-
const matches = splitIntoLines(content);
|
|
354
451
|
let replacementNameIndex = nameIndex;
|
|
355
452
|
if (sourceIndex >= 0 && name) {
|
|
356
453
|
let globalIndex = nameMapping.get(name);
|
|
@@ -361,10 +458,16 @@ class ReplaceSource extends Source {
|
|
|
361
458
|
}
|
|
362
459
|
replacementNameIndex = globalIndex;
|
|
363
460
|
}
|
|
364
|
-
|
|
365
|
-
|
|
461
|
+
// Fast path: most replacements (renamed identifiers,
|
|
462
|
+
// short inserts) carry single-line content. Skip
|
|
463
|
+
// `splitIntoLines` — and its array allocation — when
|
|
464
|
+
// we can tell the content has no embedded newline.
|
|
465
|
+
// `splitIntoLines("")` returns `[]`; emitting a zero-
|
|
466
|
+
// length chunk would still walk the loop, so handle
|
|
467
|
+
// it as a no-op explicitly.
|
|
468
|
+
if (content.length > 0 && !content.includes("\n")) {
|
|
366
469
|
onChunk(
|
|
367
|
-
|
|
470
|
+
finalSource ? undefined : content,
|
|
368
471
|
line,
|
|
369
472
|
generatedColumn +
|
|
370
473
|
(line === generatedColumnOffsetLine
|
|
@@ -375,22 +478,51 @@ class ReplaceSource extends Source {
|
|
|
375
478
|
originalColumn,
|
|
376
479
|
replacementNameIndex,
|
|
377
480
|
);
|
|
481
|
+
if (generatedColumnOffsetLine === line) {
|
|
482
|
+
generatedColumnOffset += content.length;
|
|
483
|
+
} else {
|
|
484
|
+
generatedColumnOffset = content.length;
|
|
485
|
+
generatedColumnOffsetLine = line;
|
|
486
|
+
}
|
|
487
|
+
} else if (content.length === 0) {
|
|
488
|
+
// Empty replacement: no chunk to emit, no column
|
|
489
|
+
// movement. `splitIntoLines("")` is `[]` so the
|
|
490
|
+
// existing loop already does nothing — explicit
|
|
491
|
+
// guard skips the per-call array allocation.
|
|
492
|
+
} else {
|
|
493
|
+
const matches = splitIntoLines(content);
|
|
494
|
+
for (let m = 0; m < matches.length; m++) {
|
|
495
|
+
const contentLine = matches[m];
|
|
496
|
+
onChunk(
|
|
497
|
+
finalSource ? undefined : contentLine,
|
|
498
|
+
line,
|
|
499
|
+
generatedColumn +
|
|
500
|
+
(line === generatedColumnOffsetLine
|
|
501
|
+
? generatedColumnOffset
|
|
502
|
+
: 0),
|
|
503
|
+
sourceIndex,
|
|
504
|
+
originalLine,
|
|
505
|
+
originalColumn,
|
|
506
|
+
replacementNameIndex,
|
|
507
|
+
);
|
|
378
508
|
|
|
379
|
-
|
|
380
|
-
|
|
509
|
+
// Only the first chunk has name assigned
|
|
510
|
+
replacementNameIndex = -1;
|
|
381
511
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
512
|
+
if (m === matches.length - 1 && !contentLine.endsWith("\n")) {
|
|
513
|
+
/* istanbul ignore else: pre-existing multi-line replacement cross-line case (also untested on main) */
|
|
514
|
+
if (generatedColumnOffsetLine === line) {
|
|
515
|
+
generatedColumnOffset += contentLine.length;
|
|
516
|
+
} else {
|
|
517
|
+
generatedColumnOffset = contentLine.length;
|
|
518
|
+
generatedColumnOffsetLine = line;
|
|
519
|
+
}
|
|
385
520
|
} else {
|
|
386
|
-
|
|
521
|
+
generatedLineOffset++;
|
|
522
|
+
line++;
|
|
523
|
+
generatedColumnOffset = -generatedColumn;
|
|
387
524
|
generatedColumnOffsetLine = line;
|
|
388
525
|
}
|
|
389
|
-
} else {
|
|
390
|
-
generatedLineOffset++;
|
|
391
|
-
line++;
|
|
392
|
-
generatedColumnOffset = -generatedColumn;
|
|
393
|
-
generatedColumnOffsetLine = line;
|
|
394
526
|
}
|
|
395
527
|
}
|
|
396
528
|
|
|
@@ -456,7 +588,11 @@ class ReplaceSource extends Source {
|
|
|
456
588
|
|
|
457
589
|
// Emit remaining chunk
|
|
458
590
|
if (chunkPos < chunk.length) {
|
|
459
|
-
const chunkSlice =
|
|
591
|
+
const chunkSlice = finalSource
|
|
592
|
+
? undefined
|
|
593
|
+
: chunkPos === 0
|
|
594
|
+
? chunk
|
|
595
|
+
: chunk.slice(chunkPos);
|
|
460
596
|
const line = generatedLine + generatedLineOffset;
|
|
461
597
|
onChunk(
|
|
462
598
|
chunkSlice,
|
|
@@ -471,12 +607,13 @@ class ReplaceSource extends Source {
|
|
|
471
607
|
}
|
|
472
608
|
pos = endPos;
|
|
473
609
|
},
|
|
474
|
-
(sourceIndex, source, sourceContent) => {
|
|
610
|
+
(sourceIndex, source, sourceContent, scopeBindings) => {
|
|
611
|
+
/* istanbul ignore next: non-sequential sourceIndex emission is not produced by any in-tree Source */
|
|
475
612
|
while (sourceContents.length < sourceIndex) {
|
|
476
613
|
sourceContents.push(undefined);
|
|
477
614
|
}
|
|
478
615
|
sourceContents[sourceIndex] = sourceContent;
|
|
479
|
-
onSource(sourceIndex, source, sourceContent);
|
|
616
|
+
onSource(sourceIndex, source, sourceContent, scopeBindings);
|
|
480
617
|
},
|
|
481
618
|
(nameIndex, name) => {
|
|
482
619
|
let globalIndex = nameMapping.get(name);
|
|
@@ -497,11 +634,13 @@ class ReplaceSource extends Source {
|
|
|
497
634
|
|
|
498
635
|
// Insert remaining replacements content splitted into chunks by lines
|
|
499
636
|
let line = /** @type {number} */ (generatedLine) + generatedLineOffset;
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
637
|
+
// Fast path mirroring the in-chunk replacement loop above: skip
|
|
638
|
+
// splitIntoLines + per-match loop when the trailing content has no
|
|
639
|
+
// newlines (the common case when remaining replacements are single
|
|
640
|
+
// inserts).
|
|
641
|
+
if (remainer.length > 0 && !remainer.includes("\n")) {
|
|
503
642
|
onChunk(
|
|
504
|
-
|
|
643
|
+
finalSource ? undefined : remainer,
|
|
505
644
|
line,
|
|
506
645
|
/** @type {number} */
|
|
507
646
|
(generatedColumn) +
|
|
@@ -511,19 +650,43 @@ class ReplaceSource extends Source {
|
|
|
511
650
|
-1,
|
|
512
651
|
-1,
|
|
513
652
|
);
|
|
653
|
+
/* istanbul ignore else: trailing-remainer cross-line case (also untested on main) */
|
|
654
|
+
if (generatedColumnOffsetLine === line) {
|
|
655
|
+
generatedColumnOffset += remainer.length;
|
|
656
|
+
} else {
|
|
657
|
+
generatedColumnOffset = remainer.length;
|
|
658
|
+
generatedColumnOffsetLine = line;
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
const matches = splitIntoLines(remainer);
|
|
662
|
+
for (let m = 0; m < matches.length; m++) {
|
|
663
|
+
const contentLine = matches[m];
|
|
664
|
+
onChunk(
|
|
665
|
+
finalSource ? undefined : contentLine,
|
|
666
|
+
line,
|
|
667
|
+
/** @type {number} */
|
|
668
|
+
(generatedColumn) +
|
|
669
|
+
(line === generatedColumnOffsetLine ? generatedColumnOffset : 0),
|
|
670
|
+
-1,
|
|
671
|
+
-1,
|
|
672
|
+
-1,
|
|
673
|
+
-1,
|
|
674
|
+
);
|
|
514
675
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
676
|
+
if (m === matches.length - 1 && !contentLine.endsWith("\n")) {
|
|
677
|
+
/* istanbul ignore else: trailing-remainer multi-line cross-line case (also untested on main) */
|
|
678
|
+
if (generatedColumnOffsetLine === line) {
|
|
679
|
+
generatedColumnOffset += contentLine.length;
|
|
680
|
+
} else {
|
|
681
|
+
generatedColumnOffset = contentLine.length;
|
|
682
|
+
generatedColumnOffsetLine = line;
|
|
683
|
+
}
|
|
518
684
|
} else {
|
|
519
|
-
|
|
685
|
+
generatedLineOffset++;
|
|
686
|
+
line++;
|
|
687
|
+
generatedColumnOffset = -(/** @type {number} */ (generatedColumn));
|
|
520
688
|
generatedColumnOffsetLine = line;
|
|
521
689
|
}
|
|
522
|
-
} else {
|
|
523
|
-
generatedLineOffset++;
|
|
524
|
-
line++;
|
|
525
|
-
generatedColumnOffset = -(/** @type {number} */ (generatedColumn));
|
|
526
|
-
generatedColumnOffsetLine = line;
|
|
527
690
|
}
|
|
528
691
|
}
|
|
529
692
|
|
|
@@ -533,9 +696,38 @@ class ReplaceSource extends Source {
|
|
|
533
696
|
/** @type {number} */
|
|
534
697
|
(generatedColumn) +
|
|
535
698
|
(line === generatedColumnOffsetLine ? generatedColumnOffset : 0),
|
|
699
|
+
// The streamed chunks reproduce source() exactly, so the final
|
|
700
|
+
// source can be assembled in O(replacements) string operations
|
|
701
|
+
// instead of re-concatenating every emitted chunk downstream.
|
|
702
|
+
source:
|
|
703
|
+
finalSource && needSource
|
|
704
|
+
? /** @type {string} */ (this.source())
|
|
705
|
+
: undefined,
|
|
536
706
|
};
|
|
537
707
|
}
|
|
538
708
|
|
|
709
|
+
/**
|
|
710
|
+
* Release cached data held by this source. clearCache is a memory
|
|
711
|
+
* hint: it never affects correctness or output, only how expensive
|
|
712
|
+
* the next read is. Subclasses override; the base is a no-op so
|
|
713
|
+
* every Source supports the call. Composite sources always recurse
|
|
714
|
+
* into wrapped sources. When the same child is reachable via several
|
|
715
|
+
* parents (e.g. modules shared across webpack chunks), pass a shared
|
|
716
|
+
* `visited` WeakSet so each subtree is walked at most once.
|
|
717
|
+
* Not safe to call concurrently with source/map/sourceAndMap/
|
|
718
|
+
* streamChunks/updateHash on the same instance.
|
|
719
|
+
* @param {ClearCacheOptions=} options selectors
|
|
720
|
+
* @param {WeakSet<Source>=} visited de-duplication set shared across calls
|
|
721
|
+
* @returns {void}
|
|
722
|
+
*/
|
|
723
|
+
clearCache(options, visited) {
|
|
724
|
+
if (visited !== undefined && visited.has(this)) return;
|
|
725
|
+
let v = visited;
|
|
726
|
+
if (v === undefined) v = new WeakSet();
|
|
727
|
+
v.add(this);
|
|
728
|
+
this._source.clearCache(options, v);
|
|
729
|
+
}
|
|
730
|
+
|
|
539
731
|
/**
|
|
540
732
|
* @param {HashLike} hash hash
|
|
541
733
|
* @returns {void}
|
|
@@ -545,10 +737,14 @@ class ReplaceSource extends Source {
|
|
|
545
737
|
hash.update("ReplaceSource");
|
|
546
738
|
this._source.updateHash(hash);
|
|
547
739
|
hash.update(this._name || "");
|
|
740
|
+
// Feed each replacement as multiple updates instead of building one
|
|
741
|
+
// combined template literal per replacement. The resulting digest is
|
|
742
|
+
// identical (hash.update is additive over bytes), but we avoid
|
|
743
|
+
// allocating a new string per replacement.
|
|
548
744
|
for (const repl of this._replacements) {
|
|
549
|
-
hash.update(
|
|
550
|
-
|
|
551
|
-
);
|
|
745
|
+
hash.update(`${repl.start}${repl.end}`);
|
|
746
|
+
hash.update(repl.content);
|
|
747
|
+
if (repl.name) hash.update(repl.name);
|
|
552
748
|
}
|
|
553
749
|
}
|
|
554
750
|
}
|