@nifrajs/web-svelte 4.0.2 → 5.1.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.
@@ -33,6 +33,7 @@
33
33
  const q = snapshot.path.indexOf("?")
34
34
  return {
35
35
  data: snapshot.data,
36
+ ...(snapshot.serverComponents === undefined ? {} : { serverComponents: snapshot.serverComponents }),
36
37
  actionData: snapshot.actionData,
37
38
  pending: snapshot.pending,
38
39
  // The same `params`/`path` the server render received, so `useMatches` reports the same chain.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,cAAc,CAAA;AAqF9D,8EAA8E;AAC9E,eAAO,MAAM,aAAa,EAAE,aA8B3B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,cAAc,CAAA;AAgGnF,8EAA8E;AAC9E,eAAO,MAAM,aAAa,EAAE,aA6B3B,CAAA"}
package/dist/index.js CHANGED
@@ -45,31 +45,37 @@ function oneChunk(html) {
45
45
  },
46
46
  });
47
47
  }
48
+ /** The body of one render; what `<svelte:head>` rendered goes into the document's `<head>`. */
49
+ function bodyOf(Chain, props, options) {
50
+ const out = render(Chain, { props });
51
+ if (options?.parts !== undefined)
52
+ options.parts.head += out.head;
53
+ return out.body;
54
+ }
48
55
  /** The Svelte server render adapter - pass to @nifrajs/web's `renderPage`. */
