Spaces:
Build error
Build error
| import { encode, decode } from '@jridgewell/sourcemap-codec'; | |
| import resolver from './resolve'; | |
| import maybeSort from './sort'; | |
| import buildBySources from './by-source'; | |
| import { | |
| memoizedState, | |
| memoizedBinarySearch, | |
| upperBound, | |
| lowerBound, | |
| found as bsFound, | |
| } from './binary-search'; | |
| import { | |
| COLUMN, | |
| SOURCES_INDEX, | |
| SOURCE_LINE, | |
| SOURCE_COLUMN, | |
| NAMES_INDEX, | |
| REV_GENERATED_LINE, | |
| REV_GENERATED_COLUMN, | |
| } from './sourcemap-segment'; | |
| import { parse } from './types'; | |
| import type { SourceMapSegment, ReverseSegment } from './sourcemap-segment'; | |
| import type { | |
| SourceMapV3, | |
| DecodedSourceMap, | |
| EncodedSourceMap, | |
| InvalidOriginalMapping, | |
| OriginalMapping, | |
| InvalidGeneratedMapping, | |
| GeneratedMapping, | |
| SourceMapInput, | |
| Needle, | |
| SourceNeedle, | |
| SourceMap, | |
| EachMapping, | |
| Bias, | |
| XInput, | |
| SectionedSourceMap, | |
| Ro, | |
| } from './types'; | |
| import type { Source } from './by-source'; | |
| import type { MemoState } from './binary-search'; | |
| export type { SourceMapSegment } from './sourcemap-segment'; | |
| export type { | |
| SourceMap, | |
| DecodedSourceMap, | |
| EncodedSourceMap, | |
| Section, | |
| SectionedSourceMap, | |
| SourceMapV3, | |
| Bias, | |
| EachMapping, | |
| GeneratedMapping, | |
| InvalidGeneratedMapping, | |
| InvalidOriginalMapping, | |
| Needle, | |
| OriginalMapping, | |
| OriginalMapping as Mapping, | |
| SectionedSourceMapInput, | |
| SourceMapInput, | |
| SourceNeedle, | |
| XInput, | |
| EncodedSourceMapXInput, | |
| DecodedSourceMapXInput, | |
| SectionedSourceMapXInput, | |
| SectionXInput, | |
| } from './types'; | |
| interface PublicMap { | |
| _encoded: TraceMap['_encoded']; | |
| _decoded: TraceMap['_decoded']; | |
| _decodedMemo: TraceMap['_decodedMemo']; | |
| _bySources: TraceMap['_bySources']; | |
| _bySourceMemos: TraceMap['_bySourceMemos']; | |
| } | |
| const LINE_GTR_ZERO = '`line` must be greater than 0 (lines start at line 1)'; | |
| const COL_GTR_EQ_ZERO = '`column` must be greater than or equal to 0 (columns start at column 0)'; | |
| export const LEAST_UPPER_BOUND = -1; | |
| export const GREATEST_LOWER_BOUND = 1; | |
| export { FlattenMap, FlattenMap as AnyMap } from './flatten-map'; | |
| export class TraceMap implements SourceMap { | |
| declare version: SourceMapV3['version']; | |
| declare file: SourceMapV3['file']; | |
| declare names: SourceMapV3['names']; | |
| declare sourceRoot: SourceMapV3['sourceRoot']; | |
| declare sources: SourceMapV3['sources']; | |
| declare sourcesContent: SourceMapV3['sourcesContent']; | |
| declare ignoreList: SourceMapV3['ignoreList']; | |
| declare resolvedSources: string[]; | |
| declare private _encoded: string | undefined; | |
| declare private _decoded: SourceMapSegment[][] | undefined; | |
| declare private _decodedMemo: MemoState; | |
| declare private _bySources: Source[] | undefined; | |
| declare private _bySourceMemos: MemoState[] | undefined; | |
| constructor(map: Ro<SourceMapInput>, mapUrl?: string | null) { | |
| const isString = typeof map === 'string'; | |
| if (!isString && (map as unknown as { _decodedMemo: any })._decodedMemo) return map as TraceMap; | |
| const parsed = parse(map as Exclude<SourceMapInput, TraceMap>); | |
| const { version, file, names, sourceRoot, sources, sourcesContent } = parsed; | |
| this.version = version; | |
| this.file = file; | |
| this.names = names || []; | |
| this.sourceRoot = sourceRoot; | |
| this.sources = sources; | |
| this.sourcesContent = sourcesContent; | |
| this.ignoreList = parsed.ignoreList || (parsed as XInput).x_google_ignoreList || undefined; | |
| const resolve = resolver(mapUrl, sourceRoot); | |
| this.resolvedSources = sources.map(resolve); | |
| const { mappings } = parsed; | |
| if (typeof mappings === 'string') { | |
| this._encoded = mappings; | |
| this._decoded = undefined; | |
| } else if (Array.isArray(mappings)) { | |
| this._encoded = undefined; | |
| this._decoded = maybeSort(mappings, isString); | |
| } else if ((parsed as unknown as SectionedSourceMap).sections) { | |
| throw new Error(`TraceMap passed sectioned source map, please use FlattenMap export instead`); | |
| } else { | |
| throw new Error(`invalid source map: ${JSON.stringify(parsed)}`); | |
| } | |
| this._decodedMemo = memoizedState(); | |
| this._bySources = undefined; | |
| this._bySourceMemos = undefined; | |
| } | |
| } | |
| /** | |
| * 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: unknown): PublicMap { | |
| return map as any; | |
| } | |
| /** | |
| * Returns the encoded (VLQ string) form of the SourceMap's mappings field. | |
| */ | |
| export function encodedMappings(map: TraceMap): EncodedSourceMap['mappings'] { | |
| return (cast(map)._encoded ??= encode(cast(map)._decoded!)); | |
| } | |
| /** | |
| * Returns the decoded (array of lines of segments) form of the SourceMap's mappings field. | |
| */ | |
| export function decodedMappings(map: TraceMap): Readonly<DecodedSourceMap['mappings']> { | |
| return (cast(map)._decoded ||= decode(cast(map)._encoded!)); | |
| } | |
| /** | |
| * A low-level API to find the segment associated with a generated line/column (think, from a | |
| * stack trace). Line and column here are 0-based, unlike `originalPositionFor`. | |
| */ | |
| export function traceSegment( | |
| map: TraceMap, | |
| line: number, | |
| column: number, | |
| ): Readonly<SourceMapSegment> | null { | |
| const decoded = decodedMappings(map); | |
| // It's common for parent source maps to have pointers to lines that have no | |
| // mapping (like a "//# sourceMappingURL=") at the end of the child file. | |
| if (line >= decoded.length) return null; | |
| const segments = decoded[line]; | |
| const index = traceSegmentInternal( | |
| segments, | |
| cast(map)._decodedMemo, | |
| line, | |
| column, | |
| GREATEST_LOWER_BOUND, | |
| ); | |
| return index === -1 ? null : segments[index]; | |
| } | |
| /** | |
| * A higher-level API to find the source/line/column associated with a generated line/column | |
| * (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in | |
| * `source-map` library. | |
| */ | |
| export function originalPositionFor( | |
| map: TraceMap, | |
| needle: Needle, | |
| ): OriginalMapping | InvalidOriginalMapping { | |
| let { line, column, bias } = needle; | |
| line--; | |
| if (line < 0) throw new Error(LINE_GTR_ZERO); | |
| if (column < 0) throw new Error(COL_GTR_EQ_ZERO); | |
| const decoded = decodedMappings(map); | |
| // It's common for parent source maps to have pointers to lines that have no | |
| // mapping (like a "//# sourceMappingURL=") at the end of the child file. | |
| if (line >= decoded.length) return OMapping(null, null, null, null); | |
| const segments = decoded[line]; | |
| const index = traceSegmentInternal( | |
| segments, | |
| cast(map)._decodedMemo, | |
| line, | |
| column, | |
| bias || GREATEST_LOWER_BOUND, | |
| ); | |
| if (index === -1) return OMapping(null, null, null, null); | |
| const segment = segments[index]; | |
| if (segment.length === 1) return OMapping(null, null, null, null); | |
| const { names, resolvedSources } = map; | |
| return OMapping( | |
| resolvedSources[segment[SOURCES_INDEX]], | |
| segment[SOURCE_LINE] + 1, | |
| segment[SOURCE_COLUMN], | |
| segment.length === 5 ? names[segment[NAMES_INDEX]] : null, | |
| ); | |
| } | |
| /** | |
| * Finds the generated line/column position of the provided source/line/column source position. | |
| */ | |
| export function generatedPositionFor( | |
| map: TraceMap, | |
| needle: SourceNeedle, | |
| ): GeneratedMapping | InvalidGeneratedMapping { | |
| const { source, line, column, bias } = needle; | |
| return generatedPosition(map, source, line, column, bias || GREATEST_LOWER_BOUND, false); | |
| } | |
| /** | |
| * Finds all generated line/column positions of the provided source/line/column source position. | |
| */ | |
| export function allGeneratedPositionsFor(map: TraceMap, needle: SourceNeedle): GeneratedMapping[] { | |
| const { source, line, column, bias } = needle; | |
| // SourceMapConsumer uses LEAST_UPPER_BOUND for some reason, so we follow suit. | |
| return generatedPosition(map, source, line, column, bias || LEAST_UPPER_BOUND, true); | |
| } | |
| /** | |
| * Iterates each mapping in generated position order. | |
| */ | |
| export function eachMapping(map: TraceMap, cb: (mapping: EachMapping) => void): void { | |
| const decoded = decodedMappings(map); | |
| const { names, resolvedSources } = map; | |
| for (let i = 0; i < decoded.length; i++) { | |
| const line = decoded[i]; | |
| for (let j = 0; j < line.length; j++) { | |
| const seg = line[j]; | |
| const generatedLine = i + 1; | |
| const generatedColumn = seg[0]; | |
| let source = null; | |
| let originalLine = null; | |
| let originalColumn = null; | |
| let name = null; | |
| if (seg.length !== 1) { | |
| source = resolvedSources[seg[1]]; | |
| originalLine = seg[2] + 1; | |
| originalColumn = seg[3]; | |
| } | |
| if (seg.length === 5) name = names[seg[4]]; | |
| cb({ | |
| generatedLine, | |
| generatedColumn, | |
| source, | |
| originalLine, | |
| originalColumn, | |
| name, | |
| } as EachMapping); | |
| } | |
| } | |
| } | |
| function sourceIndex(map: TraceMap, source: string): number { | |
| const { sources, resolvedSources } = map; | |
| let index = sources.indexOf(source); | |
| if (index === -1) index = resolvedSources.indexOf(source); | |
| return index; | |
| } | |
| /** | |
| * Retrieves the source content for a particular source, if its found. Returns null if not. | |
| */ | |
| export function sourceContentFor(map: TraceMap, source: string): string | null { | |
| const { sourcesContent } = map; | |
| if (sourcesContent == null) return null; | |
| const index = sourceIndex(map, source); | |
| return index === -1 ? null : sourcesContent[index]; | |
| } | |
| /** | |
| * Determines if the source is marked to ignore by the source map. | |
| */ | |
| export function isIgnored(map: TraceMap, source: string): boolean { | |
| const { ignoreList } = map; | |
| if (ignoreList == null) return false; | |
| const index = sourceIndex(map, source); | |
| return index === -1 ? false : ignoreList.includes(index); | |
| } | |
| /** | |
| * A helper that skips sorting of the input map's mappings array, which can be expensive for larger | |
| * maps. | |
| */ | |
| export function presortedDecodedMap(map: DecodedSourceMap, mapUrl?: string): TraceMap { | |
| const tracer = new TraceMap(clone(map, []), mapUrl); | |
| cast(tracer)._decoded = map.mappings; | |
| return tracer; | |
| } | |
| /** | |
| * Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects | |
| * a sourcemap, or to JSON.stringify. | |
| */ | |
| export function decodedMap( | |
| map: TraceMap, | |
| ): Omit<DecodedSourceMap, 'mappings'> & { mappings: readonly SourceMapSegment[][] } { | |
| return clone(map, decodedMappings(map)); | |
| } | |
| /** | |
| * Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects | |
| * a sourcemap, or to JSON.stringify. | |
| */ | |
| export function encodedMap(map: TraceMap): EncodedSourceMap { | |
| return clone(map, encodedMappings(map)); | |
| } | |
| function clone<T extends string | readonly SourceMapSegment[][]>( | |
| map: TraceMap | DecodedSourceMap, | |
| mappings: T, | |
| ): T extends string ? EncodedSourceMap : DecodedSourceMap { | |
| return { | |
| version: map.version, | |
| file: map.file, | |
| names: map.names, | |
| sourceRoot: map.sourceRoot, | |
| sources: map.sources, | |
| sourcesContent: map.sourcesContent, | |
| mappings, | |
| ignoreList: map.ignoreList || (map as XInput).x_google_ignoreList, | |
| } as any; | |
| } | |
| function OMapping(source: null, line: null, column: null, name: null): InvalidOriginalMapping; | |
| function OMapping( | |
| source: string, | |
| line: number, | |
| column: number, | |
| name: string | null, | |
| ): OriginalMapping; | |
| function OMapping( | |
| source: string | null, | |
| line: number | null, | |
| column: number | null, | |
| name: string | null, | |
| ): OriginalMapping | InvalidOriginalMapping { | |
| return { source, line, column, name } as any; | |
| } | |
| function GMapping(line: null, column: null): InvalidGeneratedMapping; | |
| function GMapping(line: number, column: number): GeneratedMapping; | |
| function GMapping( | |
| line: number | null, | |
| column: number | null, | |
| ): GeneratedMapping | InvalidGeneratedMapping { | |
| return { line, column } as any; | |
| } | |
| function traceSegmentInternal( | |
| segments: SourceMapSegment[], | |
| memo: MemoState, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| ): number; | |
| function traceSegmentInternal( | |
| segments: ReverseSegment[], | |
| memo: MemoState, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| ): number; | |
| function traceSegmentInternal( | |
| segments: SourceMapSegment[] | ReverseSegment[], | |
| memo: MemoState, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| ): number { | |
| let index = memoizedBinarySearch(segments, column, memo, line); | |
| if (bsFound) { | |
| index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index); | |
| } else if (bias === LEAST_UPPER_BOUND) index++; | |
| if (index === -1 || index === segments.length) return -1; | |
| return index; | |
| } | |
| function sliceGeneratedPositions( | |
| segments: ReverseSegment[], | |
| memo: MemoState, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| ): GeneratedMapping[] { | |
| let min = traceSegmentInternal(segments, memo, line, column, GREATEST_LOWER_BOUND); | |
| // We ignored the bias when tracing the segment so that we're guarnateed to find the first (in | |
| // insertion order) segment that matched. Even if we did respect the bias when tracing, we would | |
| // still need to call `lowerBound()` to find the first segment, which is slower than just looking | |
| // for the GREATEST_LOWER_BOUND to begin with. The only difference that matters for us is when the | |
| // binary search didn't match, in which case GREATEST_LOWER_BOUND just needs to increment to | |
| // match LEAST_UPPER_BOUND. | |
| if (!bsFound && bias === LEAST_UPPER_BOUND) min++; | |
| if (min === -1 || min === segments.length) return []; | |
| // We may have found the segment that started at an earlier column. If this is the case, then we | |
| // need to slice all generated segments that match _that_ column, because all such segments span | |
| // to our desired column. | |
| const matchedColumn = bsFound ? column : segments[min][COLUMN]; | |
| // The binary search is not guaranteed to find the lower bound when a match wasn't found. | |
| if (!bsFound) min = lowerBound(segments, matchedColumn, min); | |
| const max = upperBound(segments, matchedColumn, min); | |
| const result = []; | |
| for (; min <= max; min++) { | |
| const segment = segments[min]; | |
| result.push(GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN])); | |
| } | |
| return result; | |
| } | |
| function generatedPosition( | |
| map: TraceMap, | |
| source: string, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| all: false, | |
| ): GeneratedMapping | InvalidGeneratedMapping; | |
| function generatedPosition( | |
| map: TraceMap, | |
| source: string, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| all: true, | |
| ): GeneratedMapping[]; | |
| function generatedPosition( | |
| map: TraceMap, | |
| source: string, | |
| line: number, | |
| column: number, | |
| bias: Bias, | |
| all: boolean, | |
| ): GeneratedMapping | InvalidGeneratedMapping | GeneratedMapping[] { | |
| line--; | |
| if (line < 0) throw new Error(LINE_GTR_ZERO); | |
| if (column < 0) throw new Error(COL_GTR_EQ_ZERO); | |
| const { sources, resolvedSources } = map; | |
| let sourceIndex = sources.indexOf(source); | |
| if (sourceIndex === -1) sourceIndex = resolvedSources.indexOf(source); | |
| if (sourceIndex === -1) return all ? [] : GMapping(null, null); | |
| const bySourceMemos = (cast(map)._bySourceMemos ||= sources.map(memoizedState)); | |
| const generated = (cast(map)._bySources ||= buildBySources(decodedMappings(map), bySourceMemos)); | |
| const segments = generated[sourceIndex][line]; | |
| if (segments == null) return all ? [] : GMapping(null, null); | |
| const memo = bySourceMemos[sourceIndex]; | |
| if (all) return sliceGeneratedPositions(segments, memo, line, column, bias); | |
| const index = traceSegmentInternal(segments, memo, line, column, bias); | |
| if (index === -1) return GMapping(null, null); | |
| const segment = segments[index]; | |
| return GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN]); | |
| } | |