@nifrajs/web-solid 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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -3
- package/dist/index.js.map +1 -1
- package/dist/mdx.js +1 -1
- package/dist/mdx.js.map +1 -1
- package/dist/route-props.d.ts.map +1 -1
- package/dist/route-props.js +3 -0
- package/dist/route-props.js.map +1 -1
- package/dist/server-components/Outlet.d.ts +12 -0
- package/dist/server-components/Outlet.d.ts.map +1 -0
- package/dist/server-components/Outlet.jsx +36 -0
- package/dist/server-components/Outlet.jsx.map +1 -0
- package/dist/server-components/client.d.ts +3 -0
- package/dist/server-components/client.d.ts.map +1 -0
- package/dist/server-components/client.js +56 -0
- package/dist/server-components/client.js.map +1 -0
- package/dist/server-components/server.d.ts +3 -0
- package/dist/server-components/server.d.ts.map +1 -0
- package/dist/server-components/server.js +9 -0
- package/dist/server-components/server.js.map +1 -0
- package/package.json +19 -5
- package/src/index.ts +44 -3
- package/src/route-props.ts +3 -0
- package/src/server-components/Outlet.tsx +72 -0
- package/src/server-components/client.ts +64 -0
- package/src/server-components/server.ts +11 -0
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAgDjD,6EAA6E;AAC7E,eAAO,MAAM,YAAY,EAAE,aA8B1B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
|
+
import { sharedConfig } from "solid-js";
|
|
1
2
|
import { generateHydrationScript, renderToStream as solidRenderToStream, renderToString as solidRenderToString, } from "solid-js/web";
|
|
2
3
|
import { compose } from "./compose.js";
|
|
3
4
|
const HYDRATION_HEAD = generateHydrationScript();
|
|
5
|
+
// An unset NODE_ENV counts as production, as for the framework's own error pages.
|
|
6
|
+
const showsErrors = () => {
|
|
7
|
+
const nodeEnv = typeof process === "undefined" ? undefined : process.env?.NODE_ENV;
|
|
8
|
+
return nodeEnv === "development" || nodeEnv === "test";
|
|
9
|
+
};
|
|
10
|
+
/** What the browser receives for an error Solid hands to hydration: its text names server internals. */
|
|
11
|
+
function redactedError() {
|
|
12
|
+
// Built bare: a constructed Error carries a stack and, on Bun, the line, column and file it was made
|
|
13
|
+
// at, and Solid serializes every own property.
|
|
14
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Object.create over Error.prototype with a message is an Error minus its stack.
|
|
15
|
+
return Object.create(Error.prototype, {
|
|
16
|
+
message: { value: "Internal Server Error", writable: true, configurable: true },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Render `app` with every Error that Solid serializes for hydration replaced. An `<ErrorBoundary>` that
|
|
21
|
+
* caught on the server, or a failed resource, otherwise ships the message and stack in an inline script.
|
|
22
|
+
* Nested contexts copy this one, so they keep the wrapped `serialize`.
|
|
23
|
+
*/
|
|
24
|
+
function redactingErrors(app) {
|
|
25
|
+
if (showsErrors())
|
|
26
|
+
return app;
|
|
27
|
+
return () => {
|
|
28
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Solid's server renderers set a context with serialize; the check below handles its absence.
|
|
29
|
+
const context = sharedConfig.context;
|
|
30
|
+
const serialize = context?.serialize;
|
|
31
|
+
if (context !== undefined && serialize !== undefined) {
|
|
32
|
+
context.serialize = (id, value) => serialize(id, value instanceof Error ? redactedError() : value);
|
|
33
|
+
}
|
|
34
|
+
return app();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
4
37
|
/** The Solid server render adapter - pass to @nifrajs/web's `renderPage`. */
|
|
5
38
|
export const solidAdapter = {
|
|
6
39
|
// Synchronous one-pass render for non-deferred pages (renderPage's buffered fast path). Solid's
|
|
@@ -8,8 +41,11 @@ export const solidAdapter = {
|
|
|
8
41
|
// `generateHydrationScript()`) as `renderToStream`, but skips the TransformStream + Solid's
|
|
9
42
|
// streaming machinery - the heaviest of the five renderers on Bun. A page that defer()s/Suspends
|
|
10
43
|
// takes `renderToStream` below (progressive resolution needs it).
|
|
11
|
-
renderToString(chain, props) {
|
|
12
|
-
|
|
44
|
+
renderToString(chain, props, options) {
|
|
45
|
+
const app = redactingErrors(compose(chain, props));
|
|
46
|
+
return options?.nonce === undefined
|
|
47
|
+
? solidRenderToString(app)
|
|
48
|
+
: solidRenderToString(app, { nonce: options.nonce });
|
|
13
49
|
},
|
|
14
50
|
renderToStream(chain, props, options) {
|
|
15
51
|
// Solid's `renderToStream` streams `Uint8Array` chunks into a Web `WritableStream` via
|
|
@@ -18,7 +54,7 @@ export const solidAdapter = {
|
|
|
18
54
|
// <head>) seeds client hydration. A render failure errors `ts.readable`, which `renderPage`
|
|
19
55
|
// surfaces on the response body. The nonce reaches the resource and boundary scripts Solid streams.
|
|
20
56
|
const ts = new TransformStream();
|
|
21
|
-
const app = compose(chain, props);
|
|
57
|
+
const app = redactingErrors(compose(chain, props));
|
|
22
58
|
const stream = options?.nonce === undefined
|
|
23
59
|
? solidRenderToStream(app)
|
|
24
60
|
: solidRenderToStream(app, { nonce: options.nonce });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,uBAAuB,EACvB,cAAc,IAAI,mBAAmB,EACrC,cAAc,IAAI,mBAAmB,GACtC,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,cAAc,GAAG,uBAAuB,EAAE,CAAA;AAEhD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC,gGAAgG;IAChG,mFAAmF;IACnF,4FAA4F;IAC5F,iGAAiG;IACjG,kEAAkE;IAClE,cAAc,CAAC,KAAK,EAAE,KAAK
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/mdx.js
CHANGED
|
@@ -30,7 +30,7 @@ export function solidMdxBunPlugin(generate) {
|
|
|
30
30
|
async setup(build) {
|
|
31
31
|
let compile;
|
|
32
32
|
try {
|
|
33
|
-
compile = (await import(__rewriteRelativeImportExtension(MDX_MODULE))).compile;
|
|
33
|
+
compile = (await import(__rewriteRelativeImportExtension(MDX_MODULE, true))).compile;
|
|
34
34
|
}
|
|
35
35
|
catch {
|
|
36
36
|
throw new Error("@nifrajs/web-solid/mdx: compiling `.mdx` needs the `@mdx-js/mdx` package - install it (`bun add @mdx-js/mdx`).");
|
package/dist/mdx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdx.js","sourceRoot":"","sources":["../src/mdx.ts"],"names":[],"mappings":";;;;;;;;AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,oDAAoD;AACpD,OAAO,gBAAgB,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC/E,oDAAoD;AACpD,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAG5C,wGAAwG;AACxG,MAAM,UAAU,GAAG,aAAa,CAAA;AAMhC;sEACsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,QAAuB;IACvD,OAAO;QACL,IAAI,EAAE,mBAAmB,QAAQ,EAAE;QACnC,KAAK,CAAC,KAAK,CAAC,KAAK;YACf,IAAI,OAA+B,CAAA;YACnC,IAAI,CAAC;gBACH,OAAO,GAAI,CAAC,MAAM,MAAM,kCAAC,UAAU,
|
|
1
|
+
{"version":3,"file":"mdx.js","sourceRoot":"","sources":["../src/mdx.ts"],"names":[],"mappings":";;;;;;;;AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,oDAAoD;AACpD,OAAO,gBAAgB,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC/E,oDAAoD;AACpD,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAG5C,wGAAwG;AACxG,MAAM,UAAU,GAAG,aAAa,CAAA;AAMhC;sEACsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,QAAuB;IACvD,OAAO;QACL,IAAI,EAAE,mBAAmB,QAAQ,EAAE;QACnC,KAAK,CAAC,KAAK,CAAC,KAAK;YACf,IAAI,OAA+B,CAAA;YACnC,IAAI,CAAC;gBACH,OAAO,GAAI,CAAC,MAAM,MAAM,kCAAC,UAAU,QAAC,CAAiB,CAAC,OAAO,CAAA;YAC/D,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAA;YACH,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;gBACrD,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACzC,0FAA0F;gBAC1F,MAAM,GAAG,GAAG,MAAM,CAChB,MAAM,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;oBACzC,GAAG,EAAE,IAAI;oBACT,oBAAoB,EAAE,gCAAgC;iBACvD,CAAC,CACH,CAAA;gBACD,4EAA4E;gBAC5E,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE;oBACvC,QAAQ,EAAE,GAAG,IAAI,MAAM;oBACvB,OAAO,EAAE;wBACP,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;wBAC7C,CAAC,gBAAgB,EAAE,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;qBACpD;oBACD,OAAO,EAAE,KAAK;oBACd,UAAU,EAAE,KAAK;iBAClB,CAAC,CAAA;gBACF,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;YAC1F,CAAC,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-props.d.ts","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAKhF;;;GAGG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,WAAW,EAC3B,aAAa,EAAE,kBAAkB,CAAC,eAAe,CAAC,EAClD,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,GAC9C,WAAW,
|
|
1
|
+
{"version":3,"file":"route-props.d.ts","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAKhF;;;GAGG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,WAAW,EAC3B,aAAa,EAAE,kBAAkB,CAAC,eAAe,CAAC,EAClD,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,GAC9C,WAAW,CA4Cb"}
|
package/dist/route-props.js
CHANGED
|
@@ -12,6 +12,9 @@ export function routeProps(snapshot, searchSchemas, matchChains) {
|
|
|
12
12
|
},
|
|
13
13
|
// Each layout's own loader data. Without it a layout renders `data: null` on the client while the
|
|
14
14
|
// server rendered it with its data.
|
|
15
|
+
get serverComponents() {
|
|
16
|
+
return snapshot().serverComponents;
|
|
17
|
+
},
|
|
15
18
|
get layoutData() {
|
|
16
19
|
return snapshot().layoutData;
|
|
17
20
|
},
|
package/dist/route-props.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-props.js","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AACA,mGAAmG;AACnG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,QAA2B,EAC3B,aAAkD,EAClD,WAA+C;IAE/C,OAAO;QACL,IAAI,IAAI;YACN,OAAO,QAAQ,EAAE,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,kGAAkG;QAClG,oCAAoC;QACpC,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,OAAO;YACT,OAAO,QAAQ,EAAE,CAAC,OAAO,CAAA;QAC3B,CAAC;QACD,+FAA+F;QAC/F,IAAI,MAAM;YACR,OAAO,QAAQ,EAAE,CAAC,MAAM,CAAA;QAC1B,CAAC;QACD,IAAI,IAAI;YACN,OAAO,QAAQ,EAAE,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,IAAI,MAAM;YACR,+FAA+F;YAC/F,wEAAwE;YACxE,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAA;YACpB,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC/B,OAAO,aAAa,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7F,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,qFAAqF;QACrF,IAAI,UAAU;YACZ,OAAO,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1C,CAAC;KACa,CAAA;AAClB,CAAC"}
|
|
1
|
+
{"version":3,"file":"route-props.js","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AACA,mGAAmG;AACnG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,QAA2B,EAC3B,aAAkD,EAClD,WAA+C;IAE/C,OAAO;QACL,IAAI,IAAI;YACN,OAAO,QAAQ,EAAE,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,kGAAkG;QAClG,oCAAoC;QACpC,IAAI,gBAAgB;YAClB,OAAO,QAAQ,EAAE,CAAC,gBAAgB,CAAA;QACpC,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,OAAO;YACT,OAAO,QAAQ,EAAE,CAAC,OAAO,CAAA;QAC3B,CAAC;QACD,+FAA+F;QAC/F,IAAI,MAAM;YACR,OAAO,QAAQ,EAAE,CAAC,MAAM,CAAA;QAC1B,CAAC;QACD,IAAI,IAAI;YACN,OAAO,QAAQ,EAAE,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,IAAI,MAAM;YACR,+FAA+F;YAC/F,wEAAwE;YACxE,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAA;YACpB,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC/B,OAAO,aAAa,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7F,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,qFAAqF;QACrF,IAAI,UAAU;YACZ,OAAO,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1C,CAAC;KACa,CAAA;AAClB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ComponentClientAdapter, ComponentController, type ComponentKey, type ComponentState, type ServerComponent as Handle } from "@nifrajs/web/server-components/client";
|
|
2
|
+
import { type JSX } from "solid-js";
|
|
3
|
+
export declare function ServerComponent<Input, Props, View>(props: {
|
|
4
|
+
readonly state: ComponentState;
|
|
5
|
+
readonly component: Handle<Input, Props, View>;
|
|
6
|
+
readonly instanceKey: ComponentKey;
|
|
7
|
+
readonly input: NoInfer<Input>;
|
|
8
|
+
readonly parent?: string;
|
|
9
|
+
readonly controller?: ComponentController | undefined;
|
|
10
|
+
readonly clientAdapter?: ComponentClientAdapter | undefined;
|
|
11
|
+
}): JSX.Element;
|
|
12
|
+
//# sourceMappingURL=Outlet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Outlet.d.ts","sourceRoot":"","sources":["../../src/server-components/Outlet.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,IAAI,MAAM,EAE/B,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAA4B,KAAK,GAAG,EAA4B,MAAM,UAAU,CAAA;AACvF,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IACzD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;IAC9C,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAA;IACrD,QAAQ,CAAC,aAAa,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAA;CAC5D,GAAG,GAAG,CAAC,OAAO,CAsBd"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ComponentController, resolveComponentRecord, } from "@nifrajs/web/server-components/client";
|
|
2
|
+
import { createEffect, createMemo, onCleanup, onMount, Show } from "solid-js";
|
|
3
|
+
export function ServerComponent(props) {
|
|
4
|
+
const record = createMemo(() => resolveComponentRecord(props.state, props.component, props.instanceKey, props.input, props.parent ?? null));
|
|
5
|
+
return (<Show keyed when={record().instance}>
|
|
6
|
+
{(_instance) => (<Range state={props.state} record={record()} controller={props.controller} clientAdapter={props.clientAdapter}/>)}
|
|
7
|
+
</Show>);
|
|
8
|
+
}
|
|
9
|
+
function Range(props) {
|
|
10
|
+
const record = props.record;
|
|
11
|
+
let host;
|
|
12
|
+
let controller;
|
|
13
|
+
onMount(() => {
|
|
14
|
+
if (props.controller === undefined && props.clientAdapter === undefined)
|
|
15
|
+
throw new Error("component_contract");
|
|
16
|
+
controller =
|
|
17
|
+
props.controller ??
|
|
18
|
+
new ComponentController({ state: props.state, adapter: props.clientAdapter });
|
|
19
|
+
void controller.mount(host, record.instance).catch(() => {
|
|
20
|
+
throw new Error("component_failed");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
onCleanup(() => {
|
|
24
|
+
if (controller !== undefined)
|
|
25
|
+
void (props.controller === undefined
|
|
26
|
+
? controller.dispose()
|
|
27
|
+
: controller.unmount(record.instance));
|
|
28
|
+
});
|
|
29
|
+
createEffect(() => {
|
|
30
|
+
const state = props.state;
|
|
31
|
+
if (controller !== undefined)
|
|
32
|
+
void controller.adopt(state).catch(() => controller?.reportError("component_failed"));
|
|
33
|
+
});
|
|
34
|
+
return <div ref={host} data-nifra-component={record.instance} innerHTML={record.html}/>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=Outlet.jsx.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Outlet.jsx","sourceRoot":"","sources":["../../src/server-components/Outlet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EAInB,sBAAsB,GACvB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAY,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACvF,MAAM,UAAU,eAAe,CAAqB,KAQnD;IACC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAC7B,sBAAsB,CACpB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,MAAM,IAAI,IAAI,CACrB,CACF,CAAA;IACD,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAClC;MAAA,CAAC,CAAC,SAAiB,EAAE,EAAE,CAAC,CACtB,CAAC,KAAK,CACJ,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CACnB,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CACjB,UAAU,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAC7B,aAAa,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EACnC,CACH,CACH;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AACD,SAAS,KAAK,CAAC,KAKd;IACC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC3B,IAAI,IAAqB,CAAA;IACzB,IAAI,UAA2C,CAAA;IAC/C,OAAO,CAAC,GAAG,EAAE;QACX,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;YACrE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;QACvC,UAAU;YACR,KAAK,CAAC,UAAU;gBAChB,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,aAAc,EAAE,CAAC,CAAA;QAChF,KAAK,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACtD,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;QACrC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,KAAK,SAAS;YAC1B,KAAK,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS;gBAClC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE;gBACtB,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IACF,YAAY,CAAC,GAAG,EAAE;QAChB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;QACzB,IAAI,UAAU,KAAK,SAAS;YAC1B,KAAK,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,CAAA;AAC1F,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type NativeRangeOptions } from "@nifrajs/web/server-components/range";
|
|
2
|
+
export declare function solidComponentClient(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;AAG7C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,0EAwD5F"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { disposeComponentPortals } from "@nifrajs/web/server-components/portal";
|
|
2
|
+
import { nativeComponentClient, } from "@nifrajs/web/server-components/range";
|
|
3
|
+
import { batch, createComponent, createSignal } from "solid-js";
|
|
4
|
+
import { hydrate } from "solid-js/web";
|
|
5
|
+
export function solidComponentClient(options) {
|
|
6
|
+
return nativeComponentClient({
|
|
7
|
+
...options,
|
|
8
|
+
adapter: "solid",
|
|
9
|
+
runtime: {
|
|
10
|
+
hydrate(host, contract, record) {
|
|
11
|
+
// 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.
|
|
12
|
+
const [props, setProps] = createSignal(record.props);
|
|
13
|
+
const slots = Object.fromEntries(contract.slots.map((name) => [
|
|
14
|
+
name,
|
|
15
|
+
'<nifra-slot data-nifra-slot="' +
|
|
16
|
+
name +
|
|
17
|
+
'">' +
|
|
18
|
+
(record.slots?.[name] ?? "") +
|
|
19
|
+
"</nifra-slot>",
|
|
20
|
+
]));
|
|
21
|
+
const reactive = new Proxy({}, {
|
|
22
|
+
// 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.
|
|
23
|
+
get: (_, name) => (name === "slots" ? slots : props()[name]),
|
|
24
|
+
has: (_, name) => name === "slots" || Object.hasOwn(props(), name),
|
|
25
|
+
ownKeys: () => [...Object.keys(props()), "slots"],
|
|
26
|
+
getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true }),
|
|
27
|
+
});
|
|
28
|
+
const dispose = hydrate(
|
|
29
|
+
// 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.
|
|
30
|
+
() => createComponent(contract.component, reactive), host, { renderId: record.renderId });
|
|
31
|
+
let disposed = false;
|
|
32
|
+
return {
|
|
33
|
+
update(next, current) {
|
|
34
|
+
if (!disposed && current())
|
|
35
|
+
batch(() => {
|
|
36
|
+
if (current())
|
|
37
|
+
setProps(() => next);
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
async dispose() {
|
|
41
|
+
if (!disposed) {
|
|
42
|
+
disposed = true;
|
|
43
|
+
try {
|
|
44
|
+
await disposeComponentPortals(host);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
dispose();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# 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,EAAE,KAAK,EAAkB,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,MAAM,UAAU,oBAAoB,CAAC,OAAwD;IAC3F,OAAO,qBAAqB,CAAC;QAC3B,GAAG,OAAO;QACV,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE;YACP,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM;gBAC5B,2MAA2M;gBAC3M,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,KAAgC,CAAC,CAAA;gBAC/E,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,IAAI,KAAK,CACxB,EAAE,EACF;oBACE,2MAA2M;oBAC3M,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAc,CAAC,CAAC;oBACtE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC;oBAClE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC;oBACjD,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;iBAC3E,CACF,CAAA;gBACD,MAAM,OAAO,GAAG,OAAO;gBACrB,2MAA2M;gBAC3M,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,SAA+C,EAAE,QAAQ,CAAC,EACzF,IAAI,EACJ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAC9B,CAAA;gBACD,IAAI,QAAQ,GAAG,KAAK,CAAA;gBACpB,OAAO;oBACL,MAAM,CAAC,IAAI,EAAE,OAAO;wBAClB,IAAI,CAAC,QAAQ,IAAI,OAAO,EAAE;4BACxB,KAAK,CAAC,GAAG,EAAE;gCACT,IAAI,OAAO,EAAE;oCAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;4BACrC,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,OAAO,EAAE,CAAA;4BACX,CAAC;wBACH,CAAC;oBACH,CAAC;iBACF,CAAA;YACH,CAAC;SACF;KACF,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -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;AAIlF,eAAO,MAAM,qBAAqB,QAAO,4BAMtC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { nativeServerComponents } from "@nifrajs/web/server-components/native";
|
|
2
|
+
import { createComponent } from "solid-js";
|
|
3
|
+
import { renderToString } from "solid-js/web";
|
|
4
|
+
export const solidServerComponents = () => nativeServerComponents("solid", (component, props, renderId) =>
|
|
5
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
|
|
6
|
+
renderToString(() => createComponent(component, props), {
|
|
7
|
+
renderId,
|
|
8
|
+
}));
|
|
9
|
+
//# 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;AAC9E,OAAO,EAAkB,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAiC,EAAE,CACtE,sBAAsB,CAAC,OAAO,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;AAC7D,iLAAiL;AACjL,cAAc,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,SAA+C,EAAE,KAAK,CAAC,EAAE;IAC5F,QAAQ;CACT,CAAC,CACH,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/web-solid",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Solid render adapter for @nifrajs/web - SSR + hydration, with the Solid Babel build plugin (@nifrajs/web-solid/plugin).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -90,6 +90,20 @@
|
|
|
90
90
|
"bun": "./src/fn.ts",
|
|
91
91
|
"types": "./dist/fn.d.ts",
|
|
92
92
|
"default": "./dist/fn.js"
|
|
93
|
+
},
|
|
94
|
+
"./server-components": {
|
|
95
|
+
"bun": "./src/server-components/server.ts",
|
|
96
|
+
"types": "./dist/server-components/server.d.ts",
|
|
97
|
+
"default": "./dist/server-components/server.js"
|
|
98
|
+
},
|
|
99
|
+
"./server-components/client": {
|
|
100
|
+
"bun": "./src/server-components/client.ts",
|
|
101
|
+
"types": "./dist/server-components/client.d.ts",
|
|
102
|
+
"default": "./dist/server-components/client.js"
|
|
103
|
+
},
|
|
104
|
+
"./server-components/outlet": {
|
|
105
|
+
"types": "./dist/server-components/Outlet.d.ts",
|
|
106
|
+
"default": "./src/server-components/Outlet.tsx"
|
|
93
107
|
}
|
|
94
108
|
},
|
|
95
109
|
"files": [
|
|
@@ -107,10 +121,10 @@
|
|
|
107
121
|
"dependencies": {
|
|
108
122
|
"@babel/core": "^7.26.0",
|
|
109
123
|
"@babel/preset-typescript": "^7.26.0",
|
|
110
|
-
"@nifrajs/core": "
|
|
111
|
-
"@nifrajs/i18n": "
|
|
112
|
-
"@nifrajs/image": "
|
|
113
|
-
"@nifrajs/web": "
|
|
124
|
+
"@nifrajs/core": "5.1.0",
|
|
125
|
+
"@nifrajs/i18n": "5.1.0",
|
|
126
|
+
"@nifrajs/image": "5.1.0",
|
|
127
|
+
"@nifrajs/web": "5.1.0",
|
|
114
128
|
"babel-preset-solid": "^1.9.3",
|
|
115
129
|
"solid-refresh": "^0.7.8"
|
|
116
130
|
},
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Bun-plugin in `@nifrajs/web-solid/plugin`.
|
|
6
6
|
*/
|
|
7
7
|
import type { RenderAdapter } from "@nifrajs/web"
|
|
8
|
+
import { sharedConfig } from "solid-js"
|
|
8
9
|
import {
|
|
9
10
|
generateHydrationScript,
|
|
10
11
|
renderToStream as solidRenderToStream,
|
|
@@ -14,6 +15,43 @@ import { compose } from "./compose.ts"
|
|
|
14
15
|
|
|
15
16
|
const HYDRATION_HEAD = generateHydrationScript()
|
|
16
17
|
|
|
18
|
+
// An unset NODE_ENV counts as production, as for the framework's own error pages.
|
|
19
|
+
const showsErrors = (): boolean => {
|
|
20
|
+
const nodeEnv = typeof process === "undefined" ? undefined : process.env?.NODE_ENV
|
|
21
|
+
return nodeEnv === "development" || nodeEnv === "test"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** What the browser receives for an error Solid hands to hydration: its text names server internals. */
|
|
25
|
+
function redactedError(): Error {
|
|
26
|
+
// Built bare: a constructed Error carries a stack and, on Bun, the line, column and file it was made
|
|
27
|
+
// at, and Solid serializes every own property.
|
|
28
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Object.create over Error.prototype with a message is an Error minus its stack.
|
|
29
|
+
return Object.create(Error.prototype, {
|
|
30
|
+
message: { value: "Internal Server Error", writable: true, configurable: true },
|
|
31
|
+
}) as Error
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Render `app` with every Error that Solid serializes for hydration replaced. An `<ErrorBoundary>` that
|
|
36
|
+
* caught on the server, or a failed resource, otherwise ships the message and stack in an inline script.
|
|
37
|
+
* Nested contexts copy this one, so they keep the wrapped `serialize`.
|
|
38
|
+
*/
|
|
39
|
+
function redactingErrors<T>(app: () => T): () => T {
|
|
40
|
+
if (showsErrors()) return app
|
|
41
|
+
return () => {
|
|
42
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Solid's server renderers set a context with serialize; the check below handles its absence.
|
|
43
|
+
const context = sharedConfig.context as
|
|
44
|
+
| { serialize?: (id: string, value: unknown) => void }
|
|
45
|
+
| undefined
|
|
46
|
+
const serialize = context?.serialize
|
|
47
|
+
if (context !== undefined && serialize !== undefined) {
|
|
48
|
+
context.serialize = (id, value) =>
|
|
49
|
+
serialize(id, value instanceof Error ? redactedError() : value)
|
|
50
|
+
}
|
|
51
|
+
return app()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
17
55
|
/** The Solid server render adapter - pass to @nifrajs/web's `renderPage`. */
|
|
18
56
|
export const solidAdapter: RenderAdapter = {
|
|
19
57
|
// Synchronous one-pass render for non-deferred pages (renderPage's buffered fast path). Solid's
|
|
@@ -21,8 +59,11 @@ export const solidAdapter: RenderAdapter = {
|
|
|
21
59
|
// `generateHydrationScript()`) as `renderToStream`, but skips the TransformStream + Solid's
|
|
22
60
|
// streaming machinery - the heaviest of the five renderers on Bun. A page that defer()s/Suspends
|
|
23
61
|
// takes `renderToStream` below (progressive resolution needs it).
|
|
24
|
-
renderToString(chain, props) {
|
|
25
|
-
|
|
62
|
+
renderToString(chain, props, options) {
|
|
63
|
+
const app = redactingErrors(compose(chain, props))
|
|
64
|
+
return options?.nonce === undefined
|
|
65
|
+
? solidRenderToString(app)
|
|
66
|
+
: solidRenderToString(app, { nonce: options.nonce })
|
|
26
67
|
},
|
|
27
68
|
renderToStream(chain, props, options) {
|
|
28
69
|
// Solid's `renderToStream` streams `Uint8Array` chunks into a Web `WritableStream` via
|
|
@@ -31,7 +72,7 @@ export const solidAdapter: RenderAdapter = {
|
|
|
31
72
|
// <head>) seeds client hydration. A render failure errors `ts.readable`, which `renderPage`
|
|
32
73
|
// surfaces on the response body. The nonce reaches the resource and boundary scripts Solid streams.
|
|
33
74
|
const ts = new TransformStream<Uint8Array, Uint8Array>()
|
|
34
|
-
const app = compose(chain, props)
|
|
75
|
+
const app = redactingErrors(compose(chain, props))
|
|
35
76
|
const stream =
|
|
36
77
|
options?.nonce === undefined
|
|
37
78
|
? solidRenderToStream(app)
|
package/src/route-props.ts
CHANGED
|
@@ -18,6 +18,9 @@ export function routeProps(
|
|
|
18
18
|
},
|
|
19
19
|
// Each layout's own loader data. Without it a layout renders `data: null` on the client while the
|
|
20
20
|
// server rendered it with its data.
|
|
21
|
+
get serverComponents() {
|
|
22
|
+
return snapshot().serverComponents
|
|
23
|
+
},
|
|
21
24
|
get layoutData() {
|
|
22
25
|
return snapshot().layoutData
|
|
23
26
|
},
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentClientAdapter,
|
|
3
|
+
ComponentController,
|
|
4
|
+
type ComponentKey,
|
|
5
|
+
type ComponentState,
|
|
6
|
+
type ServerComponent as Handle,
|
|
7
|
+
resolveComponentRecord,
|
|
8
|
+
} from "@nifrajs/web/server-components/client"
|
|
9
|
+
import { createEffect, createMemo, type JSX, onCleanup, onMount, Show } from "solid-js"
|
|
10
|
+
export function ServerComponent<Input, Props, View>(props: {
|
|
11
|
+
readonly state: ComponentState
|
|
12
|
+
readonly component: Handle<Input, Props, View>
|
|
13
|
+
readonly instanceKey: ComponentKey
|
|
14
|
+
readonly input: NoInfer<Input>
|
|
15
|
+
readonly parent?: string
|
|
16
|
+
readonly controller?: ComponentController | undefined
|
|
17
|
+
readonly clientAdapter?: ComponentClientAdapter | undefined
|
|
18
|
+
}): JSX.Element {
|
|
19
|
+
const record = createMemo(() =>
|
|
20
|
+
resolveComponentRecord(
|
|
21
|
+
props.state,
|
|
22
|
+
props.component,
|
|
23
|
+
props.instanceKey,
|
|
24
|
+
props.input,
|
|
25
|
+
props.parent ?? null,
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
return (
|
|
29
|
+
<Show keyed when={record().instance}>
|
|
30
|
+
{(_instance: string) => (
|
|
31
|
+
<Range
|
|
32
|
+
state={props.state}
|
|
33
|
+
record={record()}
|
|
34
|
+
controller={props.controller}
|
|
35
|
+
clientAdapter={props.clientAdapter}
|
|
36
|
+
/>
|
|
37
|
+
)}
|
|
38
|
+
</Show>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
function Range(props: {
|
|
42
|
+
readonly state: ComponentState
|
|
43
|
+
readonly record: import("@nifrajs/web/server-components/client").ComponentRecord
|
|
44
|
+
readonly controller?: ComponentController | undefined
|
|
45
|
+
readonly clientAdapter?: ComponentClientAdapter | undefined
|
|
46
|
+
}): JSX.Element {
|
|
47
|
+
const record = props.record
|
|
48
|
+
let host!: HTMLDivElement
|
|
49
|
+
let controller: ComponentController | undefined
|
|
50
|
+
onMount(() => {
|
|
51
|
+
if (props.controller === undefined && props.clientAdapter === undefined)
|
|
52
|
+
throw new Error("component_contract")
|
|
53
|
+
controller =
|
|
54
|
+
props.controller ??
|
|
55
|
+
new ComponentController({ state: props.state, adapter: props.clientAdapter! })
|
|
56
|
+
void controller.mount(host, record.instance).catch(() => {
|
|
57
|
+
throw new Error("component_failed")
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
onCleanup(() => {
|
|
61
|
+
if (controller !== undefined)
|
|
62
|
+
void (props.controller === undefined
|
|
63
|
+
? controller.dispose()
|
|
64
|
+
: controller.unmount(record.instance))
|
|
65
|
+
})
|
|
66
|
+
createEffect(() => {
|
|
67
|
+
const state = props.state
|
|
68
|
+
if (controller !== undefined)
|
|
69
|
+
void controller.adopt(state).catch(() => controller?.reportError("component_failed"))
|
|
70
|
+
})
|
|
71
|
+
return <div ref={host} data-nifra-component={record.instance} innerHTML={record.html} />
|
|
72
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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 { batch, type Component, createComponent, createSignal } from "solid-js"
|
|
7
|
+
import { hydrate } from "solid-js/web"
|
|
8
|
+
export function solidComponentClient(options: Omit<NativeRangeOptions, "adapter" | "runtime">) {
|
|
9
|
+
return nativeComponentClient({
|
|
10
|
+
...options,
|
|
11
|
+
adapter: "solid",
|
|
12
|
+
runtime: {
|
|
13
|
+
hydrate(host, contract, record) {
|
|
14
|
+
// 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.
|
|
15
|
+
const [props, setProps] = createSignal(record.props as Record<string, unknown>)
|
|
16
|
+
const slots = Object.fromEntries(
|
|
17
|
+
contract.slots.map((name) => [
|
|
18
|
+
name,
|
|
19
|
+
'<nifra-slot data-nifra-slot="' +
|
|
20
|
+
name +
|
|
21
|
+
'">' +
|
|
22
|
+
(record.slots?.[name] ?? "") +
|
|
23
|
+
"</nifra-slot>",
|
|
24
|
+
]),
|
|
25
|
+
)
|
|
26
|
+
const reactive = new Proxy<Record<string, unknown>>(
|
|
27
|
+
{},
|
|
28
|
+
{
|
|
29
|
+
// 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.
|
|
30
|
+
get: (_, name) => (name === "slots" ? slots : props()[name as string]),
|
|
31
|
+
has: (_, name) => name === "slots" || Object.hasOwn(props(), name),
|
|
32
|
+
ownKeys: () => [...Object.keys(props()), "slots"],
|
|
33
|
+
getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true }),
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
const dispose = hydrate(
|
|
37
|
+
// 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.
|
|
38
|
+
() => createComponent(contract.component as Component<Record<string, unknown>>, reactive),
|
|
39
|
+
host,
|
|
40
|
+
{ renderId: record.renderId },
|
|
41
|
+
)
|
|
42
|
+
let disposed = false
|
|
43
|
+
return {
|
|
44
|
+
update(next, current) {
|
|
45
|
+
if (!disposed && current())
|
|
46
|
+
batch(() => {
|
|
47
|
+
if (current()) setProps(() => next)
|
|
48
|
+
})
|
|
49
|
+
},
|
|
50
|
+
async dispose() {
|
|
51
|
+
if (!disposed) {
|
|
52
|
+
disposed = true
|
|
53
|
+
try {
|
|
54
|
+
await disposeComponentPortals(host)
|
|
55
|
+
} finally {
|
|
56
|
+
dispose()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ServerComponentRenderAdapter } from "@nifrajs/web/server-components"
|
|
2
|
+
import { nativeServerComponents } from "@nifrajs/web/server-components/native"
|
|
3
|
+
import { type Component, createComponent } from "solid-js"
|
|
4
|
+
import { renderToString } from "solid-js/web"
|
|
5
|
+
export const solidServerComponents = (): ServerComponentRenderAdapter =>
|
|
6
|
+
nativeServerComponents("solid", (component, props, renderId) =>
|
|
7
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The native renderer receives only its registered component and schema-projected props from the shared executor.
|
|
8
|
+
renderToString(() => createComponent(component as Component<Record<string, unknown>>, props), {
|
|
9
|
+
renderId,
|
|
10
|
+
}),
|
|
11
|
+
)
|