remapping.umd.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@jridgewell/trace-mapping')) :
  3. typeof define === 'function' && define.amd ? define(['@jridgewell/trace-mapping'], factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.remapping = factory(global.traceMapping));
  5. })(this, (function (traceMapping) { 'use strict';
  6. /**
  7. * A "leaf" node in the sourcemap tree, representing an original, unmodified
  8. * source file. Recursive segment tracing ends at the `OriginalSource`.
  9. */
  10. class OriginalSource {
  11. constructor(source, content) {
  12. this.source = source;
  13. this.content = content;
  14. }
  15. /**
  16. * Tracing a `SourceMapSegment` ends when we get to an `OriginalSource`,
  17. * meaning this line/column location originated from this source file.
  18. */
  19. originalPositionFor(line, column, name) {
  20. return { column, line, name, source: this.source, content: this.content };
  21. }
  22. }
  23. /**
  24. * Puts `key` into the backing array, if it is not already present. Returns
  25. * the index of the `key` in the backing array.
  26. */
  27. let put;
  28. /**
  29. * FastStringArray acts like a `Set` (allowing only one occurrence of a string
  30. * `key`), but provides the index of the `key` in the backing array.
  31. *
  32. * This is designed to allow synchronizing a second array with the contents of
  33. * the backing array, like how `sourcesContent[i]` is the source content
  34. * associated with `source[i]`, and there are never duplicates.
  35. */
  36. class FastStringArray {
  37. constructor() {
  38. this.indexes = Object.create(null);
  39. this.array = [];
  40. }
  41. }
  42. (() => {
  43. put = (strarr, key) => {
  44. const { array, indexes } = strarr;
  45. // The key may or may not be present. If it is present, it's a number.
  46. let index = indexes[key];
  47. // If it's not yet present, we need to insert it and track the index in the
  48. // indexes.
  49. if (index === undefined) {
  50. index = indexes[key] = array.length;
  51. array.push(key);
  52. }
  53. return index;
  54. };
  55. })();
  56. const INVALID_MAPPING = undefined;
  57. const SOURCELESS_MAPPING = null;
  58. /**
  59. * traceMappings is only called on the root level SourceMapTree, and begins the process of
  60. * resolving each mapping in terms of the original source files.
  61. */
  62. let traceMappings;
  63. /**
  64. * SourceMapTree represents a single sourcemap, with the ability to trace
  65. * mappings into its child nodes (which may themselves be SourceMapTrees).
  66. */
  67. class SourceMapTree {
  68. constructor(map, sources) {
  69. this.map = map;
  70. this.sources = sources;
  71. }
  72. /**
  73. * originalPositionFor is only called on children SourceMapTrees. It recurses down
  74. * into its own child SourceMapTrees, until we find the original source map.
  75. */
  76. originalPositionFor(line, column, name) {
  77. const segment = traceMapping.traceSegment(this.map, line, column);
  78. // If we couldn't find a segment, then this doesn't exist in the sourcemap.
  79. if (segment == null)
  80. return INVALID_MAPPING;
  81. // 1-length segments only move the current generated column, there's no source information
  82. // to gather from it.
  83. if (segment.length === 1)
  84. return SOURCELESS_MAPPING;
  85. const source = this.sources[segment[1]];
  86. return source.originalPositionFor(segment[2], segment[3], segment.length === 5 ? this.map.names[segment[4]] : name);
  87. }
  88. }
  89. (() => {
  90. traceMappings = (tree) => {
  91. const mappings = [];
  92. const names = new FastStringArray();
  93. const sources = new FastStringArray();
  94. const sourcesContent = [];
  95. const { sources: rootSources, map } = tree;
  96. const rootNames = map.names;
  97. const rootMappings = traceMapping.decodedMappings(map);
  98. let lastLineWithSegment = -1;
  99. for (let i = 0; i < rootMappings.length; i++) {
  100. const segments = rootMappings[i];
  101. const tracedSegments = [];
  102. let lastSourcesIndex = -1;
  103. let lastSourceLine = -1;
  104. let lastSourceColumn = -1;
  105. for (let j = 0; j < segments.length; j++) {
  106. const segment = segments[j];
  107. let traced = SOURCELESS_MAPPING;
  108. // 1-length segments only move the current generated column, there's no source information
  109. // to gather from it.
  110. if (segment.length !== 1) {
  111. const source = rootSources[segment[1]];
  112. traced = source.originalPositionFor(segment[2], segment[3], segment.length === 5 ? rootNames[segment[4]] : '');
  113. // If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
  114. // respective segment into an original source.
  115. if (traced === INVALID_MAPPING)
  116. continue;
  117. }
  118. const genCol = segment[0];
  119. if (traced === SOURCELESS_MAPPING) {
  120. if (lastSourcesIndex === -1) {
  121. // This is a consecutive source-less segment, which doesn't carry any new information.
  122. continue;
  123. }
  124. lastSourcesIndex = lastSourceLine = lastSourceColumn = -1;
  125. tracedSegments.push([genCol]);
  126. continue;
  127. }
  128. // So we traced a segment down into its original source file. Now push a
  129. // new segment pointing to this location.
  130. const { column, line, name, content, source } = traced;
  131. // Store the source location, and ensure we keep sourcesContent up to
  132. // date with the sources array.
  133. const sourcesIndex = put(sources, source);
  134. sourcesContent[sourcesIndex] = content;
  135. if (lastSourcesIndex === sourcesIndex &&
  136. lastSourceLine === line &&
  137. lastSourceColumn === column) {
  138. // This is a duplicate mapping pointing at the exact same starting point in the source
  139. // file. It doesn't carry any new information, and only bloats the sourcemap.
  140. continue;
  141. }
  142. lastLineWithSegment = i;
  143. lastSourcesIndex = sourcesIndex;
  144. lastSourceLine = line;
  145. lastSourceColumn = column;
  146. // This looks like unnecessary duplication, but it noticeably increases performance. If we
  147. // were to push the nameIndex onto length-4 array, v8 would internally allocate 22 slots!
  148. // That's 68 wasted bytes! Array literals have the same capacity as their length, saving
  149. // memory.
  150. tracedSegments.push(name
  151. ? [genCol, sourcesIndex, line, column, put(names, name)]
  152. : [genCol, sourcesIndex, line, column]);
  153. }
  154. mappings.push(tracedSegments);
  155. }
  156. if (mappings.length > lastLineWithSegment + 1) {
  157. mappings.length = lastLineWithSegment + 1;
  158. }
  159. return traceMapping.presortedDecodedMap(Object.assign({}, tree.map, {
  160. mappings,
  161. // TODO: Make all sources relative to the sourceRoot.
  162. sourceRoot: undefined,
  163. names: names.array,
  164. sources: sources.array,
  165. sourcesContent,
  166. }));
  167. };
  168. })();
  169. function asArray(value) {
  170. if (Array.isArray(value))
  171. return value;
  172. return [value];
  173. }
  174. /**
  175. * Recursively builds a tree structure out of sourcemap files, with each node
  176. * being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of
  177. * `OriginalSource`s and `SourceMapTree`s.
  178. *
  179. * Every sourcemap is composed of a collection of source files and mappings
  180. * into locations of those source files. When we generate a `SourceMapTree` for
  181. * the sourcemap, we attempt to load each source file's own sourcemap. If it
  182. * does not have an associated sourcemap, it is considered an original,
  183. * unmodified source file.
  184. */
  185. function buildSourceMapTree(input, loader) {
  186. const maps = asArray(input).map((m) => new traceMapping.TraceMap(m, ''));
  187. const map = maps.pop();
  188. for (let i = 0; i < maps.length; i++) {
  189. if (maps[i].sources.length > 1) {
  190. throw new Error(`Transformation map ${i} must have exactly one source file.\n` +
  191. 'Did you specify these with the most recent transformation maps first?');
  192. }
  193. }
  194. let tree = build(map, loader, '', 0);
  195. for (let i = maps.length - 1; i >= 0; i--) {
  196. tree = new SourceMapTree(maps[i], [tree]);
  197. }
  198. return tree;
  199. }
  200. function build(map, loader, importer, importerDepth) {
  201. const { resolvedSources, sourcesContent } = map;
  202. const depth = importerDepth + 1;
  203. const children = resolvedSources.map((sourceFile, i) => {
  204. // The loading context gives the loader more information about why this file is being loaded
  205. // (eg, from which importer). It also allows the loader to override the location of the loaded
  206. // sourcemap/original source, or to override the content in the sourcesContent field if it's
  207. // an unmodified source file.
  208. const ctx = {
  209. importer,
  210. depth,
  211. source: sourceFile || '',
  212. content: undefined,
  213. };
  214. // Use the provided loader callback to retrieve the file's sourcemap.
  215. // TODO: We should eventually support async loading of sourcemap files.
  216. const sourceMap = loader(ctx.source, ctx);
  217. const { source, content } = ctx;
  218. // If there is no sourcemap, then it is an unmodified source file.
  219. if (!sourceMap) {
  220. // The contents of this unmodified source file can be overridden via the loader context,
  221. // allowing it to be explicitly null or a string. If it remains undefined, we fall back to
  222. // the importing sourcemap's `sourcesContent` field.
  223. const sourceContent = content !== undefined ? content : sourcesContent ? sourcesContent[i] : null;
  224. return new OriginalSource(source, sourceContent);
  225. }
  226. // Else, it's a real sourcemap, and we need to recurse into it to load its
  227. // source files.
  228. return build(new traceMapping.TraceMap(sourceMap, source), loader, source, depth);
  229. });
  230. return new SourceMapTree(map, children);
  231. }
  232. /**
  233. * A SourceMap v3 compatible sourcemap, which only includes fields that were
  234. * provided to it.
  235. */
  236. class SourceMap {
  237. constructor(map, options) {
  238. this.version = 3; // SourceMap spec says this should be first.
  239. this.file = map.file;
  240. this.mappings = options.decodedMappings ? traceMapping.decodedMappings(map) : traceMapping.encodedMappings(map);
  241. this.names = map.names;
  242. this.sourceRoot = map.sourceRoot;
  243. this.sources = map.sources;
  244. if (!options.excludeContent && 'sourcesContent' in map) {
  245. this.sourcesContent = map.sourcesContent;
  246. }
  247. }
  248. toString() {
  249. return JSON.stringify(this);
  250. }
  251. }
  252. /**
  253. * Traces through all the mappings in the root sourcemap, through the sources
  254. * (and their sourcemaps), all the way back to the original source location.
  255. *
  256. * `loader` will be called every time we encounter a source file. If it returns
  257. * a sourcemap, we will recurse into that sourcemap to continue the trace. If
  258. * it returns a falsey value, that source file is treated as an original,
  259. * unmodified source file.
  260. *
  261. * Pass `excludeContent` to exclude any self-containing source file content
  262. * from the output sourcemap.
  263. *
  264. * Pass `decodedMappings` to receive a SourceMap with decoded (instead of
  265. * VLQ encoded) mappings.
  266. */
  267. function remapping(input, loader, options) {
  268. const opts = typeof options === 'object' ? options : { excludeContent: !!options, decodedMappings: false };
  269. const tree = buildSourceMapTree(input, loader);
  270. return new SourceMap(traceMappings(tree), opts);
  271. }
  272. return remapping;
  273. }));
  274. //# sourceMappingURL=remapping.umd.js.map