Skip to content
This repository was archived by the owner on Jun 28, 2025. It is now read-only.

Commit cfa1c9b

Browse files
committed
Refactor originalPositionFor to remove polymorphism
1 parent 90a5b6b commit cfa1c9b

File tree

5 files changed

+153
-194
lines changed

5 files changed

+153
-194
lines changed

src/build-source-map-tree.ts

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { TraceMap } from '@jridgewell/trace-mapping';
22

3-
import OriginalSource from './original-source';
4-
import { SourceMapTree } from './source-map-tree';
3+
import { OriginalSource, MapSource } from './source-map-tree';
54

5+
import type { Sources } from './source-map-tree';
66
import type { SourceMapInput, SourceMapLoader, LoaderContext } from './types';
77

88
function asArray<T>(value: T | T[]): T[] {
@@ -24,7 +24,7 @@ function asArray<T>(value: T | T[]): T[] {
2424
export default function buildSourceMapTree(
2525
input: SourceMapInput | SourceMapInput[],
2626
loader: SourceMapLoader
27-
): SourceMapTree {
27+
): Sources {
2828
const maps = asArray(input).map((m) => new TraceMap(m, ''));
2929
const map = maps.pop()!;
3030

@@ -39,7 +39,7 @@ export default function buildSourceMapTree(
3939

4040
let tree = build(map, loader, '', 0);
4141
for (let i = maps.length - 1; i >= 0; i--) {
42-
tree = new SourceMapTree(maps[i], [tree]);
42+
tree = MapSource(maps[i], [tree]);
4343
}
4444
return tree;
4545
}
@@ -49,44 +49,42 @@ function build(
4949
loader: SourceMapLoader,
5050
importer: string,
5151
importerDepth: number
52-
): SourceMapTree {
52+
): Sources {
5353
const { resolvedSources, sourcesContent } = map;
5454

5555
const depth = importerDepth + 1;
56-
const children = resolvedSources.map(
57-
(sourceFile: string | null, i: number): SourceMapTree | OriginalSource => {
58-
// The loading context gives the loader more information about why this file is being loaded
59-
// (eg, from which importer). It also allows the loader to override the location of the loaded
60-
// sourcemap/original source, or to override the content in the sourcesContent field if it's
61-
// an unmodified source file.
62-
const ctx: LoaderContext = {
63-
importer,
64-
depth,
65-
source: sourceFile || '',
66-
content: undefined,
67-
};
56+
const children = resolvedSources.map((sourceFile: string | null, i: number): Sources => {
57+
// The loading context gives the loader more information about why this file is being loaded
58+
// (eg, from which importer). It also allows the loader to override the location of the loaded
59+
// sourcemap/original source, or to override the content in the sourcesContent field if it's
60+
// an unmodified source file.
61+
const ctx: LoaderContext = {
62+
importer,
63+
depth,
64+
source: sourceFile || '',
65+
content: undefined,
66+
};
6867

69-
// Use the provided loader callback to retrieve the file's sourcemap.
70-
// TODO: We should eventually support async loading of sourcemap files.
71-
const sourceMap = loader(ctx.source, ctx);
68+
// Use the provided loader callback to retrieve the file's sourcemap.
69+
// TODO: We should eventually support async loading of sourcemap files.
70+
const sourceMap = loader(ctx.source, ctx);
7271

73-
const { source, content } = ctx;
72+
const { source, content } = ctx;
7473

75-
// If there is no sourcemap, then it is an unmodified source file.
76-
if (!sourceMap) {
77-
// The contents of this unmodified source file can be overridden via the loader context,
78-
// allowing it to be explicitly null or a string. If it remains undefined, we fall back to
79-
// the importing sourcemap's `sourcesContent` field.
80-
const sourceContent =
81-
content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
82-
return new OriginalSource(source, sourceContent);
83-
}
84-
85-
// Else, it's a real sourcemap, and we need to recurse into it to load its
86-
// source files.
87-
return build(new TraceMap(sourceMap, source), loader, source, depth);
74+
// If there is no sourcemap, then it is an unmodified source file.
75+
if (!sourceMap) {
76+
// The contents of this unmodified source file can be overridden via the loader context,
77+
// allowing it to be explicitly null or a string. If it remains undefined, we fall back to
78+
// the importing sourcemap's `sourcesContent` field.
79+
const sourceContent =
80+
content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
81+
return OriginalSource(source, sourceContent);
8882
}
89-
);
9083

91-
return new SourceMapTree(map, children);
84+
// Else, it's a real sourcemap, and we need to recurse into it to load its
85+
// source files.
86+
return build(new TraceMap(sourceMap, source), loader, source, depth);
87+
});
88+
89+
return MapSource(map, children);
9290
}

src/original-source.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/source-map-tree.ts

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,65 @@ import { FastStringArray, put } from './fast-string-array';
22
import { presortedDecodedMap, traceSegment, decodedMappings } from '@jridgewell/trace-mapping';
33

44
import type { TraceMap } from '@jridgewell/trace-mapping';
5-
import type OriginalSource from './original-source';
65
import type { SourceMapSegment, SourceMapSegmentObject } from './types';
76

8-
type Sources = OriginalSource | SourceMapTree;
9-
107
const INVALID_MAPPING = undefined;
118
const SOURCELESS_MAPPING = null;
9+
const EMPTY_SOURCES: Sources[] = [];
10+
1211
type MappingSource = SourceMapSegmentObject | typeof INVALID_MAPPING | typeof SOURCELESS_MAPPING;
1312

13+
type OriginalSource = {
14+
map: TraceMap;
15+
sources: Sources[];
16+
source: string;
17+
content: string | null;
18+
};
19+
20+
type MapSource = {
21+
map: TraceMap;
22+
sources: Sources[];
23+
source: string;
24+
content: string | null;
25+
};
26+
27+
export type Sources = OriginalSource | MapSource;
28+
29+
function Source<M extends TraceMap | null>(
30+
map: TraceMap | null,
31+
sources: Sources[],
32+
source: string,
33+
content: string | null
34+
): M extends null ? OriginalSource : MapSource {
35+
return {
36+
map,
37+
sources,
38+
source,
39+
content,
40+
} as any;
41+
}
42+
43+
/**
44+
* MapSource represents a single sourcemap, with the ability to trace mappings into its child nodes
45+
* (which may themselves be SourceMapTrees).
46+
*/
47+
export function MapSource(map: TraceMap, sources: Sources[]): MapSource {
48+
return Source(map, sources, '', null);
49+
}
50+
51+
/**
52+
* A "leaf" node in the sourcemap tree, representing an original, unmodified source file. Recursive
53+
* segment tracing ends at the `OriginalSource`.
54+
*/
55+
export function OriginalSource(source: string, content: string | null): OriginalSource {
56+
return Source(null, EMPTY_SOURCES, source, content);
57+
}
58+
1459
/**
1560
* traceMappings is only called on the root level SourceMapTree, and begins the process of
1661
* resolving each mapping in terms of the original source files.
1762
*/
18-
export function traceMappings(tree: SourceMapTree): TraceMap {
63+
export function traceMappings(tree: Sources): TraceMap {
1964
const mappings: SourceMapSegment[][] = [];
2065
const names = FastStringArray();
2166
const sources = FastStringArray();
@@ -41,7 +86,8 @@ export function traceMappings(tree: SourceMapTree): TraceMap {
4186
// to gather from it.
4287
if (segment.length !== 1) {
4388
const source = rootSources[segment[1]];
44-
traced = source.originalPositionFor(
89+
traced = originalPositionFor(
90+
source,
4591
segment[2],
4692
segment[3],
4793
segment.length === 5 ? rootNames[segment[4]] : ''
@@ -117,36 +163,31 @@ export function traceMappings(tree: SourceMapTree): TraceMap {
117163
}
118164

119165
/**
120-
* SourceMapTree represents a single sourcemap, with the ability to trace
121-
* mappings into its child nodes (which may themselves be SourceMapTrees).
166+
* originalPositionFor is only called on children SourceMapTrees. It recurses down into its own
167+
* child SourceMapTrees, until we find the original source map.
122168
*/
123-
export class SourceMapTree {
124-
declare map: TraceMap;
125-
declare sources: Sources[];
126-
127-
constructor(map: TraceMap, sources: Sources[]) {
128-
this.map = map;
129-
this.sources = sources;
169+
export function originalPositionFor(
170+
source: Sources,
171+
line: number,
172+
column: number,
173+
name: string
174+
): MappingSource {
175+
if (!source.map) {
176+
return { column, line, name, source: source.source, content: source.content };
130177
}
131178

132-
/**
133-
* originalPositionFor is only called on children SourceMapTrees. It recurses down
134-
* into its own child SourceMapTrees, until we find the original source map.
135-
*/
136-
originalPositionFor(line: number, column: number, name: string): MappingSource {
137-
const segment = traceSegment(this.map, line, column);
138-
139-
// If we couldn't find a segment, then this doesn't exist in the sourcemap.
140-
if (segment == null) return INVALID_MAPPING;
141-
// 1-length segments only move the current generated column, there's no source information
142-
// to gather from it.
143-
if (segment.length === 1) return SOURCELESS_MAPPING;
144-
145-
const source = this.sources[segment[1]];
146-
return source.originalPositionFor(
147-
segment[2],
148-
segment[3],
149-
segment.length === 5 ? this.map.names[segment[4]] : name
150-
);
151-
}
179+
const segment = traceSegment(source.map, line, column);
180+
181+
// If we couldn't find a segment, then this doesn't exist in the sourcemap.
182+
if (segment == null) return INVALID_MAPPING;
183+
// 1-length segments only move the current generated column, there's no source information
184+
// to gather from it.
185+
if (segment.length === 1) return SOURCELESS_MAPPING;
186+
187+
return originalPositionFor(
188+
source.sources[segment[1]],
189+
segment[2],
190+
segment[3],
191+
segment.length === 5 ? source.map.names[segment[4]] : name
192+
);
152193
}

test/unit/original-source.ts

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)