"use strict"; /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UpdateBuffer2 = exports.UpdateBuffer = exports.UpdateBufferBase = exports.Chunk = exports.ContentCannotBeRemovedException = exports.IndexOutOfBoundException = void 0; const core_1 = require("@angular-devkit/core"); const magic_string_1 = __importDefault(require("magic-string")); const environment_options_1 = require("./environment-options"); const linked_list_1 = require("./linked-list"); class IndexOutOfBoundException extends core_1.BaseException { constructor(index, min, max = Infinity) { super(`Index ${index} outside of range [${min}, ${max}].`); } } exports.IndexOutOfBoundException = IndexOutOfBoundException; /** @deprecated Since v13.0 */ class ContentCannotBeRemovedException extends core_1.BaseException { constructor() { super(`User tried to remove content that was marked essential.`); } } exports.ContentCannotBeRemovedException = ContentCannotBeRemovedException; /** * A Chunk description, including left/right content that has been inserted. * If _left/_right is null, this means that content was deleted. If the _content is null, * it means the content itself was deleted. * * @see UpdateBuffer * @deprecated Since v13.0 */ class Chunk { constructor(start, end, originalContent) { this.start = start; this.end = end; this.originalContent = originalContent; this._left = Buffer.alloc(0); this._right = Buffer.alloc(0); this._assertLeft = false; this._assertRight = false; this.next = null; this._content = originalContent.slice(start, end); } get length() { return ((this._left ? this._left.length : 0) + (this._content ? this._content.length : 0) + (this._right ? this._right.length : 0)); } toString(encoding = 'utf-8') { return ((this._left ? this._left.toString(encoding) : '') + (this._content ? this._content.toString(encoding) : '') + (this._right ? this._right.toString(encoding) : '')); } slice(start) { if (start < this.start || start > this.end) { throw new IndexOutOfBoundException(start, this.start, this.end); } // Update _content to the new indices. const newChunk = new Chunk(start, this.end, this.originalContent); // If this chunk has _content, reslice the original _content. We move the _right so we are not // losing any data here. If this chunk has been deleted, the next chunk should also be deleted. if (this._content) { this._content = this.originalContent.slice(this.start, start); } else { newChunk._content = this._content; if (this._right === null) { newChunk._left = null; } } this.end = start; // Move _right to the new chunk. newChunk._right = this._right; this._right = this._right && Buffer.alloc(0); // Update essentials. if (this._assertRight) { newChunk._assertRight = true; this._assertRight = false; } // Update the linked list. newChunk.next = this.next; this.next = newChunk; return newChunk; } append(buffer, essential) { if (!this._right) { if (essential) { throw new ContentCannotBeRemovedException(); } return; } const outro = this._right; this._right = Buffer.alloc(outro.length + buffer.length); outro.copy(this._right, 0); buffer.copy(this._right, outro.length); if (essential) { this._assertRight = true; } } prepend(buffer, essential) { if (!this._left) { if (essential) { throw new ContentCannotBeRemovedException(); } return; } const intro = this._left; this._left = Buffer.alloc(intro.length + buffer.length); intro.copy(this._left, 0); buffer.copy(this._left, intro.length); if (essential) { this._assertLeft = true; } } assert(left, _content, right) { if (left && this._assertLeft) { throw new ContentCannotBeRemovedException(); } if (right && this._assertRight) { throw new ContentCannotBeRemovedException(); } } remove(left, content, right) { if (left) { if (this._assertLeft) { throw new ContentCannotBeRemovedException(); } this._left = null; } if (content) { this._content = null; } if (right) { if (this._assertRight) { throw new ContentCannotBeRemovedException(); } this._right = null; } } copy(target, start) { if (this._left) { this._left.copy(target, start); start += this._left.length; } if (this._content) { this._content.copy(target, start); start += this._content.length; } if (this._right) { this._right.copy(target, start); start += this._right.length; } return start; } } exports.Chunk = Chunk; /** * Base class for an update buffer implementation that allows buffers to be inserted to the _right * or _left, or deleted, while keeping indices to the original buffer. */ class UpdateBufferBase { constructor(_originalContent) { this._originalContent = _originalContent; } /** * Creates an UpdateBufferBase instance. Depending on the NG_UPDATE_BUFFER_V2 * environment variable, will either create an UpdateBuffer or an UpdateBuffer2 * instance. * * See: https://github.com/angular/angular-cli/issues/21110 * * @param originalContent The original content of the update buffer instance. * @returns An UpdateBufferBase instance. */ static create(originalContent) { return environment_options_1.updateBufferV2Enabled ? new UpdateBuffer2(originalContent) : new UpdateBuffer(originalContent); } } exports.UpdateBufferBase = UpdateBufferBase; /** * An utility class that allows buffers to be inserted to the _right or _left, or deleted, while * keeping indices to the original buffer. * * The constructor takes an original buffer, and keeps it into a linked list of chunks, smaller * buffers that keep track of _content inserted to the _right or _left of it. * * Since the Node Buffer structure is non-destructive when slicing, we try to use slicing to create * new chunks, and always keep chunks pointing to the original content. * * @deprecated Since v13.0 */ class UpdateBuffer extends UpdateBufferBase { constructor(originalContent) { super(originalContent); this._linkedList = new linked_list_1.LinkedList(new Chunk(0, originalContent.length, originalContent)); } _assertIndex(index) { if (index < 0 || index > this._originalContent.length) { throw new IndexOutOfBoundException(index, 0, this._originalContent.length); } } _slice(start) { let index; if (start >= this._originalContent.length) { index = start; } else if (start < 0) { index = this._originalContent.length + start; } else { index = this._getTextPosition(start); } this._assertIndex(index); // Find the chunk by going through the list. const h = this._linkedList.find((chunk) => index <= chunk.end); if (!h) { throw Error('Chunk cannot be found.'); } if (index == h.end && h.next !== null) { return [h, h.next]; } return [h, h.slice(index)]; } /** * Gets the position in the content based on the position in the string. * Some characters might be wider than one byte, thus we have to determine the position using * string functions. */ _getTextPosition(index) { return Buffer.from(this._originalContent.toString().substring(0, index)).length; } get length() { return this._linkedList.reduce((acc, chunk) => acc + chunk.length, 0); } get original() { return this._originalContent; } toString(encoding = 'utf-8') { return this._linkedList.reduce((acc, chunk) => acc + chunk.toString(encoding), ''); } generate() { const result = Buffer.allocUnsafe(this.length); let i = 0; this._linkedList.forEach((chunk) => { chunk.copy(result, i); i += chunk.length; }); return result; } insertLeft(index, content, assert = false) { this._slice(index)[0].append(content, assert); } insertRight(index, content, assert = false) { this._slice(index)[1].prepend(content, assert); } remove(index, length) { if (length === 0) { return; } const end = index + length; const first = this._slice(index)[1]; const last = this._slice(end)[1]; let curr; for (curr = first; curr && curr !== last; curr = curr.next) { curr.assert(curr !== first, curr !== last, curr === first); } for (curr = first; curr && curr !== last; curr = curr.next) { curr.remove(curr !== first, curr !== last, curr === first); } if (curr) { curr.remove(true, false, false); } } } exports.UpdateBuffer = UpdateBuffer; /** * An utility class that allows buffers to be inserted to the _right or _left, or deleted, while * keeping indices to the original buffer. */ class UpdateBuffer2 extends UpdateBufferBase { constructor() { super(...arguments); this._mutatableContent = new magic_string_1.default(this._originalContent.toString()); } _assertIndex(index) { if (index < 0 || index > this._originalContent.length) { throw new IndexOutOfBoundException(index, 0, this._originalContent.length); } } get length() { return this._mutatableContent.length(); } get original() { return this._originalContent; } toString() { return this._mutatableContent.toString(); } generate() { return Buffer.from(this.toString()); } insertLeft(index, content) { this._assertIndex(index); this._mutatableContent.appendLeft(index, content.toString()); } insertRight(index, content) { this._assertIndex(index); this._mutatableContent.appendRight(index, content.toString()); } remove(index, length) { this._assertIndex(index); this._mutatableContent.remove(index, index + length); } } exports.UpdateBuffer2 = UpdateBuffer2;