diff options
Diffstat (limited to 'node_modules/webpack-sources/lib/ReplaceSource.js')
| -rw-r--r-- | node_modules/webpack-sources/lib/ReplaceSource.js | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/node_modules/webpack-sources/lib/ReplaceSource.js b/node_modules/webpack-sources/lib/ReplaceSource.js new file mode 100644 index 00000000..1a678d17 --- /dev/null +++ b/node_modules/webpack-sources/lib/ReplaceSource.js @@ -0,0 +1,467 @@ +/* + 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 { content, name } = repls[i]; + let matches = splitIntoLines(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; + } + for (let m = 0; m < matches.length; m++) { + const contentLine = matches[m]; + onChunk( + contentLine, + line, + generatedColumn + + (line === generatedColumnOffsetLine + ? generatedColumnOffset + : 0), + sourceIndex, + originalLine, + originalColumn, + replacementNameIndex + ); + + // Only the first chunk has name assigned + replacementNameIndex = -1; + + if (m === matches.length - 1 && !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; + let matches = splitIntoLines(remainer); + for (let m = 0; m < matches.length; m++) { + const contentLine = matches[m]; + onChunk( + contentLine, + line, + generatedColumn + + (line === generatedColumnOffsetLine ? generatedColumnOffset : 0), + -1, + -1, + -1, + -1 + ); + + if (m === matches.length - 1 && !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; |
