ix
Version:
The Interactive Extensions for JavaScript
255 lines (237 loc) • 8.09 kB
text/typescript
import { identity } from '../util/identity.js';
import { UnaryFunction, OperatorFunction } from '../interfaces.js';
import { bindCallback } from '../util/bindcallback.js';
import {
isArrayLike,
isIterable,
isIterator,
isReadableNodeStream,
isWritableNodeStream,
} from '../util/isiterable.js';
import { toLength } from '../util/tolength.js';
/**
* This class serves as the base for all operations which support [Symbol.iterator].
*/
export abstract class IterableX<T> implements Iterable<T> {
abstract [Symbol.iterator](): Iterator<T>;
/** @nocollapse */
forEach(projection: (value: T, index: number) => void, thisArg?: any): void {
const fn = bindCallback(projection, thisArg, 2);
let i = 0;
for (const item of this) {
fn(item, i++);
}
}
/** @nocollapse */
pipe<R>(...operations: UnaryFunction<Iterable<T>, R>[]): R;
pipe<R>(...operations: OperatorFunction<T, R>[]): IterableX<R>;
pipe<R extends NodeJS.WritableStream>(writable: R, options?: { end?: boolean }): R;
pipe<R>(...args: any[]) {
let i = -1;
const n = args.length;
let acc: any = this;
while (++i < n) {
acc = args[i](IterableX.as(acc));
}
return acc;
}
/**
* Converts an existing string into an iterable of characters.
*
* @param {string} source The string to convert to an iterable.
* @returns {IterableX<string>} An terable stream of characters from the source.
*/
static as(source: string): IterableX<string>;
/**
* Converts the iterable like input into an iterable.
*
* @template T The tyep of elements in the source iterable.
* @param {Iterable<T>} source The iterable to convert to an iterable.
* @returns {IterableX<T>} An iterable stream of the source sequence.
*/
static as<T>(source: Iterable<T>): IterableX<T>;
/**
* Converts an array-like object to an iterable.
*
* @template T The type of elements in the source array-like sequence.
* @param {ArrayLike<T>} source The array-like sequence to convert to an iterable.
* @returns {IterableX<T>} The iterable containing the elements from the array-like sequence.
*/
static as<T>(source: ArrayLike<T>): IterableX<T>;
/**
* Converts the object into a singleton in an iterable sequence.
*
* @template T The type of element to turn into an iterable sequence.
* @param {T} source The item to turn into an iterable sequence.
* @returns {IterableX<T>} An iterable sequence from the source object.
*/
static as<T>(source: T): IterableX<T>;
/** @nocollapse */
static as(source: any) {
if (source instanceof IterableX) {
return source;
}
if (typeof source === 'string') {
return new FromIterable([source], identity);
}
if (isIterable(source) || isArrayLike(source)) {
return new FromIterable(source, identity);
}
return new FromIterable([source], identity);
}
/** @nocollapse */
static from<TSource, TResult = TSource>(
source: Iterable<TSource> | Iterator<TSource> | ArrayLike<TSource>,
selector: (value: TSource, index: number) => TResult = identity,
thisArg?: any
): IterableX<TResult> {
const fn = bindCallback(selector, thisArg, 2);
if (isIterable(source)) {
return new FromIterable<TSource, TResult>(source, fn);
}
if (isArrayLike(source)) {
return new FromIterable<TSource, TResult>(source, fn);
}
if (isIterator(source)) {
return new FromIterable<TSource, TResult>({ [Symbol.iterator]: () => source }, fn);
}
throw new TypeError('Input type not supported');
}
}
(<any>IterableX.prototype)[Symbol.toStringTag] = 'IterableX';
Object.defineProperty(IterableX, Symbol.hasInstance, {
writable: true,
configurable: true,
value(inst: any) {
return !!(inst && inst[Symbol.toStringTag] === 'IterableX');
},
});
/** @ignore */
export class FromIterable<TSource, TResult = TSource> extends IterableX<TResult> {
private _source: Iterable<TSource> | ArrayLike<TSource>;
private _fn: (value: TSource, index: number) => TResult;
constructor(
source: Iterable<TSource> | ArrayLike<TSource>,
fn: (value: TSource, index: number) => TResult
) {
super();
this._source = source;
this._fn = fn;
}
*[Symbol.iterator]() {
const iterable = isIterable(this._source);
let i = 0;
if (iterable) {
for (const item of <Iterable<TSource>>this._source) {
yield this._fn(item, i++);
}
} else {
const length = toLength((<ArrayLike<TSource>>this._source).length);
while (i < length) {
const val = (<ArrayLike<TSource>>this._source)[i];
yield this._fn(val, i++);
}
}
}
}
type WritableOrOperatorFunction<T, R> =
| NodeJS.WritableStream
| NodeJS.ReadWriteStream
| OperatorFunction<T, R>;
declare module '../iterable/iterablex' {
interface IterableX<T> extends Iterable<T> {
pipe(): IterableX<T>;
pipe<A>(op1: OperatorFunction<T, A>): IterableX<A>;
pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): IterableX<B>;
pipe<A, B, C>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>
): IterableX<C>;
pipe<A, B, C, D>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>
): IterableX<D>;
pipe<A, B, C, D, E>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>,
op5: OperatorFunction<D, E>
): IterableX<E>;
pipe<A, B, C, D, E, F>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>,
op5: OperatorFunction<D, E>,
op6: OperatorFunction<E, F>
): IterableX<F>;
pipe<A, B, C, D, E, F, G>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>,
op5: OperatorFunction<D, E>,
op6: OperatorFunction<E, F>,
op7: OperatorFunction<F, G>
): IterableX<G>;
pipe<A, B, C, D, E, F, G, H>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>,
op5: OperatorFunction<D, E>,
op6: OperatorFunction<E, F>,
op7: OperatorFunction<F, G>,
op8: OperatorFunction<G, H>
): IterableX<H>;
pipe<A, B, C, D, E, F, G, H, I>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>,
op5: OperatorFunction<D, E>,
op6: OperatorFunction<E, F>,
op7: OperatorFunction<F, G>,
op8: OperatorFunction<G, H>,
op9: OperatorFunction<H, I>
): IterableX<I>;
pipe<R>(...operations: OperatorFunction<T, R>[]): IterableX<R>;
pipe<A extends NodeJS.WritableStream>(op1: A, options?: { end?: boolean }): A;
}
}
try {
((isBrowser) => {
if (isBrowser) {
return;
}
IterableX.prototype['pipe'] = nodePipe;
const readableOpts = (x: any, opts = x._writableState || { objectMode: true }) => opts;
function nodePipe<T>(this: IterableX<T>, ...args: any[]) {
let i = -1;
let end: boolean;
const n = args.length;
let prev: any = this;
let next: WritableOrOperatorFunction<T, any>;
while (++i < n) {
next = args[i];
if (typeof next === 'function') {
prev = next(IterableX.as(prev));
} else if (isWritableNodeStream(next)) {
({ end = true } = args[i + 1] || {});
// prettier-ignore
return isReadableNodeStream(prev) ? prev.pipe(next, { end }) :
IterableX.as(prev).toNodeStream(readableOpts(next)).pipe(next, { end });
}
}
return prev;
}
})(typeof window === 'object' && typeof document === 'object' && document.nodeType === 9);
} catch (e) {
/* */
}
export const as = IterableX.as;
export const from = IterableX.from;