streamChunksOfSourceMap.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const getGeneratedSourceInfo = require("./getGeneratedSourceInfo");
  7. const getSource = require("./getSource");
  8. const readMappings = require("./readMappings");
  9. const splitIntoLines = require("./splitIntoLines");
  10. /** @typedef {import("../Source").RawSourceMap} RawSourceMap */
  11. /** @typedef {import("./getGeneratedSourceInfo").GeneratedSourceInfo} GeneratedSourceInfo */
  12. /** @typedef {import("./streamChunks").OnChunk} OnChunk */
  13. /** @typedef {import("./streamChunks").OnName} OnName */
  14. /** @typedef {import("./streamChunks").OnSource} OnSource */
  15. /**
  16. * @param {string} source source
  17. * @param {RawSourceMap} sourceMap source map
  18. * @param {OnChunk} onChunk on chunk
  19. * @param {OnSource} onSource on source
  20. * @param {OnName} onName on name
  21. * @returns {GeneratedSourceInfo} generated source info
  22. */
  23. const streamChunksOfSourceMapFull = (
  24. source,
  25. sourceMap,
  26. onChunk,
  27. onSource,
  28. onName,
  29. ) => {
  30. const lines = splitIntoLines(source);
  31. const linesLength = lines.length;
  32. if (linesLength === 0) {
  33. return {
  34. generatedLine: 1,
  35. generatedColumn: 0,
  36. };
  37. }
  38. const { sources, sourcesContent, names, mappings } = sourceMap;
  39. for (let i = 0; i < sources.length; i++) {
  40. onSource(
  41. i,
  42. getSource(sourceMap, i),
  43. (sourcesContent && sourcesContent[i]) || undefined,
  44. );
  45. }
  46. if (names) {
  47. for (let i = 0; i < names.length; i++) {
  48. onName(i, names[i]);
  49. }
  50. }
  51. const lastLine = lines[linesLength - 1];
  52. const lastNewLine = lastLine.endsWith("\n");
  53. const finalLine = lastNewLine ? linesLength + 1 : linesLength;
  54. const finalColumn = lastNewLine ? 0 : lastLine.length;
  55. let currentGeneratedLine = 1;
  56. let currentGeneratedColumn = 0;
  57. let mappingActive = false;
  58. let activeMappingSourceIndex = -1;
  59. let activeMappingOriginalLine = -1;
  60. let activeMappingOriginalColumn = -1;
  61. let activeMappingNameIndex = -1;
  62. /**
  63. * @param {number} generatedLine generated line
  64. * @param {number} generatedColumn generated column
  65. * @param {number} sourceIndex source index
  66. * @param {number} originalLine original line
  67. * @param {number} originalColumn original column
  68. * @param {number} nameIndex name index
  69. * @returns {void}
  70. */
  71. const onMapping = (
  72. generatedLine,
  73. generatedColumn,
  74. sourceIndex,
  75. originalLine,
  76. originalColumn,
  77. nameIndex,
  78. ) => {
  79. if (mappingActive && currentGeneratedLine <= linesLength) {
  80. let chunk;
  81. const mappingLine = currentGeneratedLine;
  82. const mappingColumn = currentGeneratedColumn;
  83. const line = lines[currentGeneratedLine - 1];
  84. if (generatedLine !== currentGeneratedLine) {
  85. chunk = line.slice(currentGeneratedColumn);
  86. currentGeneratedLine++;
  87. currentGeneratedColumn = 0;
  88. } else {
  89. chunk = line.slice(currentGeneratedColumn, generatedColumn);
  90. currentGeneratedColumn = generatedColumn;
  91. }
  92. if (chunk) {
  93. onChunk(
  94. chunk,
  95. mappingLine,
  96. mappingColumn,
  97. activeMappingSourceIndex,
  98. activeMappingOriginalLine,
  99. activeMappingOriginalColumn,
  100. activeMappingNameIndex,
  101. );
  102. }
  103. mappingActive = false;
  104. }
  105. if (generatedLine > currentGeneratedLine && currentGeneratedColumn > 0) {
  106. if (currentGeneratedLine <= linesLength) {
  107. const chunk = lines[currentGeneratedLine - 1].slice(
  108. currentGeneratedColumn,
  109. );
  110. onChunk(
  111. chunk,
  112. currentGeneratedLine,
  113. currentGeneratedColumn,
  114. -1,
  115. -1,
  116. -1,
  117. -1,
  118. );
  119. }
  120. currentGeneratedLine++;
  121. currentGeneratedColumn = 0;
  122. }
  123. // Emit each fully-passed generated line. Once we move past the last
  124. // available line we stop emitting, but still need to advance the
  125. // counter to `generatedLine` so subsequent state matches the caller.
  126. while (
  127. generatedLine > currentGeneratedLine &&
  128. currentGeneratedLine <= linesLength
  129. ) {
  130. onChunk(
  131. lines[currentGeneratedLine - 1],
  132. currentGeneratedLine,
  133. 0,
  134. -1,
  135. -1,
  136. -1,
  137. -1,
  138. );
  139. currentGeneratedLine++;
  140. }
  141. if (currentGeneratedLine < generatedLine) {
  142. currentGeneratedLine = generatedLine;
  143. }
  144. if (generatedColumn > currentGeneratedColumn) {
  145. if (currentGeneratedLine <= linesLength) {
  146. const chunk = lines[currentGeneratedLine - 1].slice(
  147. currentGeneratedColumn,
  148. generatedColumn,
  149. );
  150. onChunk(
  151. chunk,
  152. currentGeneratedLine,
  153. currentGeneratedColumn,
  154. -1,
  155. -1,
  156. -1,
  157. -1,
  158. );
  159. }
  160. currentGeneratedColumn = generatedColumn;
  161. }
  162. if (
  163. sourceIndex >= 0 &&
  164. (generatedLine < finalLine ||
  165. (generatedLine === finalLine && generatedColumn < finalColumn))
  166. ) {
  167. mappingActive = true;
  168. activeMappingSourceIndex = sourceIndex;
  169. activeMappingOriginalLine = originalLine;
  170. activeMappingOriginalColumn = originalColumn;
  171. activeMappingNameIndex = nameIndex;
  172. }
  173. };
  174. readMappings(mappings, onMapping);
  175. onMapping(finalLine, finalColumn, -1, -1, -1, -1);
  176. return {
  177. generatedLine: finalLine,
  178. generatedColumn: finalColumn,
  179. };
  180. };
  181. /**
  182. * @param {string} source source
  183. * @param {RawSourceMap} sourceMap source map
  184. * @param {OnChunk} onChunk on chunk
  185. * @param {OnSource} onSource on source
  186. * @param {OnName} _onName on name
  187. * @returns {GeneratedSourceInfo} generated source info
  188. */
  189. const streamChunksOfSourceMapLinesFull = (
  190. source,
  191. sourceMap,
  192. onChunk,
  193. onSource,
  194. _onName,
  195. ) => {
  196. const lines = splitIntoLines(source);
  197. const linesLength = lines.length;
  198. if (linesLength === 0) {
  199. return {
  200. generatedLine: 1,
  201. generatedColumn: 0,
  202. };
  203. }
  204. const { sources, sourcesContent, mappings } = sourceMap;
  205. for (let i = 0; i < sources.length; i++) {
  206. onSource(
  207. i,
  208. getSource(sourceMap, i),
  209. (sourcesContent && sourcesContent[i]) || undefined,
  210. );
  211. }
  212. let currentGeneratedLine = 1;
  213. /**
  214. * @param {number} generatedLine generated line
  215. * @param {number} _generatedColumn generated column
  216. * @param {number} sourceIndex source index
  217. * @param {number} originalLine original line
  218. * @param {number} originalColumn original column
  219. * @param {number} _nameIndex name index
  220. * @returns {void}
  221. */
  222. const onMapping = (
  223. generatedLine,
  224. _generatedColumn,
  225. sourceIndex,
  226. originalLine,
  227. originalColumn,
  228. _nameIndex,
  229. ) => {
  230. if (
  231. sourceIndex < 0 ||
  232. generatedLine < currentGeneratedLine ||
  233. generatedLine > linesLength
  234. ) {
  235. return;
  236. }
  237. // `generatedLine <= linesLength` is guaranteed by the guard above, so
  238. // every line we iterate over is in bounds — no per-iteration length
  239. // check needed.
  240. while (generatedLine > currentGeneratedLine) {
  241. onChunk(
  242. lines[currentGeneratedLine - 1],
  243. currentGeneratedLine,
  244. 0,
  245. -1,
  246. -1,
  247. -1,
  248. -1,
  249. );
  250. currentGeneratedLine++;
  251. }
  252. onChunk(
  253. lines[generatedLine - 1],
  254. generatedLine,
  255. 0,
  256. sourceIndex,
  257. originalLine,
  258. originalColumn,
  259. -1,
  260. );
  261. currentGeneratedLine++;
  262. };
  263. readMappings(mappings, onMapping);
  264. for (; currentGeneratedLine <= linesLength; currentGeneratedLine++) {
  265. onChunk(
  266. lines[currentGeneratedLine - 1],
  267. currentGeneratedLine,
  268. 0,
  269. -1,
  270. -1,
  271. -1,
  272. -1,
  273. );
  274. }
  275. const lastLine = lines[linesLength - 1];
  276. const lastNewLine = lastLine.endsWith("\n");
  277. const finalLine = lastNewLine ? linesLength + 1 : linesLength;
  278. const finalColumn = lastNewLine ? 0 : lastLine.length;
  279. return {
  280. generatedLine: finalLine,
  281. generatedColumn: finalColumn,
  282. };
  283. };
  284. /**
  285. * @param {string} source source
  286. * @param {RawSourceMap} sourceMap source map
  287. * @param {OnChunk} onChunk on chunk
  288. * @param {OnSource} onSource on source
  289. * @param {OnName} onName on name
  290. * @returns {GeneratedSourceInfo} generated source info
  291. */
  292. const streamChunksOfSourceMapFinal = (
  293. source,
  294. sourceMap,
  295. onChunk,
  296. onSource,
  297. onName,
  298. ) => {
  299. const result = getGeneratedSourceInfo(source);
  300. const { generatedLine: finalLine, generatedColumn: finalColumn } = result;
  301. if (finalLine === 1 && finalColumn === 0) return result;
  302. const { sources, sourcesContent, names, mappings } = sourceMap;
  303. for (let i = 0; i < sources.length; i++) {
  304. onSource(
  305. i,
  306. getSource(sourceMap, i),
  307. (sourcesContent && sourcesContent[i]) || undefined,
  308. );
  309. }
  310. if (names) {
  311. for (let i = 0; i < names.length; i++) {
  312. onName(i, names[i]);
  313. }
  314. }
  315. let mappingActiveLine = 0;
  316. /**
  317. * @param {number} generatedLine generated line
  318. * @param {number} generatedColumn generated column
  319. * @param {number} sourceIndex source index
  320. * @param {number} originalLine original line
  321. * @param {number} originalColumn original column
  322. * @param {number} nameIndex name index
  323. * @returns {void}
  324. */
  325. const onMapping = (
  326. generatedLine,
  327. generatedColumn,
  328. sourceIndex,
  329. originalLine,
  330. originalColumn,
  331. nameIndex,
  332. ) => {
  333. if (
  334. generatedLine >= /** @type {number} */ (finalLine) &&
  335. (generatedColumn >= /** @type {number} */ (finalColumn) ||
  336. generatedLine > /** @type {number} */ (finalLine))
  337. ) {
  338. return;
  339. }
  340. if (sourceIndex >= 0) {
  341. onChunk(
  342. undefined,
  343. generatedLine,
  344. generatedColumn,
  345. sourceIndex,
  346. originalLine,
  347. originalColumn,
  348. nameIndex,
  349. );
  350. mappingActiveLine = generatedLine;
  351. } else if (mappingActiveLine === generatedLine) {
  352. onChunk(undefined, generatedLine, generatedColumn, -1, -1, -1, -1);
  353. mappingActiveLine = 0;
  354. }
  355. };
  356. readMappings(mappings, onMapping);
  357. return result;
  358. };
  359. /**
  360. * @param {string} source source
  361. * @param {RawSourceMap} sourceMap source map
  362. * @param {OnChunk} onChunk on chunk
  363. * @param {OnSource} onSource on source
  364. * @param {OnName} _onName on name
  365. * @returns {GeneratedSourceInfo} generated source info
  366. */
  367. const streamChunksOfSourceMapLinesFinal = (
  368. source,
  369. sourceMap,
  370. onChunk,
  371. onSource,
  372. _onName,
  373. ) => {
  374. const result = getGeneratedSourceInfo(source);
  375. const { generatedLine, generatedColumn } = result;
  376. if (generatedLine === 1 && generatedColumn === 0) {
  377. return {
  378. generatedLine: 1,
  379. generatedColumn: 0,
  380. };
  381. }
  382. const { sources, sourcesContent, mappings } = sourceMap;
  383. for (let i = 0; i < sources.length; i++) {
  384. onSource(
  385. i,
  386. getSource(sourceMap, i),
  387. (sourcesContent && sourcesContent[i]) || undefined,
  388. );
  389. }
  390. const finalLine =
  391. generatedColumn === 0
  392. ? /** @type {number} */ (generatedLine) - 1
  393. : /** @type {number} */ (generatedLine);
  394. let currentGeneratedLine = 1;
  395. /**
  396. * @param {number} generatedLine generated line
  397. * @param {number} _generatedColumn generated column
  398. * @param {number} sourceIndex source index
  399. * @param {number} originalLine original line
  400. * @param {number} originalColumn original column
  401. * @param {number} _nameIndex name index
  402. * @returns {void}
  403. */
  404. const onMapping = (
  405. generatedLine,
  406. _generatedColumn,
  407. sourceIndex,
  408. originalLine,
  409. originalColumn,
  410. _nameIndex,
  411. ) => {
  412. if (
  413. sourceIndex >= 0 &&
  414. currentGeneratedLine <= generatedLine &&
  415. generatedLine <= finalLine
  416. ) {
  417. onChunk(
  418. undefined,
  419. generatedLine,
  420. 0,
  421. sourceIndex,
  422. originalLine,
  423. originalColumn,
  424. -1,
  425. );
  426. currentGeneratedLine = generatedLine + 1;
  427. }
  428. };
  429. readMappings(mappings, onMapping);
  430. return result;
  431. };
  432. /**
  433. * @param {string} source source
  434. * @param {RawSourceMap} sourceMap source map
  435. * @param {OnChunk} onChunk on chunk
  436. * @param {OnSource} onSource on source
  437. * @param {OnName} onName on name
  438. * @param {boolean} finalSource final source
  439. * @param {boolean} columns columns
  440. * @returns {GeneratedSourceInfo} generated source info
  441. */
  442. module.exports = (
  443. source,
  444. sourceMap,
  445. onChunk,
  446. onSource,
  447. onName,
  448. finalSource,
  449. columns,
  450. ) => {
  451. if (columns) {
  452. return finalSource
  453. ? streamChunksOfSourceMapFinal(
  454. source,
  455. sourceMap,
  456. onChunk,
  457. onSource,
  458. onName,
  459. )
  460. : streamChunksOfSourceMapFull(
  461. source,
  462. sourceMap,
  463. onChunk,
  464. onSource,
  465. onName,
  466. );
  467. }
  468. return finalSource
  469. ? streamChunksOfSourceMapLinesFinal(
  470. source,
  471. sourceMap,
  472. onChunk,
  473. onSource,
  474. onName,
  475. )
  476. : streamChunksOfSourceMapLinesFull(
  477. source,
  478. sourceMap,
  479. onChunk,
  480. onSource,
  481. onName,
  482. );
  483. };