/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
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const { getMap, getSourceAndMap } = require("./helpers/getFromStreamChunks");
- const streamChunks = require("./helpers/streamChunks");
- const Source = require("./Source");
- const splitIntoLines = require("./helpers/splitIntoLines");
- // since v8 7.0, Array.prototype.sort is stable
- const hasStableSort =
- typeof process === "object" &&
- process.versions &&
- typeof process.versions.v8 === "string" &&
- !/^[0-6]\./.test(process.versions.v8);
- // This is larger than max string length
- const MAX_SOURCE_POSITION = 0x20000000;
- class Replacement {
- constructor(start, end, content, name) {
- this.start = start;
- this.end = end;
- this.content = content;
- this.name = name;
- if (!hasStableSort) {
- this.index = -1;
- }
- }
- }
- class ReplaceSource extends Source {
- constructor(source, name) {
- super();
- this._source = source;
- this._name = name;
- /** @type {Replacement[]} */
- this._replacements = [];
- this._isSorted = true;
- }
- getName() {
- return this._name;
- }
- getReplacements() {
- this._sortReplacements();
- return this._replacements;
- }
- replace(start, end, newValue, name) {
- if (typeof newValue !== "string")
- throw new Error(
- "insertion must be a string, but is a " + typeof newValue
- );
- this._replacements.push(new Replacement(start, end, newValue, name));
- this._isSorted = false;
- }
- insert(pos, newValue, name) {
- if (typeof newValue !== "string")
- throw new Error(
- "insertion must be a string, but is a " +
- typeof newValue +
- ": " +
- newValue
- );
- this._replacements.push(new Replacement(pos, pos - 1, newValue, name));
- this._isSorted = false;
- }
- source() {
- if (this._replacements.length === 0) {
- return this._source.source();
- }
- let current = this._source.source();
- let pos = 0;
- const result = [];
- this._sortReplacements();
- for (const replacement of this._replacements) {
- const start = Math.floor(replacement.start);
- const end = Math.floor(replacement.end + 1);
- if (pos < start) {
- const offset = start - pos;
- result.push(current.slice(0, offset));
- current = current.slice(offset);
- pos = start;
- }
- result.push(replacement.content);
- if (pos < end) {
- const offset = end - pos;
- current = current.slice(offset);
- pos = end;
- }
- }
- result.push(current);
- return result.join("");
- }
- map(options) {
- if (this._replacements.length === 0) {
- return this._source.map(options);
- }
- return getMap(this, options);
- }
- sourceAndMap(options) {
- if (this._replacements.length === 0) {
- return this._source.sourceAndMap(options);
- }
- return getSourceAndMap(this, options);
- }
- original() {
- return this._source;
- }
- _sortReplacements() {
- if (this._isSorted) return;
- if (hasStableSort) {
- this._replacements.sort(function (a, b) {
- const diff1 = a.start - b.start;
- if (diff1 !== 0) return diff1;
- const diff2 = a.end - b.end;
- if (diff2 !== 0) return diff2;
- return 0;
- });
- } else {
- this._replacements.forEach((repl, i) => (repl.index = i));
- this._replacements.sort(function (a, b) {
- const diff1 = a.start - b.start;
- if (diff1 !== 0) return diff1;
- const diff2 = a.end - b.end;
- if (diff2 !== 0) return diff2;
- return a.index - b.index;
- });
- }
- this._isSorted = true;
- }
- streamChunks(options, onChunk, onSource, onName) {
- this._sortReplacements();
- const repls = this._replacements;
- let pos = 0;
- let i = 0;
- let replacmentEnd = -1;
- let nextReplacement =
- i < repls.length ? Math.floor(repls[i].start) : MAX_SOURCE_POSITION;
- let generatedLineOffset = 0;
- let generatedColumnOffset = 0;
- let generatedColumnOffsetLine = 0;
- const sourceContents = [];
- const nameMapping = new Map();
- const nameIndexMapping = [];
- const checkOriginalContent = (sourceIndex, line, column, expectedChunk) => {
- let content =
- sourceIndex < sourceContents.length
- ? sourceContents[sourceIndex]
- : undefined;
- if (content === undefined) return false;
- if (typeof content === "string") {
- content = splitIntoLines(content);
- sourceContents[sourceIndex] = content;
- }
- const contentLine = line <= content.length ? content[line - 1] : null;
- if (contentLine === null) return false;
- return (
- contentLine.slice(column, column + expectedChunk.length) ===
- expectedChunk
- );
- };
- let { generatedLine, generatedColumn } = streamChunks(
- this._source,
- Object.assign({}, options, { finalSource: false }),
- (
- chunk,
- generatedLine,
- generatedColumn,
- sourceIndex,
- originalLine,
- originalColumn,
- nameIndex
- ) => {
- let chunkPos = 0;
- let endPos = pos + chunk.length;
- // Skip over when it has been replaced
- if (replacmentEnd > pos) {
- // Skip over the whole chunk
- if (replacmentEnd >= endPos) {
- const line = generatedLine + generatedLineOffset;
- if (chunk.endsWith("\n")) {
- generatedLineOffset--;
- if (generatedColumnOffsetLine === line) {
- // undo exiting corrections form the current line
- generatedColumnOffset += generatedColumn;
- }
- } else if (generatedColumnOffsetLine === line) {
- generatedColumnOffset -= chunk.length;
- } else {
- generatedColumnOffset = -chunk.length;
- generatedColumnOffsetLine = line;
- }
- pos = endPos;
- return;
- }
- // Partially skip over chunk
- chunkPos = replacmentEnd - pos;
- if (
- checkOriginalContent(
- sourceIndex,
- originalLine,
- originalColumn,
- chunk.slice(0, chunkPos)
- )
- ) {
- originalColumn += chunkPos;
- }
- pos += chunkPos;
- const line = generatedLine + generatedLineOffset;
- if (generatedColumnOffsetLine === line) {
- generatedColumnOffset -= chunkPos;
- } else {
- generatedColumnOffset = -chunkPos;
- generatedColumnOffsetLine = line;
- }
- generatedColumn += chunkPos;
- }
- // Is a replacement in the chunk?
- if (nextReplacement < endPos) {
- do {
- let line = generatedLine + generatedLineOffset;
- if (nextReplacement > pos) {
- // Emit chunk until replacement
- const offset = nextReplacement - pos;
- const chunkSlice = chunk.slice(chunkPos, chunkPos + offset);
- onChunk(
- chunkSlice,
- line,
- generatedColumn +
- (line === generatedColumnOffsetLine
- ? generatedColumnOffset
- : 0),
- sourceIndex,
- originalLine,
- originalColumn,
- nameIndex < 0 || nameIndex >= nameIndexMapping.length
- ? -1
- : nameIndexMapping[nameIndex]
- );
- generatedColumn += offset;
- chunkPos += offset;
- pos = nextReplacement;
- if (
- checkOriginalContent(
- sourceIndex,
- originalLine,
- originalColumn,
- chunkSlice
- )
- ) {
- originalColumn += chunkSlice.length;
- }
- }
- // Insert replacement content splitted into chunks by lines
- const regexp = /[^\n]+\n?|\n/g;
- const { content, name } = repls[i];
- let match = regexp.exec(content);
- let replacementNameIndex = nameIndex;
- if (sourceIndex >= 0 && name) {
- let globalIndex = nameMapping.get(name);
- if (globalIndex === undefined) {
- globalIndex = nameMapping.size;
- nameMapping.set(name, globalIndex);
- onName(globalIndex, name);
- }
- replacementNameIndex = globalIndex;
- }
- while (match !== null) {
- const contentLine = match[0];
- onChunk(
- contentLine,
- line,
- generatedColumn +
- (line === generatedColumnOffsetLine
- ? generatedColumnOffset
- : 0),
- sourceIndex,
- originalLine,
- originalColumn,
- replacementNameIndex
- );
- // Only the first chunk has name assigned
- replacementNameIndex = -1;
- match = regexp.exec(content);
- if (match === null && !contentLine.endsWith("\n")) {
- if (generatedColumnOffsetLine === line) {
- generatedColumnOffset += contentLine.length;
- } else {
- generatedColumnOffset = contentLine.length;
- generatedColumnOffsetLine = line;
- }
- } else {
- generatedLineOffset++;
- line++;
- generatedColumnOffset = -generatedColumn;
- generatedColumnOffsetLine = line;
- }
- }
- // Remove replaced content by settings this variable
- replacmentEnd = Math.max(
- replacmentEnd,
- Math.floor(repls[i].end + 1)
- );
- // Move to next replacment
- i++;
- nextReplacement =
- i < repls.length
- ? Math.floor(repls[i].start)
- : MAX_SOURCE_POSITION;
- // Skip over when it has been replaced
- const offset = chunk.length - endPos + replacmentEnd - chunkPos;
- if (offset > 0) {
- // Skip over whole chunk
- if (replacmentEnd >= endPos) {
- let line = generatedLine + generatedLineOffset;
- if (chunk.endsWith("\n")) {
- generatedLineOffset--;
- if (generatedColumnOffsetLine === line) {
- // undo exiting corrections form the current line
- generatedColumnOffset += generatedColumn;
- }
- } else if (generatedColumnOffsetLine === line) {
- generatedColumnOffset -= chunk.length - chunkPos;
- } else {
- generatedColumnOffset = chunkPos - chunk.length;
- generatedColumnOffsetLine = line;
- }
- pos = endPos;
- return;
- }
- // Partially skip over chunk
- const line = generatedLine + generatedLineOffset;
- if (
- checkOriginalContent(
- sourceIndex,
- originalLine,
- originalColumn,
- chunk.slice(chunkPos, chunkPos + offset)
- )
- ) {
- originalColumn += offset;
- }
- chunkPos += offset;
- pos += offset;
- if (generatedColumnOffsetLine === line) {
- generatedColumnOffset -= offset;
- } else {
- generatedColumnOffset = -offset;
- generatedColumnOffsetLine = line;
- }
- generatedColumn += offset;
- }
- } while (nextReplacement < endPos);
- }
- // Emit remaining chunk
- if (chunkPos < chunk.length) {
- const chunkSlice = chunkPos === 0 ? chunk : chunk.slice(chunkPos);
- const line = generatedLine + generatedLineOffset;
- onChunk(
- chunkSlice,
- line,
- generatedColumn +
- (line === generatedColumnOffsetLine ? generatedColumnOffset : 0),
- sourceIndex,
- originalLine,
- originalColumn,
- nameIndex < 0 ? -1 : nameIndexMapping[nameIndex]
- );
- }
- pos = endPos;
- },
- (sourceIndex, source, sourceContent) => {
- while (sourceContents.length < sourceIndex)
- sourceContents.push(undefined);
- sourceContents[sourceIndex] = sourceContent;
- onSource(sourceIndex, source, sourceContent);
- },
- (nameIndex, name) => {
- let globalIndex = nameMapping.get(name);
- if (globalIndex === undefined) {
- globalIndex = nameMapping.size;
- nameMapping.set(name, globalIndex);
- onName(globalIndex, name);
- }
- nameIndexMapping[nameIndex] = globalIndex;
- }
- );
- // Handle remaining replacements
- let remainer = "";
- for (; i < repls.length; i++) {
- remainer += repls[i].content;
- }
- // Insert remaining replacements content splitted into chunks by lines
- let line = generatedLine + generatedLineOffset;
- const regexp = /[^\n]+\n?|\n/g;
- let match = regexp.exec(remainer);
- while (match !== null) {
- const contentLine = match[0];
- onChunk(
- contentLine,
- line,
- generatedColumn +
- (line === generatedColumnOffsetLine ? generatedColumnOffset : 0),
- -1,
- -1,
- -1,
- -1
- );
- match = regexp.exec(remainer);
- if (match === null && !contentLine.endsWith("\n")) {
- if (generatedColumnOffsetLine === line) {
- generatedColumnOffset += contentLine.length;
- } else {
- generatedColumnOffset = contentLine.length;
- generatedColumnOffsetLine = line;
- }
- } else {
- generatedLineOffset++;
- line++;
- generatedColumnOffset = -generatedColumn;
- generatedColumnOffsetLine = line;
- }
- }
- return {
- generatedLine: line,
- generatedColumn:
- generatedColumn +
- (line === generatedColumnOffsetLine ? generatedColumnOffset : 0)
- };
- }
- updateHash(hash) {
- this._sortReplacements();
- hash.update("ReplaceSource");
- this._source.updateHash(hash);
- hash.update(this._name || "");
- for (const repl of this._replacements) {
- hash.update(`${repl.start}${repl.end}${repl.content}${repl.name}`);
- }
- }
- }
- module.exports = ReplaceSource;