Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { describe, expect, it } from "@jest/globals";

import "../Iterator.prototype.map";
import "../Iterator.prototype.filter";

(globalThis as Record<string, unknown>).Iterator &&
delete (
(globalThis as Record<string, unknown>).Iterator as Record<string, unknown>
).from;

// eslint-disable-next-line import-x/first
import "./index";

describe("Iterator.from", () => {
it("can convert an Array to an Iterator", () => {
const array = [-3, 1, -4, 1, -5];
const iterator = Iterator.from(array);

expect([...iterator]).toStrictEqual(array);
});

it("can convert a Set to an Iterator", () => {
const set = new Set([-3, 1, 1, -4, -4, 1, -5]);
const iterator = Iterator.from(set);

expect([...iterator]).toStrictEqual([...set]);
});

it("can convert a Map to an Iterator", () => {
const map = new Map([
["z", 3],
["e", 1],
["d", 4],
]);
const iterator = Iterator.from(map);
expect([...iterator]).toStrictEqual([...map.entries()]);
});

it("can convert a Generator Object to an Iterator", () => {
const factory = function* (): Generator<number, void, void> {
yield 3;
yield 1;
yield 4;
yield 1;
yield 5;
};
const iterator = Iterator.from(factory());

expect([...iterator]).toStrictEqual([3, 1, 4, 1, 5]);
});

it("can convert a String to an Iterator", () => {
const string = "🚀💯🔥";
const iterator = Iterator.from(string);

expect([...iterator]).toStrictEqual([...string]);
});

it("can convert an Iterator to an IterableIterator and returns the same reference", () => {
const array = [-3, 1, -4, 1, -5];
const iterator = array.values();
const convertedIterator = Iterator.from(iterator);

expect(convertedIterator).toBe(iterator);
});

it("can convert an IterableIterator to an Iterator", () => {
const array = [-3, 1, -4, 1, -5];
const iterator = Iterator.from(array.values());
const iterator2 = Iterator.from(iterator[Symbol.iterator]());

expect([...iterator2]).toStrictEqual(array);
});

it("can convert an object with a random next() method to an Iterator", () => {
const object = {
values: [-3, 1, -4, 1, -5, 9],
index: 0,
next(): IteratorResult<number, void> {
if (this.index < this.values.length) {
return { value: this.values[this.index++], done: false };
}
return { done: true, value: undefined };
},
};
const iterator = Iterator.from(object)
.map((x) => x * 2)
.filter((x) => x > 0);

expect([...iterator]).toStrictEqual([2, 2, 18]);
});

it("throws an error if the object is not an Iterator, Iterable, or an object with a next method", () => {
const object = {};
// @ts-expect-error Invalid argument
expect(() => Iterator.from(object)).toThrow(TypeError);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import "../Iterator.prototype.toIterable/index.ts";
import { iteratorPrototype } from "../Iterator.prototype/index.ts";

declare global {
interface IteratorConstructor {
from<T>(
object: Iterator<T> | Iterable<T> | { next(): IteratorResult<T> },
): IterableIterator<T>;
}

const Iterator: IteratorConstructor;
}

(globalThis as Record<string, unknown>).Iterator ??= {};

((globalThis as Record<string, unknown>).Iterator as { from: unknown }).from ??=
function <T>(
object: Iterator<T> | Iterable<T> | { next(): IteratorResult<T> },
): IterableIterator<T> {
const toIterable = iteratorPrototype.toIterable as (
this: Iterator<T>,
) => IterableIterator<T>;

const iteratorFactory = (object as Iterable<T>)[Symbol.iterator];
if (typeof iteratorFactory === "function") {
return toIterable.call(iteratorFactory.call(object));
}

// eslint-disable-next-line no-prototype-builtins
if (iteratorPrototype.isPrototypeOf(object)) {
return toIterable.call(object as Iterator<T>);
}

if (typeof (object as Record<string, unknown>).next === "function") {
return (function* () {
yield* toIterable.call(object as Iterator<T>);
})();
}

throw new TypeError(
"Object is not an iterator, iterable, or an object with a next method",
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,53 @@ Function.returnThis = function () {
/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////"
`;

exports[`App can equip single goody: JavaScript Iterator.from 1`] = `
"////////////////////////// BEGIN ADVENTURE PACK CODE ///////////////////////////
// Adventure Pack commit fake-commit-hash
// Running at: https://example.com/

Function.returnThis = function () {
return this;
};

const iteratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([].values()),
);

iteratorPrototype.toIterable = function () {
this[Symbol.iterator] ??= Function.returnThis;
return this;
};

globalThis.Iterator ??= {};

globalThis.Iterator.from ??= function (object) {
const toIterable = iteratorPrototype.toIterable;

const iteratorFactory = object[Symbol.iterator];
if (typeof iteratorFactory === "function") {
return toIterable.call(iteratorFactory.call(object));
}

// eslint-disable-next-line no-prototype-builtins
if (iteratorPrototype.isPrototypeOf(object)) {
return toIterable.call(object);
}

if (typeof object.next === "function") {
return (function* () {
yield* toIterable.call(object);
})();
}

throw new TypeError(
"Object is not an iterator, iterable, or an object with a next method",
);
};

/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////"
`;

exports[`App can equip single goody: JavaScript Iterator.prototype 1`] = `
"////////////////////////// BEGIN ADVENTURE PACK CODE ///////////////////////////
// Adventure Pack commit fake-commit-hash
Expand Down Expand Up @@ -3280,6 +3327,79 @@ Function.returnThis = function <T>(this: T): T {
/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////"
`;

exports[`App can equip single goody: TypeScript Iterator.from 1`] = `
"////////////////////////// BEGIN ADVENTURE PACK CODE ///////////////////////////
// Adventure Pack commit fake-commit-hash
// Running at: https://example.com/

declare global {
interface FunctionConstructor {
returnThis<T>(this: T): T;
}

interface Iterator<T> {
toIterable(this: Iterator<T>): IterableIterator<T>;
}

interface IteratorConstructor {
from<T>(
object: Iterator<T> | Iterable<T> | { next(): IteratorResult<T> },
): IterableIterator<T>;
}

const Iterator: IteratorConstructor;
}

Function.returnThis = function <T>(this: T): T {
return this;
};

const iteratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([].values()),
) as Iterator<unknown, unknown, unknown>;

iteratorPrototype.toIterable = function <T>(
this: Iterator<T>,
): IterableIterator<T> {
(this as unknown as Record<symbol, unknown>)[Symbol.iterator] ??=
Function.returnThis;
return this as unknown as IterableIterator<T>;
};

(globalThis as Record<string, unknown>).Iterator ??= {};

((globalThis as Record<string, unknown>).Iterator as { from: unknown }).from ??=
function <T>(
object: Iterator<T> | Iterable<T> | { next(): IteratorResult<T> },
): IterableIterator<T> {
const toIterable = iteratorPrototype.toIterable as (
this: Iterator<T>,
) => IterableIterator<T>;

const iteratorFactory = (object as Iterable<T>)[Symbol.iterator];
if (typeof iteratorFactory === "function") {
return toIterable.call(iteratorFactory.call(object));
}

// eslint-disable-next-line no-prototype-builtins
if (iteratorPrototype.isPrototypeOf(object)) {
return toIterable.call(object as Iterator<T>);
}

if (typeof (object as Record<string, unknown>).next === "function") {
return (function* () {
yield* toIterable.call(object as Iterator<T>);
})();
}

throw new TypeError(
"Object is not an iterator, iterable, or an object with a next method",
);
};

/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////"
`;

exports[`App can equip single goody: TypeScript Iterator.prototype 1`] = `
"////////////////////////// BEGIN ADVENTURE PACK CODE ///////////////////////////
// Adventure Pack commit fake-commit-hash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,37 @@ exports[`App can render goody: JavaScript Function.returnThis 1`] = `
};"
`;

exports[`App can render goody: JavaScript Iterator.from 1`] = `
"import "Iterator.prototype";
import "Iterator.prototype.toIterable";

globalThis.Iterator ??= {};

globalThis.Iterator.from ??= function (object) {
const toIterable = iteratorPrototype.toIterable;

const iteratorFactory = object[Symbol.iterator];
if (typeof iteratorFactory === "function") {
return toIterable.call(iteratorFactory.call(object));
}

// eslint-disable-next-line no-prototype-builtins
if (iteratorPrototype.isPrototypeOf(object)) {
return toIterable.call(object);
}

if (typeof object.next === "function") {
return (function* () {
yield* toIterable.call(object);
})();
}

throw new TypeError(
"Object is not an iterator, iterable, or an object with a next method",
);
};"
`;

exports[`App can render goody: JavaScript Iterator.prototype 1`] = `
"export const iteratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([].values()),
Expand Down Expand Up @@ -1939,6 +1970,52 @@ Function.returnThis = function <T>(this: T): T {
};"
`;

exports[`App can render goody: TypeScript Iterator.from 1`] = `
"import "Iterator.prototype";
import "Iterator.prototype.toIterable";

declare global {
interface IteratorConstructor {
from<T>(
object: Iterator<T> | Iterable<T> | { next(): IteratorResult<T> },
): IterableIterator<T>;
}

const Iterator: IteratorConstructor;
}

(globalThis as Record<string, unknown>).Iterator ??= {};

((globalThis as Record<string, unknown>).Iterator as { from: unknown }).from ??=
function <T>(
object: Iterator<T> | Iterable<T> | { next(): IteratorResult<T> },
): IterableIterator<T> {
const toIterable = iteratorPrototype.toIterable as (
this: Iterator<T>,
) => IterableIterator<T>;

const iteratorFactory = (object as Iterable<T>)[Symbol.iterator];
if (typeof iteratorFactory === "function") {
return toIterable.call(iteratorFactory.call(object));
}

// eslint-disable-next-line no-prototype-builtins
if (iteratorPrototype.isPrototypeOf(object)) {
return toIterable.call(object as Iterator<T>);
}

if (typeof (object as Record<string, unknown>).next === "function") {
return (function* () {
yield* toIterable.call(object as Iterator<T>);
})();
}

throw new TypeError(
"Object is not an iterator, iterable, or an object with a next method",
);
};"
`;

exports[`App can render goody: TypeScript Iterator.prototype 1`] = `
"export const iteratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([].values()),
Expand Down
Loading