diff --git a/packages/angular_devkit/schematics/src/tree/recorder_spec.ts b/packages/angular_devkit/schematics/src/tree/recorder_spec.ts index 465b0c1a8394..1ed091c25f54 100644 --- a/packages/angular_devkit/schematics/src/tree/recorder_spec.ts +++ b/packages/angular_devkit/schematics/src/tree/recorder_spec.ts @@ -7,7 +7,6 @@ */ import { normalize } from '@angular-devkit/core'; -import { UpdateBuffer2, UpdateBufferBase } from '../utility/update-buffer'; import { SimpleFileEntry } from './entry'; import { UpdateRecorderBase, UpdateRecorderBom } from './recorder'; @@ -36,11 +35,6 @@ describe('UpdateRecorderBase', () => { const buffer = Buffer.from('Hello beautiful World'); const entry = new SimpleFileEntry(normalize('/some/path'), buffer); - // TODO: Remove once UpdateBufferBase.create defaults to UpdateBuffer2 - spyOn(UpdateBufferBase, 'create').and.callFake( - (originalContent) => new UpdateBuffer2(originalContent), - ); - const recorder = new UpdateRecorderBase(entry); recorder.remove(6, 9); recorder.insertRight(6, 'amazing'); diff --git a/packages/angular_devkit/schematics/src/utility/environment-options.ts b/packages/angular_devkit/schematics/src/utility/environment-options.ts deleted file mode 100644 index cc6042ca8b72..000000000000 --- a/packages/angular_devkit/schematics/src/utility/environment-options.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @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 - */ - -function isEnabled(variable: string): boolean { - return variable === '1' || variable.toLowerCase() === 'true'; -} - -function isPresent(variable: string | undefined): variable is string { - return typeof variable === 'string' && variable !== ''; -} - -// Use UpdateBuffer2, which uses magic-string internally. -// TODO: Switch this for the next major release to use UpdateBuffer2 by default. -const updateBufferV2 = process.env['NG_UPDATE_BUFFER_V2']; -export const updateBufferV2Enabled = isPresent(updateBufferV2) && isEnabled(updateBufferV2); diff --git a/packages/angular_devkit/schematics/src/utility/linked-list.ts b/packages/angular_devkit/schematics/src/utility/linked-list.ts deleted file mode 100644 index 8e4b20681ee1..000000000000 --- a/packages/angular_devkit/schematics/src/utility/linked-list.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @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 - */ - -export class LinkedList { - constructor(private _head: T) {} - - get(l: number) { - let c: T | null = this._head; - while (c && l > 0) { - l--; - c = c.next; - } - - return c; - } - - get head() { - return this._head; - } - get length() { - let c: T | null = this._head; - let i = 0; - while (c) { - i++; - c = c.next; - } - - return i; - } - - reduce(accumulator: (acc: R, value: T, index?: number) => R, seed: R) { - let c: T | null = this._head; - let acc = seed; - let i = 0; - while (c) { - acc = accumulator(acc, c, i); - i++; - c = c.next; - } - - return acc; - } - - find(predicate: (value: T, index?: number) => boolean) { - let c: T | null = this._head; - let i = 0; - while (c) { - if (predicate(c, i)) { - break; - } - i++; - c = c.next; - } - - return c; - } - - forEach(visitor: (value: T, index?: number) => void) { - let c: T | null = this._head; - let i = 0; - while (c) { - visitor(c, i); - i++; - c = c.next; - } - } -} diff --git a/packages/angular_devkit/schematics/src/utility/update-buffer.ts b/packages/angular_devkit/schematics/src/utility/update-buffer.ts index 6062e83d2e27..d520e4dd4805 100644 --- a/packages/angular_devkit/schematics/src/utility/update-buffer.ts +++ b/packages/angular_devkit/schematics/src/utility/update-buffer.ts @@ -8,177 +8,12 @@ import { BaseException } from '@angular-devkit/core'; import MagicString from 'magic-string'; -import { updateBufferV2Enabled } from './environment-options'; -import { LinkedList } from './linked-list'; export class IndexOutOfBoundException extends BaseException { constructor(index: number, min: number, max = Infinity) { super(`Index ${index} outside of range [${min}, ${max}].`); } } -/** @deprecated Since v13.0 */ -export class ContentCannotBeRemovedException extends BaseException { - constructor() { - super(`User tried to remove content that was marked essential.`); - } -} - -/** - * 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 - */ -export class Chunk { - private _content: Buffer | null; - private _left: Buffer | null = Buffer.alloc(0); - private _right: Buffer | null = Buffer.alloc(0); - - private _assertLeft = false; - private _assertRight = false; - - next: Chunk | null = null; - - constructor(public start: number, public end: number, public originalContent: Buffer) { - 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: BufferEncoding = 'utf-8') { - return ( - (this._left ? this._left.toString(encoding) : '') + - (this._content ? this._content.toString(encoding) : '') + - (this._right ? this._right.toString(encoding) : '') - ); - } - - slice(start: number) { - 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: Buffer, essential: boolean) { - 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: Buffer, essential: boolean) { - 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: boolean, _content: boolean, right: boolean) { - if (left && this._assertLeft) { - throw new ContentCannotBeRemovedException(); - } - - if (right && this._assertRight) { - throw new ContentCannotBeRemovedException(); - } - } - - remove(left: boolean, content: boolean, right: boolean) { - 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: Buffer, start: number) { - 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; - } -} /** * Base class for an update buffer implementation that allows buffers to be inserted to the _right @@ -195,139 +30,21 @@ export abstract class UpdateBufferBase { abstract remove(index: number, length: number): void; /** - * 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 + * Creates an UpdateBufferBase instance. * * @param originalContent The original content of the update buffer instance. * @returns An UpdateBufferBase instance. */ static create(originalContent: Buffer): UpdateBufferBase { - return updateBufferV2Enabled - ? new UpdateBuffer2(originalContent) - : new UpdateBuffer(originalContent); + return new UpdateBuffer(originalContent); } } /** * 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 */ export class UpdateBuffer extends UpdateBufferBase { - protected _linkedList: LinkedList; - - constructor(originalContent: Buffer) { - super(originalContent); - this._linkedList = new LinkedList(new Chunk(0, originalContent.length, originalContent)); - } - - protected _assertIndex(index: number) { - if (index < 0 || index > this._originalContent.length) { - throw new IndexOutOfBoundException(index, 0, this._originalContent.length); - } - } - - protected _slice(start: number): [Chunk, Chunk] { - let index: number; - - 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. - */ - protected _getTextPosition(index: number): number { - return Buffer.from(this._originalContent.toString().substring(0, index)).length; - } - - get length(): number { - return this._linkedList.reduce((acc, chunk) => acc + chunk.length, 0); - } - get original(): Buffer { - return this._originalContent; - } - - toString(encoding: BufferEncoding = 'utf-8'): string { - return this._linkedList.reduce((acc, chunk) => acc + chunk.toString(encoding), ''); - } - generate(): Buffer { - const result = Buffer.allocUnsafe(this.length); - let i = 0; - this._linkedList.forEach((chunk) => { - chunk.copy(result, i); - i += chunk.length; - }); - - return result; - } - - insertLeft(index: number, content: Buffer, assert = false) { - this._slice(index)[0].append(content, assert); - } - insertRight(index: number, content: Buffer, assert = false) { - this._slice(index)[1].prepend(content, assert); - } - - remove(index: number, length: number) { - if (length === 0) { - return; - } - - const end = index + length; - const first = this._slice(index)[1]; - const last = this._slice(end)[1]; - - let curr: Chunk | null; - 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); - } - } -} - -/** - * An utility class that allows buffers to be inserted to the _right or _left, or deleted, while - * keeping indices to the original buffer. - */ -export class UpdateBuffer2 extends UpdateBufferBase { protected _mutatableContent: MagicString = new MagicString(this._originalContent.toString()); protected _assertIndex(index: number) { diff --git a/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts b/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts index c0b120bc363c..a39de1fca71f 100644 --- a/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts +++ b/packages/angular_devkit/schematics/src/utility/update-buffer_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { UpdateBuffer, UpdateBuffer2 } from './update-buffer'; +import { UpdateBuffer } from './update-buffer'; describe('UpdateBuffer', () => { describe('inserts', () => { @@ -71,222 +71,11 @@ describe('UpdateBuffer', () => { }); }); - describe('delete', () => { - it('works for non-overlapping ranges', () => { - // 111111111122222222223333333333444444 - // 0123456789012345678901234567890123456789012345 - const mb = new UpdateBuffer(Buffer.from('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rldd ')); - - mb.remove(43, 2); - expect(mb.toString()).toBe('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rld'); - mb.remove(33, 2); - expect(mb.toString()).toBe('1 4 Ha eb llo2 Beautiful Great Wo3 5 rld'); - mb.remove(8, 2); - expect(mb.toString()).toBe('1 4 Ha ello2 Beautiful Great Wo3 5 rld'); - mb.remove(5, 2); - expect(mb.toString()).toBe('1 4 Hello2 Beautiful Great Wo3 5 rld'); - mb.remove(38, 2); - expect(mb.toString()).toBe('1 4 Hello2 Beautiful Great Wo3 rld'); - mb.remove(2, 2); - expect(mb.toString()).toBe('1 Hello2 Beautiful Great Wo3 rld'); - mb.remove(36, 2); - expect(mb.toString()).toBe('1 Hello2 Beautiful Great World'); - mb.remove(13, 2); - expect(mb.toString()).toBe('1 Hello Beautiful Great World'); - mb.remove(0, 2); - expect(mb.toString()).toBe('Hello Beautiful Great World'); - mb.remove(26, 6); - expect(mb.toString()).toBe('Hello Beautiful World'); - mb.remove(16, 10); - expect(mb.toString()).toBe('Hello World'); - }); - - it('handles overlapping ranges', () => { - // 0123456789012 - const mb = new UpdateBuffer(Buffer.from('ABCDEFGHIJKLM')); - - // Overlapping. - mb.remove(2, 5); - expect(mb.toString()).toBe('ABHIJKLM'); - mb.remove(3, 2); - expect(mb.toString()).toBe('ABHIJKLM'); - mb.remove(3, 6); - expect(mb.toString()).toBe('ABJKLM'); - mb.remove(3, 6); - expect(mb.toString()).toBe('ABJKLM'); - mb.remove(10, 1); - expect(mb.toString()).toBe('ABJLM'); - mb.remove(1, 11); - expect(mb.toString()).toBe('AM'); - }); - }); - - describe('inserts and deletes', () => { - it('works for non-overlapping indices', () => { - // 1 - // 01234567890 - const mb = new UpdateBuffer(Buffer.from('01234567890')); - - mb.insertRight(6, Buffer.from('A')); - expect(mb.toString()).toBe('012345A67890'); - mb.insertRight(2, Buffer.from('B')); - expect(mb.toString()).toBe('01B2345A67890'); - - mb.remove(3, 4); - expect(mb.toString()).toBe('01B27890'); - mb.insertRight(4, Buffer.from('C')); - expect(mb.toString()).toBe('01B27890'); - - mb.remove(2, 6); - expect(mb.toString()).toBe('01B890'); - }); - - it('works for _left/_right inserts', () => { - // 0123456789 - const mb = new UpdateBuffer(Buffer.from('0123456789')); - - mb.insertLeft(5, Buffer.from('A')); - expect(mb.toString()).toBe('01234A56789'); - mb.insertRight(5, Buffer.from('B')); - expect(mb.toString()).toBe('01234AB56789'); - mb.insertRight(10, Buffer.from('C')); - expect(mb.toString()).toBe('01234AB56789C'); - mb.remove(5, 5); - expect(mb.toString()).toBe('01234AB'); - mb.remove(0, 5); - expect(mb.toString()).toBe(''); - }); - - it('supports essential', () => { - const mb = new UpdateBuffer(Buffer.from('0123456789')); - - mb.insertLeft(5, Buffer.from('A'), true); - expect(mb.toString()).toBe('01234A56789'); - mb.remove(5, 5); - expect(mb.toString()).toBe('01234A'); - expect(() => mb.remove(0, 5)).toThrow(); - expect(mb.toString()).toBe('01234A'); - - expect(() => mb.insertRight(6, Buffer.from('B'), true)).toThrow(); - expect(mb.toString()).toBe('01234A'); - }); - - it('works for content at start/end of buffer', () => { - const buffer = new UpdateBuffer(Buffer.from('012345')); - buffer.insertLeft(0, Buffer.from('ABC')); - buffer.insertRight(6, Buffer.from('DEF')); - buffer.remove(0, 6); - expect(buffer.toString()).toBe('ABC'); - }); - - it('is able to insert after a zero-length removal', () => { - const mb = new UpdateBuffer(Buffer.from('123')); - - mb.remove(0, 0); - expect(mb.toString()).toBe('123'); - - mb.insertRight(0, Buffer.from('0')); - expect(mb.toString()).toBe('0123'); - }); - - it('is able to insert after a negative-length removal', () => { - const mb = new UpdateBuffer(Buffer.from('123')); - - mb.remove(0, -1); - expect(mb.toString()).toBe('3'); - - mb.insertRight(0, Buffer.from('0')); - expect(mb.toString()).toBe('03'); - }); - }); - - describe('generate', () => { - it('works', () => { - // 0123456789 - const mb = new UpdateBuffer(Buffer.from('0123456789')); - - mb.insertLeft(5, Buffer.from('A')); - expect(mb.toString()).toBe('01234A56789'); - mb.remove(5, 5); - expect(mb.toString()).toBe('01234A'); - mb.remove(0, 5); - expect(mb.toString()).toBe(''); - - const buffer = mb.generate(); - expect(buffer.toString()).toBe(''); - expect(buffer.length).toBe(0); - }); - }); -}); - -describe('UpdateBuffer2', () => { - describe('inserts', () => { - it('works', () => { - const mb = new UpdateBuffer2(Buffer.from('Hello World')); - - mb.insertRight(6, Buffer.from('Beautiful ')); - expect(mb.toString()).toBe('Hello Beautiful World'); - - mb.insertRight(6, Buffer.from('Great ')); - expect(mb.toString()).toBe('Hello Beautiful Great World'); - - mb.insertRight(0, Buffer.from('1 ')); - expect(mb.toString()).toBe('1 Hello Beautiful Great World'); - - mb.insertRight(5, Buffer.from('2 ')); - expect(mb.toString()).toBe('1 Hello2 Beautiful Great World'); - - mb.insertRight(8, Buffer.from('3 ')); - expect(mb.toString()).toBe('1 Hello2 Beautiful Great Wo3 rld'); - - mb.insertRight(0, Buffer.from('4 ')); - expect(mb.toString()).toBe('1 4 Hello2 Beautiful Great Wo3 rld'); - - mb.insertRight(8, Buffer.from('5 ')); - expect(mb.toString()).toBe('1 4 Hello2 Beautiful Great Wo3 5 rld'); - - mb.insertRight(1, Buffer.from('a ')); - expect(mb.toString()).toBe('1 4 Ha ello2 Beautiful Great Wo3 5 rld'); - - mb.insertRight(2, Buffer.from('b ')); - expect(mb.toString()).toBe('1 4 Ha eb llo2 Beautiful Great Wo3 5 rld'); - - mb.insertRight(7, Buffer.from('c ')); - expect(mb.toString()).toBe('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rld'); - - mb.insertRight(11, Buffer.from('d ')); - expect(mb.toString()).toBe('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rldd '); - }); - - it('works _left and _right', () => { - const mb = new UpdateBuffer2(Buffer.from('Hello World')); - - mb.insertRight(6, Buffer.from('Beautiful ')); - expect(mb.toString()).toBe('Hello Beautiful World'); - - mb.insertLeft(6, Buffer.from('Great ')); - expect(mb.toString()).toBe('Hello Great Beautiful World'); - - mb.insertLeft(6, Buffer.from('Awesome ')); - expect(mb.toString()).toBe('Hello Great Awesome Beautiful World'); - }); - - it('works with special characters', () => { - const mb = new UpdateBuffer2(Buffer.from('Ülaut')); - - mb.insertLeft(1, Buffer.from('m')); - expect(mb.toString()).toBe('Ümlaut'); - - mb.insertLeft(0, Buffer.from('Hello ')); - expect(mb.toString()).toBe('Hello Ümlaut'); - }); - }); - describe('delete', () => { it('works for non-overlapping ranges', () => { // 111111111122222222223333333333444444 // 0123456789012345678901234567890123456789012345 - const mb = new UpdateBuffer2(Buffer.from('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rldd ')); + const mb = new UpdateBuffer(Buffer.from('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rldd ')); mb.remove(43, 2); expect(mb.toString()).toBe('1 4 Ha eb llo2 Beautiful Great Wc o3 5 rld'); @@ -314,7 +103,7 @@ describe('UpdateBuffer2', () => { it('handles overlapping ranges', () => { // 0123456789012 - const mb = new UpdateBuffer2(Buffer.from('ABCDEFGHIJKLM')); + const mb = new UpdateBuffer(Buffer.from('ABCDEFGHIJKLM')); // Overlapping. mb.remove(2, 5); @@ -336,7 +125,7 @@ describe('UpdateBuffer2', () => { it('works for non-overlapping indices', () => { // 1 // 01234567890 - const mb = new UpdateBuffer2(Buffer.from('01234567890')); + const mb = new UpdateBuffer(Buffer.from('01234567890')); mb.insertRight(6, Buffer.from('A')); expect(mb.toString()).toBe('012345A67890'); @@ -354,7 +143,7 @@ describe('UpdateBuffer2', () => { it('works for _left/_right inserts', () => { // 0123456789 - const mb = new UpdateBuffer2(Buffer.from('0123456789')); + const mb = new UpdateBuffer(Buffer.from('0123456789')); mb.insertLeft(5, Buffer.from('A')); expect(mb.toString()).toBe('01234A56789'); @@ -369,7 +158,7 @@ describe('UpdateBuffer2', () => { }); it('works for content at start/end of buffer', () => { - const buffer = new UpdateBuffer2(Buffer.from('012345')); + const buffer = new UpdateBuffer(Buffer.from('012345')); buffer.insertLeft(0, Buffer.from('ABC')); buffer.insertRight(6, Buffer.from('DEF')); buffer.remove(0, 6); @@ -377,7 +166,7 @@ describe('UpdateBuffer2', () => { }); it('is able to insert after a zero-length removal', () => { - const mb = new UpdateBuffer2(Buffer.from('123')); + const mb = new UpdateBuffer(Buffer.from('123')); mb.remove(0, 0); expect(mb.toString()).toBe('123'); @@ -387,7 +176,7 @@ describe('UpdateBuffer2', () => { }); it('is able to insert after a negative-length removal', () => { - const mb = new UpdateBuffer2(Buffer.from('123')); + const mb = new UpdateBuffer(Buffer.from('123')); mb.remove(0, -1); expect(mb.toString()).toBe('3'); @@ -400,7 +189,7 @@ describe('UpdateBuffer2', () => { describe('generate', () => { it('works', () => { // 0123456789 - const mb = new UpdateBuffer2(Buffer.from('0123456789')); + const mb = new UpdateBuffer(Buffer.from('0123456789')); mb.insertLeft(5, Buffer.from('A')); expect(mb.toString()).toBe('01234A56789');