@glimmer/util
Version:
Common utilities used in Glimmer
258 lines (239 loc) • 13.4 kB
TypeScript
import { Dict, Nullable, Stack, SimpleElement, InsertPosition, Namespace, SimpleNode, Maybe, Present, PresentArray, SimpleDocument, ErrHandle, HandleResult, OkHandle, Template, TemplateOk } from "@glimmer/interfaces";
declare const EMPTY_ARRAY: readonly unknown[];
declare function emptyArray<T>(): T[];
declare const EMPTY_STRING_ARRAY: string[];
declare const EMPTY_NUMBER_ARRAY: number[];
/**
* This function returns `true` if the input array is the special empty array sentinel,
* which is sometimes used for optimizations.
*/
declare function isEmptyArray(input: unknown[] | readonly unknown[]): boolean;
declare function reverse<T>(input: T[]): IterableIterator<T>;
declare function enumerate<T>(input: Iterable<T>): IterableIterator<[
number,
T
]>;
// let alreadyWarned = false;
declare function debugAssert(test: any, msg: string): asserts test;
declare function deprecate(desc: string): void;
declare function dict<T = unknown>(): Dict<T>;
declare function isDict<T>(u: T): u is Dict & T;
declare function isObject<T>(u: T): u is object & T;
declare class StackImpl<T> implements Stack<T> {
private stack;
current: Nullable<T>;
constructor(values?: T[]);
get size(): number;
push(item: T): void;
pop(): Nullable<T>;
nth(from: number): Nullable<T>;
isEmpty(): boolean;
toArray(): T[];
}
declare let beginTestSteps: (() => void) | undefined;
declare let endTestSteps: (() => void) | undefined;
declare let verifySteps: ((type: string, steps: unknown[] | ((steps: unknown[]) => void), message?: string) => void) | undefined;
declare let logStep: ((type: string, steps: unknown) => void) | undefined;
declare let debugToString: undefined | ((value: unknown) => string);
declare function clearElement(parent: SimpleElement): void;
declare const RAW_NODE = -1;
declare const ELEMENT_NODE = 1;
declare const TEXT_NODE = 3;
declare const COMMENT_NODE = 8;
declare const DOCUMENT_NODE = 9;
declare const DOCUMENT_TYPE_NODE = 10;
declare const DOCUMENT_FRAGMENT_NODE = 11;
declare const NS_HTML: Namespace.HTML;
declare const NS_MATHML: Namespace.MathML;
declare const NS_SVG: Namespace.SVG;
declare const NS_XLINK: Namespace.XLink;
declare const NS_XML: Namespace.XML;
declare const NS_XMLNS: Namespace.XMLNS;
declare const INSERT_BEFORE_BEGIN: InsertPosition.beforebegin;
declare const INSERT_AFTER_BEGIN: InsertPosition.afterbegin;
declare const INSERT_BEFORE_END: InsertPosition.beforeend;
declare const INSERT_AFTER_END: InsertPosition.afterend;
/*
Encoding notes
We use 30 bit integers for encoding, so that we don't ever encode a non-SMI
integer to push on the stack.
Handles are >= 0
Immediates are < 0
True, False, Undefined and Null are pushed as handles into the symbol table,
with well known handles (0, 1, 2, 3)
The negative space is divided into positives and negatives. Positives are
higher numbers (-1, -2, -3, etc), negatives are lower.
We only encode immediates for two reasons:
1. To transfer over the wire, so they're smaller in general
2. When pushing values onto the stack from the low level/inner VM, which may
be converted into WASM one day.
This allows the low-level VM to always use SMIs, and to minimize using JS
values via handles for things like the stack pointer and frame pointer.
Externally, most code pushes values as JS values, except when being pulled
from the append byte code where it was already encoded.
Logically, this is because the low level VM doesn't really care about these
higher level values. For instance, the result of a userland helper may be a
number, or a boolean, or undefined/null, but it's extra work to figure that
out and push it correctly, vs. just pushing the value as a JS value with a
handle.
Note: The details could change here in the future, this is just the current
strategy.
*/
declare enum ImmediateConstants {
MAX_SMI = 1073741823,
MIN_SMI = -1073741824,
SIGN_BIT = -536870913,
MAX_INT = 536870911,
MIN_INT = -536870912,
FALSE_HANDLE = 0,
TRUE_HANDLE = 1,
NULL_HANDLE = 2,
UNDEFINED_HANDLE = 3,
ENCODED_FALSE_HANDLE = 0,
ENCODED_TRUE_HANDLE = 1,
ENCODED_NULL_HANDLE = 2,
ENCODED_UNDEFINED_HANDLE = 3
}
declare function isHandle(value: number): boolean;
declare function isNonPrimitiveHandle(value: number): boolean;
declare function constants(...values: unknown[]): unknown[];
declare function isSmallInt(value: number): boolean;
declare function encodeNegative(num: number): number;
declare function decodeNegative(num: number): number;
declare function encodePositive(num: number): number;
declare function decodePositive(num: number): number;
declare function encodeHandle(num: number): number;
declare function decodeHandle(num: number): number;
declare function encodeImmediate(num: number): number;
declare function decodeImmediate(num: number): number;
/**
Strongly hint runtimes to intern the provided string.
When do I need to use this function?
For the most part, never. Pre-mature optimization is bad, and often the
runtime does exactly what you need it to, and more often the trade-off isn't
worth it.
Why?
Runtimes store strings in at least 2 different representations:
Ropes and Symbols (interned strings). The Rope provides a memory efficient
data-structure for strings created from concatenation or some other string
manipulation like splitting.
Unfortunately checking equality of different ropes can be quite costly as
runtimes must resort to clever string comparison algorithms. These
algorithms typically cost in proportion to the length of the string.
Luckily, this is where the Symbols (interned strings) shine. As Symbols are
unique by their string content, equality checks can be done by pointer
comparison.
How do I know if my string is a rope or symbol?
Typically (warning general sweeping statement, but truthy in runtimes at
present) static strings created as part of the JS source are interned.
Strings often used for comparisons can be interned at runtime if some
criteria are met. One of these criteria can be the size of the entire rope.
For example, in chrome 38 a rope longer then 12 characters will not
intern, nor will segments of that rope.
Some numbers: http://jsperf.com/eval-vs-keys/8
Known Trick™
@private
@return {String} interned version of the provided string
*/
declare function intern(str: string): string;
declare const SERIALIZATION_FIRST_NODE_STRING = "%+b:0%";
declare function isSerializationFirstNode(node: SimpleNode): boolean;
declare let assign: {
<T extends {}, U>(target: T, source: U): T & U;
<T_1 extends {}, U_1, V>(target: T_1, source1: U_1, source2: V): T_1 & U_1 & V;
<T_2 extends {}, U_2, V_1, W>(target: T_2, source1: U_2, source2: V_1, source3: W): T_2 & U_2 & V_1 & W;
(target: object, ...sources: any[]): any;
};
declare function values<T>(obj: {
[s: string]: T;
}): T[];
type ObjectEntry<D extends object> = {
[P in keyof D]: [
P,
D[P]
];
}[keyof D];
declare function entries<D extends object>(dict: D): ObjectEntry<D>[];
type Factory<T> = new (...args: unknown[]) => T;
declare function keys<T extends object>(obj: T): Array<keyof T>;
declare function unwrap<T>(val: Maybe<T>): T;
declare function expect<T>(val: T, message: string): Present<T>;
declare function unreachable(message?: string): Error;
declare function exhausted(value: never): never;
type Lit = string | number | boolean | undefined | null | void | {};
declare const tuple: <T extends Lit[]>(...args: T) => T;
declare function isPresent<T>(value: T): value is Present<T>;
declare function assertPresent<T extends string>(value: T): asserts value is Present<T>;
declare function assertPresent<T>(value: T, message: string): asserts value is Present<T>;
declare function isPresentArray<T>(list: readonly T[]): list is PresentArray<T>;
declare function ifPresent<T, U, V>(list: T[], ifPresent: (input: PresentArray<T>) => U, otherwise: () => V): U | V;
declare function arrayToOption<T>(list: T[]): Nullable<PresentArray<T>>;
declare function assertPresentArray<T>(list: T[], message?: string): asserts list is PresentArray<T>;
declare function asPresentArray<T>(list: T[], message?: string): PresentArray<T>;
declare function getLast<T>(list: PresentArray<T>): T;
declare function getLast<T>(list: T[]): T | undefined;
declare function getFirst<T>(list: PresentArray<T>): T;
declare function getFirst<T>(list: T[]): T | undefined;
declare function mapPresentArray<T, U>(list: PresentArray<T>, mapper: (input: T) => U): PresentArray<U>;
declare function mapPresentArray<T, U>(list: PresentArray<T> | null, mapper: (input: T) => U): PresentArray<U> | null;
interface GenericElementTags {
HTML: HTMLElement;
SVG: SVGElement;
ELEMENT: HTMLElement | SVGElement;
}
interface GenericNodeTags {
NODE: Node;
}
type GenericNodeTag = keyof GenericNodeTags;
interface BrowserElementTags extends HTMLElementTagNameMap, GenericElementTags {
}
type BrowserElementTag = keyof BrowserElementTags;
interface BrowserTags extends BrowserElementTags, GenericNodeTags {
}
type BrowserTag = keyof BrowserTags;
type NodeCheck<N extends Node> = (node: Node) => node is N;
type SugaryNodeCheck<K extends BrowserTag = BrowserTag> = NodeCheck<BrowserTags[K]> | K | K[];
type NodeForSugaryCheck<S extends SugaryNodeCheck<BrowserTag>> = S extends NodeCheck<infer N> ? N : S extends keyof BrowserTags ? BrowserTags[S] : S extends (keyof BrowserTags)[] ? BrowserTags[S[number]] : never;
type BrowserNode = Element | Document | DocumentFragment | Text | Comment | Node;
declare function castToSimple(doc: Document | SimpleDocument): SimpleDocument;
declare function castToSimple(elem: Element | SimpleElement): SimpleElement;
declare function castToSimple(node: Node | SimpleNode): SimpleNode;
// If passed a document, verify we're in the browser and return it as a Document
declare function castToBrowser(doc: Document | SimpleDocument): Document;
// If we don't know what this is, but the check requires it to be an element,
// the cast will mandate that it's a browser element
declare function castToBrowser<S extends SugaryNodeCheck<BrowserElementTag>>(node: BrowserNode | SimpleNode, check: S): NodeForSugaryCheck<S>;
// Finally, if it's a more generic check, the cast will mandate that it's a
// browser node and return a BrowserNodeUtils corresponding to the check
declare function castToBrowser<S extends SugaryNodeCheck<GenericNodeTag>>(element: BrowserNode | SimpleNode, check: S): NodeForSugaryCheck<S>;
declare function castToBrowser<K extends keyof HTMLElementTagNameMap>(element: SimpleElement | Element, check: K): HTMLElementTagNameMap[K];
declare function isSimpleElement(node: Maybe<SimpleNode | Node>): node is SimpleElement;
declare function isElement(node: Maybe<Node | SimpleNode>): node is Element;
declare function checkBrowserNode<S extends SugaryNodeCheck>(node: Node | SimpleNode | null, check: S): NodeForSugaryCheck<S>;
declare function strip(strings: TemplateStringsArray, ...args: unknown[]): string;
declare function unwrapHandle(handle: HandleResult): number;
declare function unwrapTemplate(template: Template): TemplateOk;
declare function extractHandle(handle: HandleResult): number;
declare function isOkHandle(handle: HandleResult): handle is OkHandle;
declare function isErrHandle(handle: HandleResult): handle is ErrHandle;
declare function buildUntouchableThis(source: string): null | object;
type FIXME<T, S extends string> = (T & S) | T;
/**
* This constant exists to make it easier to differentiate normal logs from
* errant console.logs. LOCAL_LOGGER should only be used inside a
* LOCAL_SHOULD_LOG check.
*
* It does not alleviate the need to check LOCAL_SHOULD_LOG, which is used
* for stripping.
*/
declare const LOCAL_LOGGER: Console;
/**
* This constant exists to make it easier to differentiate normal logs from
* errant console.logs. LOGGER can be used outside of LOCAL_SHOULD_LOG checks,
* and is meant to be used in the rare situation where a console.* call is
* actually appropriate.
*/
declare const LOGGER: Console;
declare function assertNever(value: never, desc?: string): never;
export { EMPTY_ARRAY, emptyArray, EMPTY_STRING_ARRAY, EMPTY_NUMBER_ARRAY, isEmptyArray, reverse, enumerate, debugAssert as assert, deprecate, dict, isDict, isObject, StackImpl as Stack, beginTestSteps, endTestSteps, logStep, verifySteps, debugToString, clearElement, RAW_NODE, ELEMENT_NODE, TEXT_NODE, COMMENT_NODE, DOCUMENT_NODE, DOCUMENT_TYPE_NODE, DOCUMENT_FRAGMENT_NODE, NS_HTML, NS_MATHML, NS_SVG, NS_XLINK, NS_XML, NS_XMLNS, INSERT_BEFORE_BEGIN, INSERT_AFTER_BEGIN, INSERT_BEFORE_END, INSERT_AFTER_END, ImmediateConstants, isHandle, isNonPrimitiveHandle, constants, isSmallInt, encodeNegative, decodeNegative, encodePositive, decodePositive, encodeHandle, decodeHandle, encodeImmediate, decodeImmediate, intern, isSerializationFirstNode, SERIALIZATION_FIRST_NODE_STRING, assign, entries, values, Factory, keys, unwrap, expect, unreachable, exhausted, Lit, tuple, isPresent, assertPresent, isPresentArray, ifPresent, arrayToOption, assertPresentArray, asPresentArray, getLast, getFirst, mapPresentArray, castToBrowser, castToSimple, checkBrowserNode as checkNode, isElement, isSimpleElement, strip, unwrapHandle, unwrapTemplate, extractHandle, isOkHandle, isErrHandle, buildUntouchableThis, FIXME, LOCAL_LOGGER, LOGGER, assertNever };
//# sourceMappingURL=index.d.ts.map