49
56
  export const svelteAdapter = {
57
+ documentParts: true,
50
58
  // Svelte SSR is already synchronous + string-based, so this is the native shape: `renderToString`
51
59
  // returns the body directly (renderPage buffers it on the non-deferred fast path), and
52
60
  // `renderToStream` wraps the same string in a one-chunk stream for the deferred path. No streaming
53
61
  // renderer to skip here - but going straight to a string avoids the per-request stream allocation.
54
62
  // The seam allows `string | Promise<string>`, so the first render can resolve `Chain` while every
55
63
  // render after it stays a plain synchronous call - see `loadChain`.
56
- renderToString(chain, props) {
64
+ renderToString(chain, props, options) {
57
65
  const Chain = loadChain();
58
66
  const props$ = { chain, props, layoutData: props.layoutData };
59
67
  return Chain instanceof Promise
60
- ? Chain.then((C) => render(C, { props: props$ }).body)
61
- : render(Chain, { props: props$ }).body;
68
+ ? Chain.then((C) => bodyOf(C, props$, options))
69
+ : bodyOf(Chain, props$, options);
62
70
  },
63
- renderToStream(chain, props) {
71
+ renderToStream(chain, props, options) {
64
72
  // `Chain` folds the layout chain (page innermost gets `props`; layouts wrap via their `children`
65
- // snippet). Svelte's `render` returns { head, body }; the body goes into #root. (Svelte's dynamic
66
- // `head` - from <svelte:head> - isn't surfaced through the seam's static `hydrationHead`; nifra's
67
- // own meta/head API manages the document head instead.)
73
+ // snippet). Svelte's `render` returns { head, body }; the body goes into #root.
68
74
  const Chain = loadChain();
69
75
  const props$ = { chain, props, layoutData: props.layoutData };
70
76
  return Chain instanceof Promise
71
- ? Chain.then((C) => oneChunk(render(C, { props: props$ }).body))
72
- : oneChunk(render(Chain, { props: props$ }).body);
77
+ ? Chain.then((C) => oneChunk(bodyOf(C, props$, options)))
78
+ : oneChunk(bodyOf(Chain, props$, options));
73
79
  },
74
80
  // Svelte's client `hydrate` reconciles against the existing DOM; no per-document bootstrap script is
75
81
  // needed (contrast Solid's generateHydrationScript) - the seam allows the empty string.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,eAAe,CAAA;AAkCvD,IAAI,cAAiD,CAAA;AACrD,IAAI,YAAwD,CAAA;AAC5D;;;;GAIG;AACH,IAAI,MAAM,GAAyB,aAAa,CAAA;AAChD,sEAAsE;AACtE,MAAM,SAAS,GAAG,GAA2D,EAAE;IAC7E,IAAI,cAAc,KAAK,SAAS;QAAE,OAAO,cAAc,CAAA;IACvD,oGAAoG;IACpG,iGAAiG;IACjG,oGAAoG;IACpG,+FAA+F;IAC/F,qGAAqG;IACrG,oFAAoF;IACpF,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;IAC9B,YAAY,KAAK,CACf,IAAI,KAAK,SAAS;QAChB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;YACV,8FAA8F;YAC9F,wEAAwE;YACxE,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,eAAe,CAAC;YAC3C,IAAI,CAAC,eAAe,CAAC;SACtB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;YACxB,MAAM,GAAI,MAA2C,CAAC,MAAM,CAAA;YAC5D,OAAQ,GAA0C,CAAC,OAAO,CAAA;QAC5D,CAAC,CAAC;QACJ,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAgC,CAAC,CAC7E,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QACX,cAAc,GAAG,CAAC,CAAA;QAClB,OAAO,CAAC,CAAA;IACV,CAAC,CAAC,CAAA;IACF,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA;AAED,8FAA8F;AAC9F,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC5C,OAAO,IAAI,cAAc,CAAC;QACxB,KAAK,CAAC,UAAU;YACd,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACzB,UAAU,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,kGAAkG;IAClG,uFAAuF;IACvF,mGAAmG;IACnG,mGAAmG;IACnG,kGAAkG;IAClG,oEAAoE;IACpE,cAAc,CAAC,KAAK,EAAE,KAAK;QACzB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;QACzB,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAA;QAC7D,OAAO,KAAK,YAAY,OAAO;YAC7B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC;YACtD,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAA;IAC3C,CAAC;IACD,cAAc,CAAC,KAAK,EAAE,KAAK;QACzB,iGAAiG;QACjG,kGAAkG;QAClG,kGAAkG;QAClG,wDAAwD;QACxD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;QACzB,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAA;QAC7D,OAAO,KAAK,YAAY,OAAO;YAC7B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAChE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IACrD,CAAC;IACD,qGAAqG;IACrG,wFAAwF;IACxF,aAAa;QACX,OAAO,EAAE,CAAA;IACX,CAAC;CACF,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type NativeRangeOptions } from "@nifrajs/web/server-components/range";
2
+ export declare function svelteComponentClient(options: Omit<NativeRangeOptions, "adapter" | "runtime">): import("@nifrajs/web/server-components/client").ComponentClientAdapter;
3
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/server-components/client.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,sCAAsC,CAAA;AAI7C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,0EAmD7F"}
@@ -0,0 +1,58 @@
1
+ import { disposeComponentPortals } from "@nifrajs/web/server-components/portal";
2
+ import { nativeComponentClient, } from "@nifrajs/web/server-components/range";
3
+ import { flushSync, hydrate, tick, unmount } from "svelte";
4
+ import Island from "./Island.svelte";
5
+ import { islandState } from "./state.svelte.js";
6
+ export function svelteComponentClient(options) {
7
+ return nativeComponentClient({
8
+ ...options,
9
+ adapter: "svelte",
10
+ runtime: {
11
+ hydrate(host, contract, record) {
12
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
13
+ const state = islandState(record.props);
14
+ const slots = Object.fromEntries(contract.slots.map((name) => [
15
+ name,
16
+ '<nifra-slot data-nifra-slot="' +
17
+ name +
18
+ '">' +
19
+ (record.slots?.[name] ?? "") +
20
+ "</nifra-slot>",
21
+ ]));
22
+ const instance = hydrate(Island, {
23
+ target: host,
24
+ props: {
25
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
26
+ component: contract.component,
27
+ state,
28
+ slots,
29
+ },
30
+ });
31
+ flushSync();
32
+ let disposed = false;
33
+ return {
34
+ ready: tick(),
35
+ update(next, current) {
36
+ if (!disposed && current())
37
+ flushSync(() => {
38
+ if (current())
39
+ state.props = next;
40
+ });
41
+ },
42
+ async dispose() {
43
+ if (!disposed) {
44
+ disposed = true;
45
+ try {
46
+ await disposeComponentPortals(host);
47
+ }
48
+ finally {
49
+ await unmount(instance);
50
+ }
51
+ }
52
+ },
53
+ };
54
+ },
55
+ },
56
+ });
57
+ }
58
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/server-components/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAEL,qBAAqB,GACtB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAkB,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAC1E,OAAO,MAAM,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,MAAM,UAAU,qBAAqB,CAAC,OAAwD;IAC5F,OAAO,qBAAqB,CAAC;QAC3B,GAAG,OAAO;QACV,OAAO,EAAE,QAAQ;QACjB,OAAO,EAAE;YACP,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM;gBAC5B,2MAA2M;gBAC3M,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,KAAgC,CAAC,CAAA;gBAClE,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAC9B,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;oBAC3B,IAAI;oBACJ,+BAA+B;wBAC7B,IAAI;wBACJ,IAAI;wBACJ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;wBAC5B,eAAe;iBAClB,CAAC,CACH,CAAA;gBACD,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE;oBAC/B,MAAM,EAAE,IAAI;oBACZ,KAAK,EAAE;wBACL,2MAA2M;wBAC3M,SAAS,EAAE,QAAQ,CAAC,SAA+C;wBACnE,KAAK;wBACL,KAAK;qBACN;iBACF,CAAC,CAAA;gBACF,SAAS,EAAE,CAAA;gBACX,IAAI,QAAQ,GAAG,KAAK,CAAA;gBACpB,OAAO;oBACL,KAAK,EAAE,IAAI,EAAE;oBACb,MAAM,CAAC,IAAI,EAAE,OAAO;wBAClB,IAAI,CAAC,QAAQ,IAAI,OAAO,EAAE;4BACxB,SAAS,CAAC,GAAG,EAAE;gCACb,IAAI,OAAO,EAAE;oCAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAA;4BACnC,CAAC,CAAC,CAAA;oBACN,CAAC;oBACD,KAAK,CAAC,OAAO;wBACX,IAAI,CAAC,QAAQ,EAAE,CAAC;4BACd,QAAQ,GAAG,IAAI,CAAA;4BACf,IAAI,CAAC;gCACH,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAA;4BACrC,CAAC;oCAAS,CAAC;gCACT,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAA;4BACzB,CAAC;wBACH,CAAC;oBACH,CAAC;iBACF,CAAA;YACH,CAAC;SACF;KACF,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ServerComponentRenderAdapter } from "@nifrajs/web/server-components";
2
+ export declare const svelteServerComponents: () => ServerComponentRenderAdapter;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server-components/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAA;AAKlF,eAAO,MAAM,sBAAsB,QAAO,4BAcvC,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { nativeServerComponents } from "@nifrajs/web/server-components/native";
2
+ import { render } from "svelte/server";
3
+ import Island from "./Island.svelte";
4
+ export const svelteServerComponents = () => nativeServerComponents("svelte", (component, props, renderId) => renderId.endsWith("-view")
5
+ ? // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
6
+ render(component, { props }).body
7
+ : render(Island, {
8
+ props: {
9
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
10
+ component: component,
11
+ state: { props },
12
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
13
+ slots: props.slots,
14
+ },
15
+ }).body);
16
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server-components/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAA;AAE9E,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,MAAM,MAAM,iBAAiB,CAAA;AACpC,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAiC,EAAE,CACvE,sBAAsB,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAC9D,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;IACxB,CAAC,CAAC,iLAAiL;QACjL,MAAM,CAAC,SAA+C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI;IACzE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE;QACb,KAAK,EAAE;YACL,iLAAiL;YACjL,SAAS,EAAE,SAA+C;YAC1D,KAAK,EAAE,EAAE,KAAK,EAAE;YAChB,iLAAiL;YACjL,KAAK,EAAE,KAAK,CAAC,KAA+B;SAC7C;KACF,CAAC,CAAC,IAAI,CACZ,CAAA"}
@@ -0,0 +1,4 @@
1
+ export declare function islandState(props: Record<string, unknown>): {
2
+ props: Record<string, unknown>;
3
+ };
4
+ //# sourceMappingURL=state.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.svelte.d.ts","sourceRoot":"","sources":["../../src/server-components/state.svelte.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAGzD"}
@@ -0,0 +1,5 @@
1
+ export function islandState(props) {
2
+ const state = $state({ props });
3
+ return state;
4
+ }
5
+ //# sourceMappingURL=state.svelte.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.svelte.js","sourceRoot":"","sources":["../../src/server-components/state.svelte.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,KAA8B;IACxD,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAC/B,OAAO,KAAK,CAAA;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/web-svelte",
3
- "version": "4.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "Svelte 5 render adapter for @nifrajs/web - SSR + hydration, with the .svelte compiler Bun plugin (@nifrajs/web-svelte/plugin).",
5
5
  "keywords": [
6
6
  "bun",
@@ -88,6 +88,20 @@
88
88
  "bun": "./src/fn.ts",
89
89
  "types": "./dist/fn.d.ts",
90
90
  "default": "./dist/fn.js"
91
+ },
92
+ "./server-components": {
93
+ "bun": "./src/server-components/server.ts",
94
+ "types": "./dist/server-components/server.d.ts",
95
+ "default": "./src/server-components/server.ts"
96
+ },
97
+ "./server-components/client": {
98
+ "bun": "./src/server-components/client.ts",
99
+ "types": "./dist/server-components/client.d.ts",
100
+ "default": "./src/server-components/client.ts"
101
+ },
102
+ "./server-components/outlet": {
103
+ "types": "./src/server-components/ServerComponent.svelte.d.ts",
104
+ "default": "./src/server-components/ServerComponent.svelte"
91
105
  }
92
106
  },
93
107
  "files": [
@@ -96,17 +110,17 @@
96
110
  ],
97
111
  "sideEffects": false,
98
112
  "scripts": {
99
- "build": "tsc -p tsconfig.build.json && bun run ../../scripts/fix-dts.ts dist && cp src/*.svelte dist/ && cp src/*.svelte.d.ts dist/"
113
+ "build": "node ../../scripts/gen-component-svelte-types.mjs && tsc -p tsconfig.build.json && bun run ../../scripts/fix-dts.ts dist && cp src/*.svelte dist/ && cp src/*.svelte.d.ts dist/"
100
114
  },
101
115
  "license": "MIT",
102
116
  "publishConfig": {
103
117
  "access": "public"
104
118
  },
105
119
  "dependencies": {
106
- "@nifrajs/core": "4.0.2",
107
- "@nifrajs/i18n": "4.0.2",
108
- "@nifrajs/image": "4.0.2",
109
- "@nifrajs/web": "4.0.2"
120
+ "@nifrajs/core": "5.1.0",
121
+ "@nifrajs/i18n": "5.1.0",
122
+ "@nifrajs/image": "5.1.0",
123
+ "@nifrajs/web": "5.1.0"
110
124
  },
111
125
  "peerDependencies": {
112
126
  "mdsvex": "^0.12",
@@ -118,7 +132,8 @@
118
132
  }
119
133
  },
