diff --git a/tools/adventure-pack/goodies/typescript/Array.prototype.slidingWindows/index.ts b/tools/adventure-pack/goodies/typescript/Array.prototype.slidingWindows/index.ts index be2d1614..887863e8 100644 --- a/tools/adventure-pack/goodies/typescript/Array.prototype.slidingWindows/index.ts +++ b/tools/adventure-pack/goodies/typescript/Array.prototype.slidingWindows/index.ts @@ -1,105 +1,25 @@ -type ArraySliceProxyHandler = { - get( - target: ArraySlice, - property: string | symbol, - receiver: ArraySlice, - ): unknown; -}; - -class ArraySlice { - private constructor( - public readonly array: ReadonlyArray, - public readonly start: number, - public readonly end: number, - ) {} - - get length(): number { - return this.end - this.start + 1; - } - - at(index: number): T | undefined { - const adjustedIndex = index < 0 ? index + this.length : index; - return this.array[this.start + adjustedIndex]; - } - - *[Symbol.iterator](this: ArraySlice): Generator { - for (let i = this.start; i <= this.end; ++i) { - yield this.array[i]; - } - } - - slide(delta: number = 1): IndexableArraySlice { - return ArraySlice.get(this.array, this.start + delta, this.end + delta); - } - - isPrefix(): boolean { - return this.start === 0; - } - - isSuffix(): boolean { - return this.end === this.array.length - 1; - } - - private declare static proxyHandler?: ArraySliceProxyHandler; - - static get( - array: ReadonlyArray, - start: number, - end: number, - ): IndexableArraySlice { - ArraySlice.proxyHandler ??= { - get( - target: ArraySlice, - property: string | symbol, - receiver: ArraySlice, - ): unknown { - if (typeof property === "string") { - const index = parseInt(property, 10); - if (String(index) === property) { - if (index < 0 || index >= receiver.length) { - return undefined; - } - return receiver.at(index); - } - } - - return (target as unknown as Record)[ - property - ]; - }, - }; - - return new Proxy( - new ArraySlice(array, start, end), - ArraySlice.proxyHandler as ArraySliceProxyHandler, - ) as IndexableArraySlice; - } -} - -type IndexableArraySlice = ArraySlice & { - [index: number]: T | undefined; -}; +import { type IndexableArraySlice, ArraySlice } from "../ArraySlice"; declare global { interface ReadonlyArray { slidingWindows( this: ReadonlyArray, windowSize: number, - ): Generator, void, void>; + ): Generator, void, void>; } interface Array { slidingWindows( this: ReadonlyArray, windowSize: number, - ): Generator, void, void>; + ): Generator, void, void>; } } Array.prototype.slidingWindows = function* ( this: ReadonlyArray, windowSize: number, -): Generator, void, void> { +): Generator, void, void> { for ( let win: IndexableArraySlice | null = ArraySlice.get( this, @@ -112,7 +32,3 @@ Array.prototype.slidingWindows = function* ( yield win; } }; - -// Needed to fix the error "Augmentations for the global scope can only be directly nested in external modules or ambient module declarations. ts(2669)" -// See: https://stackoverflow.com/questions/57132428/augmentations-for-the-global-scope-can-only-be-directly-nested-in-external-modul -export {}; diff --git a/tools/adventure-pack/goodies/typescript/ArraySlice/index.ts b/tools/adventure-pack/goodies/typescript/ArraySlice/index.ts new file mode 100644 index 00000000..56a09a8d --- /dev/null +++ b/tools/adventure-pack/goodies/typescript/ArraySlice/index.ts @@ -0,0 +1,79 @@ +type ArraySliceProxyHandler = { + get( + target: ArraySlice, + property: PropertyKey, + receiver: ArraySlice, + ): unknown; +}; + +export type IndexableArraySlice = ArraySlice & { + [index: number]: T | undefined; +}; + +export class ArraySlice { + private constructor( + public readonly array: ReadonlyArray, + public readonly start: number, + public readonly end: number, + ) {} + + get length(): number { + return this.end - this.start + 1; + } + + at(index: number): T | undefined { + const adjustedIndex = index < 0 ? index + this.length : index; + return this.array[this.start + adjustedIndex]; + } + + *[Symbol.iterator](this: ArraySlice): Generator { + for (let i = this.start; i <= this.end; ++i) { + yield this.array[i]; + } + } + + slide(delta: number = 1): IndexableArraySlice { + return ArraySlice.get(this.array, this.start + delta, this.end + delta); + } + + isPrefix(): boolean { + return this.start === 0; + } + + isSuffix(): boolean { + return this.end === this.array.length - 1; + } + + private declare static proxyHandler?: ArraySliceProxyHandler; + + static get( + array: ReadonlyArray, + start: number, + end: number, + ): IndexableArraySlice { + ArraySlice.proxyHandler ??= { + get( + target: ArraySlice, + property: PropertyKey, + receiver: ArraySlice, + ): unknown { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { + if (index < 0 || index >= receiver.length) { + return undefined; + } + return receiver.at(index); + } + } + + return (target as unknown as Record)[property]; + }, + }; + + return new Proxy( + new ArraySlice(array, start, end), + ArraySlice.proxyHandler as ArraySliceProxyHandler, + ) as IndexableArraySlice; + } +} diff --git a/tools/adventure-pack/src/app/__tests__/__snapshots__/equip-test.ts.snap b/tools/adventure-pack/src/app/__tests__/__snapshots__/equip-test.ts.snap index 02abc76f..05da2527 100644 --- a/tools/adventure-pack/src/app/__tests__/__snapshots__/equip-test.ts.snap +++ b/tools/adventure-pack/src/app/__tests__/__snapshots__/equip-test.ts.snap @@ -140,9 +140,9 @@ class ArraySlice { static get(array, start, end) { ArraySlice.proxyHandler ??= { get(target, property, receiver) { - if (typeof property === "string") { - const index = parseInt(property, 10); - if (String(index) === property) { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { if (index < 0 || index >= receiver.length) { return undefined; } @@ -188,6 +188,72 @@ Array.prototype.swap = function (i, j) { /////////////////////////// END ADVENTURE PACK CODE ////////////////////////////" `; +exports[`App can equip single goody: JavaScript ArraySlice 1`] = ` +"////////////////////////// BEGIN ADVENTURE PACK CODE /////////////////////////// +// Adventure Pack commit fake-commit-hash +// Running at: https://example.com/ + +class ArraySlice { + constructor(array, start, end) { + this.array = array; + this.start = start; + this.end = end; + } + + get length() { + return this.end - this.start + 1; + } + + at(index) { + const adjustedIndex = index < 0 ? index + this.length : index; + return this.array[this.start + adjustedIndex]; + } + + *[Symbol.iterator]() { + for (let i = this.start; i <= this.end; ++i) { + yield this.array[i]; + } + } + + slide(delta = 1) { + return ArraySlice.get(this.array, this.start + delta, this.end + delta); + } + + isPrefix() { + return this.start === 0; + } + + isSuffix() { + return this.end === this.array.length - 1; + } + + static get(array, start, end) { + ArraySlice.proxyHandler ??= { + get(target, property, receiver) { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { + if (index < 0 || index >= receiver.length) { + return undefined; + } + return receiver.at(index); + } + } + + return target[property]; + }, + }; + + return new Proxy( + new ArraySlice(array, start, end), + ArraySlice.proxyHandler, + ); + } +} + +/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////" +`; + exports[`App can equip single goody: JavaScript BinaryHeap 1`] = ` "////////////////////////// BEGIN ADVENTURE PACK CODE /////////////////////////// // Adventure Pack commit fake-commit-hash @@ -911,25 +977,29 @@ declare global { slidingWindows( this: ReadonlyArray, windowSize: number, - ): Generator, void, void>; + ): Generator, void, void>; } interface ReadonlyArray { slidingWindows( this: ReadonlyArray, windowSize: number, - ): Generator, void, void>; + ): Generator, void, void>; } } type ArraySliceProxyHandler = { get( target: ArraySlice, - property: string | symbol, + property: PropertyKey, receiver: ArraySlice, ): unknown; }; +type IndexableArraySlice = ArraySlice & { + [index: number]: T | undefined; +}; + class ArraySlice { private constructor( public readonly array: ReadonlyArray, @@ -974,12 +1044,12 @@ class ArraySlice { ArraySlice.proxyHandler ??= { get( target: ArraySlice, - property: string | symbol, + property: PropertyKey, receiver: ArraySlice, ): unknown { - if (typeof property === "string") { - const index = parseInt(property, 10); - if (String(index) === property) { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { if (index < 0 || index >= receiver.length) { return undefined; } @@ -987,9 +1057,7 @@ class ArraySlice { } } - return (target as unknown as Record)[ - property - ]; + return (target as unknown as Record)[property]; }, }; @@ -1000,14 +1068,10 @@ class ArraySlice { } } -type IndexableArraySlice = ArraySlice & { - [index: number]: T | undefined; -}; - Array.prototype.slidingWindows = function* ( this: ReadonlyArray, windowSize: number, -): Generator, void, void> { +): Generator, void, void> { for ( let win: IndexableArraySlice | null = ArraySlice.get( this, @@ -1044,6 +1108,94 @@ Array.prototype.swap = function (this: T[], i: number, j: number): void { /////////////////////////// END ADVENTURE PACK CODE ////////////////////////////" `; +exports[`App can equip single goody: TypeScript ArraySlice 1`] = ` +"////////////////////////// BEGIN ADVENTURE PACK CODE /////////////////////////// +// Adventure Pack commit fake-commit-hash +// Running at: https://example.com/ + +type ArraySliceProxyHandler = { + get( + target: ArraySlice, + property: PropertyKey, + receiver: ArraySlice, + ): unknown; +}; + +type IndexableArraySlice = ArraySlice & { + [index: number]: T | undefined; +}; + +class ArraySlice { + private constructor( + public readonly array: ReadonlyArray, + public readonly start: number, + public readonly end: number, + ) {} + + get length(): number { + return this.end - this.start + 1; + } + + at(index: number): T | undefined { + const adjustedIndex = index < 0 ? index + this.length : index; + return this.array[this.start + adjustedIndex]; + } + + *[Symbol.iterator](this: ArraySlice): Generator { + for (let i = this.start; i <= this.end; ++i) { + yield this.array[i]; + } + } + + slide(delta: number = 1): IndexableArraySlice { + return ArraySlice.get(this.array, this.start + delta, this.end + delta); + } + + isPrefix(): boolean { + return this.start === 0; + } + + isSuffix(): boolean { + return this.end === this.array.length - 1; + } + + private declare static proxyHandler?: ArraySliceProxyHandler; + + static get( + array: ReadonlyArray, + start: number, + end: number, + ): IndexableArraySlice { + ArraySlice.proxyHandler ??= { + get( + target: ArraySlice, + property: PropertyKey, + receiver: ArraySlice, + ): unknown { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { + if (index < 0 || index >= receiver.length) { + return undefined; + } + return receiver.at(index); + } + } + + return (target as unknown as Record)[property]; + }, + }; + + return new Proxy( + new ArraySlice(array, start, end), + ArraySlice.proxyHandler as ArraySliceProxyHandler, + ) as IndexableArraySlice; + } +} + +/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////" +`; + exports[`App can equip single goody: TypeScript BinaryHeap 1`] = ` "////////////////////////// BEGIN ADVENTURE PACK CODE /////////////////////////// // Adventure Pack commit fake-commit-hash diff --git a/tools/adventure-pack/src/app/__tests__/__snapshots__/render-test.ts.snap b/tools/adventure-pack/src/app/__tests__/__snapshots__/render-test.ts.snap index dca3b7ef..a253b8d1 100644 --- a/tools/adventure-pack/src/app/__tests__/__snapshots__/render-test.ts.snap +++ b/tools/adventure-pack/src/app/__tests__/__snapshots__/render-test.ts.snap @@ -73,7 +73,29 @@ final class AP { `; exports[`App can render goody: JavaScript Array.prototype.slidingWindows 1`] = ` -"class ArraySlice { +"import "ArraySlice"; + +Array.prototype.slidingWindows = function* (windowSize) { + for ( + let win = ArraySlice.get(this, 0, windowSize - 1); + win != null; + win = win.isSuffix() ? null : win.slide() + ) { + yield win; + } +};" +`; + +exports[`App can render goody: JavaScript Array.prototype.swap 1`] = ` +"Array.prototype.swap = function (i, j) { + const tmp = this[i]; + this[i] = this[j]; + this[j] = tmp; +};" +`; + +exports[`App can render goody: JavaScript ArraySlice 1`] = ` +"export class ArraySlice { constructor(array, start, end) { this.array = array; this.start = start; @@ -110,9 +132,9 @@ exports[`App can render goody: JavaScript Array.prototype.slidingWindows 1`] = ` static get(array, start, end) { ArraySlice.proxyHandler ??= { get(target, property, receiver) { - if (typeof property === "string") { - const index = parseInt(property, 10); - if (String(index) === property) { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { if (index < 0 || index >= receiver.length) { return undefined; } @@ -129,25 +151,7 @@ exports[`App can render goody: JavaScript Array.prototype.slidingWindows 1`] = ` ArraySlice.proxyHandler, ); } -} - -Array.prototype.slidingWindows = function* (windowSize) { - for ( - let win = ArraySlice.get(this, 0, windowSize - 1); - win != null; - win = win.isSuffix() ? null : win.slide() - ) { - yield win; - } -};" -`; - -exports[`App can render goody: JavaScript Array.prototype.swap 1`] = ` -"Array.prototype.swap = function (i, j) { - const tmp = this[i]; - this[i] = this[j]; - this[j] = tmp; -};" +}" `; exports[`App can render goody: JavaScript BinaryHeap 1`] = ` @@ -559,31 +563,70 @@ del set_up_adventure_pack" `; exports[`App can render goody: TypeScript Array.prototype.slidingWindows 1`] = ` -"declare global { +"import "ArraySlice"; + +declare global { interface Array { slidingWindows( this: ReadonlyArray, windowSize: number, - ): Generator, void, void>; + ): Generator, void, void>; } interface ReadonlyArray { slidingWindows( this: ReadonlyArray, windowSize: number, - ): Generator, void, void>; + ): Generator, void, void>; + } +} + +Array.prototype.slidingWindows = function* ( + this: ReadonlyArray, + windowSize: number, +): Generator, void, void> { + for ( + let win: IndexableArraySlice | null = ArraySlice.get( + this, + 0, + windowSize - 1, + ); + win != null; + win = win.isSuffix() ? null : win.slide() + ) { + yield win; + } +};" +`; + +exports[`App can render goody: TypeScript Array.prototype.swap 1`] = ` +"declare global { + interface Array { + swap(this: T[], i: number, j: number): void; } } -type ArraySliceProxyHandler = { +Array.prototype.swap = function (this: T[], i: number, j: number): void { + const tmp = this[i]; + this[i] = this[j]; + this[j] = tmp; +};" +`; + +exports[`App can render goody: TypeScript ArraySlice 1`] = ` +"type ArraySliceProxyHandler = { get( target: ArraySlice, - property: string | symbol, + property: PropertyKey, receiver: ArraySlice, ): unknown; }; -class ArraySlice { +export type IndexableArraySlice = ArraySlice & { + [index: number]: T | undefined; +}; + +export class ArraySlice { private constructor( public readonly array: ReadonlyArray, public readonly start: number, @@ -627,12 +670,12 @@ class ArraySlice { ArraySlice.proxyHandler ??= { get( target: ArraySlice, - property: string | symbol, + property: PropertyKey, receiver: ArraySlice, ): unknown { - if (typeof property === "string") { - const index = parseInt(property, 10); - if (String(index) === property) { + if (typeof property === "string" || typeof property === "number") { + const index = parseInt(String(property), 10); + if (String(index) === String(property)) { if (index < 0 || index >= receiver.length) { return undefined; } @@ -640,9 +683,7 @@ class ArraySlice { } } - return (target as unknown as Record)[ - property - ]; + return (target as unknown as Record)[property]; }, }; @@ -651,42 +692,7 @@ class ArraySlice { ArraySlice.proxyHandler as ArraySliceProxyHandler, ) as IndexableArraySlice; } -} - -type IndexableArraySlice = ArraySlice & { - [index: number]: T | undefined; -}; - -Array.prototype.slidingWindows = function* ( - this: ReadonlyArray, - windowSize: number, -): Generator, void, void> { - for ( - let win: IndexableArraySlice | null = ArraySlice.get( - this, - 0, - windowSize - 1, - ); - win != null; - win = win.isSuffix() ? null : win.slide() - ) { - yield win; - } -};" -`; - -exports[`App can render goody: TypeScript Array.prototype.swap 1`] = ` -"declare global { - interface Array { - swap(this: T[], i: number, j: number): void; - } -} - -Array.prototype.swap = function (this: T[], i: number, j: number): void { - const tmp = this[i]; - this[i] = this[j]; - this[j] = tmp; -};" +}" `; exports[`App can render goody: TypeScript BinaryHeap 1`] = `