sourcemap-codec.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. export type SourceMapSegment =
  2. | [number]
  3. | [number, number, number, number]
  4. | [number, number, number, number, number];
  5. export type SourceMapLine = SourceMapSegment[];
  6. export type SourceMapMappings = SourceMapLine[];
  7. const comma = ','.charCodeAt(0);
  8. const semicolon = ';'.charCodeAt(0);
  9. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  10. const intToChar = new Uint8Array(64); // 64 possible chars.
  11. const charToInt = new Uint8Array(128); // z is 122 in ASCII
  12. for (let i = 0; i < chars.length; i++) {
  13. const c = chars.charCodeAt(i);
  14. intToChar[i] = c;
  15. charToInt[c] = i;
  16. }
  17. // Provide a fallback for older environments.
  18. const td =
  19. typeof TextDecoder !== 'undefined'
  20. ? /* #__PURE__ */ new TextDecoder()
  21. : typeof Buffer !== 'undefined'
  22. ? {
  23. decode(buf: Uint8Array) {
  24. const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
  25. return out.toString();
  26. },
  27. }
  28. : {
  29. decode(buf: Uint8Array) {
  30. let out = '';
  31. for (let i = 0; i < buf.length; i++) {
  32. out += String.fromCharCode(buf[i]);
  33. }
  34. return out;
  35. },
  36. };
  37. export function decode(mappings: string): SourceMapMappings {
  38. const state: [number, number, number, number, number] = new Int32Array(5) as any;
  39. const decoded: SourceMapMappings = [];
  40. let index = 0;
  41. do {
  42. const semi = indexOf(mappings, index);
  43. const line: SourceMapLine = [];
  44. let sorted = true;
  45. let lastCol = 0;
  46. state[0] = 0;
  47. for (let i = index; i < semi; i++) {
  48. let seg: SourceMapSegment;
  49. i = decodeInteger(mappings, i, state, 0); // genColumn
  50. const col = state[0];
  51. if (col < lastCol) sorted = false;
  52. lastCol = col;
  53. if (hasMoreVlq(mappings, i, semi)) {
  54. i = decodeInteger(mappings, i, state, 1); // sourcesIndex
  55. i = decodeInteger(mappings, i, state, 2); // sourceLine
  56. i = decodeInteger(mappings, i, state, 3); // sourceColumn
  57. if (hasMoreVlq(mappings, i, semi)) {
  58. i = decodeInteger(mappings, i, state, 4); // namesIndex
  59. seg = [col, state[1], state[2], state[3], state[4]];
  60. } else {
  61. seg = [col, state[1], state[2], state[3]];
  62. }
  63. } else {
  64. seg = [col];
  65. }
  66. line.push(seg);
  67. }
  68. if (!sorted) sort(line);
  69. decoded.push(line);
  70. index = semi + 1;
  71. } while (index <= mappings.length);
  72. return decoded;
  73. }
  74. function indexOf(mappings: string, index: number): number {
  75. const idx = mappings.indexOf(';', index);
  76. return idx === -1 ? mappings.length : idx;
  77. }
  78. function decodeInteger(mappings: string, pos: number, state: SourceMapSegment, j: number): number {
  79. let value = 0;
  80. let shift = 0;
  81. let integer = 0;
  82. do {
  83. const c = mappings.charCodeAt(pos++);
  84. integer = charToInt[c];
  85. value |= (integer & 31) << shift;
  86. shift += 5;
  87. } while (integer & 32);
  88. const shouldNegate = value & 1;
  89. value >>>= 1;
  90. if (shouldNegate) {
  91. value = -0x80000000 | -value;
  92. }
  93. state[j] += value;
  94. return pos;
  95. }
  96. function hasMoreVlq(mappings: string, i: number, length: number): boolean {
  97. if (i >= length) return false;
  98. return mappings.charCodeAt(i) !== comma;
  99. }
  100. function sort(line: SourceMapSegment[]) {
  101. line.sort(sortComparator);
  102. }
  103. function sortComparator(a: SourceMapSegment, b: SourceMapSegment): number {
  104. return a[0] - b[0];
  105. }
  106. export function encode(decoded: SourceMapMappings): string;
  107. export function encode(decoded: Readonly<SourceMapMappings>): string;
  108. export function encode(decoded: Readonly<SourceMapMappings>): string {
  109. const state: [number, number, number, number, number] = new Int32Array(5) as any;
  110. const bufLength = 1024 * 16;
  111. const subLength = bufLength - 36;
  112. const buf = new Uint8Array(bufLength);
  113. const sub = buf.subarray(0, subLength);
  114. let pos = 0;
  115. let out = '';
  116. for (let i = 0; i < decoded.length; i++) {
  117. const line = decoded[i];
  118. if (i > 0) {
  119. if (pos === bufLength) {
  120. out += td.decode(buf);
  121. pos = 0;
  122. }
  123. buf[pos++] = semicolon;
  124. }
  125. if (line.length === 0) continue;
  126. state[0] = 0;
  127. for (let j = 0; j < line.length; j++) {
  128. const segment = line[j];
  129. // We can push up to 5 ints, each int can take at most 7 chars, and we
  130. // may push a comma.
  131. if (pos > subLength) {
  132. out += td.decode(sub);
  133. buf.copyWithin(0, subLength, pos);
  134. pos -= subLength;
  135. }
  136. if (j > 0) buf[pos++] = comma;
  137. pos = encodeInteger(buf, pos, state, segment, 0); // genColumn
  138. if (segment.length === 1) continue;
  139. pos = encodeInteger(buf, pos, state, segment, 1); // sourcesIndex
  140. pos = encodeInteger(buf, pos, state, segment, 2); // sourceLine
  141. pos = encodeInteger(buf, pos, state, segment, 3); // sourceColumn
  142. if (segment.length === 4) continue;
  143. pos = encodeInteger(buf, pos, state, segment, 4); // namesIndex
  144. }
  145. }
  146. return out + td.decode(buf.subarray(0, pos));
  147. }
  148. function encodeInteger(
  149. buf: Uint8Array,
  150. pos: number,
  151. state: SourceMapSegment,
  152. segment: SourceMapSegment,
  153. j: number,
  154. ): number {
  155. const next = segment[j];
  156. let num = next - state[j];
  157. state[j] = next;
  158. num = num < 0 ? (-num << 1) | 1 : num << 1;
  159. do {
  160. let clamped = num & 0b011111;
  161. num >>>= 5;
  162. if (num > 0) clamped |= 0b100000;
  163. buf[pos++] = intToChar[clamped];
  164. } while (num > 0);
  165. return pos;
  166. }