120
134
  "devDependencies": {
121
- "svelte": "^5.3.0"
135
+ "svelte": "^5.3.0",
136
+ "svelte2tsx": "0.7.61"
122
137
  },
123
138
  "repository": {
124
139
  "type": "git",
package/src/Router.svelte CHANGED
@@ -33,6 +33,7 @@
33
33
  const q = snapshot.path.indexOf("?")
34
34
  return {
35
35
  data: snapshot.data,
36
+ ...(snapshot.serverComponents === undefined ? {} : { serverComponents: snapshot.serverComponents }),
36
37
  actionData: snapshot.actionData,
37
38
  pending: snapshot.pending,
38
39
  // The same `params`/`path` the server render received, so `useMatches` reports the same chain.
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * `.svelte` files, so this adapter ships a build plugin at `@nifrajs/web-svelte/plugin` (like
7
7
  * `@nifrajs/web-solid/plugin`) - there is no callable-component runtime.
8
8
  */
9
- import type { RenderAdapter, RenderProps } from "@nifrajs/web"
9
+ import type { RenderAdapter, RenderProps, RenderStreamOptions } from "@nifrajs/web"
10
10
  import { ssrModuleLoader } from "@nifrajs/web"
11
11
  import type { Component } from "svelte"
12
12
  import { render as runtimeRender } from "svelte/server"
@@ -91,31 +91,41 @@ function oneChunk(html: string): ReadableStream<Uint8Array> {
91
91
  })
92
92
  }
