PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/node_modules/webpack/node_modules/webpack-sources/lib/ReplaceSource.js

https://gitlab.com/ahmad.jamal/sally
JavaScript | 471 lines | 414 code | 33 blank | 24 comment | 65 complexity | 26d63216cec52630a37b450350e1cde5 MD5 | raw file
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { getMap, getSourceAndMap } = require("./helpers/getFromStreamChunks");
  7. const streamChunks = require("./helpers/streamChunks");
  8. const Source = require("./Source");
  9. const splitIntoLines = require("./helpers/splitIntoLines");
  10. // since v8 7.0, Array.prototype.sort is stable
  11. const hasStableSort =
  12. typeof process === "object" &&
  13. process.versions &&
  14. typeof process.versions.v8 === "string" &&
  15. !/^[0-6]\./.test(process.versions.v8);
  16. // This is larger than max string length
  17. const MAX_SOURCE_POSITION = 0x20000000;
  18. class Replacement {
  19. constructor(start, end, content, name) {
  20. this.start = start;
  21. this.end = end;
  22. this.content = content;
  23. this.name = name;
  24. if (!hasStableSort) {
  25. this.index = -1;
  26. }
  27. }
  28. }
  29. class ReplaceSource extends Source {
  30. constructor(source, name) {
  31. super();
  32. this._source = source;
  33. this._name = name;
  34. /** @type {Replacement[]} */
  35. this._replacements = [];
  36. this._isSorted = true;
  37. }
  38. getName() {
  39. return this._name;
  40. }
  41. getReplacements() {
  42. this._sortReplacements();
  43. return this._replacements;
  44. }
  45. replace(start, end, newValue, name) {
  46. if (typeof newValue !== "string")
  47. throw new Error(
  48. "insertion must be a string, but is a " + typeof newValue
  49. );
  50. this._replacements.push(new Replacement(start, end, newValue, name));
  51. this._isSorted = false;
  52. }
  53. insert(pos, newValue, name) {
  54. if (typeof newValue !== "string")
  55. throw new Error(
  56. "insertion must be a string, but is a " +
  57. typeof newValue +
  58. ": " +
  59. newValue
  60. );
  61. this._replacements.push(new Replacement(pos, pos - 1, newValue, name));
  62. this._isSorted = false;
  63. }
  64. source() {
  65. if (this._replacements.length === 0) {
  66. return this._source.source();
  67. }
  68. let current = this._source.source();
  69. let pos = 0;
  70. const result = [];
  71. this._sortReplacements();
  72. for (const replacement of this._replacements) {
  73. const start = Math.floor(replacement.start);
  74. const end = Math.floor(replacement.end + 1);
  75. if (pos < start) {
  76. const offset = start - pos;
  77. result.push(current.slice(0, offset));
  78. current = current.slice(offset);
  79. pos = start;
  80. }
  81. result.push(replacement.content);
  82. if (pos < end) {
  83. const offset = end - pos;
  84. current = current.slice(offset);
  85. pos = end;
  86. }
  87. }
  88. result.push(current);
  89. return result.join("");
  90. }
  91. map(options) {
  92. if (this._replacements.length === 0) {
  93. return this._source.map(options);
  94. }
  95. return getMap(this, options);
  96. }
  97. sourceAndMap(options) {
  98. if (this._replacements.length === 0) {
  99. return this._source.sourceAndMap(options);
  100. }
  101. return getSourceAndMap(this, options);
  102. }
  103. original() {
  104. return this._source;
  105. }
  106. _sortReplacements() {
  107. if (this._isSorted) return;
  108. if (hasStableSort) {
  109. this._replacements.sort(function (a, b) {
  110. const diff1 = a.start - b.start;
  111. if (diff1 !== 0) return diff1;
  112. const diff2 = a.end - b.end;
  113. if (diff2 !== 0) return diff2;
  114. return 0;
  115. });
  116. } else {
  117. this._replacements.forEach((repl, i) => (repl.index = i));
  118. this._replacements.sort(function (a, b) {
  119. const diff1 = a.start - b.start;
  120. if (diff1 !== 0) return diff1;
  121. const diff2 = a.end - b.end;
  122. if (diff2 !== 0) return diff2;
  123. return a.index - b.index;
  124. });
  125. }
  126. this._isSorted = true;
  127. }
  128. streamChunks(options, onChunk, onSource, onName) {
  129. this._sortReplacements();
  130. const repls = this._replacements;
  131. let pos = 0;
  132. let i = 0;
  133. let replacmentEnd = -1;
  134. let nextReplacement =
  135. i < repls.length ? Math.floor(repls[i].start) : MAX_SOURCE_POSITION;
  136. let generatedLineOffset = 0;
  137. let generatedColumnOffset = 0;
  138. let generatedColumnOffsetLine = 0;
  139. const sourceContents = [];
  140. const nameMapping = new Map();
  141. const nameIndexMapping = [];
  142. const checkOriginalContent = (sourceIndex, line, column, expectedChunk) => {
  143. let content =
  144. sourceIndex < sourceContents.length
  145. ? sourceContents[sourceIndex]
  146. : undefined;
  147. if (content === undefined) return false;
  148. if (typeof content === "string") {
  149. content = splitIntoLines(content);
  150. sourceContents[sourceIndex] = content;
  151. }
  152. const contentLine = line <= content.length ? content[line - 1] : null;
  153. if (contentLine === null) return false;
  154. return (
  155. contentLine.slice(column, column + expectedChunk.length) ===
  156. expectedChunk
  157. );
  158. };
  159. let { generatedLine, generatedColumn } = streamChunks(
  160. this._source,
  161. Object.assign({}, options, { finalSource: false }),
  162. (
  163. chunk,
  164. generatedLine,
  165. generatedColumn,
  166. sourceIndex,
  167. originalLine,
  168. originalColumn,
  169. nameIndex
  170. ) => {
  171. let chunkPos = 0;
  172. let endPos = pos + chunk.length;
  173. // Skip over when it has been replaced
  174. if (replacmentEnd > pos) {
  175. // Skip over the whole chunk
  176. if (replacmentEnd >= endPos) {
  177. const line = generatedLine + generatedLineOffset;
  178. if (chunk.endsWith("\n")) {
  179. generatedLineOffset--;
  180. if (generatedColumnOffsetLine === line) {
  181. // undo exiting corrections form the current line
  182. generatedColumnOffset += generatedColumn;
  183. }
  184. } else if (generatedColumnOffsetLine === line) {
  185. generatedColumnOffset -= chunk.length;
  186. } else {
  187. generatedColumnOffset = -chunk.length;
  188. generatedColumnOffsetLine = line;
  189. }
  190. pos = endPos;
  191. return;
  192. }
  193. // Partially skip over chunk
  194. chunkPos = replacmentEnd - pos;
  195. if (
  196. checkOriginalContent(
  197. sourceIndex,
  198. originalLine,
  199. originalColumn,
  200. chunk.slice(0, chunkPos)
  201. )
  202. ) {
  203. originalColumn += chunkPos;
  204. }
  205. pos += chunkPos;
  206. const line = generatedLine + generatedLineOffset;
  207. if (generatedColumnOffsetLine === line) {
  208. generatedColumnOffset -= chunkPos;
  209. } else {
  210. generatedColumnOffset = -chunkPos;
  211. generatedColumnOffsetLine = line;
  212. }
  213. generatedColumn += chunkPos;
  214. }
  215. // Is a replacement in the chunk?
  216. if (nextReplacement < endPos) {
  217. do {
  218. let line = generatedLine + generatedLineOffset;
  219. if (nextReplacement > pos) {
  220. // Emit chunk until replacement
  221. const offset = nextReplacement - pos;
  222. const chunkSlice = chunk.slice(chunkPos, chunkPos + offset);
  223. onChunk(
  224. chunkSlice,
  225. line,
  226. generatedColumn +
  227. (line === generatedColumnOffsetLine
  228. ? generatedColumnOffset
  229. : 0),
  230. sourceIndex,
  231. originalLine,
  232. originalColumn,
  233. nameIndex < 0 || nameIndex >= nameIndexMapping.length
  234. ? -1
  235. : nameIndexMapping[nameIndex]
  236. );
  237. generatedColumn += offset;
  238. chunkPos += offset;
  239. pos = nextReplacement;
  240. if (
  241. checkOriginalContent(
  242. sourceIndex,
  243. originalLine,
  244. originalColumn,
  245. chunkSlice
  246. )
  247. ) {
  248. originalColumn += chunkSlice.length;
  249. }
  250. }
  251. // Insert replacement content splitted into chunks by lines
  252. const regexp = /[^\n]+\n?|\n/g;
  253. const { content, name } = repls[i];
  254. let match = regexp.exec(content);
  255. let replacementNameIndex = nameIndex;
  256. if (sourceIndex >= 0 && name) {
  257. let globalIndex = nameMapping.get(name);
  258. if (globalIndex === undefined) {
  259. globalIndex = nameMapping.size;
  260. nameMapping.set(name, globalIndex);
  261. onName(globalIndex, name);
  262. }
  263. replacementNameIndex = globalIndex;
  264. }
  265. while (match !== null) {
  266. const contentLine = match[0];
  267. onChunk(
  268. contentLine,
  269. line,
  270. generatedColumn +
  271. (line === generatedColumnOffsetLine
  272. ? generatedColumnOffset
  273. : 0),
  274. sourceIndex,
  275. originalLine,
  276. originalColumn,
  277. replacementNameIndex
  278. );
  279. // Only the first chunk has name assigned
  280. replacementNameIndex = -1;
  281. match = regexp.exec(content);
  282. if (match === null && !contentLine.endsWith("\n")) {
  283. if (generatedColumnOffsetLine === line) {
  284. generatedColumnOffset += contentLine.length;
  285. } else {
  286. generatedColumnOffset = contentLine.length;
  287. generatedColumnOffsetLine = line;
  288. }
  289. } else {
  290. generatedLineOffset++;
  291. line++;
  292. generatedColumnOffset = -generatedColumn;
  293. generatedColumnOffsetLine = line;
  294. }
  295. }
  296. // Remove replaced content by settings this variable
  297. replacmentEnd = Math.max(
  298. replacmentEnd,
  299. Math.floor(repls[i].end + 1)
  300. );
  301. // Move to next replacment
  302. i++;
  303. nextReplacement =
  304. i < repls.length
  305. ? Math.floor(repls[i].start)
  306. : MAX_SOURCE_POSITION;
  307. // Skip over when it has been replaced
  308. const offset = chunk.length - endPos + replacmentEnd - chunkPos;
  309. if (offset > 0) {
  310. // Skip over whole chunk
  311. if (replacmentEnd >= endPos) {
  312. let line = generatedLine + generatedLineOffset;
  313. if (chunk.endsWith("\n")) {
  314. generatedLineOffset--;
  315. if (generatedColumnOffsetLine === line) {
  316. // undo exiting corrections form the current line
  317. generatedColumnOffset += generatedColumn;
  318. }
  319. } else if (generatedColumnOffsetLine === line) {
  320. generatedColumnOffset -= chunk.length - chunkPos;
  321. } else {
  322. generatedColumnOffset = chunkPos - chunk.length;
  323. generatedColumnOffsetLine = line;
  324. }
  325. pos = endPos;
  326. return;
  327. }
  328. // Partially skip over chunk
  329. const line = generatedLine + generatedLineOffset;
  330. if (
  331. checkOriginalContent(
  332. sourceIndex,
  333. originalLine,
  334. originalColumn,
  335. chunk.slice(chunkPos, chunkPos + offset)
  336. )
  337. ) {
  338. originalColumn += offset;
  339. }
  340. chunkPos += offset;
  341. pos += offset;
  342. if (generatedColumnOffsetLine === line) {
  343. generatedColumnOffset -= offset;
  344. } else {
  345. generatedColumnOffset = -offset;
  346. generatedColumnOffsetLine = line;
  347. }
  348. generatedColumn += offset;
  349. }
  350. } while (nextReplacement < endPos);
  351. }
  352. // Emit remaining chunk
  353. if (chunkPos < chunk.length) {
  354. const chunkSlice = chunkPos === 0 ? chunk : chunk.slice(chunkPos);
  355. const line = generatedLine + generatedLineOffset;
  356. onChunk(
  357. chunkSlice,
  358. line,
  359. generatedColumn +
  360. (line === generatedColumnOffsetLine ? generatedColumnOffset : 0),
  361. sourceIndex,
  362. originalLine,
  363. originalColumn,
  364. nameIndex < 0 ? -1 : nameIndexMapping[nameIndex]
  365. );
  366. }
  367. pos = endPos;
  368. },
  369. (sourceIndex, source, sourceContent) => {
  370. while (sourceContents.length < sourceIndex)
  371. sourceContents.push(undefined);
  372. sourceContents[sourceIndex] = sourceContent;
  373. onSource(sourceIndex, source, sourceContent);
  374. },
  375. (nameIndex, name) => {
  376. let globalIndex = nameMapping.get(name);
  377. if (globalIndex === undefined) {
  378. globalIndex = nameMapping.size;
  379. nameMapping.set(name, globalIndex);
  380. onName(globalIndex, name);
  381. }
  382. nameIndexMapping[nameIndex] = globalIndex;
  383. }
  384. );
  385. // Handle remaining replacements
  386. let remainer = "";
  387. for (; i < repls.length; i++) {
  388. remainer += repls[i].content;
  389. }
  390. // Insert remaining replacements content splitted into chunks by lines
  391. let line = generatedLine + generatedLineOffset;
  392. const regexp = /[^\n]+\n?|\n/g;
  393. let match = regexp.exec(remainer);
  394. while (match !== null) {
  395. const contentLine = match[0];
  396. onChunk(
  397. contentLine,
  398. line,
  399. generatedColumn +
  400. (line === generatedColumnOffsetLine ? generatedColumnOffset : 0),
  401. -1,
  402. -1,
  403. -1,
  404. -1
  405. );
  406. match = regexp.exec(remainer);
  407. if (match === null && !contentLine.endsWith("\n")) {
  408. if (generatedColumnOffsetLine === line) {
  409. generatedColumnOffset += contentLine.length;
  410. } else {
  411. generatedColumnOffset = contentLine.length;
  412. generatedColumnOffsetLine = line;
  413. }
  414. } else {
  415. generatedLineOffset++;
  416. line++;
  417. generatedColumnOffset = -generatedColumn;
  418. generatedColumnOffsetLine = line;
  419. }
  420. }
  421. return {
  422. generatedLine: line,
  423. generatedColumn:
  424. generatedColumn +
  425. (line === generatedColumnOffsetLine ? generatedColumnOffset : 0)
  426. };
  427. }
  428. updateHash(hash) {
  429. this._sortReplacements();
  430. hash.update("ReplaceSource");
  431. this._source.updateHash(hash);
  432. hash.update(this._name || "");
  433. for (const repl of this._replacements) {
  434. hash.update(`${repl.start}${repl.end}${repl.content}${repl.name}`);
  435. }
  436. }
  437. }
  438. module.exports = ReplaceSource;