@dom-expressions/runtime 0.50.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +348 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/package.json +36 -0
- package/src/client.d.ts +117 -0
- package/src/client.js +893 -0
- package/src/constants.d.ts +23 -0
- package/src/constants.js +517 -0
- package/src/jsx-customize.mjs +58 -0
- package/src/jsx-h.d.ts +4148 -0
- package/src/jsx-properties.d.ts +93 -0
- package/src/jsx-update.mjs +96 -0
- package/src/jsx.d.ts +4134 -0
- package/src/reconcile.d.ts +1 -0
- package/src/reconcile.js +149 -0
- package/src/serializer.d.ts +3 -0
- package/src/serializer.js +54 -0
- package/src/server.d.ts +193 -0
- package/src/server.js +1302 -0
- package/src/universal.d.ts +38 -0
- package/src/universal.js +352 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function reconcileArrays(parentNode: Node, a: Node[], b: Node[]): void;
|
package/src/reconcile.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Slightly modified version of: https://github.com/WebReflection/udomdiff/blob/master/index.js
|
|
2
|
+
import { $$SLOT } from "./constants";
|
|
3
|
+
|
|
4
|
+
export default function reconcileArrays(parentNode, a, b, marker) {
|
|
5
|
+
let bLength = b.length,
|
|
6
|
+
aEnd = a.length,
|
|
7
|
+
bEnd = bLength,
|
|
8
|
+
aStart = 0,
|
|
9
|
+
bStart = 0,
|
|
10
|
+
tail = a[aEnd - 1],
|
|
11
|
+
tailTag = tail[$$SLOT],
|
|
12
|
+
// Ownership tag: an unclaimed node (no `$$SLOT`) is fair game; a tagged
|
|
13
|
+
// node belongs only to the slot whose marker matches. If `a`'s tail has
|
|
14
|
+
// migrated to another slot — same parent or otherwise — `tail.nextSibling`
|
|
15
|
+
// points into a region we don't own, so fall back to `marker`. `marker ||
|
|
16
|
+
// null` keeps non-multi (root-mode) callers happy.
|
|
17
|
+
after =
|
|
18
|
+
tail.parentNode === parentNode && (!tailTag || tailTag === marker)
|
|
19
|
+
? tail.nextSibling
|
|
20
|
+
: marker || null,
|
|
21
|
+
map = null,
|
|
22
|
+
anchor,
|
|
23
|
+
anchorTag;
|
|
24
|
+
|
|
25
|
+
while (aStart < aEnd || bStart < bEnd) {
|
|
26
|
+
// common prefix
|
|
27
|
+
if (a[aStart] === b[bStart]) {
|
|
28
|
+
aStart++;
|
|
29
|
+
bStart++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
// common suffix
|
|
33
|
+
while (a[aEnd - 1] === b[bEnd - 1]) {
|
|
34
|
+
aEnd--;
|
|
35
|
+
bEnd--;
|
|
36
|
+
}
|
|
37
|
+
// append
|
|
38
|
+
if (aEnd === aStart) {
|
|
39
|
+
let node;
|
|
40
|
+
if (bEnd < bLength) {
|
|
41
|
+
if (bStart) {
|
|
42
|
+
const prev = b[bStart - 1];
|
|
43
|
+
const prevTag = prev[$$SLOT];
|
|
44
|
+
node =
|
|
45
|
+
prev.parentNode === parentNode && (!prevTag || prevTag === marker)
|
|
46
|
+
? prev.nextSibling
|
|
47
|
+
: after;
|
|
48
|
+
} else node = b[bEnd - bStart];
|
|
49
|
+
} else node = after;
|
|
50
|
+
|
|
51
|
+
while (bStart < bEnd) {
|
|
52
|
+
const n = b[bStart++];
|
|
53
|
+
parentNode.insertBefore(n, node);
|
|
54
|
+
if (marker) n[$$SLOT] = marker;
|
|
55
|
+
}
|
|
56
|
+
// remove
|
|
57
|
+
} else if (bEnd === bStart) {
|
|
58
|
+
while (aStart < aEnd) {
|
|
59
|
+
const n = a[aStart++];
|
|
60
|
+
if (!map || !map.has(n)) {
|
|
61
|
+
const tag = n[$$SLOT];
|
|
62
|
+
if (n.parentNode === parentNode && (!tag || tag === marker)) n.remove();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// swap backward — symmetric end-swap detected. Walk inward with a single
|
|
66
|
+
// stable front anchor (a[aStart]); each move targets the same DOM-position
|
|
67
|
+
// so the browser's adjacency cache stays warm and per-call native
|
|
68
|
+
// `insertBefore` cost drops sharply on reorder-heavy patterns (e.g. reverse).
|
|
69
|
+
// Only optimize when the anchor still belongs to us; otherwise fall through
|
|
70
|
+
// to the map branch which gates each destructive op. The anchor and its
|
|
71
|
+
// tag are read once per detected swap and reused — important on hot
|
|
72
|
+
// reorder benches (`reconcile-permute reverse`) where this branch fires
|
|
73
|
+
// on every inner-loop step.
|
|
74
|
+
} else if (
|
|
75
|
+
(anchor = a[aStart]) === b[bEnd - 1] &&
|
|
76
|
+
b[bStart] === a[aEnd - 1] &&
|
|
77
|
+
anchor.parentNode === parentNode &&
|
|
78
|
+
(!(anchorTag = anchor[$$SLOT]) || anchorTag === marker)
|
|
79
|
+
) {
|
|
80
|
+
// Tightest inner loop in the file; one `insertBefore` per iter plus an
|
|
81
|
+
// end-condition probe. Splitting on `marker` avoids a per-iter branch in
|
|
82
|
+
// the hot path — js-framework-benchmark `05_swap1k` regresses ~6.5% when
|
|
83
|
+
// this is collapsed (validated 2026-05-16 on Chrome headless).
|
|
84
|
+
if (marker) {
|
|
85
|
+
do {
|
|
86
|
+
const n = a[--aEnd];
|
|
87
|
+
parentNode.insertBefore(n, anchor);
|
|
88
|
+
n[$$SLOT] = marker;
|
|
89
|
+
bStart++;
|
|
90
|
+
if (aStart >= aEnd - 1 || bStart >= bEnd) break;
|
|
91
|
+
} while (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]);
|
|
92
|
+
} else {
|
|
93
|
+
do {
|
|
94
|
+
parentNode.insertBefore(a[--aEnd], anchor);
|
|
95
|
+
bStart++;
|
|
96
|
+
if (aStart >= aEnd - 1 || bStart >= bEnd) break;
|
|
97
|
+
} while (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]);
|
|
98
|
+
}
|
|
99
|
+
// fallback to map
|
|
100
|
+
} else {
|
|
101
|
+
if (!map) {
|
|
102
|
+
map = new Map();
|
|
103
|
+
let i = bStart;
|
|
104
|
+
|
|
105
|
+
while (i < bEnd) map.set(b[i], i++);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const index = map.get(a[aStart]);
|
|
109
|
+
if (index != null) {
|
|
110
|
+
if (bStart < index && index < bEnd) {
|
|
111
|
+
let i = aStart,
|
|
112
|
+
sequence = 1,
|
|
113
|
+
t;
|
|
114
|
+
|
|
115
|
+
while (++i < aEnd && i < bEnd) {
|
|
116
|
+
if ((t = map.get(a[i])) == null || t !== index + sequence) break;
|
|
117
|
+
sequence++;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (sequence > index - bStart) {
|
|
121
|
+
const head = a[aStart];
|
|
122
|
+
const headTag = head[$$SLOT];
|
|
123
|
+
const node =
|
|
124
|
+
head.parentNode === parentNode && (!headTag || headTag === marker) ? head : after;
|
|
125
|
+
while (bStart < index) {
|
|
126
|
+
const n = b[bStart++];
|
|
127
|
+
parentNode.insertBefore(n, node);
|
|
128
|
+
if (marker) n[$$SLOT] = marker;
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
const oldNode = a[aStart++];
|
|
132
|
+
const newNode = b[bStart++];
|
|
133
|
+
const oldTag = oldNode[$$SLOT];
|
|
134
|
+
if (oldNode.parentNode === parentNode && (!oldTag || oldTag === marker)) {
|
|
135
|
+
parentNode.replaceChild(newNode, oldNode);
|
|
136
|
+
} else {
|
|
137
|
+
parentNode.insertBefore(newNode, after);
|
|
138
|
+
}
|
|
139
|
+
if (marker) newNode[$$SLOT] = marker;
|
|
140
|
+
}
|
|
141
|
+
} else aStart++;
|
|
142
|
+
} else {
|
|
143
|
+
const n = a[aStart++];
|
|
144
|
+
const nTag = n[$$SLOT];
|
|
145
|
+
if (n.parentNode === parentNode && (!nTag || nTag === marker)) n.remove();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Feature, Serializer, getCrossReferenceHeader } from "seroval";
|
|
2
|
+
import {
|
|
3
|
+
AbortSignalPlugin,
|
|
4
|
+
CustomEventPlugin,
|
|
5
|
+
DOMExceptionPlugin,
|
|
6
|
+
EventPlugin,
|
|
7
|
+
FormDataPlugin,
|
|
8
|
+
HeadersPlugin,
|
|
9
|
+
ReadableStreamPlugin,
|
|
10
|
+
RequestPlugin,
|
|
11
|
+
ResponsePlugin,
|
|
12
|
+
URLPlugin,
|
|
13
|
+
URLSearchParamsPlugin
|
|
14
|
+
} from "seroval-plugins/web";
|
|
15
|
+
|
|
16
|
+
const ES2017FLAG =
|
|
17
|
+
Feature.AggregateError | // ES2021
|
|
18
|
+
Feature.BigIntTypedArray; // ES2020;
|
|
19
|
+
|
|
20
|
+
const GLOBAL_IDENTIFIER = "_$HY.r"; // TODO this is a pending name
|
|
21
|
+
|
|
22
|
+
export function createSerializer({ onData, onDone, scopeId, onError, plugins: customPlugins }) {
|
|
23
|
+
const defaultPlugins = [
|
|
24
|
+
AbortSignalPlugin,
|
|
25
|
+
// BlobPlugin,
|
|
26
|
+
CustomEventPlugin,
|
|
27
|
+
DOMExceptionPlugin,
|
|
28
|
+
EventPlugin,
|
|
29
|
+
// FilePlugin,
|
|
30
|
+
FormDataPlugin,
|
|
31
|
+
HeadersPlugin,
|
|
32
|
+
ReadableStreamPlugin,
|
|
33
|
+
RequestPlugin,
|
|
34
|
+
ResponsePlugin,
|
|
35
|
+
URLSearchParamsPlugin,
|
|
36
|
+
URLPlugin
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const allPlugins = customPlugins ? [...customPlugins, ...defaultPlugins] : defaultPlugins;
|
|
40
|
+
|
|
41
|
+
return new Serializer({
|
|
42
|
+
scopeId,
|
|
43
|
+
plugins: allPlugins,
|
|
44
|
+
globalIdentifier: GLOBAL_IDENTIFIER,
|
|
45
|
+
disabledFeatures: ES2017FLAG,
|
|
46
|
+
onData,
|
|
47
|
+
onDone,
|
|
48
|
+
onError
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getLocalHeaderScript(id) {
|
|
53
|
+
return getCrossReferenceHeader(id) + ";";
|
|
54
|
+
}
|
package/src/server.d.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { JSX } from "./jsx.js";
|
|
2
|
+
export const DOMWithState: Record<string, Record<string, 1 | 2>>;
|
|
3
|
+
export const ChildProperties: Set<string>;
|
|
4
|
+
export const DelegatedEvents: Set<string>;
|
|
5
|
+
export const DOMElements: Set<string>;
|
|
6
|
+
export const SVGElements: Set<string>;
|
|
7
|
+
export const MathMLElements: Set<string>;
|
|
8
|
+
export const VoidElements: Set<string>;
|
|
9
|
+
export const RawTextElements: Set<string>;
|
|
10
|
+
export const Namespaces: Record<string, string>;
|
|
11
|
+
|
|
12
|
+
type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;
|
|
13
|
+
|
|
14
|
+
export function renderToString<T>(
|
|
15
|
+
fn: () => T,
|
|
16
|
+
options?: {
|
|
17
|
+
nonce?: string;
|
|
18
|
+
renderId?: string;
|
|
19
|
+
noScripts?: boolean;
|
|
20
|
+
plugins?: any[];
|
|
21
|
+
manifest?: Record<
|
|
22
|
+
string,
|
|
23
|
+
{ file: string; css?: string[]; isEntry?: boolean; imports?: string[] }
|
|
24
|
+
> & { _base?: string };
|
|
25
|
+
onError?: (err: any) => void;
|
|
26
|
+
}
|
|
27
|
+
): string;
|
|
28
|
+
/** @deprecated use renderToStream which also returns a promise */
|
|
29
|
+
export function renderToStringAsync<T>(
|
|
30
|
+
fn: () => T,
|
|
31
|
+
options?: {
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
nonce?: string;
|
|
34
|
+
renderId?: string;
|
|
35
|
+
noScripts?: boolean;
|
|
36
|
+
plugins?: any[];
|
|
37
|
+
manifest?: Record<
|
|
38
|
+
string,
|
|
39
|
+
{ file: string; css?: string[]; isEntry?: boolean; imports?: string[] }
|
|
40
|
+
> & { _base?: string };
|
|
41
|
+
onError?: (err: any) => void;
|
|
42
|
+
}
|
|
43
|
+
): Promise<string>;
|
|
44
|
+
export function renderToStream<T>(
|
|
45
|
+
fn: () => T,
|
|
46
|
+
options?: {
|
|
47
|
+
nonce?: string;
|
|
48
|
+
renderId?: string;
|
|
49
|
+
noScripts?: boolean;
|
|
50
|
+
plugins?: any[];
|
|
51
|
+
manifest?: Record<
|
|
52
|
+
string,
|
|
53
|
+
{ file: string; css?: string[]; isEntry?: boolean; imports?: string[] }
|
|
54
|
+
> & { _base?: string };
|
|
55
|
+
onCompleteShell?: (info: { write: (v: string) => void }) => void;
|
|
56
|
+
onCompleteAll?: (info: { write: (v: string) => void }) => void;
|
|
57
|
+
onError?: (err: any) => void;
|
|
58
|
+
}
|
|
59
|
+
): {
|
|
60
|
+
then: (fn: (html: string) => void) => void;
|
|
61
|
+
pipe: (writable: { write: (v: string) => void; end: () => void }) => void;
|
|
62
|
+
pipeTo: (writable: WritableStream) => Promise<void>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export function HydrationScript(props: { nonce?: string; eventNames?: string[] }): JSX.Element;
|
|
66
|
+
export function ssr(template: string[] | string, ...nodes: any[]): { t: string };
|
|
67
|
+
export function ssrElement(
|
|
68
|
+
name: string,
|
|
69
|
+
props: any,
|
|
70
|
+
children: any,
|
|
71
|
+
needsId: boolean
|
|
72
|
+
): { t: string };
|
|
73
|
+
export function ssrClassName(value: string | { [k: string]: boolean } | Array<any>): string;
|
|
74
|
+
export function ssrStyle(value: string | { [k: string]: string }): string;
|
|
75
|
+
export function ssrStyleProperty(name: string, value: any): string;
|
|
76
|
+
export function ssrAttribute(key: string, value: any): string;
|
|
77
|
+
export function ssrGroup<T extends () => any[]>(fn: T, n: number): T;
|
|
78
|
+
export function ssrHydrationKey(): string;
|
|
79
|
+
export function resolveSSRNode(node: any, result?: any, top?: boolean): any;
|
|
80
|
+
export function escape(s: any, attr?: boolean): any;
|
|
81
|
+
export function applyRef(
|
|
82
|
+
r: ((element: any) => void) | ((element: any) => void)[],
|
|
83
|
+
element: any
|
|
84
|
+
): void;
|
|
85
|
+
export function useAssets(fn: () => JSX.Element): void;
|
|
86
|
+
export function getAssets(): string;
|
|
87
|
+
export function getHydrationKey(): string | undefined;
|
|
88
|
+
export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void): void;
|
|
89
|
+
export function memo<T>(fn: () => T, equal: boolean): () => T;
|
|
90
|
+
export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element;
|
|
91
|
+
export function mergeProps(...sources: unknown[]): unknown;
|
|
92
|
+
export function getOwner(): unknown;
|
|
93
|
+
export function generateHydrationScript(options?: {
|
|
94
|
+
nonce?: string;
|
|
95
|
+
eventNames?: string[];
|
|
96
|
+
}): string;
|
|
97
|
+
export declare const RequestContext: unique symbol;
|
|
98
|
+
export interface RequestEvent {
|
|
99
|
+
request: Request;
|
|
100
|
+
locals: Record<string | number | symbol, any>;
|
|
101
|
+
}
|
|
102
|
+
export function getRequestEvent(): RequestEvent | undefined;
|
|
103
|
+
|
|
104
|
+
export function Assets(props: { children?: JSX.Element }): JSX.Element;
|
|
105
|
+
export function untrack<T>(fn: () => T): T;
|
|
106
|
+
|
|
107
|
+
// client-only APIs
|
|
108
|
+
|
|
109
|
+
/** @deprecated not supported on the server side */
|
|
110
|
+
export function style(
|
|
111
|
+
node: Element,
|
|
112
|
+
value: { [k: string]: string },
|
|
113
|
+
prev?: { [k: string]: string }
|
|
114
|
+
): void;
|
|
115
|
+
|
|
116
|
+
/** @deprecated not supported on the server side */
|
|
117
|
+
export function insert<T>(
|
|
118
|
+
parent: MountableElement,
|
|
119
|
+
accessor: (() => T) | T,
|
|
120
|
+
marker?: Node | null,
|
|
121
|
+
init?: JSX.Element
|
|
122
|
+
): JSX.Element;
|
|
123
|
+
|
|
124
|
+
/** @deprecated not supported on the server side */
|
|
125
|
+
export function spread<T>(node: Element, accessor: T, skipChildren?: Boolean): void;
|
|
126
|
+
|
|
127
|
+
/** @deprecated not supported on the server side */
|
|
128
|
+
export function delegateEvents(eventNames: string[]): void;
|
|
129
|
+
/** @deprecated not supported on the server side */
|
|
130
|
+
export function registerDelegatedRoot(root: MountableElement): void;
|
|
131
|
+
/** @deprecated not supported on the server side */
|
|
132
|
+
export function unregisterDelegatedRoot(root: MountableElement): void;
|
|
133
|
+
/** @deprecated not supported on the server side */
|
|
134
|
+
export function registerDelegatedContainer(
|
|
135
|
+
container: MountableElement,
|
|
136
|
+
owner?: MountableElement
|
|
137
|
+
): void;
|
|
138
|
+
/** @deprecated not supported on the server side */
|
|
139
|
+
export function unregisterDelegatedContainer(
|
|
140
|
+
container: MountableElement,
|
|
141
|
+
owner?: MountableElement
|
|
142
|
+
): void;
|
|
143
|
+
/** @deprecated not supported on the server side */
|
|
144
|
+
export function getDelegatedRoot(node: MountableElement): MountableElement | undefined;
|
|
145
|
+
/** @deprecated not supported on the server side */
|
|
146
|
+
export function dynamicProperty(props: unknown, key: string): unknown;
|
|
147
|
+
/** @deprecated not supported on the server side */
|
|
148
|
+
export function setAttribute(node: Element, name: string, value: string): void;
|
|
149
|
+
/** @deprecated not supported on the server side */
|
|
150
|
+
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
|
|
151
|
+
|
|
152
|
+
/** @deprecated not supported on the server side */
|
|
153
|
+
export function addEvent(node: Element, name: string, handler: () => void, delegate: boolean): void;
|
|
154
|
+
|
|
155
|
+
/** @deprecated not supported on the server side */
|
|
156
|
+
export function render(code: () => JSX.Element, element: MountableElement): () => void;
|
|
157
|
+
/**
|
|
158
|
+
* @deprecated not supported on the server side
|
|
159
|
+
* @param flag
|
|
160
|
+
* - `undefined` — clone the template as-is (uses `cloneNode`).
|
|
161
|
+
* - `1` — use `document.importNode` instead of `cloneNode`.
|
|
162
|
+
* - `2` — the template html is wrapped; the outer tag is stripped at clone time.
|
|
163
|
+
*/
|
|
164
|
+
export function template(html: string, flag?: 1 | 2): () => Element;
|
|
165
|
+
/** @deprecated not supported on the server side */
|
|
166
|
+
export function setProperty(node: Element, name: string, value: any): void;
|
|
167
|
+
/** @deprecated not supported on the server side */
|
|
168
|
+
export function className(node: Element, value: string): void;
|
|
169
|
+
/** @deprecated not supported on the server side */
|
|
170
|
+
export function assign(node: Element, props: any, skipChildren?: Boolean): void;
|
|
171
|
+
|
|
172
|
+
/** @deprecated not supported on the server side */
|
|
173
|
+
export function hydrate(
|
|
174
|
+
fn: () => JSX.Element,
|
|
175
|
+
node: MountableElement,
|
|
176
|
+
options?: { renderId?: string; owner?: unknown }
|
|
177
|
+
): () => void;
|
|
178
|
+
|
|
179
|
+
/** @deprecated not supported on the server side */
|
|
180
|
+
export function getNextElement(template?: () => Element): Element;
|
|
181
|
+
/** @deprecated not supported on the server side */
|
|
182
|
+
export function getNextMatch(start: Node, elementName: string): Element;
|
|
183
|
+
/** @deprecated not supported on the server side */
|
|
184
|
+
export function getNextMarker(start: Node): [Node, Array<Node>];
|
|
185
|
+
/** @deprecated not supported on the server side */
|
|
186
|
+
export function runHydrationEvents(): void;
|
|
187
|
+
/** @deprecated not supported on the server side */
|
|
188
|
+
export function ref(
|
|
189
|
+
fn: () => ((element: Element) => void) | ((element: Element) => void)[],
|
|
190
|
+
element: Element
|
|
191
|
+
): void;
|
|
192
|
+
/** @deprecated not supported on the server side */
|
|
193
|
+
export function setStyleProperty(node: Element, name: string, value: any): void;
|