@ecopages/ecopages-jsx 0.2.0-beta.25 → 0.2.0-beta.27
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 +1 -0
- package/package.json +3 -3
- package/src/ecopages-jsx-radiant-ssr-policy.d.ts +6 -5
- package/src/ecopages-jsx-radiant-ssr-policy.js +9 -9
- package/src/ecopages-jsx-render-session.d.ts +0 -7
- package/src/ecopages-jsx-render-session.js +2 -17
- package/src/ecopages-jsx-renderer.d.ts +14 -0
- package/src/ecopages-jsx-renderer.js +32 -4
package/README.md
CHANGED
|
@@ -99,6 +99,7 @@ Ecopages JSX can own the outer page shell or just a nested foreign subtree. When
|
|
|
99
99
|
|
|
100
100
|
Important:
|
|
101
101
|
|
|
102
|
+
- Cross-integration JSX children must use `EcoEmbed`. Opaque plain objects fail fast at core foreign-child queue boundaries; they are not coerced with `String(object)`.
|
|
102
103
|
- Components that may render foreign children must declare those children in `config.dependencies.components`.
|
|
103
104
|
- Ecopages validates mixed-renderer ownership from declared dependencies during render preparation. It does not treat rendered HTML alone as the source of truth.
|
|
104
105
|
- Ecopages JSX keeps raw-markup preservation and asset collection inside the JSX renderer.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/ecopages-jsx",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.27",
|
|
4
4
|
"description": "JSX integration plugin for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"directory": "packages/integrations/ecopages-jsx"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ecopages/mdx": "0.2.0-beta.
|
|
34
|
+
"@ecopages/mdx": "0.2.0-beta.27",
|
|
35
35
|
"@mdx-js/mdx": "^3.1.1",
|
|
36
36
|
"vfile": "^6.0.3"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@ecopages/core": "0.2.0-beta.
|
|
39
|
+
"@ecopages/core": "0.2.0-beta.27",
|
|
40
40
|
"@ecopages/jsx": "0.3.0-beta.2",
|
|
41
41
|
"@ecopages/radiant": "0.3.0-beta.2"
|
|
42
42
|
}
|
|
@@ -4,13 +4,14 @@ import { createMarkupNodeLike } from '@ecopages/jsx';
|
|
|
4
4
|
*
|
|
5
5
|
* @remarks
|
|
6
6
|
* Radiant's server bridge and light-DOM shim are loaded lazily because most JSX
|
|
7
|
-
* renders do not need them.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* renders do not need them. Resolved runtime modules are cached on the policy
|
|
8
|
+
* instance so repeated renders on the same renderer reuse one load without
|
|
9
|
+
* process-wide mutable class state. Light-DOM constructor installation remains
|
|
10
|
+
* process-global and idempotent via {@link ensureRadiantLightDomGlobals}.
|
|
10
11
|
*/
|
|
11
12
|
export declare class EcopagesJsxRadiantSsrPolicy {
|
|
12
|
-
private
|
|
13
|
-
private
|
|
13
|
+
private runtimeModules;
|
|
14
|
+
private runtimeModulesPromise;
|
|
14
15
|
private readonly enabled;
|
|
15
16
|
constructor(enabled: boolean);
|
|
16
17
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createMarkupNodeLike } from "@ecopages/jsx";
|
|
2
2
|
import { createServerHydrationBindingState, withServerHydrationBindingState } from "@ecopages/jsx/server";
|
|
3
3
|
class EcopagesJsxRadiantSsrPolicy {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
runtimeModules;
|
|
5
|
+
runtimeModulesPromise;
|
|
6
6
|
enabled;
|
|
7
7
|
constructor(enabled) {
|
|
8
8
|
this.enabled = enabled;
|
|
@@ -23,7 +23,7 @@ class EcopagesJsxRadiantSsrPolicy {
|
|
|
23
23
|
if (!this.enabled) {
|
|
24
24
|
return render();
|
|
25
25
|
}
|
|
26
|
-
const runtimeModules = await
|
|
26
|
+
const runtimeModules = await this.runtimeModulesPromise;
|
|
27
27
|
if (!runtimeModules) {
|
|
28
28
|
return render();
|
|
29
29
|
}
|
|
@@ -37,7 +37,7 @@ class EcopagesJsxRadiantSsrPolicy {
|
|
|
37
37
|
* generated host HTML without escaping it back into plain text.
|
|
38
38
|
*/
|
|
39
39
|
renderIntrinsicElementMarkup(instance) {
|
|
40
|
-
const renderBridge =
|
|
40
|
+
const renderBridge = this.runtimeModules?.resolveRadiantElementRenderBridge(instance);
|
|
41
41
|
if (!renderBridge) {
|
|
42
42
|
return void 0;
|
|
43
43
|
}
|
|
@@ -51,13 +51,13 @@ class EcopagesJsxRadiantSsrPolicy {
|
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
async ensureRuntimeInstalled() {
|
|
54
|
-
if (!
|
|
54
|
+
if (!this.runtimeModulesPromise) {
|
|
55
55
|
const radiantLightDomShimEntry = import.meta.resolve("@ecopages/radiant/server/light-dom-shim");
|
|
56
56
|
const radiantElementSsrRuntimeModuleUrl = new URL(
|
|
57
57
|
"./radiant-element-ssr-bridge.js",
|
|
58
58
|
radiantLightDomShimEntry
|
|
59
59
|
).href;
|
|
60
|
-
|
|
60
|
+
this.runtimeModulesPromise = (async () => {
|
|
61
61
|
const lightDomShimModule2 = await import(radiantLightDomShimEntry);
|
|
62
62
|
ensureRadiantLightDomGlobals(lightDomShimModule2.installLightDomShim);
|
|
63
63
|
const radiantElementSsrRuntimeModule = await import(radiantElementSsrRuntimeModuleUrl);
|
|
@@ -66,12 +66,12 @@ class EcopagesJsxRadiantSsrPolicy {
|
|
|
66
66
|
resolveRadiantElementRenderBridge: radiantElementSsrRuntimeModule.resolveRadiantElementRenderBridge,
|
|
67
67
|
withServerRadiantElementSsrRuntime: radiantElementSsrRuntimeModule.withServerRadiantElementSsrRuntime
|
|
68
68
|
};
|
|
69
|
-
|
|
69
|
+
this.runtimeModules = modules;
|
|
70
70
|
return modules;
|
|
71
71
|
})();
|
|
72
72
|
}
|
|
73
|
-
await
|
|
74
|
-
const lightDomShimModule =
|
|
73
|
+
await this.runtimeModulesPromise;
|
|
74
|
+
const lightDomShimModule = this.runtimeModules;
|
|
75
75
|
if (lightDomShimModule) {
|
|
76
76
|
ensureRadiantLightDomGlobals(lightDomShimModule.installLightDomShim);
|
|
77
77
|
}
|
|
@@ -24,13 +24,6 @@ export declare class EcopagesJsxRenderSession {
|
|
|
24
24
|
* JSX SSR scope bridge.
|
|
25
25
|
*/
|
|
26
26
|
withActiveScope<T>(render: () => T): T;
|
|
27
|
-
/**
|
|
28
|
-
* Runs work with the current page-level hydrate binding namespace attached to JSX SSR.
|
|
29
|
-
*
|
|
30
|
-
* This lets page, layout, and document renders participate in one client-owned
|
|
31
|
-
* hydration root even though Ecopages composes them through multiple sibling render calls.
|
|
32
|
-
*/
|
|
33
|
-
withHydrationBindingScope<T>(render: () => T): T;
|
|
34
27
|
beginCollectedAssetFrame(): ProcessedAsset[];
|
|
35
28
|
endCollectedAssetFrame(frame: ProcessedAsset[]): ProcessedAsset[];
|
|
36
29
|
recordCollectedAssets(collectedAssets: ProcessedAsset[]): ProcessedAsset[];
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import {
|
|
3
|
-
createServerHydrationBindingState,
|
|
4
|
-
getActiveSsrScopeValue,
|
|
5
|
-
withActiveSsrScopeValue,
|
|
6
|
-
withServerHydrationBindingState
|
|
7
|
-
} from "@ecopages/jsx/server";
|
|
2
|
+
import { getActiveSsrScopeValue, withActiveSsrScopeValue } from "@ecopages/jsx/server";
|
|
8
3
|
const ECOPAGES_JSX_SSR_RENDER_STATE_KEY = /* @__PURE__ */ Symbol.for("@ecopages/ecopages-jsx.ssr-render-state");
|
|
9
4
|
const renderStateStorage = new AsyncLocalStorage();
|
|
10
5
|
class EcopagesJsxRenderSession {
|
|
@@ -32,23 +27,13 @@ class EcopagesJsxRenderSession {
|
|
|
32
27
|
return renderStateStorage.run(jsxScopeState, () => render());
|
|
33
28
|
}
|
|
34
29
|
const state = {
|
|
35
|
-
collectedAssetFrames: []
|
|
36
|
-
hydrationBindingState: createServerHydrationBindingState()
|
|
30
|
+
collectedAssetFrames: []
|
|
37
31
|
};
|
|
38
32
|
return renderStateStorage.run(
|
|
39
33
|
state,
|
|
40
34
|
() => withActiveSsrScopeValue(ECOPAGES_JSX_SSR_RENDER_STATE_KEY, state, render)
|
|
41
35
|
);
|
|
42
36
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Runs work with the current page-level hydrate binding namespace attached to JSX SSR.
|
|
45
|
-
*
|
|
46
|
-
* This lets page, layout, and document renders participate in one client-owned
|
|
47
|
-
* hydration root even though Ecopages composes them through multiple sibling render calls.
|
|
48
|
-
*/
|
|
49
|
-
withHydrationBindingScope(render) {
|
|
50
|
-
return withServerHydrationBindingState(this.getState().hydrationBindingState, render);
|
|
51
|
-
}
|
|
52
37
|
beginCollectedAssetFrame() {
|
|
53
38
|
const state = this.getState();
|
|
54
39
|
const frame = [];
|
|
@@ -16,7 +16,21 @@ export declare class EcopagesJsxRenderer extends IntegrationRenderer<JsxRenderab
|
|
|
16
16
|
private readonly mdxExtensions;
|
|
17
17
|
private readonly renderSession;
|
|
18
18
|
private readonly radiantSsrPolicy;
|
|
19
|
+
/**
|
|
20
|
+
* Serializes foreign-child props for string-first boundaries.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* DOM nodes become `outerHTML`. JSX trees, arrays, and template results are
|
|
24
|
+
* stringified here. Other values are left for core foreign-child interception,
|
|
25
|
+
* which rejects opaque plain objects before queueing. Cross-integration
|
|
26
|
+
* children must use `EcoEmbed`.
|
|
27
|
+
*/
|
|
19
28
|
private normalizeForeignChildProps;
|
|
29
|
+
/**
|
|
30
|
+
* Returns whether children are a JSX/template shape this renderer can
|
|
31
|
+
* serialize before foreign-child queueing.
|
|
32
|
+
*/
|
|
33
|
+
private isJsxSerializableForeignChild;
|
|
20
34
|
/**
|
|
21
35
|
* Re-renders queued JSX children inside the owning renderer so nested custom
|
|
22
36
|
* elements and queued foreign subtrees contribute assets to the same frame.
|
|
@@ -16,6 +16,15 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
16
16
|
mdxExtensions;
|
|
17
17
|
renderSession;
|
|
18
18
|
radiantSsrPolicy;
|
|
19
|
+
/**
|
|
20
|
+
* Serializes foreign-child props for string-first boundaries.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* DOM nodes become `outerHTML`. JSX trees, arrays, and template results are
|
|
24
|
+
* stringified here. Other values are left for core foreign-child interception,
|
|
25
|
+
* which rejects opaque plain objects before queueing. Cross-integration
|
|
26
|
+
* children must use `EcoEmbed`.
|
|
27
|
+
*/
|
|
19
28
|
normalizeForeignChildProps(props) {
|
|
20
29
|
if (!("children" in props)) {
|
|
21
30
|
return props;
|
|
@@ -30,10 +39,29 @@ class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
30
39
|
children: children.outerHTML
|
|
31
40
|
};
|
|
32
41
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
if (this.isJsxSerializableForeignChild(children)) {
|
|
43
|
+
return {
|
|
44
|
+
...props,
|
|
45
|
+
children: renderToString(children)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return props;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns whether children are a JSX/template shape this renderer can
|
|
52
|
+
* serialize before foreign-child queueing.
|
|
53
|
+
*/
|
|
54
|
+
isJsxSerializableForeignChild(children) {
|
|
55
|
+
if (Array.isArray(children)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (children === null || typeof children !== "object") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if ("$$typeof" in children) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return "strings" in children && Array.isArray(children.strings);
|
|
37
65
|
}
|
|
38
66
|
/**
|
|
39
67
|
* Re-renders queued JSX children inside the owning renderer so nested custom
|