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.
		
		
		
		
		
			
		
			
				
					205 lines
				
				8.3 KiB
			
		
		
			
		
	
	
					205 lines
				
				8.3 KiB
			| 
											3 years ago
										 | import { decodedMappings, traceSegment, TraceMap } from '@jridgewell/trace-mapping'; | ||
|  | import { GenMapping, addSegment, setSourceContent, decodedMap, encodedMap } from '@jridgewell/gen-mapping'; | ||
|  | 
 | ||
|  | const SOURCELESS_MAPPING = { | ||
|  |     source: null, | ||
|  |     column: null, | ||
|  |     line: null, | ||
|  |     name: null, | ||
|  |     content: null, | ||
|  | }; | ||
|  | const EMPTY_SOURCES = []; | ||
|  | function Source(map, sources, source, content) { | ||
|  |     return { | ||
|  |         map, | ||
|  |         sources, | ||
|  |         source, | ||
|  |         content, | ||
|  |     }; | ||
|  | } | ||
|  | /** | ||
|  |  * MapSource represents a single sourcemap, with the ability to trace mappings into its child nodes | ||
|  |  * (which may themselves be SourceMapTrees). | ||
|  |  */ | ||
|  | function MapSource(map, sources) { | ||
|  |     return Source(map, sources, '', null); | ||
|  | } | ||
|  | /** | ||
|  |  * A "leaf" node in the sourcemap tree, representing an original, unmodified source file. Recursive | ||
|  |  * segment tracing ends at the `OriginalSource`. | ||
|  |  */ | ||
|  | function OriginalSource(source, content) { | ||
|  |     return Source(null, EMPTY_SOURCES, source, content); | ||
|  | } | ||
|  | /** | ||
|  |  * traceMappings is only called on the root level SourceMapTree, and begins the process of | ||
|  |  * resolving each mapping in terms of the original source files. | ||
|  |  */ | ||
|  | function traceMappings(tree) { | ||
|  |     const gen = new GenMapping({ file: tree.map.file }); | ||
|  |     const { sources: rootSources, map } = tree; | ||
|  |     const rootNames = map.names; | ||
|  |     const rootMappings = decodedMappings(map); | ||
|  |     for (let i = 0; i < rootMappings.length; i++) { | ||
|  |         const segments = rootMappings[i]; | ||
|  |         let lastSource = null; | ||
|  |         let lastSourceLine = null; | ||
|  |         let lastSourceColumn = null; | ||
|  |         for (let j = 0; j < segments.length; j++) { | ||
|  |             const segment = segments[j]; | ||
|  |             const genCol = segment[0]; | ||
|  |             let traced = SOURCELESS_MAPPING; | ||
|  |             // 1-length segments only move the current generated column, there's no source information
 | ||
|  |             // to gather from it.
 | ||
|  |             if (segment.length !== 1) { | ||
|  |                 const source = rootSources[segment[1]]; | ||
|  |                 traced = originalPositionFor(source, segment[2], segment[3], segment.length === 5 ? rootNames[segment[4]] : ''); | ||
|  |                 // If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
 | ||
|  |                 // respective segment into an original source.
 | ||
|  |                 if (traced == null) | ||
|  |                     continue; | ||
|  |             } | ||
|  |             // So we traced a segment down into its original source file. Now push a
 | ||
|  |             // new segment pointing to this location.
 | ||
|  |             const { column, line, name, content, source } = traced; | ||
|  |             if (line === lastSourceLine && column === lastSourceColumn && source === lastSource) { | ||
|  |                 continue; | ||
|  |             } | ||
|  |             lastSourceLine = line; | ||
|  |             lastSourceColumn = column; | ||
|  |             lastSource = source; | ||
|  |             // Sigh, TypeScript can't figure out source/line/column are either all null, or all non-null...
 | ||
|  |             addSegment(gen, i, genCol, source, line, column, name); | ||
|  |             if (content != null) | ||
|  |                 setSourceContent(gen, source, content); | ||
|  |         } | ||
|  |     } | ||
|  |     return gen; | ||
|  | } | ||
|  | /** | ||
|  |  * originalPositionFor is only called on children SourceMapTrees. It recurses down into its own | ||
|  |  * child SourceMapTrees, until we find the original source map. | ||
|  |  */ | ||
|  | function originalPositionFor(source, line, column, name) { | ||
|  |     if (!source.map) { | ||
|  |         return { column, line, name, source: source.source, content: source.content }; | ||
|  |     } | ||
|  |     const segment = traceSegment(source.map, line, column); | ||
|  |     // If we couldn't find a segment, then this doesn't exist in the sourcemap.
 | ||
|  |     if (segment == null) | ||
|  |         return null; | ||
|  |     // 1-length segments only move the current generated column, there's no source information
 | ||
|  |     // to gather from it.
 | ||
|  |     if (segment.length === 1) | ||
|  |         return SOURCELESS_MAPPING; | ||
|  |     return originalPositionFor(source.sources[segment[1]], segment[2], segment[3], segment.length === 5 ? source.map.names[segment[4]] : name); | ||
|  | } | ||
|  | 
 | ||
