| 1 | import { TraceMap } from '@jridgewell/trace-mapping';
|
|---|
| 2 |
|
|---|
| 3 | import { OriginalSource, MapSource } from './source-map-tree';
|
|---|
| 4 |
|
|---|
| 5 | import type { Sources, MapSource as MapSourceType } from './source-map-tree';
|
|---|
| 6 | import type { SourceMapInput, SourceMapLoader, LoaderContext } from './types';
|
|---|
| 7 |
|
|---|
| 8 | function asArray<T>(value: T | T[]): T[] {
|
|---|
| 9 | if (Array.isArray(value)) return value;
|
|---|
| 10 | return [value];
|
|---|
| 11 | }
|
|---|
| 12 |
|
|---|
| 13 | /**
|
|---|
| 14 | * Recursively builds a tree structure out of sourcemap files, with each node
|
|---|
| 15 | * being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of
|
|---|
| 16 | * `OriginalSource`s and `SourceMapTree`s.
|
|---|
| 17 | *
|
|---|
| 18 | * Every sourcemap is composed of a collection of source files and mappings
|
|---|
| 19 | * into locations of those source files. When we generate a `SourceMapTree` for
|
|---|
| 20 | * the sourcemap, we attempt to load each source file's own sourcemap. If it
|
|---|
| 21 | * does not have an associated sourcemap, it is considered an original,
|
|---|
| 22 | * unmodified source file.
|
|---|
| 23 | */
|
|---|
| 24 | export default function buildSourceMapTree(
|
|---|
| 25 | input: SourceMapInput | SourceMapInput[],
|
|---|
| 26 | loader: SourceMapLoader,
|
|---|
| 27 | ): MapSourceType {
|
|---|
| 28 | const maps = asArray(input).map((m) => new TraceMap(m, ''));
|
|---|
| 29 | const map = maps.pop()!;
|
|---|
| 30 |
|
|---|
| 31 | for (let i = 0; i < maps.length; i++) {
|
|---|
| 32 | if (maps[i].sources.length > 1) {
|
|---|
| 33 | throw new Error(
|
|---|
| 34 | `Transformation map ${i} must have exactly one source file.\n` +
|
|---|
| 35 | 'Did you specify these with the most recent transformation maps first?',
|
|---|
| 36 | );
|
|---|
| 37 | }
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | let tree = build(map, loader, '', 0);
|
|---|
| 41 | for (let i = maps.length - 1; i >= 0; i--) {
|
|---|
| 42 | tree = MapSource(maps[i], [tree]);
|
|---|
| 43 | }
|
|---|
| 44 | return tree;
|
|---|
| 45 | }
|
|---|
| 46 |
|
|---|
| 47 | function build(
|
|---|
| 48 | map: TraceMap,
|
|---|
| 49 | loader: SourceMapLoader,
|
|---|
| 50 | importer: string,
|
|---|
| 51 | importerDepth: number,
|
|---|
| 52 | ): MapSourceType {
|
|---|
| 53 | const { resolvedSources, sourcesContent, ignoreList } = map;
|
|---|
| 54 |
|
|---|
| 55 | const depth = importerDepth + 1;
|
|---|
| 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 | ignore: undefined,
|
|---|
| 67 | };
|
|---|
| 68 |
|
|---|
| 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);
|
|---|
| 72 |
|
|---|
| 73 | const { source, content, ignore } = ctx;
|
|---|
| 74 |
|
|---|
| 75 | // If there is a sourcemap, then we need to recurse into it to load its source files.
|
|---|
| 76 | if (sourceMap) return build(new TraceMap(sourceMap, source), loader, source, depth);
|
|---|
| 77 |
|
|---|
| 78 | // Else, it's an unmodified source file.
|
|---|
| 79 | // The contents of this unmodified source file can be overridden via the loader context,
|
|---|
| 80 | // allowing it to be explicitly null or a string. If it remains undefined, we fall back to
|
|---|
| 81 | // the importing sourcemap's `sourcesContent` field.
|
|---|
| 82 | const sourceContent =
|
|---|
| 83 | content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
|
|---|
| 84 | const ignored = ignore !== undefined ? ignore : ignoreList ? ignoreList.includes(i) : false;
|
|---|
| 85 | return OriginalSource(source, sourceContent, ignored);
|
|---|
| 86 | });
|
|---|
| 87 |
|
|---|
| 88 | return MapSource(map, children);
|
|---|
| 89 | }
|
|---|