1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
import { traceSegment, decodedMappings, presortedDecodedMap, TraceMap, encodedMappings } from '@jridgewell/trace-mapping';
/**
* A "leaf" node in the sourcemap tree, representing an original, unmodified
* source file. Recursive segment tracing ends at the `OriginalSource`.
*/
class OriginalSource {
constructor(source, content) {
this.source = source;
this.content = content;
}
/**
* Tracing a `SourceMapSegment` ends when we get to an `OriginalSource`,
* meaning this line/column location originated from this source file.
*/
originalPositionFor(line, column, name) {
return { column, line, name, source: this.source, content: this.content };
}
}
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
let put;
/**
* FastStringArray acts like a `Set` (allowing only one occurrence of a string
* `key`), but provides the index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of
* the backing array, like how `sourcesContent[i]` is the source content
* associated with `source[i]`, and there are never duplicates.
*/
class FastStringArray {
constructor() {
this.indexes = Object.create(null);
this.array = [];
}
}
(() => {
put = (strarr, key) => {
const { array, indexes } = strarr;
// The key may or may not be present. If it is present, it's a number.
let index = indexes[key];
// If it's not yet present, we need to insert it and track the index in the
// indexes.
if (index === undefined) {
index = indexes[key] = array.length;
array.push(key);
}
return index;
};
})();
const INVALID_MAPPING = undefined;
const SOURCELESS_MAPPING = null;
/**
* traceMappings is only called on the root level SourceMapTree, and begins the process of
* resolving each mapping in terms of the original source files.
*/
let traceMappings;
/**
* SourceMapTree represents a single sourcemap, with the ability to trace
* mappings into its child nodes (which may themselves be SourceMapTrees).
*/
class SourceMapTree {
constructor(map, sources) {
this.map = map;
this.sources = sources;
}
/**
* originalPositionFor is only called on children SourceMapTrees. It recurses down
* into its own child SourceMapTrees, until we find the original source map.
*/
originalPositionFor(line, column, name) {
const segment = traceSegment(this.map, line, column);
// If we couldn't find a segment, then this doesn't exist in the sourcemap.
if (segment == null)
return INVALID_MAPPING;
// 1-length segments only move the current generated column, there's no source information
// to gather from it.
if (segment.length === 1)
return SOURCELESS_MAPPING;
const source = this.sources[segment[1]];
return source.originalPositionFor(segment[2], segment[3], segment.length === 5 ? this.map.names[segment[4]] : name);
}
}
(() => {
traceMappings = (tree) => {
const mappings = [];
const names = new FastStringArray();
const sources = new FastStringArray();
const sourcesContent = [];
const { sources: rootSources, map } = tree;
const rootNames = map.names;
const rootMappings = decodedMappings(map);
let lastLineWithSegment = -1;
for (let i = 0; i < rootMappings.length; i++) {
const segments = rootMappings[i];
const tracedSegments = [];
let lastSourcesIndex = -1;
let lastSourceLine = -1;
let lastSourceColumn = -1;
for (let j = 0; j < segments.length; j++) {
const segment = segments[j];
let traced = SOURCELESS_MAPPING;
// 1-length segments only move the current generated column, there's no source information
// to gather from it.
if (segment.length !== 1) {
const source = rootSources[segment[1]];
traced = source.originalPositionFor(segment[2], segment[3], segment.length === 5 ? rootNames[segment[4]] : '');
// If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
// respective segment into an original source.
if (traced === INVALID_MAPPING)
continue;
}
const genCol = segment[0];
if (traced === SOURCELESS_MAPPING) {
if (lastSourcesIndex === -1) {
// This is a consecutive source-less segment, which doesn't carry any new information.
continue;
}
lastSourcesIndex = lastSourceLine = lastSourceColumn = -1;
tracedSegments.push([genCol]);
continue;
}
// So we traced a segment down into its original source file. Now push a
// new segment pointing to this location.
const { column, line, name, content, source } = traced;
// Store the source location, and ensure we keep sourcesContent up to
// date with the sources array.
const sourcesIndex = put(sources, source);
sourcesContent[sourcesIndex] = content;
if (lastSourcesIndex === sourcesIndex &&
lastSourceLine === line &&
lastSourceColumn === column) {
// This is a duplicate mapping pointing at the exact same starting point in the source
// file. It doesn't carry any new information, and only bloats the sourcemap.
continue;
}
lastLineWithSegment = i;
lastSourcesIndex = sourcesIndex;
lastSourceLine = line;
lastSourceColumn = column;
// This looks like unnecessary duplication, but it noticeably increases performance. If we
// were to push the nameIndex onto length-4 array, v8 would internally allocate 22 slots!
// That's 68 wasted bytes! Array literals have the same capacity as their length, saving
// memory.
tracedSegments.push(name
? [genCol, sourcesIndex, line, column, put(names, name)]
: [genCol, sourcesIndex, line, column]);
}
mappings.push(tracedSegments);
}
if (mappings.length > lastLineWithSegment + 1) {
mappings.length = lastLineWithSegment + 1;
}
return presortedDecodedMap(Object.assign({}, tree.map, {
mappings,
// TODO: Make all sources relative to the sourceRoot.
sourceRoot: undefined,
names: names.array,
sources: sources.array,
sourcesContent,
}));
};
})();
function asArray(value) {
if (Array.isArray(value))
return value;
return [value];
}
/**
* Recursively builds a tree structure out of sourcemap files, with each node
* being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of
* `OriginalSource`s and `SourceMapTree`s.
*
* Every sourcemap is composed of a collection of source files and mappings
* into locations of those source files. When we generate a `SourceMapTree` for
* the sourcemap, we attempt to load each source file's own sourcemap. If it
* does not have an associated sourcemap, it is considered an original,
* unmodified source file.
*/
function buildSourceMapTree(input, loader) {
const maps = asArray(input).map((m) => new TraceMap(m, ''));
const map = maps.pop();
for (let i = 0; i < maps.length; i++) {
if (maps[i].sources.length > 1) {
throw new Error(`Transformation map ${i} must have exactly one source file.\n` +
'Did you specify these with the most recent transformation maps first?');
}
}
let tree = build(map, loader, '', 0);
for (let i = maps.length - 1; i >= 0; i--) {
tree = new SourceMapTree(maps[i], [tree]);
}
return tree;
}
function build(map, loader, importer, importerDepth) {
const { resolvedSources, sourcesContent } = map;
const depth = importerDepth + 1;
const children = resolvedSources.map((sourceFile, i) => {
// The loading context gives the loader more information about why this file is being loaded
// (eg, from which importer). It also allows the loader to override the location of the loaded
// sourcemap/original source, or to override the content in the sourcesContent field if it's
// an unmodified source file.
const ctx = {
importer,
depth,
source: sourceFile || '',
content: undefined,
};
// Use the provided loader callback to retrieve the file's sourcemap.
// TODO: We should eventually support async loading of sourcemap files.
const sourceMap = loader(ctx.source, ctx);
const { source, content } = ctx;
// If there is no sourcemap, then it is an unmodified source file.
if (!sourceMap) {
// The contents of this unmodified source file can be overridden via the loader context,
// allowing it to be explicitly null or a string. If it remains undefined, we fall back to
// the importing sourcemap's `sourcesContent` field.
const sourceContent = content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
return new OriginalSource(source, sourceContent);
}
// Else, it's a real sourcemap, and we need to recurse into it to load its
// source files.
return build(new TraceMap(sourceMap, source), loader, source, depth);
});
return new SourceMapTree(map, children);
}
/**
* A SourceMap v3 compatible sourcemap, which only includes fields that were
* provided to it.
*/
class SourceMap {
constructor(map, options) {
this.version = 3; // SourceMap spec says this should be first.
this.file = map.file;
this.mappings = options.decodedMappings ? decodedMappings(map) : encodedMappings(map);
this.names = map.names;
this.sourceRoot = map.sourceRoot;
this.sources = map.sources;
if (!options.excludeContent && 'sourcesContent' in map) {
this.sourcesContent = map.sourcesContent;
}
}
toString() {
return JSON.stringify(this);
}
}
/**
* Traces through all the mappings in the root sourcemap, through the sources
* (and their sourcemaps), all the way back to the original source location.
*
* `loader` will be called every time we encounter a source file. If it returns
* a sourcemap, we will recurse into that sourcemap to continue the trace. If
* it returns a falsey value, that source file is treated as an original,
* unmodified source file.
*
* Pass `excludeContent` to exclude any self-containing source file content
* from the output sourcemap.
*
* Pass `decodedMappings` to receive a SourceMap with decoded (instead of
* VLQ encoded) mappings.
*/
function remapping(input, loader, options) {
const opts = typeof options === 'object' ? options : { excludeContent: !!options, decodedMappings: false };
const tree = buildSourceMapTree(input, loader);
return new SourceMap(traceMappings(tree), opts);
}
export { remapping as default };
//# sourceMappingURL=remapping.mjs.map
|