gen-mapping.mjs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import { SetArray, put } from '@jridgewell/set-array';
  2. import { encode } from '@jridgewell/sourcemap-codec';
  3. import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping';
  4. const COLUMN = 0;
  5. const SOURCES_INDEX = 1;
  6. const SOURCE_LINE = 2;
  7. const SOURCE_COLUMN = 3;
  8. const NAMES_INDEX = 4;
  9. const NO_NAME = -1;
  10. /**
  11. * A low-level API to associate a generated position with an original source position. Line and
  12. * column here are 0-based, unlike `addMapping`.
  13. */
  14. let addSegment;
  15. /**
  16. * A high-level API to associate a generated position with an original source position. Line is
  17. * 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
  18. */
  19. let addMapping;
  20. /**
  21. * Same as `addSegment`, but will only add the segment if it generates useful information in the
  22. * resulting map. This only works correctly if segments are added **in order**, meaning you should
  23. * not add a segment with a lower generated line/column than one that came before.
  24. */
  25. let maybeAddSegment;
  26. /**
  27. * Same as `addMapping`, but will only add the mapping if it generates useful information in the
  28. * resulting map. This only works correctly if mappings are added **in order**, meaning you should
  29. * not add a mapping with a lower generated line/column than one that came before.
  30. */
  31. let maybeAddMapping;
  32. /**
  33. * Adds/removes the content of the source file to the source map.
  34. */
  35. let setSourceContent;
  36. /**
  37. * Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
  38. * a sourcemap, or to JSON.stringify.
  39. */
  40. let toDecodedMap;
  41. /**
  42. * Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
  43. * a sourcemap, or to JSON.stringify.
  44. */
  45. let toEncodedMap;
  46. /**
  47. * Constructs a new GenMapping, using the already present mappings of the input.
  48. */
  49. let fromMap;
  50. /**
  51. * Returns an array of high-level mapping objects for every recorded segment, which could then be
  52. * passed to the `source-map` library.
  53. */
  54. let allMappings;
  55. // This split declaration is only so that terser can elminiate the static initialization block.
  56. let addSegmentInternal;
  57. /**
  58. * Provides the state to generate a sourcemap.
  59. */
  60. class GenMapping {
  61. constructor({ file, sourceRoot } = {}) {
  62. this._names = new SetArray();
  63. this._sources = new SetArray();
  64. this._sourcesContent = [];
  65. this._mappings = [];
  66. this.file = file;
  67. this.sourceRoot = sourceRoot;
  68. }
  69. }
  70. (() => {
  71. addSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
  72. return addSegmentInternal(false, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content);
  73. };
  74. maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
  75. return addSegmentInternal(true, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content);
  76. };
  77. addMapping = (map, mapping) => {
  78. return addMappingInternal(false, map, mapping);
  79. };
  80. maybeAddMapping = (map, mapping) => {
  81. return addMappingInternal(true, map, mapping);
  82. };
  83. setSourceContent = (map, source, content) => {
  84. const { _sources: sources, _sourcesContent: sourcesContent } = map;
  85. sourcesContent[put(sources, source)] = content;
  86. };
  87. toDecodedMap = (map) => {
  88. const { file, sourceRoot, _mappings: mappings, _sources: sources, _sourcesContent: sourcesContent, _names: names, } = map;
  89. removeEmptyFinalLines(mappings);
  90. return {
  91. version: 3,
  92. file: file || undefined,
  93. names: names.array,
  94. sourceRoot: sourceRoot || undefined,
  95. sources: sources.array,
  96. sourcesContent,
  97. mappings,
  98. };
  99. };
  100. toEncodedMap = (map) => {
  101. const decoded = toDecodedMap(map);
  102. return Object.assign(Object.assign({}, decoded), { mappings: encode(decoded.mappings) });
  103. };
  104. allMappings = (map) => {
  105. const out = [];
  106. const { _mappings: mappings, _sources: sources, _names: names } = map;
  107. for (let i = 0; i < mappings.length; i++) {
  108. const line = mappings[i];
  109. for (let j = 0; j < line.length; j++) {
  110. const seg = line[j];
  111. const generated = { line: i + 1, column: seg[COLUMN] };
  112. let source = undefined;
  113. let original = undefined;
  114. let name = undefined;
  115. if (seg.length !== 1) {
  116. source = sources.array[seg[SOURCES_INDEX]];
  117. original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
  118. if (seg.length === 5)
  119. name = names.array[seg[NAMES_INDEX]];
  120. }
  121. out.push({ generated, source, original, name });
  122. }
  123. }
  124. return out;
  125. };
  126. fromMap = (input) => {
  127. const map = new TraceMap(input);
  128. const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
  129. putAll(gen._names, map.names);
  130. putAll(gen._sources, map.sources);
  131. gen._sourcesContent = map.sourcesContent || map.sources.map(() => null);
  132. gen._mappings = decodedMappings(map);
  133. return gen;
  134. };
  135. // Internal helpers
  136. addSegmentInternal = (skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
  137. const { _mappings: mappings, _sources: sources, _sourcesContent: sourcesContent, _names: names, } = map;
  138. const line = getLine(mappings, genLine);
  139. const index = getColumnIndex(line, genColumn);
  140. if (!source) {
  141. if (skipable && skipSourceless(line, index))
  142. return;
  143. return insert(line, index, [genColumn]);
  144. }
  145. const sourcesIndex = put(sources, source);
  146. const namesIndex = name ? put(names, name) : NO_NAME;
  147. if (sourcesIndex === sourcesContent.length)
  148. sourcesContent[sourcesIndex] = content !== null && content !== void 0 ? content : null;
  149. if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
  150. return;
  151. }
  152. return insert(line, index, name
  153. ? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex]
  154. : [genColumn, sourcesIndex, sourceLine, sourceColumn]);
  155. };
  156. })();
  157. function getLine(mappings, index) {
  158. for (let i = mappings.length; i <= index; i++) {
  159. mappings[i] = [];
  160. }
  161. return mappings[index];
  162. }
  163. function getColumnIndex(line, genColumn) {
  164. let index = line.length;
  165. for (let i = index - 1; i >= 0; index = i--) {
  166. const current = line[i];
  167. if (genColumn >= current[COLUMN])
  168. break;
  169. }
  170. return index;
  171. }
  172. function insert(array, index, value) {
  173. for (let i = array.length; i > index; i--) {
  174. array[i] = array[i - 1];
  175. }
  176. array[index] = value;
  177. }
  178. function removeEmptyFinalLines(mappings) {
  179. const { length } = mappings;
  180. let len = length;
  181. for (let i = len - 1; i >= 0; len = i, i--) {
  182. if (mappings[i].length > 0)
  183. break;
  184. }
  185. if (len < length)
  186. mappings.length = len;
  187. }
  188. function putAll(strarr, array) {
  189. for (let i = 0; i < array.length; i++)
  190. put(strarr, array[i]);
  191. }
  192. function skipSourceless(line, index) {
  193. // The start of a line is already sourceless, so adding a sourceless segment to the beginning
  194. // doesn't generate any useful information.
  195. if (index === 0)
  196. return true;
  197. const prev = line[index - 1];
  198. // If the previous segment is also sourceless, then adding another sourceless segment doesn't
  199. // genrate any new information. Else, this segment will end the source/named segment and point to
  200. // a sourceless position, which is useful.
  201. return prev.length === 1;
  202. }
  203. function skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex) {
  204. // A source/named segment at the start of a line gives position at that genColumn
  205. if (index === 0)
  206. return false;
  207. const prev = line[index - 1];
  208. // If the previous segment is sourceless, then we're transitioning to a source.
  209. if (prev.length === 1)
  210. return false;
  211. // If the previous segment maps to the exact same source position, then this segment doesn't
  212. // provide any new position information.
  213. return (sourcesIndex === prev[SOURCES_INDEX] &&
  214. sourceLine === prev[SOURCE_LINE] &&
  215. sourceColumn === prev[SOURCE_COLUMN] &&
  216. namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME));
  217. }
  218. function addMappingInternal(skipable, map, mapping) {
  219. const { generated, source, original, name, content } = mapping;
  220. if (!source) {
  221. return addSegmentInternal(skipable, map, generated.line - 1, generated.column, null, null, null, null, null);
  222. }
  223. const s = source;
  224. return addSegmentInternal(skipable, map, generated.line - 1, generated.column, s, original.line - 1, original.column, name, content);
  225. }
  226. export { GenMapping, addMapping, addSegment, allMappings, fromMap, maybeAddMapping, maybeAddSegment, setSourceContent, toDecodedMap, toEncodedMap };
  227. //# sourceMappingURL=gen-mapping.mjs.map