deleight
Version:
A library with 9 modules for writing more expressive web applications with traditional HTML, CSS and JavaScript.
343 lines (342 loc) • 11.6 kB
TypeScript
/**
* Exports the {@link Action} type along with many useful {@link Step}
* types which are used to create 'live' functions.
*
* Actions are very expressive, more controllable and mutable almost
* without any performance penalty. The flow is easy to track as it is
* declarative. You can create new step types which interpret your code
* at speed.
*
* Actions can be used for:
*
* 1. regular code - You can use Actions to implement common patterns
* like function chaining and passing the same arguments to multiple functions.
*
* 2. declarative programming - You can build up code for the same action in
* multiple places, allowing you to be more declarative in how you write code.
* This is a powerful feature for reactivity.
*
* 3. implementing other libraries that will generate code on the fly - You can
* create and modify actions easily without parsing and compiling from
* string (unlike Function constructor). This allows you to maintain a consistently
* good performance throughout your code.
*
* 4. custom DSLs - You can write your own steps to interpret code however you
* like. for example your own markup language
*
* 5. etc - You can probably do other cool things with Actions that we havent yet
* to mention or envisage.
*
* Actions can be used in any JavaScript environment as it has no
* platform dependencies. It is of course performant and lightweight.
*
* Currently this module has not been fully tested.
*
* @module
*
*/
import { IKey } from "../types.js";
export interface IEnvironment {
values: Iterator<any>;
scope?: any;
args?: Iterable<any>;
}
export type IPriority = 0 | 1;
export type IInterpreter = (values: Iterable<any>, env: IEnvironment) => (Iterable<any> | undefined);
export declare function isGenerator(value: any): boolean;
export declare function isAsyncGenerator(value: any): boolean;
export declare class Action<T = any, U extends any[] = any> {
values: Iterable<any> | {
IKey: any;
};
arrayScope?: boolean;
constructor(values: Iterable<any> | {
IKey: any;
}, arrayScope?: boolean);
getValues(scope: T, ...args: any[]): any;
getScope(...args: any[]): T;
start(scope: T, ...args: U): {
index: number;
runner: any;
env: IEnvironment;
};
genWith(scope: T, ...args: U): Generator<any, void, any>;
gen(...args: U): Generator<never, Generator<any, void, any>, unknown>;
callWith(scope: T, ...args: U): any;
call(...args: U): any;
}
export declare function action(code: Iterable<any>): Action<any, any>;
export declare class Closer {
runner: Step;
constructor(runner: Step);
}
/**
* Used directly to end the last step or called with a step to
* end that step.
*
* @example
*
*/
export declare function $(runner: Step): Closer;
/**
* Interpretes values and returns replacement values.
*
* An action is simply an array of different values. Steps are some
* of these values that provide interpretations for the values following
* them (up until the next termination step or the end of the action).
*
* A simple policy is adopted to resolve situations when a step encounters
* another while collecting the values it runs with:
*
* 1. If the next step has a `priority` of `0`, the current step, along with any others it is nested
* within, finish immediately.
*
* 2. If instead the next step has a priority of `1`, it will be started and any values
* it `yield`s will be collected by the preceding step as part of the values it
* uses to run.
*
* To force a step to stop collecting values, esplicitly terminate with the `$`
* function value. If the step we want to stop has nested steps which are still
* collecting values, simply call the `$` function with the step.
*
* @example
*
*
*/
export declare class Step {
priority: IPriority;
endBy?: Step | Closer;
constructor(priority?: IPriority);
/**
* Collect all the values used in the step and pass to
* {@link Step#runWith}. Then replace the collected values
* with those returned by {@link Step#runWith}.
*
* This method concludes by starting the next step, if there is
* one. Not starting the next step can cause the action to end
* prematurely. This should be noted if overriding this method in
* a subclass. It is best to override {@link Step#runWith} if you
* just want to implement the step's own behaviour.
*
* @example
*
*
* @param env
* @param parent
*/
run(env: IEnvironment, parent?: Step): any;
getValue(value: any, env: IEnvironment): any;
getValues(env: IEnvironment): any;
/**
* Default execution is to call all function values in parallel and/or
* yield any generators. The final result from calling the functions will
* also be yield*ed if it is not undefined. All other values, along with
* non-generator function return values are used to build the args for
* the next function encountered. Will also yield* generators returned by
* fuctions.
*
* @example
*
*
* @param env
* @param values
*/
runWith(env: IEnvironment, values: Iterable<any>): Iterable<any>;
}
export declare function defineSteps(type: typeof Step): [Step, Step, Step, Step];
export declare const r: Step, run: Step, R: Step, Run: Step;
export declare class FunctionValue {
value: Function;
}
/**
* Call to wrap functions to use them as literal arguments in WithStep
* and PipeStep which interpret functions specially.
*
* @param f
* @returns
*/
export declare function fn(f: Function): FunctionValue;
/**
* WithStep executes by calling any function values with the
* scope and main args as the first 2 arguments followed by any
* further arguments built within the step.
*
* Where a generator is encountered, either among the values or as a function
* return value, it will immediately be yielded and 'forgotten'.
*
* The final result from calling the functions will be yield*ed
* if it is not undefined.
*
* All yielded values effectively replace the step
* and all its input values in the list of values used in the rest of the action
*
* All other values are used to build the args for
* the next function encountered.
*
* @example
*
*
*/
export declare class WithStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, any>;
}
export declare const w: Step, withr: Step, W: Step, Withr: Step;
/**
* The pipe step used to interprete values as chained functions (or extra
* arguments to them).
*
* @example
*
*/
export declare class PipeStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, unknown>;
}
export declare const p: Step, pipe: Step, P: Step, Pipe: Step;
/**
* A special step which enables a user to create
* on-the-fly steps by initializing with an interpreter
* function.
*
* @example
*
*
*/
export declare class FuncStep extends Step {
interpreter: IInterpreter;
constructor(interpreter: IInterpreter, priority?: IPriority);
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, unknown>;
}
export declare function Func(interpreter: IInterpreter): FuncStep;
export declare const F: typeof Func;
export declare function func(interpreter: IInterpreter): FuncStep;
export declare const f: typeof Func;
/**
* Values are fetched from the action arguments during this step.
*
* @example
*
*/
export declare class ArgsStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, unknown>;
}
export declare const a: Step, args: Step, A: Step, Args: Step;
/**
* A single action value (iterable) is spread into multiple action values
* during this step.
*
* @example
*
*/
export declare class ManyStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, any>;
}
export declare const m: Step, many: Step, M: Step, Many: Step;
/**
* Multiple action values are collected into a single value (iterable)
* during this step.
*
* @example
*/
export declare class OneStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<Iterable<any>, void, unknown>;
}
export declare const o: Step, one: Step, O: Step, One: Step;
/**
* Properties are fetched from the scope or specified objects during
* this step. The step can fetch properties at any depth. Also the
* scope itself can be returned.
*
* @example
*
*/
export declare class GetStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, unknown>;
}
export declare const g: Step, getr: Step, G: Step, Getr: Step;
/**
* Properties are set on the scope or specified objects during
* this step. The step can set properties at any depth. Also the
* scope itself can be set
*
* @example
*
*/
export declare class SetStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, unknown>;
}
export declare const s: Step, setr: Step, S: Step, Setr: Step;
/**
* Methods are called on the scope or specified objects during
* this step. The step can call methods at any depth.
*
* @example
*
*/
export declare class CallStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, unknown>;
}
export declare const c: Step, callr: Step, C: Step, Callr: Step;
/**
* Properties are deleted from the scope or specified objects during
* this step. The step can delete properties at any depth. Also the
* scope itself can be deleted
*
* @example
*
*/
export declare class DeleteStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<never, void, unknown>;
}
export declare const d: Step, delr: Step, D: Step, Delr: Step;
/**
* Used to silence the values yielded by nested steps by simply doing
* nothing. This allows us to perform 'side-effect' operations from
* within another step which yield values we don't want to send back to the
* previous step.
*
* @example
*
*/
export declare class NullStep extends Step {
runWith(env: IEnvironment, values: Iterable<any>): Generator<never, void, unknown>;
}
export declare const n: Step, nullr: Step, N: Step, Nullr: Step;
/**
* A special step which joins multiple steps so that the values
* of earlier steps are built from the values from the subsequent ones.
*
* It is used to alias a chain of steps that would have had to be
* written out every time.
*
* @example
*
*
*/
export declare class EarlyStep extends Step {
values: Iterable<any>;
constructor(values: Iterable<any>, priority?: IPriority);
runWith(env: IEnvironment, values: Iterable<any>): Generator<any, void, any>;
}
export declare function Est(values: Iterable<any>): EarlyStep;
export declare const E: typeof Est;
export declare function est(values: Iterable<any>): EarlyStep;
export declare const e: typeof est;
/**
* Allows more flexibility than EarlyStep in where new values
* are placed. However this means we must use static arrays to hold the
* values in the template. To use more transient iterables here,
* please implement your own.
*
*/
export declare class StepTemplate {
values: any[];
map: ITemplateMap;
constructor(values: any[], map: ITemplateMap);
run(values: {
[key: IKey]: any;
}, priority?: IPriority): EarlyStep;
}
export type ITemplateMap = {
[key: IKey]: number | typeof $;
};
export declare function template(values: any[], map: ITemplateMap): StepTemplate;