From 1027ce6bfc068dc71cc16861d2e09b155fee9293 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 1 Mar 2024 01:22:38 -0500 Subject: [PATCH] Add `ignoreList` support --- src/any-map.ts | 38 +++++++++++++++++++++++++++++------- src/trace-mapping.ts | 30 ++++++++++++++++++++++------ src/types.ts | 19 ++++++++++++++++-- test/any-map.test.ts | 10 +++++++++- test/trace-mapping.test.ts | 40 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 16 deletions(-) diff --git a/src/any-map.ts b/src/any-map.ts index 7138d73..4ea033d 100644 --- a/src/any-map.ts +++ b/src/any-map.ts @@ -8,11 +8,13 @@ import { } from './sourcemap-segment'; import type { - Section, - SectionedSourceMap, DecodedSourceMap, SectionedSourceMapInput, Ro, + DecodedSourceMapXInput, + EncodedSourceMapXInput, + SectionedSourceMapXInput, + SectionXInput, } from './types'; import type { SourceMapSegment } from './sourcemap-segment'; @@ -25,14 +27,29 @@ export const AnyMap: AnyMap = function (map, mapUrl) { const parsed = typeof map === 'string' ? (JSON.parse(map) as Exclude) : map; - if (!('sections' in parsed)) return new TraceMap(parsed, mapUrl); + if (!('sections' in parsed)) { + return new TraceMap(parsed as DecodedSourceMapXInput | EncodedSourceMapXInput, mapUrl); + } const mappings: SourceMapSegment[][] = []; const sources: string[] = []; const sourcesContent: (string | null)[] = []; const names: string[] = []; + const ignoreList: number[] = []; - recurse(parsed, mapUrl, mappings, sources, sourcesContent, names, 0, 0, Infinity, Infinity); + recurse( + parsed, + mapUrl, + mappings, + sources, + sourcesContent, + names, + ignoreList, + 0, + 0, + Infinity, + Infinity, + ); const joined: DecodedSourceMap = { version: 3, @@ -41,18 +58,20 @@ export const AnyMap: AnyMap = function (map, mapUrl) { sources, sourcesContent, mappings, + ignoreList, }; return presortedDecodedMap(joined); } as AnyMap; function recurse( - input: Ro, + input: Ro, mapUrl: string | null | undefined, mappings: SourceMapSegment[][], sources: string[], sourcesContent: (string | null)[], names: string[], + ignoreList: number[], lineOffset: number, columnOffset: number, stopLine: number, @@ -82,6 +101,7 @@ function recurse( sources, sourcesContent, names, + ignoreList, lineOffset + offset.line, columnOffset + offset.column, sl, @@ -91,12 +111,13 @@ function recurse( } function addSection( - input: Ro, + input: Ro, mapUrl: string | null | undefined, mappings: SourceMapSegment[][], sources: string[], sourcesContent: (string | null)[], names: string[], + ignoreList: number[], lineOffset: number, columnOffset: number, stopLine: number, @@ -108,13 +129,16 @@ function addSection( const sourcesOffset = sources.length; const namesOffset = names.length; const decoded = decodedMappings(map); - const { resolvedSources, sourcesContent: contents } = map; + const { resolvedSources, sourcesContent: contents, ignoreList: ignores } = map; append(sources, resolvedSources); append(names, map.names); + if (contents) append(sourcesContent, contents); else for (let i = 0; i < resolvedSources.length; i++) sourcesContent.push(null); + if (ignores) for (let i = 0; i < ignores.length; i++) ignoreList.push(ignores[i] + sourcesOffset); + for (let i = 0; i < decoded.length; i++) { const lineI = lineOffset + i; diff --git a/src/trace-mapping.ts b/src/trace-mapping.ts index 157f299..d1bb593 100644 --- a/src/trace-mapping.ts +++ b/src/trace-mapping.ts @@ -36,6 +36,7 @@ import type { SourceMap, EachMapping, Bias, + XInput, } from './types'; import type { Source } from './by-source'; import type { MemoState } from './binary-search'; @@ -84,6 +85,7 @@ export class TraceMap implements SourceMap { declare sourceRoot: SourceMapV3['sourceRoot']; declare sources: SourceMapV3['sources']; declare sourcesContent: SourceMapV3['sourcesContent']; + declare ignoreList: SourceMapV3['ignoreList']; declare resolvedSources: string[]; private declare _encoded: string | undefined; @@ -108,6 +110,7 @@ export class TraceMap implements SourceMap { this.sourceRoot = sourceRoot; this.sources = sources; this.sourcesContent = sourcesContent; + this.ignoreList = parsed.ignoreList || (parsed as XInput).x_google_ignoreList || undefined; const from = resolve(sourceRoot || '', stripFilename(mapUrl)); this.resolvedSources = sources.map((s) => resolve(s || '', from)); @@ -276,19 +279,33 @@ export function eachMapping(map: TraceMap, cb: (mapping: EachMapping) => void): } } +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 { sources, resolvedSources, sourcesContent } = map; + const { sourcesContent } = map; if (sourcesContent == null) return null; - - let index = sources.indexOf(source); - if (index === -1) index = resolvedSources.indexOf(source); - + 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. @@ -318,7 +335,7 @@ export function encodedMap(map: TraceMap): EncodedSourceMap { } function clone( - map: TraceMap | DecodedSourceMap | EncodedSourceMap, + map: TraceMap | DecodedSourceMap, mappings: T, ): T extends string ? EncodedSourceMap : DecodedSourceMap { return { @@ -329,6 +346,7 @@ function clone( sources: map.sources, sourcesContent: map.sourcesContent, mappings, + ignoreList: map.ignoreList || (map as XInput).x_google_ignoreList, } as any; } diff --git a/src/types.ts b/src/types.ts index daccb4c..1540ca4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,6 +8,7 @@ export interface SourceMapV3 { sources: (string | null)[]; sourcesContent?: (string | null)[]; version: 3; + ignoreList?: number[]; } export interface EncodedSourceMap extends SourceMapV3 { @@ -57,9 +58,22 @@ export type InvalidGeneratedMapping = { export type Bias = typeof GREATEST_LOWER_BOUND | typeof LEAST_UPPER_BOUND; -export type SourceMapInput = string | Ro | Ro | TraceMap; +export type XInput = { x_google_ignoreList?: SourceMapV3['ignoreList'] }; +export type EncodedSourceMapXInput = EncodedSourceMap & XInput; +export type DecodedSourceMapXInput = DecodedSourceMap & XInput; +export type SectionedSourceMapXInput = Omit & { + sections: SectionXInput[]; +}; +export type SectionXInput = Omit & { + map: EncodedSourceMapXInput | DecodedSourceMapXInput | SectionedSourceMapXInput; +}; -export type SectionedSourceMapInput = SourceMapInput | Ro; +export type SourceMapInput = + | string + | Ro + | Ro + | TraceMap; +export type SectionedSourceMapInput = SourceMapInput | Ro; export type Needle = { line: number; column: number; bias?: Bias }; export type SourceNeedle = { source: string; line: number; column: number; bias?: Bias }; @@ -90,6 +104,7 @@ export abstract class SourceMap { declare sources: SourceMapV3['sources']; declare sourcesContent: SourceMapV3['sourcesContent']; declare resolvedSources: SourceMapV3['sources']; + declare ignoreList: SourceMapV3['ignoreList']; } export type Ro = T extends Array diff --git a/test/any-map.test.ts b/test/any-map.test.ts index 6185988..8148e43 100644 --- a/test/any-map.test.ts +++ b/test/any-map.test.ts @@ -23,6 +23,7 @@ describe('AnyMap', () => { sources: ['first.js'], sourcesContent: ['firstsource'], mappings: 'AAAAA,CAAC', + ignoreList: [0], }, }, { @@ -58,9 +59,11 @@ describe('AnyMap', () => { offset: { line: 0, column: 1 }, map: { version: 3, + names: [], sources: ['fourth.js'], sourcesContent: ['fourthsource'], - mappings: 'AAAA', + mappings: [[[0, 0, 0, 0]]], + ignoreList: [0], }, }, ], @@ -124,6 +127,11 @@ describe('AnyMap', () => { 'fourthsource', ]); }); + + it('ignoreList', () => { + const tracer = new AnyMap(map); + assert.deepEqual(tracer.ignoreList, [0, 3]); + }); }); describe('typescript readonly type', () => { diff --git a/test/trace-mapping.test.ts b/test/trace-mapping.test.ts index 0689403..fc6de16 100644 --- a/test/trace-mapping.test.ts +++ b/test/trace-mapping.test.ts @@ -16,6 +16,7 @@ import { GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND, allGeneratedPositionsFor, + isIgnored, } from '../src/trace-mapping'; import type { @@ -151,6 +152,45 @@ describe('TraceMap', () => { }); }); + describe('isIgnored', () => { + it('returns false if no ignoreList', () => { + const tracer = new TraceMap(replaceField(map, 'ignoreList', undefined)); + const source = tracer.sources[0]!; + assert.equal(isIgnored(tracer, source), false); + }); + + it('returns false if source not found', () => { + const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); + assert.equal(isIgnored(tracer, 'foobar'), false); + }); + + it('returns false if not ignored', () => { + const tracer = new TraceMap(replaceField(map, 'ignoreList', [])); + const source = tracer.sources[0]!; + assert.equal(isIgnored(tracer, source), false); + }); + + it('returns true if ignored', () => { + const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); + const source = tracer.sources[0]!; + assert.equal(isIgnored(tracer, source), true); + }); + + it('returns ignored for resolved source', () => { + const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); + const source = tracer.resolvedSources[0]!; + assert.equal(isIgnored(tracer, source), true); + }); + + it('supports deprecated x_google_ignoreList', () => { + const tracer = new TraceMap( + replaceField(map, 'x_google_ignoreList' as 'ignoreList', [0]), + ); + const source = tracer.sources[0]!; + assert.equal(isIgnored(tracer, source), true); + }); + }); + describe('resolvedSources', () => { it('unresolved without sourceRoot', () => { const tracer = new TraceMap(replaceField(map, 'sourceRoot', undefined));