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.
230 lines
9.0 KiB
230 lines
9.0 KiB
import { SetArray, put } 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; |
|
/** |
|
* A low-level API to associate a generated position with an original source position. Line and |
|
* column here are 0-based, unlike `addMapping`. |
|
*/ |
|
let addSegment; |
|
/** |
|
* A high-level API to associate a generated position with an original source position. Line is |
|
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library. |
|
*/ |
|
let addMapping; |
|
/** |
|
* 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. |
|
*/ |
|
let maybeAddSegment; |
|
/** |
|
* 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. |
|
*/ |
|
let maybeAddMapping; |
|
/** |
|
* Adds/removes the content of the source file to the source map. |
|
*/ |
|
let setSourceContent; |
|
/** |
|
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects |
|
* a sourcemap, or to JSON.stringify. |
|
*/ |
|
let toDecodedMap; |
|
/** |
|
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects |
|
* a sourcemap, or to JSON.stringify. |
|
*/ |
|
let toEncodedMap; |
|
/** |
|
* Constructs a new GenMapping, using the already present mappings of the input. |
|
*/ |
|
let fromMap; |
|
/** |
|
* Returns an array of high-level mapping objects for every recorded segment, which could then be |
|
* passed to the `source-map` library. |
|
*/ |
|
let allMappings; |
|
// This split declaration is only so that terser can elminiate the static initialization block. |
|
let addSegmentInternal; |
|
/** |
|
* 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; |
|
} |
|
} |
|
(() => { |
|
addSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => { |
|
return addSegmentInternal(false, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content); |
|
}; |
|
maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => { |
|
return addSegmentInternal(true, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content); |
|
}; |
|
addMapping = (map, mapping) => { |
|
return addMappingInternal(false, map, mapping); |
|
}; |
|
maybeAddMapping = (map, mapping) => { |
|
return addMappingInternal(true, map, mapping); |
|
}; |
|
setSourceContent = (map, source, content) => { |
|
const { _sources: sources, _sourcesContent: sourcesContent } = map; |
|
sourcesContent[put(sources, source)] = content; |
|
}; |
|
toDecodedMap = (map) => { |
|
const { file, sourceRoot, _mappings: mappings, _sources: sources, _sourcesContent: sourcesContent, _names: names, } = map; |
|
removeEmptyFinalLines(mappings); |
|
return { |
|
version: 3, |
|
file: file || undefined, |
|
names: names.array, |
|
sourceRoot: sourceRoot || undefined, |
|
sources: sources.array, |
|
sourcesContent, |
|
mappings, |
|
}; |
|
}; |
|
toEncodedMap = (map) => { |
|
const decoded = toDecodedMap(map); |
|
return Object.assign(Object.assign({}, decoded), { mappings: encode(decoded.mappings) }); |
|
}; |
|
allMappings = (map) => { |
|
const out = []; |
|
const { _mappings: mappings, _sources: sources, _names: names } = 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; |
|
}; |
|
fromMap = (input) => { |
|
const map = new TraceMap(input); |
|
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot }); |
|
putAll(gen._names, map.names); |
|
putAll(gen._sources, map.sources); |
|
gen._sourcesContent = map.sourcesContent || map.sources.map(() => null); |
|
gen._mappings = decodedMappings(map); |
|
return gen; |
|
}; |
|
// Internal helpers |
|
addSegmentInternal = (skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => { |
|
const { _mappings: mappings, _sources: sources, _sourcesContent: sourcesContent, _names: names, } = 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(strarr, array) { |
|
for (let i = 0; i < array.length; i++) |
|
put(strarr, 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); |
|
} |
|
const s = source; |
|
return addSegmentInternal(skipable, map, generated.line - 1, generated.column, s, original.line - 1, original.column, name, content); |
|
} |
|
|
|
export { GenMapping, addMapping, addSegment, allMappings, fromMap, maybeAddMapping, maybeAddSegment, setSourceContent, toDecodedMap, toEncodedMap }; |
|
//# sourceMappingURL=gen-mapping.mjs.map
|
|
|