You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
8.9 KiB
231 lines
8.9 KiB
2 months ago
|
import { SetArray, put, remove } from '@jridgewell/set-array';
|
||
|
import { encode } from '@jridgewell/sourcemap-codec';
|
||
|
import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping';
|
||
|
|
||
|
const COLUMN = 0;
|
||
|
const SOURCES_INDEX = 1;
|
||
|
const SOURCE_LINE = 2;
|
||
|
const SOURCE_COLUMN = 3;
|
||
|
const NAMES_INDEX = 4;
|
||
|
|
||
|
const NO_NAME = -1;
|
||
|
/**
|
||
|
* Provides the state to generate a sourcemap.
|
||
|
*/
|
||
|
class GenMapping {
|
||
|
constructor({ file, sourceRoot } = {}) {
|
||
|
this._names = new SetArray();
|
||
|
this._sources = new SetArray();
|
||
|
this._sourcesContent = [];
|
||
|
this._mappings = [];
|
||
|
this.file = file;
|
||
|
this.sourceRoot = sourceRoot;
|
||
|
this._ignoreList = new SetArray();
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Typescript doesn't allow friend access to private fields, so this just casts the map into a type
|
||
|
* with public access modifiers.
|
||
|
*/
|
||
|
function cast(map) {
|
||
|
return map;
|
||
|
}
|
||
|
function addSegment(map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
|
||
|
return addSegmentInternal(false, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content);
|
||
|
}
|
||
|
function addMapping(map, mapping) {
|
||
|
return addMappingInternal(false, map, mapping);
|
||
|
}
|
||
|
/**
|
||
|
* Same as `addSegment`, but will only add the segment if it generates useful information in the
|
||
|
* resulting map. This only works correctly if segments are added **in order**, meaning you should
|
||
|
* not add a segment with a lower generated line/column than one that came before.
|
||
|
*/
|
||
|
const maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
|
||
|
return addSegmentInternal(true, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content);
|
||
|
};
|
||
|
/**
|
||
|
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
|
||
|
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
|
||
|
* not add a mapping with a lower generated line/column than one that came before.
|
||
|
*/
|
||
|
const maybeAddMapping = (map, mapping) => {
|
||
|
return addMappingInternal(true, map, mapping);
|
||
|
};
|
||
|
/**
|
||
|
* Adds/removes the content of the source file to the source map.
|
||
|
*/
|
||
|
function setSourceContent(map, source, content) {
|
||
|
const { _sources: sources, _sourcesContent: sourcesContent } = cast(map);
|
||
|
const index = put(sources, source);
|
||
|
sourcesContent[index] = content;
|
||
|
}
|
||
|
function setIgnore(map, source, ignore = true) {
|
||
|
const { _sources: sources, _sourcesContent: sourcesContent, _ignoreList: ignoreList } = cast(map);
|
||
|
const index = put(sources, source);
|
||
|
if (index === sourcesContent.length)
|
||
|
sourcesContent[index] = null;
|
||
|
if (ignore)
|
||
|
put(ignoreList, index);
|
||
|
else
|
||
|
remove(ignoreList, index);
|
||
|
}
|
||
|
/**
|
||
|
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
|
||
|
* a sourcemap, or to JSON.stringify.
|
||
|
*/
|
||
|
function toDecodedMap(map) {
|
||
|
const { _mappings: mappings, _sources: sources, _sourcesContent: sourcesContent, _names: names, _ignoreList: ignoreList, } = cast(map);
|
||
|
removeEmptyFinalLines(mappings);
|
||
|
return {
|
||
|
version: 3,
|
||
|
file: map.file || undefined,
|
||
|
names: names.array,
|
||
|
sourceRoot: map.sourceRoot || undefined,
|
||
|
sources: sources.array,
|
||
|
sourcesContent,
|
||
|
mappings,
|
||
|
ignoreList: ignoreList.array,
|
||
|
};
|
||
|
}
|
||
|
/**
|
||
|
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
|
||
|
* a sourcemap, or to JSON.stringify.
|
||
|
*/
|
||
|
function toEncodedMap(map) {
|
||
|
const decoded = toDecodedMap(map);
|
||
|
return Object.assign(Object.assign({}, decoded), { mappings: encode(decoded.mappings) });
|
||
|
}
|
||
|
/**
|
||
|
* Constructs a new GenMapping, using the already present mappings of the input.
|
||
|
*/
|
||
|
function fromMap(input) {
|
||
|
const map = new TraceMap(input);
|
||
|
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
|
||
|
putAll(cast(gen)._names, map.names);
|
||
|
putAll(cast(gen)._sources, map.sources);
|
||
|
cast(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
|
||
|
cast(gen)._mappings = decodedMappings(map);
|
||
|
if (map.ignoreList)
|
||
|
putAll(cast(gen)._ignoreList, map.ignoreList);
|
||
|
return gen;
|
||
|
}
|
||
|
/**
|
||
|
* Returns an array of high-level mapping objects for every recorded segment, which could then be
|
||
|
* passed to the `source-map` library.
|
||
|
*/
|
||
|
function allMappings(map) {
|
||
|
const out = [];
|
||
|
const { _mappings: mappings, _sources: sources, _names: names } = cast(map);
|
||
|
for (let i = 0; i < mappings.length; i++) {
|
||
|
const line = mappings[i];
|
||
|
for (let j = 0; j < line.length; j++) {
|
||
|
const seg = line[j];
|
||
|
const generated = { line: i + 1, column: seg[COLUMN] };
|
||
|
let source = undefined;
|
||
|
let original = undefined;
|
||
|
let name = undefined;
|
||
|
if (seg.length !== 1) {
|
||
|
source = sources.array[seg[SOURCES_INDEX]];
|
||
|
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
|
||
|
if (seg.length === 5)
|
||
|
name = names.array[seg[NAMES_INDEX]];
|
||
|
}
|
||
|
out.push({ generated, source, original, name });
|
||
|
}
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
// This split declaration is only so that terser can elminiate the static initialization block.
|
||
|
function addSegmentInternal(skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
|
||
|
const { _mappings: mappings, _sources: sources, _sourcesContent: sourcesContent, _names: names, } = cast(map);
|
||
|
const line = getLine(mappings, genLine);
|
||
|
const index = getColumnIndex(line, genColumn);
|
||
|
if (!source) {
|
||
|
if (skipable && skipSourceless(line, index))
|
||
|
return;
|
||
|
return insert(line, index, [genColumn]);
|
||
|
}
|
||
|
const sourcesIndex = put(sources, source);
|
||
|
const namesIndex = name ? put(names, name) : NO_NAME;
|
||
|
if (sourcesIndex === sourcesContent.length)
|
||
|
sourcesContent[sourcesIndex] = content !== null && content !== void 0 ? content : null;
|
||
|
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
|
||
|
return;
|
||
|
}
|
||
|
return insert(line, index, name
|
||
|
? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex]
|
||
|
: [genColumn, sourcesIndex, sourceLine, sourceColumn]);
|
||
|
}
|
||
|
function getLine(mappings, index) {
|
||
|
for (let i = mappings.length; i <= index; i++) {
|
||
|
mappings[i] = [];
|
||
|
}
|
||
|
return mappings[index];
|
||
|
}
|
||
|
function getColumnIndex(line, genColumn) {
|
||
|
let index = line.length;
|
||
|
for (let i = index - 1; i >= 0; index = i--) {
|
||
|
const current = line[i];
|
||
|
if (genColumn >= current[COLUMN])
|
||
|
break;
|
||
|
}
|
||
|
return index;
|
||
|
}
|
||
|
function insert(array, index, value) {
|
||
|
for (let i = array.length; i > index; i--) {
|
||
|
array[i] = array[i - 1];
|
||
|
}
|
||
|
array[index] = value;
|
||
|
}
|
||
|
function removeEmptyFinalLines(mappings) {
|
||
|
const { length } = mappings;
|
||
|
let len = length;
|
||
|
for (let i = len - 1; i >= 0; len = i, i--) {
|
||
|
if (mappings[i].length > 0)
|
||
|
break;
|
||
|
}
|
||
|
if (len < length)
|
||
|
mappings.length = len;
|
||
|
}
|
||
|
function putAll(setarr, array) {
|
||
|
for (let i = 0; i < array.length; i++)
|
||
|
put(setarr, array[i]);
|
||
|
}
|
||
|
function skipSourceless(line, index) {
|
||
|
// The start of a line is already sourceless, so adding a sourceless segment to the beginning
|
||
|
// doesn't generate any useful information.
|
||
|
if (index === 0)
|
||
|
return true;
|
||
|
const prev = line[index - 1];
|
||
|
// If the previous segment is also sourceless, then adding another sourceless segment doesn't
|
||
|
// genrate any new information. Else, this segment will end the source/named segment and point to
|
||
|
// a sourceless position, which is useful.
|
||
|
return prev.length === 1;
|
||
|
}
|
||
|
function skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex) {
|
||
|
// A source/named segment at the start of a line gives position at that genColumn
|
||
|
if (index === 0)
|
||
|
return false;
|
||
|
const prev = line[index - 1];
|
||
|
// If the previous segment is sourceless, then we're transitioning to a source.
|
||
|
if (prev.length === 1)
|
||
|
return false;
|
||
|
// If the previous segment maps to the exact same source position, then this segment doesn't
|
||
|
// provide any new position information.
|
||
|
return (sourcesIndex === prev[SOURCES_INDEX] &&
|
||
|
sourceLine === prev[SOURCE_LINE] &&
|
||
|
sourceColumn === prev[SOURCE_COLUMN] &&
|
||
|
namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME));
|
||
|
}
|
||
|
function addMappingInternal(skipable, map, mapping) {
|
||
|
const { generated, source, original, name, content } = mapping;
|
||
|
if (!source) {
|
||
|
return addSegmentInternal(skipable, map, generated.line - 1, generated.column, null, null, null, null, null);
|
||
|
}
|
||
|
return addSegmentInternal(skipable, map, generated.line - 1, generated.column, source, original.line - 1, original.column, name, content);
|
||
|
}
|
||
|
|
||
|
export { GenMapping, addMapping, addSegment, allMappings, fromMap, maybeAddMapping, maybeAddSegment, setIgnore, setSourceContent, toDecodedMap, toEncodedMap };
|
||
|
//# sourceMappingURL=gen-mapping.mjs.map
|