gen-mapping.mjs 8.9 KB

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