93
93
 
94
+ /** The body of one render; what `<svelte:head>` rendered goes into the document's `<head>`. */
95
+ function bodyOf(
96
+ Chain: Component<ChainProps>,
97
+ props: ChainProps,
98
+ options?: RenderStreamOptions,
99
+ ): string {
100
+ const out = render(Chain, { props })
101
+ if (options?.parts !== undefined) options.parts.head += out.head
102
+ return out.body
103
+ }
104
+
94
105
  /** The Svelte server render adapter - pass to @nifrajs/web's `renderPage`. */
95
106
  export const svelteAdapter: RenderAdapter = {
107
+ documentParts: true,
96
108
  // Svelte SSR is already synchronous + string-based, so this is the native shape: `renderToString`
97
109
  // returns the body directly (renderPage buffers it on the non-deferred fast path), and
98
110
  // `renderToStream` wraps the same string in a one-chunk stream for the deferred path. No streaming
99
111
  // renderer to skip here - but going straight to a string avoids the per-request stream allocation.
100
112
  // The seam allows `string | Promise<string>`, so the first render can resolve `Chain` while every
101
113
  // render after it stays a plain synchronous call - see `loadChain`.
102
- renderToString(chain, props) {
114
+ renderToString(chain, props, options) {
103
115
  const Chain = loadChain()
104
116
  const props$ = { chain, props, layoutData: props.layoutData }
105
117
  return Chain instanceof Promise
106
- ? Chain.then((C) => render(C, { props: props$ }).body)
107
- : render(Chain, { props: props$ }).body
118
+ ? Chain.then((C) => bodyOf(C, props$, options))
119
+ : bodyOf(Chain, props$, options)
108
120
  },
109
- renderToStream(chain, props) {
121
+ renderToStream(chain, props, options) {
110
122
  // `Chain` folds the layout chain (page innermost gets `props`; layouts wrap via their `children`
111
- // snippet). Svelte's `render` returns { head, body }; the body goes into #root. (Svelte's dynamic
112
- // `head` - from <svelte:head> - isn't surfaced through the seam's static `hydrationHead`; nifra's
113
- // own meta/head API manages the document head instead.)
123
+ // snippet). Svelte's `render` returns { head, body }; the body goes into #root.
114
124
  const Chain = loadChain()
115
125
  const props$ = { chain, props, layoutData: props.layoutData }
116
126
  return Chain instanceof Promise
117
- ? Chain.then((C) => oneChunk(render(C, { props: props$ }).body))
118
- : oneChunk(render(Chain, { props: props$ }).body)
127
+ ? Chain.then((C) => oneChunk(bodyOf(C, props$, options)))
128
+ : oneChunk(bodyOf(Chain, props$, options))
119
129
  },
120
130
  // Svelte's client `hydrate` reconciles against the existing DOM; no per-document bootstrap script is
121
131
  // needed (contrast Solid's generateHydrationScript) - the seam allows the empty string.
@@ -0,0 +1,5 @@
1
+ <script lang="ts">
2
+ import type { Component } from "svelte"
3
+ let { component: Native, state, slots }: { component: Component<Record<string, unknown>>; state: { props: Record<string, unknown> }; slots: Record<string, string> } = $props()
4
+ </script>
5
+ <Native {...state.props} {slots} />
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte"
3
+ import { ComponentController, type ComponentClientAdapter, type ComponentState, type ComponentRecord } from "@nifrajs/web/server-components/client"
4
+ let { state, record, controller, clientAdapter }: { state: ComponentState; record: ComponentRecord; controller?: ComponentController | undefined | undefined; clientAdapter?: ComponentClientAdapter | undefined | undefined } = $props()
5
+ // svelte-ignore state_referenced_locally
6
+ const initial = record.html
7
+ // svelte-ignore state_referenced_locally
8
+ const instance = record.instance
9
+ let host: HTMLElement
10
+ let active: ComponentController | undefined
11
+ onMount(() => {
12
+ if (controller === undefined && clientAdapter === undefined) throw new Error("component_contract")
13
+ active = controller ?? new ComponentController({ state, adapter: clientAdapter! })
14
+ void active.mount(host, instance).catch(() => active?.reportError("component_failed"))
15
+ return () => { if (active !== undefined) void (controller === undefined ? active.dispose() : active.unmount(instance)) }
16
+ })
17
+ $effect(() => { const next = state; if (active !== undefined) void active.adopt(next).catch(() => active?.reportError("component_failed")) })
18
+ </script>
19
+ <nifra-component bind:this={host} data-nifra-component={instance}>{@html initial}</nifra-component>
@@ -0,0 +1,9 @@
1
+ <script lang="ts" generics="Input, Props, View">
2
+ import Range from "./Range.svelte"
3
+ import { ComponentController, resolveComponentRecord, type ComponentClientAdapter, type ComponentState, type ComponentKey, type ServerComponent as Handle } from "@nifrajs/web/server-components/client"
4
+ let { state, component, instanceKey, input, parent, controller, clientAdapter }: {
5
+ state: ComponentState; component: Handle<Input, Props, View>; instanceKey: ComponentKey; input: NoInfer<Input>; parent?: string; controller?: ComponentController | undefined; clientAdapter?: ComponentClientAdapter | undefined
6
+ } = $props()
7
+ const record = $derived(resolveComponentRecord(state, component, instanceKey, input, parent ?? null))
8
+ </script>
9
+ {#key record.instance}<Range {state} {record} {controller} {clientAdapter} />{/key}
@@ -0,0 +1,35 @@
1
+ // Generated by scripts/gen-component-svelte-types.mjs from ServerComponent.svelte.
2
+ import { ComponentController, type ComponentClientAdapter, type ComponentState, type ComponentKey, type ServerComponent as Handle } from "@nifrajs/web/server-components/client";
3
+ declare function $$render<Input, Props, View>(): {
4
+ props: {
5
+ state: ComponentState;
6
+ component: Handle<Input, Props, View>;
7
+ instanceKey: ComponentKey;
8
+ input: NoInfer<Input>;
9
+ parent?: string;
10
+ controller?: ComponentController | undefined;
11
+ clientAdapter?: ComponentClientAdapter | undefined;
12
+ };
13
+ exports: {};
14
+ bindings: "";
15
+ slots: {};
16
+ events: {};
17
+ };
18
+ declare class __sveltets_Render<Input, Props, View> {
19
+ props(): ReturnType<typeof $$render<Input, Props, View>>['props'];
20
+ events(): ReturnType<typeof $$render<Input, Props, View>>['events'];
21
+ slots(): ReturnType<typeof $$render<Input, Props, View>>['slots'];
22
+ bindings(): "";
23
+ exports(): {};
24
+ }
25
+ interface $$IsomorphicComponent {
26
+ new <Input, Props, View>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Input, Props, View>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Input, Props, View>['props']>, ReturnType<__sveltets_Render<Input, Props, View>['events']>, ReturnType<__sveltets_Render<Input, Props, View>['slots']>> & {
27
+ $$bindings?: ReturnType<__sveltets_Render<Input, Props, View>['bindings']>;
28
+ } & ReturnType<__sveltets_Render<Input, Props, View>['exports']>;
29
+ <Input, Props, View>(internal: unknown, props: ReturnType<__sveltets_Render<Input, Props, View>['props']> & {}): ReturnType<__sveltets_Render<Input, Props, View>['exports']>;
30
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any, any>['bindings']>;
31
+ }
32
+ declare const ServerComponent: $$IsomorphicComponent;
33
+ type ServerComponent<Input, Props, View> = InstanceType<typeof ServerComponent<Input, Props, View>>;
34
+ export default ServerComponent;
35
+ //# sourceMappingURL=ServerComponent.svelte.d.ts.map
@@ -0,0 +1,60 @@
1
+ import { disposeComponentPortals } from "@nifrajs/web/server-components/portal"
2
+ import {
3
+ type NativeRangeOptions,
4
+ nativeComponentClient,
5
+ } from "@nifrajs/web/server-components/range"
6
+ import { type Component, flushSync, hydrate, tick, unmount } from "svelte"
7
+ import Island from "./Island.svelte"
8
+ import { islandState } from "./state.svelte.ts"
9
+ export function svelteComponentClient(options: Omit<NativeRangeOptions, "adapter" | "runtime">) {
10
+ return nativeComponentClient({
11
+ ...options,
12
+ adapter: "svelte",
13
+ runtime: {
14
+ hydrate(host, contract, record) {
15
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
16
+ const state = islandState(record.props as Record<string, unknown>)
17
+ const slots = Object.fromEntries(
18
+ contract.slots.map((name) => [
19
+ name,
20
+ '<nifra-slot data-nifra-slot="' +
21
+ name +
22
+ '">' +
23
+ (record.slots?.[name] ?? "") +
24
+ "</nifra-slot>",
25
+ ]),
26
+ )
27
+ const instance = hydrate(Island, {
28
+ target: host,
29
+ props: {
30
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
31
+ component: contract.component as Component<Record<string, unknown>>,
32
+ state,
33
+ slots,
34
+ },
35
+ })
36
+ flushSync()
37
+ let disposed = false
38
+ return {
39
+ ready: tick(),
40
+ update(next, current) {
41
+ if (!disposed && current())
42
+ flushSync(() => {
43
+ if (current()) state.props = next
44
+ })
45
+ },
46
+ async dispose() {
47
+ if (!disposed) {
48
+ disposed = true
49
+ try {
50
+ await disposeComponentPortals(host)
51
+ } finally {
52
+ await unmount(instance)
53
+ }
54
+ }
55
+ },
56
+ }
57
+ },
58
+ },
59
+ })
60
+ }
@@ -0,0 +1,20 @@
1
+ import type { ServerComponentRenderAdapter } from "@nifrajs/web/server-components"
2
+ import { nativeServerComponents } from "@nifrajs/web/server-components/native"
3
+ import type { Component } from "svelte"
4
+ import { render } from "svelte/server"
5
+ import Island from "./Island.svelte"
6
+ export const svelteServerComponents = (): ServerComponentRenderAdapter =>
7
+ nativeServerComponents("svelte", (component, props, renderId) =>
8
+ renderId.endsWith("-view")
9
+ ? // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
10
+ render(component as Component<Record<string, unknown>>, { props }).body
11
+ : render(Island, {
12
+ props: {
13
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
14
+ component: component as Component<Record<string, unknown>>,
15
+ state: { props },
16
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
17
+ slots: props.slots as Record<string, string>,
18
+ },
19
+ }).body,
20
+ )
@@ -0,0 +1,4 @@
1
+ export function islandState(props: Record<string, unknown>) {
2
+ const state = $state({ props })
3
+ return state
4
+ }