|  | function asArray(value) { | ||
|  |     if (Array.isArray(value)) | ||
|  |         return value; | ||
|  |     return [value]; | ||
|  | } | ||
|  | /** | ||
|  |  * Recursively builds a tree structure out of sourcemap files, with each node | ||
|  |  * being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of | ||
|  |  * `OriginalSource`s and `SourceMapTree`s. | ||
|  |  * | ||
|  |  * Every sourcemap is composed of a collection of source files and mappings | ||
|  |  * into locations of those source files. When we generate a `SourceMapTree` for | ||
|  |  * the sourcemap, we attempt to load each source file's own sourcemap. If it | ||
|  |  * does not have an associated sourcemap, it is considered an original, | ||
|  |  * unmodified source file. | ||
|  |  */ | ||
|  | function buildSourceMapTree(input, loader) { | ||
|  |     const maps = asArray(input).map((m) => new TraceMap(m, '')); | ||
|  |     const map = maps.pop(); | ||
|  |     for (let i = 0; i < maps.length; i++) { | ||
|  |         if (maps[i].sources.length > 1) { | ||
|  |             throw new Error(`Transformation map ${i} must have exactly one source file.\n` + | ||
|  |                 'Did you specify these with the most recent transformation maps first?'); | ||
|  |         } | ||
|  |     } | ||
|  |     let tree = build(map, loader, '', 0); | ||
|  |     for (let i = maps.length - 1; i >= 0; i--) { | ||
|  |         tree = MapSource(maps[i], [tree]); | ||
|  |     } | ||
|  |     return tree; | ||
|  | } | ||
|  | function build(map, loader, importer, importerDepth) { | ||
|  |     const { resolvedSources, sourcesContent } = map; | ||
|  |     const depth = importerDepth + 1; | ||
|  |     const children = resolvedSources.map((sourceFile, i) => { | ||
|  |         // The loading context gives the loader more information about why this file is being loaded
 | ||
|  |         // (eg, from which importer). It also allows the loader to override the location of the loaded
 | ||
|  |         // sourcemap/original source, or to override the content in the sourcesContent field if it's
 | ||
|  |         // an unmodified source file.
 | ||
|  |         const ctx = { | ||
|  |             importer, | ||
|  |             depth, | ||
|  |             source: sourceFile || '', | ||
|  |             content: undefined, | ||
|  |         }; | ||
|  |         // Use the provided loader callback to retrieve the file's sourcemap.
 | ||
|  |         // TODO: We should eventually support async loading of sourcemap files.
 | ||
|  |         const sourceMap = loader(ctx.source, ctx); | ||
|  |         const { source, content } = ctx; | ||
|  |         // If there is a sourcemap, then we need to recurse into it to load its source files.
 | ||
|  |         if (sourceMap) | ||
|  |             return build(new TraceMap(sourceMap, source), loader, source, depth); | ||
|  |         // Else, it's an an unmodified source file.
 | ||
|  |         // The contents of this unmodified source file can be overridden via the loader context,
 | ||
|  |         // allowing it to be explicitly null or a string. If it remains undefined, we fall back to
 | ||
|  |         // the importing sourcemap's `sourcesContent` field.
 | ||
|  |         const sourceContent = content !== undefined ? content : sourcesContent ? sourcesContent[i] : null; | ||
|  |         return OriginalSource(source, sourceContent); | ||
|  |     }); | ||
|  |     return MapSource(map, children); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * A SourceMap v3 compatible sourcemap, which only includes fields that were | ||
|  |  * provided to it. | ||
|  |  */ | ||
|  | class SourceMap { | ||
|  |     constructor(map, options) { | ||
|  |         const out = options.decodedMappings ? decodedMap(map) : encodedMap(map); | ||
|  |         this.version = out.version; // SourceMap spec says this should be first.
 | ||
|  |         this.file = out.file; | ||
|  |         this.mappings = out.mappings; | ||
|  |         this.names = out.names; | ||
|  |         this.sourceRoot = out.sourceRoot; | ||
|  |         this.sources = out.sources; | ||
|  |         if (!options.excludeContent) { | ||
|  |             this.sourcesContent = out.sourcesContent; | ||
|  |         } | ||
|  |     } | ||
|  |     toString() { | ||
|  |         return JSON.stringify(this); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Traces through all the mappings in the root sourcemap, through the sources | ||
|  |  * (and their sourcemaps), all the way back to the original source location. | ||
|  |  * | ||
|  |  * `loader` will be called every time we encounter a source file. If it returns | ||
|  |  * a sourcemap, we will recurse into that sourcemap to continue the trace. If | ||
|  |  * it returns a falsey value, that source file is treated as an original, | ||
|  |  * unmodified source file. | ||
|  |  * | ||
|  |  * Pass `excludeContent` to exclude any self-containing source file content | ||
|  |  * from the output sourcemap. | ||
|  |  * | ||
|  |  * Pass `decodedMappings` to receive a SourceMap with decoded (instead of | ||
|  |  * VLQ encoded) mappings. | ||
|  |  */ | ||
|  | function remapping(input, loader, options) { | ||
|  |     const opts = typeof options === 'object' ? options : { excludeContent: !!options, decodedMappings: false }; | ||
|  |     const tree = buildSourceMapTree(input, loader); | ||
|  |     return new SourceMap(traceMappings(tree), opts); | ||
|  | } | ||
|  | 
 | ||
|  | export { remapping as default }; | ||
|  | //# sourceMappingURL=remapping.mjs.map
 |