flatten-map.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import { TraceMap, presortedDecodedMap, decodedMappings } from './trace-mapping';
  2. import {
  3. COLUMN,
  4. SOURCES_INDEX,
  5. SOURCE_LINE,
  6. SOURCE_COLUMN,
  7. NAMES_INDEX,
  8. } from './sourcemap-segment';
  9. import { parse } from './types';
  10. import type {
  11. DecodedSourceMap,
  12. DecodedSourceMapXInput,
  13. EncodedSourceMapXInput,
  14. SectionedSourceMapXInput,
  15. SectionedSourceMapInput,
  16. SectionXInput,
  17. Ro,
  18. } from './types';
  19. import type { SourceMapSegment } from './sourcemap-segment';
  20. type FlattenMap = {
  21. new (map: Ro<SectionedSourceMapInput>, mapUrl?: string | null): TraceMap;
  22. (map: Ro<SectionedSourceMapInput>, mapUrl?: string | null): TraceMap;
  23. };
  24. export const FlattenMap: FlattenMap = function (map, mapUrl) {
  25. const parsed = parse(map as SectionedSourceMapInput);
  26. if (!('sections' in parsed)) {
  27. return new TraceMap(parsed as DecodedSourceMapXInput | EncodedSourceMapXInput, mapUrl);
  28. }
  29. const mappings: SourceMapSegment[][] = [];
  30. const sources: string[] = [];
  31. const sourcesContent: (string | null)[] = [];
  32. const names: string[] = [];
  33. const ignoreList: number[] = [];
  34. recurse(
  35. parsed,
  36. mapUrl,
  37. mappings,
  38. sources,
  39. sourcesContent,
  40. names,
  41. ignoreList,
  42. 0,
  43. 0,
  44. Infinity,
  45. Infinity,
  46. );
  47. const joined: DecodedSourceMap = {
  48. version: 3,
  49. file: parsed.file,
  50. names,
  51. sources,
  52. sourcesContent,
  53. mappings,
  54. ignoreList,
  55. };
  56. return presortedDecodedMap(joined);
  57. } as FlattenMap;
  58. function recurse(
  59. input: SectionedSourceMapXInput,
  60. mapUrl: string | null | undefined,
  61. mappings: SourceMapSegment[][],
  62. sources: string[],
  63. sourcesContent: (string | null)[],
  64. names: string[],
  65. ignoreList: number[],
  66. lineOffset: number,
  67. columnOffset: number,
  68. stopLine: number,
  69. stopColumn: number,
  70. ) {
  71. const { sections } = input;
  72. for (let i = 0; i < sections.length; i++) {
  73. const { map, offset } = sections[i];
  74. let sl = stopLine;
  75. let sc = stopColumn;
  76. if (i + 1 < sections.length) {
  77. const nextOffset = sections[i + 1].offset;
  78. sl = Math.min(stopLine, lineOffset + nextOffset.line);
  79. if (sl === stopLine) {
  80. sc = Math.min(stopColumn, columnOffset + nextOffset.column);
  81. } else if (sl < stopLine) {
  82. sc = columnOffset + nextOffset.column;
  83. }
  84. }
  85. addSection(
  86. map,
  87. mapUrl,
  88. mappings,
  89. sources,
  90. sourcesContent,
  91. names,
  92. ignoreList,
  93. lineOffset + offset.line,
  94. columnOffset + offset.column,
  95. sl,
  96. sc,
  97. );
  98. }
  99. }
  100. function addSection(
  101. input: SectionXInput['map'],
  102. mapUrl: string | null | undefined,
  103. mappings: SourceMapSegment[][],
  104. sources: string[],
  105. sourcesContent: (string | null)[],
  106. names: string[],
  107. ignoreList: number[],
  108. lineOffset: number,
  109. columnOffset: number,
  110. stopLine: number,
  111. stopColumn: number,
  112. ) {
  113. const parsed = parse(input);
  114. if ('sections' in parsed) return recurse(...(arguments as unknown as Parameters<typeof recurse>));
  115. const map = new TraceMap(parsed, mapUrl);
  116. const sourcesOffset = sources.length;
  117. const namesOffset = names.length;
  118. const decoded = decodedMappings(map);
  119. const { resolvedSources, sourcesContent: contents, ignoreList: ignores } = map;
  120. append(sources, resolvedSources);
  121. append(names, map.names);
  122. if (contents) append(sourcesContent, contents);
  123. else for (let i = 0; i < resolvedSources.length; i++) sourcesContent.push(null);
  124. if (ignores) for (let i = 0; i < ignores.length; i++) ignoreList.push(ignores[i] + sourcesOffset);
  125. for (let i = 0; i < decoded.length; i++) {
  126. const lineI = lineOffset + i;
  127. // We can only add so many lines before we step into the range that the next section's map
  128. // controls. When we get to the last line, then we'll start checking the segments to see if
  129. // they've crossed into the column range. But it may not have any columns that overstep, so we
  130. // still need to check that we don't overstep lines, too.
  131. if (lineI > stopLine) return;
  132. // The out line may already exist in mappings (if we're continuing the line started by a
  133. // previous section). Or, we may have jumped ahead several lines to start this section.
  134. const out = getLine(mappings, lineI);
  135. // On the 0th loop, the section's column offset shifts us forward. On all other lines (since the
  136. // map can be multiple lines), it doesn't.
  137. const cOffset = i === 0 ? columnOffset : 0;
  138. const line = decoded[i];
  139. for (let j = 0; j < line.length; j++) {
  140. const seg = line[j];
  141. const column = cOffset + seg[COLUMN];
  142. // If this segment steps into the column range that the next section's map controls, we need
  143. // to stop early.
  144. if (lineI === stopLine && column >= stopColumn) return;
  145. if (seg.length === 1) {
  146. out.push([column]);
  147. continue;
  148. }
  149. const sourcesIndex = sourcesOffset + seg[SOURCES_INDEX];
  150. const sourceLine = seg[SOURCE_LINE];
  151. const sourceColumn = seg[SOURCE_COLUMN];
  152. out.push(
  153. seg.length === 4
  154. ? [column, sourcesIndex, sourceLine, sourceColumn]
  155. : [column, sourcesIndex, sourceLine, sourceColumn, namesOffset + seg[NAMES_INDEX]],
  156. );
  157. }
  158. }
  159. }
  160. function append<T>(arr: T[], other: T[]) {
  161. for (let i = 0; i < other.length; i++) arr.push(other[i]);
  162. }
  163. function getLine<T>(arr: T[][], index: number): T[] {
  164. for (let i = arr.length; i <= index; i++) arr[i] = [];
  165. return arr[index];
  166. }