mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
409 lines
16 KiB
TypeScript
409 lines
16 KiB
TypeScript
import "./tags-html";
|
|
|
|
declare module "*.marko" {
|
|
const template: Marko.Template;
|
|
export default template;
|
|
}
|
|
|
|
declare global {
|
|
namespace NodeJS {
|
|
interface ReadableStream {}
|
|
}
|
|
|
|
namespace Marko {
|
|
/** A mutable global object for the current render. */
|
|
export interface Global {
|
|
[x: PropertyKey]: unknown;
|
|
/** An AbortSignal instance that, when aborted, stops further streamed content. */
|
|
signal?: AbortSignal;
|
|
/** A CSP Nonce to add to each script output from Marko. */
|
|
cspNonce?: string;
|
|
/** Used for rendering multiple Marko templates in a single hydrated page. */
|
|
renderId?: string;
|
|
/** Used to uniquely identify a instance of a Marko runtime. */
|
|
runtimeId?: string;
|
|
/** A list of globals that should be serialized to the browser. */
|
|
serializedGlobals?: Record<string, boolean>;
|
|
/** @deprecated prefer `renderId` */
|
|
widgetIdPrefix?: string;
|
|
/** @deprecated prefer `renderId` */
|
|
componentIdPrefix?: string;
|
|
}
|
|
|
|
export type TemplateInput<Input> = Input & {
|
|
$global?: Global;
|
|
};
|
|
|
|
/** The result of calling `template.render`. */
|
|
export type RenderedTemplate<Component = unknown> = Promise<
|
|
Component extends Marko.Component ? RenderResult<Component> : string
|
|
> &
|
|
AsyncIterable<string> & {
|
|
toReadable(): ReadableStream<Uint8Array<ArrayBufferLike>>;
|
|
pipe(stream: {
|
|
write(chunk: string): unknown;
|
|
end(): unknown;
|
|
flush?(): void;
|
|
}): void;
|
|
toString(): string;
|
|
};
|
|
|
|
/** The result of calling `template.mount`. */
|
|
export type MountedTemplate<Input = unknown, Return = unknown> = {
|
|
get value(): Return extends { value: infer Value } ? Value : void;
|
|
set value(
|
|
next: Return extends { valueChange?(next: infer Next): any }
|
|
? Next
|
|
: never,
|
|
);
|
|
update(input: Marko.TemplateInput<Input>): void;
|
|
destroy(): void;
|
|
};
|
|
|
|
export interface Out<Component = unknown>
|
|
extends Marko.RenderedTemplate<Component> {
|
|
/** The underlying ReadableStream Marko is writing into. */
|
|
stream: unknown;
|
|
/** A mutable global object for the current render. */
|
|
global: Global;
|
|
/** Disable all async rendering. Will error if something beings async. */
|
|
sync(): void;
|
|
/** Returns true if async rendering is disabled. */
|
|
isSync(): boolean;
|
|
/** Write unescaped text at the current stream position. */
|
|
write(val: string | void): this;
|
|
/** Write javascript content to be merged with the scripts Marko sends out on the next flush. */
|
|
script(val: string | void): this;
|
|
/** Starts a new async/forked stream. */
|
|
beginAsync(options?: {
|
|
name?: string;
|
|
timeout?: number;
|
|
last?: boolean;
|
|
}): Out;
|
|
/** Marks the current stream as complete (async streams may still be executing). */
|
|
end(val?: string | void): this;
|
|
emit(eventName: PropertyKey, ...args: any[]): boolean;
|
|
on(eventName: PropertyKey, listener: (...args: any[]) => any): this;
|
|
once(eventName: PropertyKey, listener: (...args: any[]) => any): this;
|
|
prependListener(
|
|
eventName: PropertyKey,
|
|
listener: (...args: any[]) => any,
|
|
): this;
|
|
removeListener(
|
|
eventName: PropertyKey,
|
|
listener: (...args: any[]) => any,
|
|
): this;
|
|
/** Register a callback executed when the last async out has completed. */
|
|
onLast(listener: (next: () => void) => unknown): this;
|
|
/** Emits an error on the stream. */
|
|
error(e: Error): this;
|
|
/** Schedules a Marko to flush buffered html to the underlying stream. */
|
|
flush(): this;
|
|
/** Creates a detached out stream (used for out of order flushing). */
|
|
createOut(): Out;
|
|
/** Write escaped text at the current stream position. */
|
|
text(val: string | void): void;
|
|
}
|
|
|
|
/** Body content created from by a component, typically held in an object with a renderBody property. */
|
|
|
|
export interface Body<
|
|
in Params extends readonly any[] = [],
|
|
out Return = void,
|
|
> {}
|
|
|
|
/** Valid data types which can be passed in as a <${dynamic}/> tag name. */
|
|
export type Renderable =
|
|
| { renderBody: Body<any, any> | Template | string }
|
|
| Body<any, any>
|
|
| Template
|
|
| string;
|
|
|
|
/** Extract the return tag type from a renderBody. */
|
|
export type BodyReturnType<B> =
|
|
B extends Body<any, infer Return> ? Return : never;
|
|
|
|
/** Extract the tag parameter types received by a renderBody. */
|
|
export type BodyParameters<B> =
|
|
B extends Body<infer Params, any> ? Params : never;
|
|
|
|
export class Component<Input = unknown, State = unknown>
|
|
implements Emitter
|
|
{
|
|
/** A unique id for this instance. */
|
|
public readonly id: string;
|
|
/** The top level element rendered by this instance. */
|
|
public readonly el: Element | void;
|
|
/** The attributes passed to this instance. */
|
|
public readonly input: Input;
|
|
/** @deprecated */
|
|
public readonly els: Element[];
|
|
/** Mutable state that when changed causes a rerender. */
|
|
state: State;
|
|
|
|
/** Returns the amount of event handlers listening to a specific event. */
|
|
listenerCount(eventName: PropertyKey): number;
|
|
/**
|
|
* Used to wrap an existing event emitted and ensure that all events are
|
|
* cleaned up once this component is destroyed
|
|
* */
|
|
subscribeTo(
|
|
emitter: unknown,
|
|
): Omit<Emitter, "listenerCount" | "prependListener" | "emit">;
|
|
/** Emits an event on the component instance. */
|
|
emit(eventName: PropertyKey, ...args: any[]): boolean;
|
|
/** Listen to an event on the component instance. */
|
|
on(eventName: PropertyKey, listener: (...args: any[]) => any): this;
|
|
/** Listen to an event on the component instance once. */
|
|
once(eventName: PropertyKey, listener: (...args: any[]) => any): this;
|
|
/** Listen to an event on the component instance before all other listeners. */
|
|
prependListener(
|
|
eventName: PropertyKey,
|
|
listener: (...args: any[]) => any,
|
|
): this;
|
|
/** Remove a listener from the component instance. */
|
|
removeListener(
|
|
eventName: PropertyKey,
|
|
listener: (...args: any[]) => any,
|
|
): this;
|
|
/** Remove all listeners from the component instance. */
|
|
removeAllListeners(eventName?: PropertyKey): this;
|
|
/** Removes the component instance from the DOM and cleans up all active event handlers including all children. */
|
|
destroy(): void;
|
|
/** Schedule an update (similar to if a state had been changed). */
|
|
forceUpdate(): void;
|
|
/** Generates a unique id derived from the current unique instance id (similar to :scoped in the template). */
|
|
elId(key?: string, index?: number): string;
|
|
/** @alias elId */
|
|
getElId(key?: string, index?: number): string;
|
|
/** Gets an element reference by its `key` attribute in the template. */
|
|
getEl<T extends Element | void = Element | void>(
|
|
key?: string,
|
|
index?: number,
|
|
): T;
|
|
/** Gets all element references by their `key` attribute in the template. */
|
|
getEls<T extends Element[] = Element[]>(key: string): T;
|
|
/** Gets a component reference by its `key` attribute in the template. */
|
|
getComponent<T extends Component | void = Component | void>(
|
|
key: string,
|
|
index?: number,
|
|
): T;
|
|
/** Gets all component references by their `key` attribute in the template. */
|
|
getComponents<T extends Component[] = Component[]>(key: string): T;
|
|
/** True if this instance has been removed from the dom. */
|
|
/** True if this instance is scheduled to rerender. */
|
|
isDestroyed(): boolean;
|
|
/** Replace the entire state object with a new one, removing old properties. */
|
|
replaceState(state: this["state"]): void;
|
|
/**
|
|
* Update a property on this.state (should prefer mutating this.state directly).
|
|
* When passed an object as the first argument, it will be merged into the state.
|
|
*/
|
|
setState<Key extends PropertyKey>(
|
|
name: Key & keyof this["state"],
|
|
value: (this["state"] & Record<PropertyKey, unknown>)[Key],
|
|
): void;
|
|
setState(value: Partial<this["state"]>): void;
|
|
|
|
/** Schedules an update related to a specific state property and optionally updates the value. */
|
|
setStateDirty<Key extends PropertyKey>(
|
|
name: Key & keyof this["state"],
|
|
value?: (this["state"] & Record<PropertyKey, unknown>)[Key],
|
|
): void;
|
|
/** Synchronously flush any scheduled updates. */
|
|
update(): void;
|
|
/** Appends the dom for the current instance to a parent DOM element. */
|
|
appendTo(target: ParentNode): this;
|
|
/** Inserts the dom for the current instance after a sibling DOM element. */
|
|
insertAfter(target: ChildNode): this;
|
|
/** Inserts the dom for the current instance before a sibling DOM element. */
|
|
insertBefore(target: ChildNode): this;
|
|
/** Prepends the dom for the current instance to a parent DOM element. */
|
|
prependTo(target: ParentNode): this;
|
|
/** Replaces an existing DOM element with the dom for the current instance. */
|
|
replace(target: ChildNode): this;
|
|
/** Replaces the children of an existing DOM element with the dom for the current instance. */
|
|
replaceChildrenOf(target: ParentNode): this;
|
|
// /** Called when the component is firsted created. */
|
|
// onCreate?(input: this["input"], out: Marko.Out): void;
|
|
// /** Called every time the component receives input from it's parent. */
|
|
// onInput?(input: this["input"], out: Marko.Out): void | this["input"];
|
|
// /** Called after a component has successfully rendered, but before it's update has been applied to the dom. */
|
|
// onRender?(out: Marko.Out): void;
|
|
// /** Called after the first time the component renders and is attached to the dom. */
|
|
// onMount?(): void;
|
|
// /** Called when a components render has been applied to the DOM (excluding when it is initially mounted). */
|
|
// onUpdate?(): void;
|
|
// /** Called when a component is destroyed and removed from the dom. */
|
|
// onDestroy?(): void;
|
|
}
|
|
|
|
/** The top level api for a Marko Template. */
|
|
export abstract class Template<Input = unknown, Return = unknown> {
|
|
/** Creates a Marko compatible output stream. */
|
|
createOut(): Out;
|
|
|
|
/**
|
|
* The folowing types are processed up by the @marko/language-tools
|
|
* and inlined into the compiled template.
|
|
*
|
|
* This is done to support generics on each of these methods
|
|
* until TypeScript supports higher kinded types.
|
|
*
|
|
* https://github.com/microsoft/TypeScript/issues/1213
|
|
*/
|
|
|
|
/** @marko-overload-start */
|
|
/** Asynchronously render the template. */
|
|
abstract render(
|
|
input: Marko.TemplateInput<Input>,
|
|
stream?: {
|
|
write: (chunk: string) => void;
|
|
end: (chunk?: string) => void;
|
|
},
|
|
): Marko.Out<Marko.Component>;
|
|
|
|
/** Asynchronously render the template in buffered mode. */
|
|
abstract render(
|
|
input: Marko.TemplateInput<Input>,
|
|
cb?: (
|
|
err: Error | null,
|
|
result: Marko.RenderResult<Marko.Component>,
|
|
) => void,
|
|
): Marko.Out<Marko.Component>;
|
|
|
|
/** Synchronously render the template. */
|
|
abstract renderSync(
|
|
input: Marko.TemplateInput<Input>,
|
|
): Marko.RenderResult<Marko.Component>;
|
|
|
|
/** Synchronously render a template to a string. */
|
|
abstract renderToString(input: Marko.TemplateInput<Input>): string;
|
|
|
|
/** Render a template and return a stream.Readable in nodejs or a ReadableStream in a web worker environment. */
|
|
abstract stream(
|
|
input: Marko.TemplateInput<Input>,
|
|
): ReadableStream<string> & NodeJS.ReadableStream;
|
|
|
|
/** Render and attach the template to a DOM node. */
|
|
abstract mount(
|
|
input: Marko.TemplateInput<Input>,
|
|
reference: Node,
|
|
position?: "afterbegin" | "afterend" | "beforebegin" | "beforeend",
|
|
): Marko.MountedTemplate<typeof input>;
|
|
/** @marko-overload-end */
|
|
}
|
|
|
|
export interface RenderResult<
|
|
out Component extends Marko.Component = Marko.Component,
|
|
> {
|
|
/** Returns the component created as a result of rendering the template. */
|
|
getComponent(): Component;
|
|
getComponents(selector?: any): any;
|
|
/** Triggers the mount lifecycle of a component without necessarily attaching it to the DOM. */
|
|
afterInsert(host?: any): this;
|
|
/** Gets the DOM node rendered by a template. */
|
|
getNode(host?: any): Node;
|
|
/** Gets the HTML output of the rendered template. */
|
|
toString(): string;
|
|
/** Appends the dom of the rendered template to a parent DOM element. */
|
|
appendTo(target: ParentNode): this;
|
|
/** Inserts the dom of the rendered template after a sibling DOM element. */
|
|
insertAfter(target: ChildNode): this;
|
|
/** Inserts the dom of the rendered template before a sibling DOM element. */
|
|
insertBefore(target: ChildNode): this;
|
|
/** Prepends the dom of the rendered template to a parent DOM element. */
|
|
prependTo(target: ParentNode): this;
|
|
/** Replaces an existing DOM element with the dom of the rendered template. */
|
|
replace(target: ChildNode): this;
|
|
/** Replaces the children of an existing DOM element with the dom of the rendered template. */
|
|
replaceChildrenOf(target: ParentNode): this;
|
|
out: Out<Component>;
|
|
/** @deprecated */
|
|
document: any;
|
|
/** @deprecated */
|
|
getOutput(): string;
|
|
/** @deprecated */
|
|
html: string;
|
|
/** @deprecated */
|
|
context: Out<Component>;
|
|
}
|
|
|
|
export interface Emitter {
|
|
listenerCount(eventName: PropertyKey): number;
|
|
emit(eventName: PropertyKey, ...args: any[]): boolean;
|
|
on(eventName: PropertyKey, listener: (...args: any[]) => any): this;
|
|
once(eventName: PropertyKey, listener: (...args: any[]) => any): this;
|
|
prependListener(
|
|
eventName: PropertyKey,
|
|
listener: (...args: any[]) => any,
|
|
): this;
|
|
removeListener(
|
|
eventName: PropertyKey,
|
|
listener: (...args: any[]) => any,
|
|
): this;
|
|
removeAllListeners(eventName?: PropertyKey): this;
|
|
}
|
|
|
|
export type AttrTag<T> = T & {
|
|
[Symbol.iterator](): Iterator<T>;
|
|
};
|
|
|
|
export interface NativeTag<
|
|
Input extends Record<string, any>,
|
|
Return extends Element,
|
|
> {
|
|
input: Input;
|
|
return: { value: () => Return };
|
|
}
|
|
export interface NativeTags {
|
|
[name: string]: NativeTag<Record<string, any>, Element>;
|
|
}
|
|
|
|
export type Input<Name> = 0 extends 1 & Name
|
|
? any
|
|
: Name extends string
|
|
? Name extends keyof NativeTags
|
|
? NativeTags[Name]["input"]
|
|
: Record<string, unknown>
|
|
: Name extends
|
|
| Template<infer Input, any>
|
|
| { _(): () => (input: infer Input) => any }
|
|
? Input
|
|
: Name extends Body<infer Args, any>
|
|
? Args extends {
|
|
length: infer Length;
|
|
}
|
|
? number extends Length
|
|
? Args[0] | undefined
|
|
: 0 extends Length
|
|
? undefined
|
|
: Args[0]
|
|
: never
|
|
: never;
|
|
|
|
export type Return<Name> = 0 extends 1 & Name
|
|
? any
|
|
: Name extends string
|
|
? Name extends keyof NativeTags
|
|
? NativeTags[Name]["return"]
|
|
: () => Element
|
|
: Name extends
|
|
| { _(): () => (input: any) => { return: infer Return } }
|
|
| Template<any, infer Return>
|
|
| Body<any, infer Return>
|
|
? Return
|
|
: never;
|
|
|
|
/** @deprecated @see {@link Marko.AttrTag} */
|
|
export type RepeatableAttrTag<T> = AttrTag<T>;
|
|
|
|
/** @deprecated @see {@link Marko.Input} */
|
|
export type NativeTagInput<Name extends keyof NativeTags> =
|
|
NativeTags[Name]["input"];
|
|
/** @deprecated @see {@link Marko.Return} */
|
|
export type NativeTagReturn<Name extends keyof NativeTags> =
|
|
NativeTags[Name]["return"];
|
|
}
|
|
}
|