elements-kit 0.18.2 → 0.20.0
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/README.md +14 -0
- package/dist/await.d.mts +53 -0
- package/dist/await.mjs +70 -0
- package/dist/custom-elements.d.mts +1 -1
- package/dist/{element-xQwq4ulc.mjs → element-D3om9yA6.mjs} +71 -4
- package/dist/for.d.mts +20 -26
- package/dist/for.mjs +29 -4
- package/dist/fragment-ROPRBJQd.mjs +61 -0
- package/dist/hydrate/index.d.mts +41 -0
- package/dist/hydrate/index.mjs +2 -0
- package/dist/hydrate-6Qx4n4Su.mjs +380 -0
- package/dist/integrations/astro-client.d.mts +17 -0
- package/dist/integrations/astro-client.mjs +23 -0
- package/dist/integrations/astro-server.d.mts +15 -0
- package/dist/integrations/astro-server.mjs +39 -0
- package/dist/integrations/astro-slots.d.mts +12 -0
- package/dist/integrations/astro-slots.mjs +30 -0
- package/dist/integrations/astro.d.mts +46 -0
- package/dist/integrations/astro.mjs +64 -0
- package/dist/integrations/react.d.mts +1 -1
- package/dist/integrations/react.mjs +2 -2
- package/dist/jsx-runtime/index.d.mts +1 -1
- package/dist/jsx-runtime/index.mjs +2 -20
- package/dist/{lib-Dkc7cV5F.mjs → lib-DYypKhxk.mjs} +63 -5
- package/dist/render.mjs +1 -1
- package/dist/{scope-CnEvL9Nd.mjs → scope-zbEG299F.mjs} +1 -1
- package/dist/server/index.d.mts +43 -0
- package/dist/server/index.mjs +2 -0
- package/dist/server-iFSng50i.mjs +385 -0
- package/dist/signals/index.d.mts +2 -2
- package/dist/signals/index.mjs +5 -3
- package/dist/{slot-B8y0aEoz.d.mts → slot-BFWXeFRl.d.mts} +6 -0
- package/dist/{slot-Cne__au7.mjs → slot-Dvf30O0B.mjs} +12 -1
- package/dist/slot.d.mts +1 -1
- package/dist/slot.mjs +1 -1
- package/dist/{infer-B7T5hi_D.d.mts → types-ncUK9rki.d.mts} +140 -141
- package/dist/ui/overlay/index.css +70 -0
- package/dist/ui/overlay/index.d.mts +209 -0
- package/dist/ui/overlay/index.mjs +1001 -0
- package/dist/ui/overlay/overlay.css +332 -0
- package/dist/utilities/_observe.mjs +1 -1
- package/dist/utilities/active-element.d.mts +1 -1
- package/dist/utilities/active-element.mjs +1 -1
- package/dist/utilities/async.d.mts +19 -2
- package/dist/utilities/async.mjs +49 -2
- package/dist/utilities/context.mjs +1 -1
- package/dist/utilities/debounced.d.mts +1 -1
- package/dist/utilities/debounced.mjs +1 -1
- package/dist/utilities/dom-lifecycle.bench.mjs +3255 -1
- package/dist/utilities/dom-lifecycle.mjs +1 -1
- package/dist/utilities/element-rect.d.mts +1 -1
- package/dist/utilities/element-rect.mjs +1 -1
- package/dist/utilities/element-scroll.d.mts +1 -1
- package/dist/utilities/event-driven.d.mts +1 -1
- package/dist/utilities/event-driven.mjs +1 -1
- package/dist/utilities/event-listener.d.mts +1 -1
- package/dist/utilities/event-listener.mjs +1 -1
- package/dist/utilities/focus-within.d.mts +1 -1
- package/dist/utilities/focus-within.mjs +1 -1
- package/dist/utilities/form-object.d.mts +96 -0
- package/dist/utilities/form-object.mjs +285 -0
- package/dist/utilities/hover.d.mts +1 -1
- package/dist/utilities/hover.mjs +1 -1
- package/dist/utilities/interval.d.mts +1 -1
- package/dist/utilities/interval.mjs +1 -1
- package/dist/utilities/location.d.mts +1 -1
- package/dist/utilities/location.mjs +1 -1
- package/dist/utilities/media-devices.d.mts +1 -1
- package/dist/utilities/media-devices.mjs +1 -1
- package/dist/utilities/media-player.d.mts +1 -1
- package/dist/utilities/media-query.d.mts +1 -1
- package/dist/utilities/media-query.mjs +1 -1
- package/dist/utilities/network.d.mts +1 -1
- package/dist/utilities/network.mjs +1 -1
- package/dist/utilities/orientation.d.mts +1 -1
- package/dist/utilities/orientation.mjs +1 -1
- package/dist/utilities/previous.d.mts +1 -1
- package/dist/utilities/previous.mjs +1 -1
- package/dist/utilities/promise.d.mts +19 -2
- package/dist/utilities/promise.mjs +33 -3
- package/dist/utilities/retry.mjs +1 -1
- package/dist/utilities/routing.d.mts +1 -1
- package/dist/utilities/routing.mjs +1 -1
- package/dist/utilities/search-params.d.mts +1 -1
- package/dist/utilities/storage.d.mts +1 -1
- package/dist/utilities/throttled.d.mts +1 -1
- package/dist/utilities/throttled.mjs +1 -1
- package/dist/utilities/timeout.d.mts +1 -1
- package/dist/utilities/timeout.mjs +1 -1
- package/dist/utilities/window-focus.d.mts +1 -1
- package/dist/utilities/window-focus.mjs +1 -1
- package/dist/utilities/window-size.d.mts +1 -1
- package/dist/utilities/window-size.mjs +1 -1
- package/package.json +20 -5
- package/dist/benchmark.CX_oY03V-CSLAXL5f.mjs +0 -5145
- package/dist/magic-string.es-CXefOmkB.mjs +0 -1011
- package/dist/test.BmQO5GaM-Ddz9cDxc.mjs +0 -8266
- package/dist/utilities/active-element.test.d.mts +0 -1
- package/dist/utilities/active-element.test.mjs +0 -25
- package/dist/utilities/async.test.d.mts +0 -1
- package/dist/utilities/async.test.mjs +0 -275
- package/dist/utilities/context.test.d.mts +0 -1
- package/dist/utilities/context.test.mjs +0 -171
- package/dist/utilities/debounced.test.d.mts +0 -1
- package/dist/utilities/debounced.test.mjs +0 -68
- package/dist/utilities/dom-lifecycle.test.d.mts +0 -1
- package/dist/utilities/dom-lifecycle.test.mjs +0 -228
- package/dist/utilities/element-rect.test.d.mts +0 -1
- package/dist/utilities/element-rect.test.mjs +0 -101
- package/dist/utilities/element-scroll.test.d.mts +0 -1
- package/dist/utilities/element-scroll.test.mjs +0 -27
- package/dist/utilities/event-listener.test.d.mts +0 -1
- package/dist/utilities/event-listener.test.mjs +0 -60
- package/dist/utilities/focus-within.test.d.mts +0 -1
- package/dist/utilities/focus-within.test.mjs +0 -51
- package/dist/utilities/hover.test.d.mts +0 -1
- package/dist/utilities/hover.test.mjs +0 -54
- package/dist/utilities/intersection-observer.test.d.mts +0 -1
- package/dist/utilities/intersection-observer.test.mjs +0 -46
- package/dist/utilities/interval.test.d.mts +0 -1
- package/dist/utilities/interval.test.mjs +0 -115
- package/dist/utilities/location.test.d.mts +0 -1
- package/dist/utilities/location.test.mjs +0 -61
- package/dist/utilities/long-press.test.d.mts +0 -1
- package/dist/utilities/long-press.test.mjs +0 -54
- package/dist/utilities/media-devices.test.d.mts +0 -1
- package/dist/utilities/media-devices.test.mjs +0 -44
- package/dist/utilities/media-player.test.d.mts +0 -1
- package/dist/utilities/media-player.test.mjs +0 -102
- package/dist/utilities/mutation-observer.test.d.mts +0 -1
- package/dist/utilities/mutation-observer.test.mjs +0 -48
- package/dist/utilities/network.test.d.mts +0 -1
- package/dist/utilities/network.test.mjs +0 -30
- package/dist/utilities/on-click-outside.test.d.mts +0 -1
- package/dist/utilities/on-click-outside.test.mjs +0 -45
- package/dist/utilities/previous.test.d.mts +0 -1
- package/dist/utilities/previous.test.mjs +0 -90
- package/dist/utilities/promise.test.d.mts +0 -1
- package/dist/utilities/promise.test.mjs +0 -158
- package/dist/utilities/retry.test.d.mts +0 -1
- package/dist/utilities/retry.test.mjs +0 -85
- package/dist/utilities/routing.test.d.mts +0 -1
- package/dist/utilities/routing.test.mjs +0 -129
- package/dist/utilities/search-params.test.d.mts +0 -1
- package/dist/utilities/search-params.test.mjs +0 -37
- package/dist/utilities/ssr.test.d.mts +0 -1
- package/dist/utilities/ssr.test.mjs +0 -65
- package/dist/utilities/storage.test.d.mts +0 -1
- package/dist/utilities/storage.test.mjs +0 -139
- package/dist/utilities/throttled.test.d.mts +0 -1
- package/dist/utilities/throttled.test.mjs +0 -58
- package/dist/utilities/timeout.test.d.mts +0 -1
- package/dist/utilities/timeout.test.mjs +0 -121
- package/dist/utilities/window-size.test.d.mts +0 -1
- package/dist/utilities/window-size.test.mjs +0 -43
- /package/dist/{custom-elements-D5CgfNKc.d.mts → custom-elements-DK5VUcWA.d.mts} +0 -0
|
@@ -290,10 +290,10 @@ let activeSub;
|
|
|
290
290
|
let activeOwner;
|
|
291
291
|
/** Ring-buffer of effects waiting to be flushed. */
|
|
292
292
|
const queued = [];
|
|
293
|
-
const SIGNAL = Symbol("signal");
|
|
294
|
-
const COMPUTED = Symbol("computed");
|
|
295
|
-
const EFFECT = Symbol("effect");
|
|
296
|
-
const EFFECT_SCOPE = Symbol("effectScope");
|
|
293
|
+
const SIGNAL = Symbol.for("elements-kit.signal");
|
|
294
|
+
const COMPUTED = Symbol.for("elements-kit.computed");
|
|
295
|
+
const EFFECT = Symbol.for("elements-kit.effect");
|
|
296
|
+
const EFFECT_SCOPE = Symbol.for("elements-kit.effectScope");
|
|
297
297
|
const { link, unlink, propagate, checkDirty, shallowPropagate } = createReactiveSystem({
|
|
298
298
|
/**
|
|
299
299
|
* Called by the graph engine when a Mutable+Dirty node needs to recompute.
|
|
@@ -509,7 +509,65 @@ function computed(getter) {
|
|
|
509
509
|
* stop(); // final cleanup: abort the last fetch
|
|
510
510
|
* ```
|
|
511
511
|
*/
|
|
512
|
+
/**
|
|
513
|
+
* @internal Method key for settling a reactive async wrapper from a
|
|
514
|
+
* serialized server snapshot (hydration seeding). Lives here so
|
|
515
|
+
* `utilities/promise`, `utilities/async` and `hydrate` can share it without
|
|
516
|
+
* a utility-to-utility dependency.
|
|
517
|
+
*/
|
|
518
|
+
const SEED = Symbol.for("elements-kit.seed");
|
|
519
|
+
/**
|
|
520
|
+
* @internal Method key for the hydrate claim protocol: the claim walk hands
|
|
521
|
+
* each async wrapper its ek-data record (or undefined). The wrapper decides —
|
|
522
|
+
* seed and discard any deferred run, or execute the deferred run now.
|
|
523
|
+
*/
|
|
524
|
+
const CLAIM = Symbol.for("elements-kit.claim");
|
|
525
|
+
/**
|
|
526
|
+
* @internal Brand stamped on dynamic-region getters that rendered as async
|
|
527
|
+
* insertion points on the server (Await boundaries). The value carries
|
|
528
|
+
* the number of ek-data ids the server consumed for the region and a
|
|
529
|
+
* pending-probe — the hydrate walk advances its counter and keeps the
|
|
530
|
+
* server content while the region is pending instead of flashing the
|
|
531
|
+
* fallback.
|
|
532
|
+
*/
|
|
533
|
+
const ASYNC_REGION = Symbol.for("elements-kit.async-region");
|
|
534
|
+
const MODE_STATE = Symbol.for("elements-kit.mode-state");
|
|
535
|
+
const modeState = globalThis[MODE_STATE] ??= {
|
|
536
|
+
deferAsyncRuns: false,
|
|
537
|
+
inertEffects: false
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* @internal While true (hydrate evaluation phase), `Async.run()` records
|
|
541
|
+
* intent instead of executing — the claim walk or the post-walk flush decides
|
|
542
|
+
* whether the fetcher actually runs. Returns the previous flag.
|
|
543
|
+
*/
|
|
544
|
+
function setDeferAsyncRuns(value) {
|
|
545
|
+
const prev = modeState.deferAsyncRuns;
|
|
546
|
+
modeState.deferAsyncRuns = value;
|
|
547
|
+
return prev;
|
|
548
|
+
}
|
|
549
|
+
/** @internal */
|
|
550
|
+
function shouldDeferAsyncRuns() {
|
|
551
|
+
return modeState.deferAsyncRuns;
|
|
552
|
+
}
|
|
553
|
+
/** @internal True during server rendering (see {@link setInertEffects}). */
|
|
554
|
+
function effectsInert() {
|
|
555
|
+
return modeState.inertEffects;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Toggle inert-effect mode. While inert, `effect()` neither executes its body
|
|
559
|
+
* nor tracks dependencies — it returns a no-op stop function. Used by the
|
|
560
|
+
* server renderer: a server render is a one-shot snapshot, effects are
|
|
561
|
+
* client-only. Returns the previous flag so callers can restore it.
|
|
562
|
+
*/
|
|
563
|
+
function setInertEffects(value) {
|
|
564
|
+
const prev = modeState.inertEffects;
|
|
565
|
+
modeState.inertEffects = value;
|
|
566
|
+
return prev;
|
|
567
|
+
}
|
|
568
|
+
const inertStop = () => {};
|
|
512
569
|
function effect(fn) {
|
|
570
|
+
if (modeState.inertEffects) return inertStop;
|
|
513
571
|
const e = {
|
|
514
572
|
fn,
|
|
515
573
|
subs: void 0,
|
|
@@ -997,4 +1055,4 @@ function purgeDeps(sub) {
|
|
|
997
1055
|
while (dep !== void 0) dep = unlink(dep, sub);
|
|
998
1056
|
}
|
|
999
1057
|
//#endregion
|
|
1000
|
-
export {
|
|
1058
|
+
export { untracked as C, trigger as S, onCleanup as _, EFFECT_SCOPE as a, shouldDeferAsyncRuns as b, batch as c, effectScope as d, effectsInert as f, isSignal as g, isEffectScope as h, EFFECT as i, computed as l, isEffect as m, CLAIM as n, SEED as o, isComputed as p, COMPUTED as r, SIGNAL as s, ASYNC_REGION as t, effect as u, setDeferAsyncRuns as v, signal as x, setInertEffects as y };
|
package/dist/render.mjs
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/server/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Render a component tree to a streaming HTML response in any JavaScript
|
|
4
|
+
* runtime — no DOM required.
|
|
5
|
+
*
|
|
6
|
+
* Pass a thunk, not evaluated JSX: JSX evaluates eagerly, so the server
|
|
7
|
+
* renderer must be installed before the first jsx call runs.
|
|
8
|
+
*
|
|
9
|
+
* Streaming is in-order: all HTML preceding an async insertion point
|
|
10
|
+
* (`promise`/`async` reactive values used as children) flushes immediately;
|
|
11
|
+
* the stream then awaits the value and continues. Resolved values are
|
|
12
|
+
* serialized into a `<script type="application/json" id="ek-data">` tag at
|
|
13
|
+
* the end of the stream so the client hydration pass reuses them instead of
|
|
14
|
+
* refetching.
|
|
15
|
+
*
|
|
16
|
+
* Server semantics: signal/computed reads are a one-shot snapshot (no
|
|
17
|
+
* subscriptions), effects do not run, `on:` handlers and `ref` are skipped —
|
|
18
|
+
* interactivity attaches on the client via `elements-kit/hydrate`.
|
|
19
|
+
*
|
|
20
|
+
* Errors — including rejected async values — propagate and abort the stream.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* export default {
|
|
25
|
+
* fetch: () => new Response(renderToStream(() => <App />), {
|
|
26
|
+
* headers: { "content-type": "text/html" },
|
|
27
|
+
* }),
|
|
28
|
+
* };
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function renderToStream(app: () => unknown): ReadableStream<Uint8Array>;
|
|
32
|
+
/**
|
|
33
|
+
* Render a component tree to a complete HTML string. Convenience wrapper
|
|
34
|
+
* around {@link renderToStream} — same semantics, buffered to one string.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* const html = await renderToString(() => <App />);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare function renderToString(app: () => unknown): Promise<string>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { renderToStream, renderToString };
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { C as untracked, y as setInertEffects } from "./lib-DYypKhxk.mjs";
|
|
2
|
+
import { isReactive, resolveProps } from "./signals/index.mjs";
|
|
3
|
+
import { isReactivePromiseLike } from "./utilities/promise.mjs";
|
|
4
|
+
import { isAsyncLike } from "./utilities/async.mjs";
|
|
5
|
+
import { c as BooleanAttributes, l as ChildProperties, r as setRenderer, s as AttrAliases, u as Properties } from "./element-D3om9yA6.mjs";
|
|
6
|
+
import { isForComponent } from "./for.mjs";
|
|
7
|
+
import { n as isFragmentComponent } from "./fragment-ROPRBJQd.mjs";
|
|
8
|
+
//#region src/server/escape.ts
|
|
9
|
+
function escapeDelim(s, delim, escDelim) {
|
|
10
|
+
let iDelim = s.indexOf(delim);
|
|
11
|
+
let iAmp = s.indexOf("&");
|
|
12
|
+
if (iDelim < 0 && iAmp < 0) return s;
|
|
13
|
+
let left = 0;
|
|
14
|
+
let out = "";
|
|
15
|
+
while (iDelim >= 0 && iAmp >= 0) if (iDelim < iAmp) {
|
|
16
|
+
if (left < iDelim) out += s.substring(left, iDelim);
|
|
17
|
+
out += escDelim;
|
|
18
|
+
left = iDelim + 1;
|
|
19
|
+
iDelim = s.indexOf(delim, left);
|
|
20
|
+
} else {
|
|
21
|
+
if (left < iAmp) out += s.substring(left, iAmp);
|
|
22
|
+
out += "&";
|
|
23
|
+
left = iAmp + 1;
|
|
24
|
+
iAmp = s.indexOf("&", left);
|
|
25
|
+
}
|
|
26
|
+
if (iDelim >= 0) do {
|
|
27
|
+
if (left < iDelim) out += s.substring(left, iDelim);
|
|
28
|
+
out += escDelim;
|
|
29
|
+
left = iDelim + 1;
|
|
30
|
+
iDelim = s.indexOf(delim, left);
|
|
31
|
+
} while (iDelim >= 0);
|
|
32
|
+
else while (iAmp >= 0) {
|
|
33
|
+
if (left < iAmp) out += s.substring(left, iAmp);
|
|
34
|
+
out += "&";
|
|
35
|
+
left = iAmp + 1;
|
|
36
|
+
iAmp = s.indexOf("&", left);
|
|
37
|
+
}
|
|
38
|
+
return left < s.length ? out + s.substring(left) : out;
|
|
39
|
+
}
|
|
40
|
+
/** Escape a text node. */
|
|
41
|
+
function escapeHtml(value) {
|
|
42
|
+
return escapeDelim(value, "<", "<");
|
|
43
|
+
}
|
|
44
|
+
/** Escape a double-quoted attribute value. */
|
|
45
|
+
function escapeAttr(value) {
|
|
46
|
+
return escapeDelim(value, "\"", """);
|
|
47
|
+
}
|
|
48
|
+
/** Neutralize `<\/script>` sequences inside an inline JSON script payload. */
|
|
49
|
+
function escapeScriptJson(json) {
|
|
50
|
+
return json.replace(/<\//g, "<\\/");
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/server/jsx.ts
|
|
54
|
+
/**
|
|
55
|
+
* Server output node. Nested jsx calls return `SNode`s so the children walk
|
|
56
|
+
* can distinguish pre-rendered markup (spliced verbatim) from user strings
|
|
57
|
+
* (escaped).
|
|
58
|
+
*/
|
|
59
|
+
var SNode = class {
|
|
60
|
+
chunks;
|
|
61
|
+
constructor(chunks) {
|
|
62
|
+
this.chunks = chunks;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* An async insertion point. The stream awaits it in document order and
|
|
67
|
+
* assigns its serialization id at emit time — ids must follow document
|
|
68
|
+
* order (not jsx-evaluation order, which is children-first) so the hydrate
|
|
69
|
+
* walk, which runs in document order, aligns with them.
|
|
70
|
+
*/
|
|
71
|
+
var AsyncChunk = class {
|
|
72
|
+
instance;
|
|
73
|
+
constructor(instance) {
|
|
74
|
+
this.instance = instance;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/** Normalize an arbitrary resolved value into chunks (used by the stream). */
|
|
78
|
+
function resolveChildChunks(value) {
|
|
79
|
+
return child(value);
|
|
80
|
+
}
|
|
81
|
+
const SLOT_OPEN = "<!--{-->";
|
|
82
|
+
const SLOT_CLOSE = "<!--}-->";
|
|
83
|
+
const FOR_OPEN = "<!--<For>-->";
|
|
84
|
+
const FOR_CLOSE = "<!--</For>-->";
|
|
85
|
+
const VoidElements = new Set([
|
|
86
|
+
"area",
|
|
87
|
+
"base",
|
|
88
|
+
"br",
|
|
89
|
+
"col",
|
|
90
|
+
"embed",
|
|
91
|
+
"hr",
|
|
92
|
+
"img",
|
|
93
|
+
"input",
|
|
94
|
+
"link",
|
|
95
|
+
"meta",
|
|
96
|
+
"param",
|
|
97
|
+
"source",
|
|
98
|
+
"track",
|
|
99
|
+
"wbr"
|
|
100
|
+
]);
|
|
101
|
+
const PropertyAttrAliases = {
|
|
102
|
+
className: "class",
|
|
103
|
+
defaultValue: "value",
|
|
104
|
+
defaultChecked: "checked",
|
|
105
|
+
defaultSelected: "selected",
|
|
106
|
+
defaultMuted: "muted",
|
|
107
|
+
indeterminate: null
|
|
108
|
+
};
|
|
109
|
+
function serverJsx(type, props) {
|
|
110
|
+
if (typeof type === "string") return element(type, props);
|
|
111
|
+
if (isFragmentComponent(type)) {
|
|
112
|
+
if (props.html) {
|
|
113
|
+
const source = props.children;
|
|
114
|
+
const value = typeof source === "function" ? untracked(source) : source;
|
|
115
|
+
return new SNode([
|
|
116
|
+
SLOT_OPEN,
|
|
117
|
+
value == null ? "" : String(value),
|
|
118
|
+
SLOT_CLOSE
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
return new SNode(childList(props.children));
|
|
122
|
+
}
|
|
123
|
+
if (isForComponent(type)) return forElement(props);
|
|
124
|
+
if (typeof type === "function" && !type.prototype?.render) return new SNode(child(type(resolveProps(props))));
|
|
125
|
+
const name = typeof type === "function" ? type.name ?? "anonymous" : String(type);
|
|
126
|
+
throw new Error(`Server rendering does not support class components ("${name}"); v1 supports intrinsic elements, function components, Fragment and For.`);
|
|
127
|
+
}
|
|
128
|
+
function element(tag, props) {
|
|
129
|
+
let attrs = "";
|
|
130
|
+
let classBase;
|
|
131
|
+
const classToggles = [];
|
|
132
|
+
let styleBase;
|
|
133
|
+
const styleEntries = [];
|
|
134
|
+
let childrenValue;
|
|
135
|
+
let textValue;
|
|
136
|
+
for (const [key, rawValue] of Object.entries(props)) {
|
|
137
|
+
if (key === "children") {
|
|
138
|
+
childrenValue = rawValue;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (key === "ref") continue;
|
|
142
|
+
const colonIdx = key.indexOf(":");
|
|
143
|
+
const ns = colonIdx > 0 ? key.slice(0, colonIdx) : "";
|
|
144
|
+
if (ns === "on") continue;
|
|
145
|
+
const value = isReactive(rawValue) ? untracked(rawValue) : rawValue;
|
|
146
|
+
if (ns) {
|
|
147
|
+
const name = key.slice(colonIdx + 1);
|
|
148
|
+
if (ns === "prop") continue;
|
|
149
|
+
if (ns === "class") {
|
|
150
|
+
if (value) classToggles.push(name);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (ns === "style") {
|
|
154
|
+
if (value != null && value !== false) styleEntries.push(`${name}:${String(value)}`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (ns === "xlink" || ns === "xml") {
|
|
158
|
+
attrs += attr(key, value);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (key === "class") {
|
|
163
|
+
classBase = value == null ? void 0 : String(value);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (key === "style") {
|
|
167
|
+
styleBase = styleToString(value);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (ChildProperties.has(key)) {
|
|
171
|
+
if (key === "innerHTML") throw new Error("innerHTML is not supported in server rendering — there is no raw HTML sink.");
|
|
172
|
+
textValue = value;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (Properties.has(key)) {
|
|
176
|
+
const alias = PropertyAttrAliases[key];
|
|
177
|
+
if (alias === null) continue;
|
|
178
|
+
if (alias === "class") {
|
|
179
|
+
classBase = value == null ? void 0 : String(value);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
attrs += attr(alias ?? key.toLowerCase(), value);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
attrs += attr(AttrAliases[key] ?? key, value);
|
|
186
|
+
}
|
|
187
|
+
const classes = [...classBase !== void 0 && classBase !== "" ? [classBase] : [], ...classToggles];
|
|
188
|
+
if (classBase !== void 0 || classToggles.length > 0) attrs += attr("class", classes.join(" "));
|
|
189
|
+
const styles = [...styleBase ? [styleBase] : [], ...styleEntries];
|
|
190
|
+
if (styles.length > 0) attrs += attr("style", styles.join(";"));
|
|
191
|
+
const open = `<${tag}${attrs}>`;
|
|
192
|
+
if (VoidElements.has(tag)) return new SNode([open]);
|
|
193
|
+
return new SNode([
|
|
194
|
+
open,
|
|
195
|
+
...textValue != null ? [escapeHtml(String(textValue))] : childList(childrenValue),
|
|
196
|
+
`</${tag}>`
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
function attr(name, value) {
|
|
200
|
+
if (BooleanAttributes.has(name)) return value ? ` ${name}` : "";
|
|
201
|
+
if (value == null || value === false) return "";
|
|
202
|
+
if (value === true || value === "") return ` ${name}`;
|
|
203
|
+
return ` ${name}="${escapeAttr(String(value))}"`;
|
|
204
|
+
}
|
|
205
|
+
function styleToString(value) {
|
|
206
|
+
if (value == null || value === false) return void 0;
|
|
207
|
+
if (typeof value === "string") return value;
|
|
208
|
+
if (typeof value === "object") return Object.entries(value).filter(([, v]) => v != null && v !== false).map(([k, v]) => `${k.includes("-") ? k : k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`).join(";");
|
|
209
|
+
return String(value);
|
|
210
|
+
}
|
|
211
|
+
function childList(raw) {
|
|
212
|
+
if (raw == null) return [];
|
|
213
|
+
if (Array.isArray(raw)) return raw.flat(Infinity).flatMap(child);
|
|
214
|
+
return child(raw);
|
|
215
|
+
}
|
|
216
|
+
function child(c) {
|
|
217
|
+
if (c == null || typeof c === "boolean") return [];
|
|
218
|
+
if (c instanceof SNode) return [c];
|
|
219
|
+
if (isReactivePromiseLike(c) || isAsyncLike(c)) return asyncChild(c);
|
|
220
|
+
if (typeof c === "function") return [
|
|
221
|
+
SLOT_OPEN,
|
|
222
|
+
...child(untracked(c)),
|
|
223
|
+
SLOT_CLOSE
|
|
224
|
+
];
|
|
225
|
+
if (typeof c === "string") return [escapeHtml(c)];
|
|
226
|
+
if (typeof c === "number" || typeof c === "bigint") return [String(c)];
|
|
227
|
+
if (Array.isArray(c)) return c.flat(Infinity).flatMap(child);
|
|
228
|
+
return [escapeHtml(String(c))];
|
|
229
|
+
}
|
|
230
|
+
function asyncChild(p) {
|
|
231
|
+
return [
|
|
232
|
+
SLOT_OPEN,
|
|
233
|
+
new AsyncChunk(p),
|
|
234
|
+
SLOT_CLOSE
|
|
235
|
+
];
|
|
236
|
+
}
|
|
237
|
+
function forElement(props) {
|
|
238
|
+
const each = props.each;
|
|
239
|
+
const items = untracked(() => typeof each === "function" ? each() : each);
|
|
240
|
+
const by = props.by ?? ((_item, index) => index);
|
|
241
|
+
const render = props.children;
|
|
242
|
+
const chunks = [FOR_OPEN];
|
|
243
|
+
(items ?? []).forEach((item, index) => {
|
|
244
|
+
const key = encodeURIComponent(String(by(item, index)));
|
|
245
|
+
chunks.push(`<!--[${key}]-->`, ...child(render(item, index)), `<!--[/${key}]-->`);
|
|
246
|
+
});
|
|
247
|
+
chunks.push(FOR_CLOSE);
|
|
248
|
+
return new SNode(chunks);
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/server/index.ts
|
|
252
|
+
/**
|
|
253
|
+
* Render a component tree to a streaming HTML response in any JavaScript
|
|
254
|
+
* runtime — no DOM required.
|
|
255
|
+
*
|
|
256
|
+
* Pass a thunk, not evaluated JSX: JSX evaluates eagerly, so the server
|
|
257
|
+
* renderer must be installed before the first jsx call runs.
|
|
258
|
+
*
|
|
259
|
+
* Streaming is in-order: all HTML preceding an async insertion point
|
|
260
|
+
* (`promise`/`async` reactive values used as children) flushes immediately;
|
|
261
|
+
* the stream then awaits the value and continues. Resolved values are
|
|
262
|
+
* serialized into a `<script type="application/json" id="ek-data">` tag at
|
|
263
|
+
* the end of the stream so the client hydration pass reuses them instead of
|
|
264
|
+
* refetching.
|
|
265
|
+
*
|
|
266
|
+
* Server semantics: signal/computed reads are a one-shot snapshot (no
|
|
267
|
+
* subscriptions), effects do not run, `on:` handlers and `ref` are skipped —
|
|
268
|
+
* interactivity attaches on the client via `elements-kit/hydrate`.
|
|
269
|
+
*
|
|
270
|
+
* Errors — including rejected async values — propagate and abort the stream.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```tsx
|
|
274
|
+
* export default {
|
|
275
|
+
* fetch: () => new Response(renderToStream(() => <App />), {
|
|
276
|
+
* headers: { "content-type": "text/html" },
|
|
277
|
+
* }),
|
|
278
|
+
* };
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
function renderToStream(app) {
|
|
282
|
+
const encoder = new TextEncoder();
|
|
283
|
+
return new ReadableStream({ async start(controller) {
|
|
284
|
+
try {
|
|
285
|
+
const ctx = {
|
|
286
|
+
records: [],
|
|
287
|
+
counter: 0
|
|
288
|
+
};
|
|
289
|
+
const root = evaluate(app);
|
|
290
|
+
let buffer = "";
|
|
291
|
+
const flush = () => {
|
|
292
|
+
if (buffer.length === 0) return;
|
|
293
|
+
controller.enqueue(encoder.encode(buffer));
|
|
294
|
+
buffer = "";
|
|
295
|
+
};
|
|
296
|
+
const emit = async (node) => {
|
|
297
|
+
if (node == null || typeof node === "boolean") return;
|
|
298
|
+
if (typeof node === "string") {
|
|
299
|
+
buffer += node;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (node instanceof SNode) {
|
|
303
|
+
for (const chunk of node.chunks) await emit(chunk);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (node instanceof AsyncChunk) {
|
|
307
|
+
flush();
|
|
308
|
+
const id = ctx.counter++;
|
|
309
|
+
const value = await node.instance;
|
|
310
|
+
ctx.records.push({
|
|
311
|
+
id,
|
|
312
|
+
value
|
|
313
|
+
});
|
|
314
|
+
const prevInert = setInertEffects(true);
|
|
315
|
+
setRenderer({ jsx: serverJsx });
|
|
316
|
+
let chunks;
|
|
317
|
+
try {
|
|
318
|
+
chunks = resolveChildChunks(value);
|
|
319
|
+
} finally {
|
|
320
|
+
setRenderer(null);
|
|
321
|
+
setInertEffects(prevInert);
|
|
322
|
+
}
|
|
323
|
+
for (const chunk of chunks) await emit(chunk);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
buffer += String(node);
|
|
327
|
+
};
|
|
328
|
+
await emit(root);
|
|
329
|
+
if (ctx.records.length > 0) buffer += serializeRecords(ctx.records);
|
|
330
|
+
flush();
|
|
331
|
+
controller.close();
|
|
332
|
+
} catch (error) {
|
|
333
|
+
controller.error(error);
|
|
334
|
+
}
|
|
335
|
+
} });
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Render a component tree to a complete HTML string. Convenience wrapper
|
|
339
|
+
* around {@link renderToStream} — same semantics, buffered to one string.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```tsx
|
|
343
|
+
* const html = await renderToString(() => <App />);
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
async function renderToString(app) {
|
|
347
|
+
const reader = renderToStream(app).getReader();
|
|
348
|
+
const decoder = new TextDecoder();
|
|
349
|
+
let out = "";
|
|
350
|
+
for (;;) {
|
|
351
|
+
const { done, value } = await reader.read();
|
|
352
|
+
if (done) break;
|
|
353
|
+
out += decoder.decode(value, { stream: true });
|
|
354
|
+
}
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
357
|
+
/** Run `app` with the server renderer installed and effects inert. */
|
|
358
|
+
function evaluate(app) {
|
|
359
|
+
const prevInert = setInertEffects(true);
|
|
360
|
+
setRenderer({ jsx: serverJsx });
|
|
361
|
+
try {
|
|
362
|
+
return app();
|
|
363
|
+
} finally {
|
|
364
|
+
setRenderer(null);
|
|
365
|
+
setInertEffects(prevInert);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function serializeRecords(records) {
|
|
369
|
+
const parts = [];
|
|
370
|
+
for (const record of records) {
|
|
371
|
+
if (typeof record.value === "function") continue;
|
|
372
|
+
let json;
|
|
373
|
+
try {
|
|
374
|
+
json = JSON.stringify({ value: record.value });
|
|
375
|
+
} catch {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (json === void 0) continue;
|
|
379
|
+
parts.push(`"${record.id}":${json}`);
|
|
380
|
+
}
|
|
381
|
+
if (parts.length === 0) return "";
|
|
382
|
+
return `<script type="application/json" id="ek-data">${escapeScriptJson(`{${parts.join(",")}}`)}<\/script>`;
|
|
383
|
+
}
|
|
384
|
+
//#endregion
|
|
385
|
+
export { serverJsx as i, renderToString as n, SNode as r, renderToStream as t };
|
package/dist/signals/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { COMPUTED, Computed, EFFECT, EFFECT_SCOPE, MaybeReactive, SIGNAL, Signal, Updater, batch, computed, effect, effectScope, isComputed, isEffect, isEffectScope, isReactive, isSignal, onCleanup, reactive, resolve, resolveProps, signal, trigger, untracked };
|
|
1
|
+
import { A as EFFECT_SCOPE, B as isSignal, C as isReactive, D as CLAIM, E as resolveProps, F as effect, H as signal, I as effectScope, L as isComputed, M as SIGNAL, N as batch, O as COMPUTED, P as computed, R as isEffect, S as Updater, T as resolve, U as trigger, V as onCleanup, W as untracked, b as MaybeReactive, j as SEED, k as EFFECT, w as reactive, x as Signal, y as Computed, z as isEffectScope } from "../types-ncUK9rki.mjs";
|
|
2
|
+
export { CLAIM, COMPUTED, Computed, EFFECT, EFFECT_SCOPE, MaybeReactive, SEED, SIGNAL, Signal, Updater, batch, computed, effect, effectScope, isComputed, isEffect, isEffectScope, isReactive, isSignal, onCleanup, reactive, resolve, resolveProps, signal, trigger, untracked };
|
package/dist/signals/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as untracked, S as trigger, _ as onCleanup, a as EFFECT_SCOPE, c as batch, d as effectScope, g as isSignal, h as isEffectScope, i as EFFECT, l as computed, m as isEffect, n as CLAIM, o as SEED, p as isComputed, r as COMPUTED, s as SIGNAL, u as effect, x as signal } from "../lib-DYypKhxk.mjs";
|
|
2
2
|
import "../polyfill-CdZVCxdo.mjs";
|
|
3
3
|
//#region src/signals/index.ts
|
|
4
4
|
/**
|
|
@@ -108,7 +108,9 @@ function resolve(value) {
|
|
|
108
108
|
* props.label(); // "n"
|
|
109
109
|
* ```
|
|
110
110
|
*/
|
|
111
|
+
const RESOLVED_PROPS = Symbol.for("elements-kit.resolved-props");
|
|
111
112
|
function resolveProps(raw) {
|
|
113
|
+
if (raw[RESOLVED_PROPS]) return raw;
|
|
112
114
|
const ownKeys = Reflect.ownKeys(raw);
|
|
113
115
|
const ownKeySet = new Set(ownKeys);
|
|
114
116
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -122,7 +124,7 @@ function resolveProps(raw) {
|
|
|
122
124
|
return getter;
|
|
123
125
|
};
|
|
124
126
|
return new Proxy(raw, {
|
|
125
|
-
get: (_target, key) => get(key),
|
|
127
|
+
get: (_target, key) => key === RESOLVED_PROPS ? true : get(key),
|
|
126
128
|
has: (_target, key) => ownKeySet.has(key),
|
|
127
129
|
ownKeys: () => ownKeys,
|
|
128
130
|
getOwnPropertyDescriptor: (_target, key) => ownKeySet.has(key) ? {
|
|
@@ -134,4 +136,4 @@ function resolveProps(raw) {
|
|
|
134
136
|
});
|
|
135
137
|
}
|
|
136
138
|
//#endregion
|
|
137
|
-
export { COMPUTED, EFFECT, EFFECT_SCOPE, SIGNAL, batch, computed, effect, effectScope, isComputed, isEffect, isEffectScope, isReactive, isSignal, onCleanup, reactive, resolve, resolveProps, signal, trigger, untracked };
|
|
139
|
+
export { CLAIM, COMPUTED, EFFECT, EFFECT_SCOPE, SEED, SIGNAL, batch, computed, effect, effectScope, isComputed, isEffect, isEffectScope, isReactive, isSignal, onCleanup, reactive, resolve, resolveProps, signal, trigger, untracked };
|
|
@@ -18,6 +18,12 @@ declare global {
|
|
|
18
18
|
*/
|
|
19
19
|
declare class Slot {
|
|
20
20
|
#private;
|
|
21
|
+
/**
|
|
22
|
+
* Bind a slot to an existing marker pair (server-rendered comments) instead
|
|
23
|
+
* of creating new ones. Used by the hydrate claim pass: content between the
|
|
24
|
+
* markers is adopted as the slot's current content.
|
|
25
|
+
*/
|
|
26
|
+
static claim(start: Comment, end: Comment): Slot;
|
|
21
27
|
/**
|
|
22
28
|
* Render the slot as a DocumentFragment.
|
|
23
29
|
* If not yet mounted, inserts the comment markers and optional default content.
|
|
@@ -18,11 +18,22 @@ function resolveNode(c) {
|
|
|
18
18
|
* A lightweight slot that reserves a region in the DOM using comment markers.
|
|
19
19
|
* Content between the markers can be replaced dynamically without wrapper elements.
|
|
20
20
|
*/
|
|
21
|
-
var Slot = class {
|
|
21
|
+
var Slot = class Slot {
|
|
22
22
|
#start;
|
|
23
23
|
#end;
|
|
24
24
|
#pending;
|
|
25
25
|
/**
|
|
26
|
+
* Bind a slot to an existing marker pair (server-rendered comments) instead
|
|
27
|
+
* of creating new ones. Used by the hydrate claim pass: content between the
|
|
28
|
+
* markers is adopted as the slot's current content.
|
|
29
|
+
*/
|
|
30
|
+
static claim(start, end) {
|
|
31
|
+
const slot = new Slot();
|
|
32
|
+
slot.#start = start;
|
|
33
|
+
slot.#end = end;
|
|
34
|
+
return slot;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
26
37
|
* Render the slot as a DocumentFragment.
|
|
27
38
|
* If not yet mounted, inserts the comment markers and optional default content.
|
|
28
39
|
* If already mounted, extracts and returns the current content WITHOUT disposing
|
package/dist/slot.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as Slot, t as SLOTS } from "./slot-
|
|
1
|
+
import { n as Slot, t as SLOTS } from "./slot-BFWXeFRl.mjs";
|
|
2
2
|
export { SLOTS, Slot };
|
package/dist/slot.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as Slot, t as SLOTS } from "./slot-
|
|
1
|
+
import { n as Slot, t as SLOTS } from "./slot-Dvf30O0B.mjs";
|
|
2
2
|
export { SLOTS, Slot };
|