@ecopages/react 0.2.0-alpha.2 → 0.2.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -39
- package/README.md +160 -18
- package/package.json +6 -6
- package/src/react-hmr-strategy.d.ts +26 -21
- package/src/react-hmr-strategy.js +91 -110
- package/src/react-renderer.d.ts +165 -41
- package/src/react-renderer.js +451 -158
- package/src/react.constants.d.ts +1 -0
- package/src/react.constants.js +4 -0
- package/src/react.plugin.d.ts +37 -108
- package/src/react.plugin.js +125 -54
- package/src/react.types.d.ts +88 -0
- package/src/react.types.js +0 -0
- package/src/router-adapter.d.ts +2 -2
- package/src/services/react-bundle.service.d.ts +4 -25
- package/src/services/react-bundle.service.js +39 -91
- package/src/services/react-hmr-page-metadata-cache.d.ts +9 -0
- package/src/services/react-hmr-page-metadata-cache.js +18 -2
- package/src/services/react-hydration-asset.service.d.ts +7 -6
- package/src/services/react-hydration-asset.service.js +29 -17
- package/src/services/react-mdx-config-dependency.service.d.ts +36 -0
- package/src/services/react-mdx-config-dependency.service.js +122 -0
- package/src/services/react-page-module.service.d.ts +8 -2
- package/src/services/react-page-module.service.js +44 -37
- package/src/services/react-page-payload.service.d.ts +46 -0
- package/src/services/react-page-payload.service.js +67 -0
- package/src/services/react-runtime-bundle.service.d.ts +14 -12
- package/src/services/react-runtime-bundle.service.js +103 -180
- package/src/utils/client-graph-boundary-plugin.js +149 -11
- package/src/utils/component-config-traversal.d.ts +36 -0
- package/src/utils/component-config-traversal.js +54 -0
- package/src/utils/declared-modules.d.ts +1 -1
- package/src/utils/declared-modules.js +7 -16
- package/src/utils/dynamic.test.browser.d.ts +1 -0
- package/src/utils/dynamic.test.browser.js +33 -0
- package/src/utils/hydration-scripts.d.ts +19 -4
- package/src/utils/hydration-scripts.js +102 -39
- package/src/utils/hydration-scripts.test.browser.d.ts +1 -0
- package/src/utils/hydration-scripts.test.browser.js +126 -0
- package/src/utils/reachability-analyzer.d.ts +12 -1
- package/src/utils/reachability-analyzer.js +101 -5
- package/src/utils/react-dom-runtime-interop-plugin.d.ts +5 -0
- package/src/utils/react-dom-runtime-interop-plugin.js +29 -0
- package/src/utils/react-mdx-loader-plugin.js +13 -5
- package/src/utils/react-runtime-specifier-map.d.ts +6 -0
- package/src/utils/react-runtime-specifier-map.js +37 -0
- package/src/utils/use-sync-external-store-shim-plugin.d.ts +5 -0
- package/src/utils/use-sync-external-store-shim-plugin.js +41 -0
- package/src/react-hmr-strategy.ts +0 -444
- package/src/react-renderer.ts +0 -403
- package/src/react.plugin.ts +0 -241
- package/src/router-adapter.ts +0 -95
- package/src/services/react-bundle.service.ts +0 -212
- package/src/services/react-hmr-page-metadata-cache.ts +0 -24
- package/src/services/react-hydration-asset.service.ts +0 -260
- package/src/services/react-page-module.service.ts +0 -214
- package/src/services/react-runtime-bundle.service.ts +0 -271
- package/src/utils/client-graph-boundary-plugin.ts +0 -590
- package/src/utils/client-only.ts +0 -27
- package/src/utils/declared-modules.ts +0 -99
- package/src/utils/dynamic.ts +0 -27
- package/src/utils/hmr-scripts.ts +0 -47
- package/src/utils/html-boundary.ts +0 -66
- package/src/utils/hydration-scripts.ts +0 -338
- package/src/utils/reachability-analyzer.ts +0 -440
- package/src/utils/react-mdx-loader-plugin.ts +0 -40
|
@@ -9,6 +9,42 @@ function getHmrImportStatement(isMdx) {
|
|
|
9
9
|
function getComponentType(isMdx) {
|
|
10
10
|
return isMdx ? "MDX" : "React";
|
|
11
11
|
}
|
|
12
|
+
function getDevPageRootCleanupScript() {
|
|
13
|
+
return `window.__ECO_PAGES__ = window.__ECO_PAGES__ || {};
|
|
14
|
+
window.__ECO_PAGES__.react = window.__ECO_PAGES__.react || {};
|
|
15
|
+
window.__ECO_PAGES__.react.cleanupPageRoot = () => {
|
|
16
|
+
const activeRoot = window.__ECO_PAGES__.react?.pageRoot || root;
|
|
17
|
+
if (!activeRoot) {
|
|
18
|
+
window.__ECO_PAGES__.react.pageRoot = null;
|
|
19
|
+
window.__ECO_PAGES__?.navigation?.releaseOwnership?.("react-router");
|
|
20
|
+
delete window.__ECO_PAGES__.page;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
window.__ECO_PAGES__.react.pageRoot = null;
|
|
24
|
+
window.__ECO_PAGES__?.navigation?.releaseOwnership?.("react-router");
|
|
25
|
+
delete window.__ECO_PAGES__.page;
|
|
26
|
+
root = null;
|
|
27
|
+
activeRoot.unmount();
|
|
28
|
+
};`;
|
|
29
|
+
}
|
|
30
|
+
function getProdPageRootCleanupScript() {
|
|
31
|
+
return 'window.__ECO_PAGES__=window.__ECO_PAGES__||{};window.__ECO_PAGES__.react=window.__ECO_PAGES__.react||{};window.__ECO_PAGES__.react.cleanupPageRoot=()=>{const a=window.__ECO_PAGES__.react?.pageRoot||root;if(!a){window.__ECO_PAGES__.react.pageRoot=null;window.__ECO_PAGES__?.navigation?.releaseOwnership?.("react-router");delete window.__ECO_PAGES__.page;return}window.__ECO_PAGES__.react.pageRoot=null;window.__ECO_PAGES__?.navigation?.releaseOwnership?.("react-router");delete window.__ECO_PAGES__.page;root=null;a.unmount()};';
|
|
32
|
+
}
|
|
33
|
+
function getDevRouterBootstrapRegistrationScript() {
|
|
34
|
+
return `const currentOwnerState = window.__ECO_PAGES__?.navigation?.getOwnerState?.();
|
|
35
|
+
if (!(currentOwnerState?.owner === "react-router" && currentOwnerState.canHandleSpaNavigation)) {
|
|
36
|
+
window.__ECO_PAGES__?.navigation?.register({
|
|
37
|
+
owner: "react-router",
|
|
38
|
+
cleanupBeforeHandoff: async () => {
|
|
39
|
+
window.__ECO_PAGES__?.react?.cleanupPageRoot?.();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
window.__ECO_PAGES__?.navigation?.claimOwnership?.("react-router");
|
|
43
|
+
}`;
|
|
44
|
+
}
|
|
45
|
+
function getProdRouterBootstrapRegistrationScript() {
|
|
46
|
+
return 'const o=window.__ECO_PAGES__?.navigation?.getOwnerState?.();if(!(o?.owner==="react-router"&&o.canHandleSpaNavigation)){window.__ECO_PAGES__?.navigation?.register({owner:"react-router",cleanupBeforeHandoff:async()=>{window.__ECO_PAGES__?.react?.cleanupPageRoot?.()}});window.__ECO_PAGES__?.navigation?.claimOwnership?.("react-router")}';
|
|
47
|
+
}
|
|
12
48
|
function createDevScriptWithRouter(options) {
|
|
13
49
|
const { importPath, isMdx, router, reactImportPath, reactDomClientImportPath, routerImportPath } = options;
|
|
14
50
|
const { components, getRouterProps } = router;
|
|
@@ -21,10 +57,13 @@ import { createElement } from "${reactImportPath}";
|
|
|
21
57
|
import { ${components.router}, ${components.pageContent} } from "${routerImportPath}";
|
|
22
58
|
${getImportStatement(importPath, isMdx)}
|
|
23
59
|
|
|
24
|
-
window.
|
|
25
|
-
window.
|
|
26
|
-
window.
|
|
27
|
-
|
|
60
|
+
window.__ECO_PAGES__ = window.__ECO_PAGES__ || {};
|
|
61
|
+
window.__ECO_PAGES__.hmrHandlers = window.__ECO_PAGES__.hmrHandlers || {};
|
|
62
|
+
window.__ECO_PAGES__.react = window.__ECO_PAGES__.react || {};
|
|
63
|
+
window.__ECO_PAGES__.react.pageRoot = window.__ECO_PAGES__.react.pageRoot || null;
|
|
64
|
+
let root = window.__ECO_PAGES__.react.pageRoot;
|
|
65
|
+
${getDevPageRootCleanupScript()}
|
|
66
|
+
${getDevRouterBootstrapRegistrationScript()}
|
|
28
67
|
|
|
29
68
|
const getPageData = () => {
|
|
30
69
|
const el = document.getElementById("__ECO_PAGE_DATA__");
|
|
@@ -36,7 +75,7 @@ const getPageData = () => {
|
|
|
36
75
|
|
|
37
76
|
const props = getPageData();
|
|
38
77
|
|
|
39
|
-
window.
|
|
78
|
+
window.__ECO_PAGES__.page = {
|
|
40
79
|
module: "${importPath}",
|
|
41
80
|
props
|
|
42
81
|
};
|
|
@@ -47,21 +86,30 @@ const createTree = (Component, props) => {
|
|
|
47
86
|
};
|
|
48
87
|
|
|
49
88
|
const mount = () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
if (window.__ECO_PAGES__.react?.pageRoot) {
|
|
90
|
+
root = window.__ECO_PAGES__.react.pageRoot;
|
|
91
|
+
root.render(createTree(Page, props));
|
|
92
|
+
} else {
|
|
93
|
+
root = hydrateRoot(document.body, createTree(Page, props), {
|
|
94
|
+
onRecoverableError: (err) => console.warn("[ecopages] Hydration error:", err)
|
|
95
|
+
});
|
|
96
|
+
window.__ECO_PAGES__.react.pageRoot = root;
|
|
97
|
+
}
|
|
98
|
+
window.__ECO_PAGES__.hmrHandlers["${importPath}"] = async (newUrl) => {
|
|
60
99
|
try {
|
|
61
100
|
const newModule = await import(newUrl);
|
|
101
|
+
const nextProps = getPageData();
|
|
62
102
|
${getHmrImportStatement(isMdx)}
|
|
63
|
-
|
|
64
|
-
|
|
103
|
+
window.__ECO_PAGES__.page = {
|
|
104
|
+
module: "${importPath}",
|
|
105
|
+
props: nextProps
|
|
106
|
+
};
|
|
107
|
+
root.render(createTree(NewPage, nextProps));
|
|
108
|
+
if (window.__ECO_PAGES__?.navigation?.getOwnerState().owner === "react-router") {
|
|
109
|
+
console.log("[ecopages] ${getComponentType(isMdx)} component updated via router");
|
|
110
|
+
} else {
|
|
111
|
+
console.log("[ecopages] ${getComponentType(isMdx)} component updated");
|
|
112
|
+
}
|
|
65
113
|
} catch (e) {
|
|
66
114
|
console.error("[ecopages] Failed to hot-reload ${getComponentType(isMdx)} component:", e);
|
|
67
115
|
}
|
|
@@ -82,8 +130,12 @@ import { hydrateRoot } from "${reactDomClientImportPath}";
|
|
|
82
130
|
import { createElement } from "${reactImportPath}";
|
|
83
131
|
${getImportStatement(importPath, isMdx)}
|
|
84
132
|
|
|
85
|
-
window.
|
|
86
|
-
|
|
133
|
+
window.__ECO_PAGES__ = window.__ECO_PAGES__ || {};
|
|
134
|
+
window.__ECO_PAGES__.hmrHandlers = window.__ECO_PAGES__.hmrHandlers || {};
|
|
135
|
+
window.__ECO_PAGES__.react = window.__ECO_PAGES__.react || {};
|
|
136
|
+
window.__ECO_PAGES__.react.pageRoot = window.__ECO_PAGES__.react.pageRoot || null;
|
|
137
|
+
let root = window.__ECO_PAGES__.react.pageRoot;
|
|
138
|
+
${getDevPageRootCleanupScript()}
|
|
87
139
|
|
|
88
140
|
const getPageData = () => {
|
|
89
141
|
const el = document.getElementById("__ECO_PAGE_DATA__");
|
|
@@ -95,7 +147,7 @@ const getPageData = () => {
|
|
|
95
147
|
|
|
96
148
|
const props = getPageData();
|
|
97
149
|
|
|
98
|
-
window.
|
|
150
|
+
window.__ECO_PAGES__.page = {
|
|
99
151
|
module: "${importPath}",
|
|
100
152
|
props
|
|
101
153
|
};
|
|
@@ -103,14 +155,21 @@ window.__ECO_PAGE__ = {
|
|
|
103
155
|
const createTree = (Component, props) => {
|
|
104
156
|
const Layout = Component.config?.layout;
|
|
105
157
|
const pageElement = createElement(Component, props);
|
|
106
|
-
|
|
158
|
+
const layoutProps = props?.locals ? { locals: props.locals } : null;
|
|
159
|
+
return Layout ? createElement(Layout, layoutProps, pageElement) : pageElement;
|
|
107
160
|
};
|
|
108
161
|
|
|
109
162
|
const mount = () => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
163
|
+
if (window.__ECO_PAGES__.react?.pageRoot) {
|
|
164
|
+
root = window.__ECO_PAGES__.react.pageRoot;
|
|
165
|
+
root.render(createTree(Page, props));
|
|
166
|
+
} else {
|
|
167
|
+
root = hydrateRoot(document.body, createTree(Page, props), {
|
|
168
|
+
onRecoverableError: (err) => console.warn("[ecopages] Hydration error:", err)
|
|
169
|
+
});
|
|
170
|
+
window.__ECO_PAGES__.react.pageRoot = root;
|
|
171
|
+
}
|
|
172
|
+
window.__ECO_PAGES__.hmrHandlers["${importPath}"] = async (newUrl) => {
|
|
114
173
|
try {
|
|
115
174
|
const newModule = await import(newUrl);
|
|
116
175
|
${getHmrImportStatement(isMdx)}
|
|
@@ -136,16 +195,16 @@ function createProdScriptWithRouter(options) {
|
|
|
136
195
|
throw new Error("routerImportPath is required when router adapter is configured");
|
|
137
196
|
}
|
|
138
197
|
if (isMdx) {
|
|
139
|
-
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import{${components.router} as R,${components.pageContent} as PC}from"${routerImportPath}";import*as M from"${importPath}";const P=M.default;if(M.config)P.config=M.config;const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.
|
|
198
|
+
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import{${components.router} as R,${components.pageContent} as PC}from"${routerImportPath}";import*as M from"${importPath}";const P=M.default;if(M.config)P.config=M.config;window.__ECO_PAGES__=window.__ECO_PAGES__||{};window.__ECO_PAGES__.react=window.__ECO_PAGES__.react||{};window.__ECO_PAGES__.react.pageRoot=window.__ECO_PAGES__.react.pageRoot||null;let root=window.__ECO_PAGES__.react.pageRoot;${getProdPageRootCleanupScript()}${getProdRouterBootstrapRegistrationScript()}const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.__ECO_PAGES__.page={module:"${importPath}",props:pr};const ct=(C,p)=>ce(R,${getRouterProps("C", "p")},ce(PC));const m=()=>{if(window.__ECO_PAGES__.react?.pageRoot){root=window.__ECO_PAGES__.react.pageRoot;root.render(ct(P,pr));return}root=hr(document.body,ct(P,pr),{onRecoverableError:(e)=>console.warn("[ecopages] Hydration error:",e)});window.__ECO_PAGES__.react.pageRoot=root};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",m):m()`;
|
|
140
199
|
}
|
|
141
|
-
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import{${components.router} as R,${components.pageContent} as PC}from"${routerImportPath}";import P from"${importPath}";const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.
|
|
200
|
+
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import{${components.router} as R,${components.pageContent} as PC}from"${routerImportPath}";import P from"${importPath}";window.__ECO_PAGES__=window.__ECO_PAGES__||{};window.__ECO_PAGES__.react=window.__ECO_PAGES__.react||{};window.__ECO_PAGES__.react.pageRoot=window.__ECO_PAGES__.react.pageRoot||null;let root=window.__ECO_PAGES__.react.pageRoot;${getProdPageRootCleanupScript()}${getProdRouterBootstrapRegistrationScript()}const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.__ECO_PAGES__.page={module:"${importPath}",props:pr};const ct=(C,p)=>ce(R,${getRouterProps("C", "p")},ce(PC));const m=()=>{if(window.__ECO_PAGES__.react?.pageRoot){root=window.__ECO_PAGES__.react.pageRoot;root.render(ct(P,pr));return}root=hr(document.body,ct(P,pr),{onRecoverableError:(e)=>console.warn("[ecopages] Hydration error:",e)});window.__ECO_PAGES__.react.pageRoot=root};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",m):m()`;
|
|
142
201
|
}
|
|
143
202
|
function createProdScriptWithoutRouter(options) {
|
|
144
203
|
const { importPath, isMdx, reactImportPath, reactDomClientImportPath } = options;
|
|
145
204
|
if (isMdx) {
|
|
146
|
-
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import*as M from"${importPath}";const P=M.default;if(M.config)P.config=M.config;const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.
|
|
205
|
+
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import*as M from"${importPath}";const P=M.default;if(M.config)P.config=M.config;window.__ECO_PAGES__=window.__ECO_PAGES__||{};window.__ECO_PAGES__.react=window.__ECO_PAGES__.react||{};window.__ECO_PAGES__.react.pageRoot=window.__ECO_PAGES__.react.pageRoot||null;let root=window.__ECO_PAGES__.react.pageRoot;${getProdPageRootCleanupScript()}const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.__ECO_PAGES__.page={module:"${importPath}",props:pr};const ct=(C,p)=>{const L=C.config?.layout;const pe=ce(C,p);const lp=p?.locals?{locals:p.locals}:null;return L?ce(L,lp,pe):pe};const m=()=>{if(window.__ECO_PAGES__.react?.pageRoot){root=window.__ECO_PAGES__.react.pageRoot;root.render(ct(P,pr));return}root=hr(document.body,ct(P,pr),{onRecoverableError:(e)=>console.warn("[ecopages] Hydration error:",e)});window.__ECO_PAGES__.react.pageRoot=root};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",m):m()`;
|
|
147
206
|
}
|
|
148
|
-
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import P from"${importPath}";const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.
|
|
207
|
+
return `import{hydrateRoot as hr}from"${reactDomClientImportPath}";import{createElement as ce}from"${reactImportPath}";import P from"${importPath}";window.__ECO_PAGES__=window.__ECO_PAGES__||{};window.__ECO_PAGES__.react=window.__ECO_PAGES__.react||{};window.__ECO_PAGES__.react.pageRoot=window.__ECO_PAGES__.react.pageRoot||null;let root=window.__ECO_PAGES__.react.pageRoot;${getProdPageRootCleanupScript()}const gd=()=>{const e=document.getElementById("__ECO_PAGE_DATA__");if(e?.textContent){try{return JSON.parse(e.textContent)}catch{}}return{}};const pr=gd();window.__ECO_PAGES__.page={module:"${importPath}",props:pr};const ct=(C,p)=>{const L=C.config?.layout;const pe=ce(C,p);const lp=p?.locals?{locals:p.locals}:null;return L?ce(L,lp,pe):pe};const m=()=>{if(window.__ECO_PAGES__.react?.pageRoot){root=window.__ECO_PAGES__.react.pageRoot;root.render(ct(P,pr));return}root=hr(document.body,ct(P,pr),{onRecoverableError:(e)=>console.warn("[ecopages] Hydration error:",e)});window.__ECO_PAGES__.react.pageRoot=root};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",m):m()`;
|
|
149
208
|
}
|
|
150
209
|
function createHydrationScript(options) {
|
|
151
210
|
const { isDevelopment, router } = options;
|
|
@@ -156,10 +215,8 @@ function createHydrationScript(options) {
|
|
|
156
215
|
}
|
|
157
216
|
function createIslandHydrationScript(options) {
|
|
158
217
|
const targetSelector = JSON.stringify(options.targetSelector);
|
|
159
|
-
const serializedProps = JSON.stringify(options.props ?? {});
|
|
160
218
|
const componentRef = JSON.stringify(options.componentRef ?? "");
|
|
161
219
|
const componentFile = JSON.stringify(options.componentFile ?? "");
|
|
162
|
-
const mountedAttribute = "data-eco-react-mounted";
|
|
163
220
|
if (options.isDevelopment) {
|
|
164
221
|
return `
|
|
165
222
|
import { createRoot } from "${options.reactDomClientImportPath}";
|
|
@@ -195,18 +252,24 @@ const resolveComponent = () => {
|
|
|
195
252
|
};
|
|
196
253
|
|
|
197
254
|
const mount = () => {
|
|
198
|
-
const
|
|
255
|
+
const targets = document.querySelectorAll(${targetSelector});
|
|
199
256
|
const Component = resolveComponent();
|
|
200
|
-
if (!
|
|
257
|
+
if (!Component || targets.length === 0) {
|
|
201
258
|
return;
|
|
202
259
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
260
|
+
targets.forEach((target) => {
|
|
261
|
+
if (!(target instanceof HTMLElement)) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const props = JSON.parse(atob(target.getAttribute("data-eco-props") || "e30="));
|
|
265
|
+
const container = document.createElement("eco-island");
|
|
266
|
+
container.style.display = "block";
|
|
267
|
+
target.replaceWith(container);
|
|
268
|
+
const root = createRoot(container);
|
|
269
|
+
root.render(createElement(Component, props));
|
|
270
|
+
});
|
|
207
271
|
};
|
|
208
272
|
|
|
209
|
-
document.addEventListener("eco:after-swap", mount);
|
|
210
273
|
if (document.readyState === "loading") {
|
|
211
274
|
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
212
275
|
} else {
|
|
@@ -214,7 +277,7 @@ if (document.readyState === "loading") {
|
|
|
214
277
|
}
|
|
215
278
|
`.trim();
|
|
216
279
|
}
|
|
217
|
-
return `import{createRoot as cr}from"${options.reactDomClientImportPath}";import{createElement as ce}from"${options.reactImportPath}";import*as M from"${options.importPath}";const r=${componentRef};const f=${componentFile};const mv=Object.values(M);const c=mv.find((e)=>{if(typeof e!=="function")return false;const ec=e.config?.__eco;if(!ec)return false;if(r&&ec.id===r)return true;if(f&&ec.file===f)return true;return false;})??(typeof M.default==="function"?M.default:mv.find((e)=>typeof e==="function")??null);const m=()=>{const
|
|
280
|
+
return `import{createRoot as cr}from"${options.reactDomClientImportPath}";import{createElement as ce}from"${options.reactImportPath}";import*as M from"${options.importPath}";const r=${componentRef};const f=${componentFile};const mv=Object.values(M);const c=mv.find((e)=>{if(typeof e!=="function")return false;const ec=e.config?.__eco;if(!ec)return false;if(r&&ec.id===r)return true;if(f&&ec.file===f)return true;return false;})??(typeof M.default==="function"?M.default:mv.find((e)=>typeof e==="function")??null);const m=()=>{const ts=document.querySelectorAll(${targetSelector});if(!c||ts.length===0)return;ts.forEach((t)=>{if(!(t instanceof HTMLElement))return;const p=JSON.parse(atob(t.getAttribute("data-eco-props")||"e30="));const ct=document.createElement("eco-island");ct.style.display="block";t.replaceWith(ct);cr(ct).render(ce(c,p))})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",m,{once:true}):m()`;
|
|
218
281
|
}
|
|
219
282
|
export {
|
|
220
283
|
createHydrationScript,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createHydrationScript } from "./hydration-scripts.js";
|
|
3
|
+
const routerAdapter = {
|
|
4
|
+
name: "eco-router",
|
|
5
|
+
bundle: {
|
|
6
|
+
importPath: "/assets/router.js",
|
|
7
|
+
outputName: "router",
|
|
8
|
+
externals: []
|
|
9
|
+
},
|
|
10
|
+
importMapKey: "@ecopages/react-router",
|
|
11
|
+
components: {
|
|
12
|
+
router: "EcoRouter",
|
|
13
|
+
pageContent: "PageContent"
|
|
14
|
+
},
|
|
15
|
+
getRouterProps: (page, props) => `{ page: ${page}, pageProps: ${props} }`
|
|
16
|
+
};
|
|
17
|
+
function createModuleUrl(source) {
|
|
18
|
+
return `data:text/javascript;base64,${btoa(source)}`;
|
|
19
|
+
}
|
|
20
|
+
async function importModule(moduleUrl) {
|
|
21
|
+
await import(
|
|
22
|
+
/* @vite-ignore */
|
|
23
|
+
moduleUrl
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
function createRuntimeModules() {
|
|
27
|
+
const reactImportPath = createModuleUrl("export const createElement = (...args) => ({ args });");
|
|
28
|
+
const reactDomClientImportPath = createModuleUrl(`
|
|
29
|
+
export const hydrateRoot = (container, tree, options) => {
|
|
30
|
+
const runtime = window.__ECO_REACT_HYDRATION_TEST__;
|
|
31
|
+
runtime.hydrateCalls.push({
|
|
32
|
+
containerTag: container.tagName,
|
|
33
|
+
hasRecoverableErrorHandler: typeof options?.onRecoverableError === "function",
|
|
34
|
+
tree,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
render() {},
|
|
39
|
+
unmount() {
|
|
40
|
+
runtime.unmountCount += 1;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
`);
|
|
45
|
+
const importPath = createModuleUrl("export default function Page() { return null; }");
|
|
46
|
+
const routerImportPath = createModuleUrl(`
|
|
47
|
+
export function EcoRouter(props) {
|
|
48
|
+
return props;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function PageContent() {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
`);
|
|
55
|
+
return {
|
|
56
|
+
importPath,
|
|
57
|
+
reactImportPath,
|
|
58
|
+
reactDomClientImportPath,
|
|
59
|
+
routerImportPath
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
describe("createHydrationScript browser execution", () => {
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
document.body.innerHTML = "";
|
|
65
|
+
const testWindow = window;
|
|
66
|
+
delete testWindow.__ECO_PAGES__;
|
|
67
|
+
delete testWindow.__ECO_REACT_HYDRATION_TEST__;
|
|
68
|
+
});
|
|
69
|
+
it("registers router ownership and cleanup when the browser hydration bootstrap runs", async () => {
|
|
70
|
+
const runtimeModules = createRuntimeModules();
|
|
71
|
+
const testWindow = window;
|
|
72
|
+
testWindow.__ECO_REACT_HYDRATION_TEST__ = {
|
|
73
|
+
hydrateCalls: [],
|
|
74
|
+
claimedOwners: [],
|
|
75
|
+
releasedOwners: [],
|
|
76
|
+
registrations: [],
|
|
77
|
+
unmountCount: 0
|
|
78
|
+
};
|
|
79
|
+
testWindow.__ECO_PAGES__ = {
|
|
80
|
+
navigation: {
|
|
81
|
+
getOwnerState: () => ({
|
|
82
|
+
owner: "html",
|
|
83
|
+
canHandleSpaNavigation: false
|
|
84
|
+
}),
|
|
85
|
+
register: (registration) => {
|
|
86
|
+
testWindow.__ECO_REACT_HYDRATION_TEST__?.registrations.push(registration);
|
|
87
|
+
},
|
|
88
|
+
claimOwnership: (owner) => {
|
|
89
|
+
testWindow.__ECO_REACT_HYDRATION_TEST__?.claimedOwners.push(owner);
|
|
90
|
+
},
|
|
91
|
+
releaseOwnership: (owner) => {
|
|
92
|
+
testWindow.__ECO_REACT_HYDRATION_TEST__?.releasedOwners.push(owner);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
document.body.innerHTML = `<script id="__ECO_PAGE_DATA__" type="application/json">${JSON.stringify({
|
|
97
|
+
title: "Hello React",
|
|
98
|
+
locals: { theme: "dark" }
|
|
99
|
+
})}<\/script>`;
|
|
100
|
+
const script = createHydrationScript({
|
|
101
|
+
...runtimeModules,
|
|
102
|
+
isDevelopment: true,
|
|
103
|
+
isMdx: false,
|
|
104
|
+
router: routerAdapter
|
|
105
|
+
});
|
|
106
|
+
await importModule(createModuleUrl(script));
|
|
107
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.hydrateCalls).toHaveLength(1);
|
|
108
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.hydrateCalls[0]?.containerTag).toBe("BODY");
|
|
109
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.hydrateCalls[0]?.hasRecoverableErrorHandler).toBe(true);
|
|
110
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.claimedOwners).toEqual(["react-router"]);
|
|
111
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.registrations).toHaveLength(1);
|
|
112
|
+
expect(typeof testWindow.__ECO_PAGES__?.react?.cleanupPageRoot).toBe("function");
|
|
113
|
+
expect(testWindow.__ECO_PAGES__?.page).toEqual({
|
|
114
|
+
module: runtimeModules.importPath,
|
|
115
|
+
props: {
|
|
116
|
+
title: "Hello React",
|
|
117
|
+
locals: { theme: "dark" }
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
await testWindow.__ECO_PAGES__?.react?.cleanupPageRoot?.();
|
|
121
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.unmountCount).toBe(1);
|
|
122
|
+
expect(testWindow.__ECO_REACT_HYDRATION_TEST__?.releasedOwners).toEqual(["react-router"]);
|
|
123
|
+
expect(testWindow.__ECO_PAGES__?.page).toBeUndefined();
|
|
124
|
+
expect(testWindow.__ECO_PAGES__?.react?.pageRoot).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -42,6 +42,15 @@ export type ReachabilityResult = {
|
|
|
42
42
|
*/
|
|
43
43
|
analyzed: boolean;
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Optional export filter supplied by the client graph boundary when a local
|
|
47
|
+
* module is imported through a narrower named-export surface.
|
|
48
|
+
*
|
|
49
|
+
* `'*'` means the whole module namespace is considered reachable, while a
|
|
50
|
+
* `Set` restricts analysis to the named exports that are actually requested by
|
|
51
|
+
* downstream client-reachable modules.
|
|
52
|
+
*/
|
|
53
|
+
type ExplicitlyRequestedExports = Set<string> | '*';
|
|
45
54
|
/**
|
|
46
55
|
* Analyzes a module using Oxc AST and extracts a strict reachability graph
|
|
47
56
|
* starting from client roots (`render`, `errorBoundary`, `loadingFallback` of `eco.page` or `eco.component`).
|
|
@@ -50,6 +59,8 @@ export type ReachabilityResult = {
|
|
|
50
59
|
* @param filename - Absolute or relative path to the module file.
|
|
51
60
|
* @param program - Optional pre-parsed Oxc program AST. When supplied, the
|
|
52
61
|
* internal `parseSync` call is skipped entirely (avoids double-parsing).
|
|
62
|
+
* @param explicitlyRequestedExports - Optional named export filter propagated
|
|
63
|
+
* from a downstream importer when this module is only partially reachable.
|
|
53
64
|
*/
|
|
54
|
-
export declare function analyzeReachability(source: string, filename: string, program?: ReturnType<typeof parseSync>['program']): ReachabilityResult;
|
|
65
|
+
export declare function analyzeReachability(source: string, filename: string, program?: ReturnType<typeof parseSync>['program'], explicitlyRequestedExports?: ExplicitlyRequestedExports): ReachabilityResult;
|
|
55
66
|
export {};
|
|
@@ -7,7 +7,7 @@ function parserLanguageForFile(filename) {
|
|
|
7
7
|
if (extension === ".jsx") return "jsx";
|
|
8
8
|
return "js";
|
|
9
9
|
}
|
|
10
|
-
function analyzeReachability(source, filename, program) {
|
|
10
|
+
function analyzeReachability(source, filename, program, explicitlyRequestedExports) {
|
|
11
11
|
let resolvedProgram;
|
|
12
12
|
if (program) {
|
|
13
13
|
resolvedProgram = program;
|
|
@@ -114,12 +114,86 @@ function analyzeReachability(source, filename, program) {
|
|
|
114
114
|
potentialClientRoots.push(node);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
function getExportedName(specifier) {
|
|
118
|
+
if (specifier?.exported?.type === "Identifier") return specifier.exported.name;
|
|
119
|
+
if (typeof specifier?.exported?.value === "string") return specifier.exported.value;
|
|
120
|
+
if (specifier?.local?.type === "Identifier") return specifier.local.name;
|
|
121
|
+
if (typeof specifier?.local?.value === "string") return specifier.local.value;
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
function getReexportedImportName(specifier) {
|
|
125
|
+
if (specifier?.local?.type === "Identifier") return specifier.local.name;
|
|
126
|
+
if (typeof specifier?.local?.value === "string") return specifier.local.value;
|
|
127
|
+
if (specifier?.imported?.type === "Identifier") return specifier.imported.name;
|
|
128
|
+
if (typeof specifier?.imported?.value === "string") return specifier.imported.value;
|
|
129
|
+
return getExportedName(specifier);
|
|
130
|
+
}
|
|
131
|
+
function getLocalExportName(specifier) {
|
|
132
|
+
if (specifier?.local?.type === "Identifier") return specifier.local.name;
|
|
133
|
+
if (typeof specifier?.local?.value === "string") return specifier.local.value;
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
function isExplicitlyRequestedExport(name) {
|
|
137
|
+
if (explicitlyRequestedExports === "*") return true;
|
|
138
|
+
return explicitlyRequestedExports?.has(name) ?? false;
|
|
139
|
+
}
|
|
117
140
|
let isFallbackRoots = false;
|
|
118
141
|
if (potentialClientRoots.length === 0) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
if (explicitlyRequestedExports) {
|
|
143
|
+
for (const node of resolvedProgram.body) {
|
|
144
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
145
|
+
const exportNode = node;
|
|
146
|
+
if (exportNode.source && exportNode.specifiers?.length) {
|
|
147
|
+
const hasRequestedReexport = exportNode.specifiers.some((specifier) => {
|
|
148
|
+
const exportedName = getExportedName(specifier);
|
|
149
|
+
return exportedName ? isExplicitlyRequestedExport(exportedName) : false;
|
|
150
|
+
});
|
|
151
|
+
if (hasRequestedReexport) {
|
|
152
|
+
potentialClientRoots.push(node);
|
|
153
|
+
}
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (exportNode.declaration?.type === "FunctionDeclaration" || exportNode.declaration?.type === "ClassDeclaration") {
|
|
157
|
+
const declarationName = exportNode.declaration.id?.name;
|
|
158
|
+
if (declarationName && isExplicitlyRequestedExport(declarationName)) {
|
|
159
|
+
potentialClientRoots.push(node);
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (exportNode.declaration?.type === "VariableDeclaration") {
|
|
164
|
+
const hasRequestedDeclaration = exportNode.declaration.declarations.some(
|
|
165
|
+
(declaration) => declaration.id?.type === "Identifier" && isExplicitlyRequestedExport(declaration.id.name)
|
|
166
|
+
);
|
|
167
|
+
if (hasRequestedDeclaration) {
|
|
168
|
+
potentialClientRoots.push(node);
|
|
169
|
+
}
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (exportNode.specifiers?.length) {
|
|
173
|
+
const hasRequestedSpecifier = exportNode.specifiers.some((specifier) => {
|
|
174
|
+
const exportedName = getExportedName(specifier);
|
|
175
|
+
return exportedName ? isExplicitlyRequestedExport(exportedName) : false;
|
|
176
|
+
});
|
|
177
|
+
if (hasRequestedSpecifier) {
|
|
178
|
+
potentialClientRoots.push(node);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
182
|
+
if (isExplicitlyRequestedExport("default")) {
|
|
183
|
+
potentialClientRoots.push(node);
|
|
184
|
+
}
|
|
185
|
+
} else if (node.type === "ExportAllDeclaration") {
|
|
186
|
+
if (explicitlyRequestedExports === "*") {
|
|
187
|
+
potentialClientRoots.push(node);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
isFallbackRoots = true;
|
|
193
|
+
for (const node of resolvedProgram.body) {
|
|
194
|
+
if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration" || node.type === "ExportAllDeclaration") {
|
|
195
|
+
potentialClientRoots.push(node);
|
|
196
|
+
}
|
|
123
197
|
}
|
|
124
198
|
}
|
|
125
199
|
}
|
|
@@ -167,6 +241,28 @@ function analyzeReachability(source, filename, program) {
|
|
|
167
241
|
markImportReachable(node.source.value, "*");
|
|
168
242
|
return;
|
|
169
243
|
}
|
|
244
|
+
if (node.type === "ExportNamedDeclaration" && typeof node.source?.value === "string") {
|
|
245
|
+
for (const specifier of node.specifiers ?? []) {
|
|
246
|
+
const importedName = getReexportedImportName(specifier);
|
|
247
|
+
if (importedName) {
|
|
248
|
+
markImportReachable(node.source.value, importedName);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (node.type === "ExportNamedDeclaration" && !node.source && explicitlyRequestedExports && node.specifiers?.length) {
|
|
254
|
+
for (const specifier of node.specifiers) {
|
|
255
|
+
const exportedName = getExportedName(specifier);
|
|
256
|
+
if (!exportedName || !isExplicitlyRequestedExport(exportedName)) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const localName = getLocalExportName(specifier);
|
|
260
|
+
if (localName && !currentScope.has(localName)) {
|
|
261
|
+
checkIdentifier(localName);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
170
266
|
if (node.type === "Identifier" || node.type === "JSXIdentifier" && /^[A-Z]/.test(node.name)) {
|
|
171
267
|
if (!currentScope.has(node.name)) {
|
|
172
268
|
checkIdentifier(node.name);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function createReactDomRuntimeInteropPlugin(options) {
|
|
4
|
+
const reactDomFileFilter = /[\\/]react-dom[\\/].*\.js$/;
|
|
5
|
+
const reactRequirePattern = /\brequire\((['"])react\1\)/g;
|
|
6
|
+
const reactSpecifier = options?.reactSpecifier ?? "react";
|
|
7
|
+
return {
|
|
8
|
+
name: options?.name ?? "react-dom-runtime-interop",
|
|
9
|
+
setup(build) {
|
|
10
|
+
build.onLoad({ filter: reactDomFileFilter }, (args) => {
|
|
11
|
+
const content = fs.readFileSync(args.path, "utf-8");
|
|
12
|
+
if (!reactRequirePattern.test(content)) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
reactRequirePattern.lastIndex = 0;
|
|
16
|
+
const rewritten = content.replace(reactRequirePattern, "__ecopages_react_runtime");
|
|
17
|
+
return {
|
|
18
|
+
contents: `import * as __ecopages_react_runtime from '${reactSpecifier}';
|
|
19
|
+
${rewritten}`,
|
|
20
|
+
loader: "js",
|
|
21
|
+
resolveDir: path.dirname(args.path)
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
createReactDomRuntimeInteropPlugin
|
|
29
|
+
};
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { compile } from "@mdx-js/mdx";
|
|
4
|
-
import
|
|
4
|
+
import sourceMap from "source-map";
|
|
5
5
|
import { VFile } from "vfile";
|
|
6
|
+
function resolveCompileFormat(filePath, compilerOptions) {
|
|
7
|
+
const configuredFormat = compilerOptions?.format;
|
|
8
|
+
if (configuredFormat && configuredFormat !== "detect") {
|
|
9
|
+
return configuredFormat;
|
|
10
|
+
}
|
|
11
|
+
return path.extname(filePath).toLowerCase() === ".md" ? "mdx" : configuredFormat;
|
|
12
|
+
}
|
|
6
13
|
function createReactMdxLoaderPlugin(compilerOptions) {
|
|
7
14
|
const mdxExtensions = compilerOptions?.mdxExtensions ?? [".mdx"];
|
|
8
|
-
const mdExtensions = compilerOptions?.mdExtensions ?? [
|
|
15
|
+
const mdExtensions = compilerOptions?.mdExtensions ?? [];
|
|
9
16
|
const allExtensions = [...mdxExtensions, ...mdExtensions];
|
|
10
17
|
const escapedExts = allExtensions.map((ext) => ext.replace(".", "\\."));
|
|
11
18
|
const filter = new RegExp(`(${escapedExts.join("|")})(\\?.*)?$`);
|
|
@@ -18,13 +25,14 @@ function createReactMdxLoaderPlugin(compilerOptions) {
|
|
|
18
25
|
const file = new VFile({ path: filePath, value: source });
|
|
19
26
|
const compiled = await compile(file, {
|
|
20
27
|
...compilerOptions,
|
|
21
|
-
|
|
28
|
+
format: resolveCompileFormat(filePath, compilerOptions),
|
|
29
|
+
SourceMapGenerator: sourceMap.SourceMapGenerator
|
|
22
30
|
});
|
|
23
|
-
const
|
|
31
|
+
const inlineSourceMap = compiled.map ? `
|
|
24
32
|
//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(compiled.map)).toString("base64")}
|
|
25
33
|
` : "";
|
|
26
34
|
return {
|
|
27
|
-
contents: `${String(compiled.value)}${
|
|
35
|
+
contents: `${String(compiled.value)}${inlineSourceMap}`,
|
|
28
36
|
loader: compilerOptions?.jsx ? "jsx" : "js",
|
|
29
37
|
resolveDir: path.dirname(args.path)
|
|
30
38
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReactRouterAdapter } from '../router-adapter.js';
|
|
2
|
+
import type { ReactRuntimeImports } from '../services/react-runtime-bundle.service.js';
|
|
3
|
+
export declare const REACT_RUNTIME_SPECIFIERS: readonly ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom/client"];
|
|
4
|
+
export declare function buildReactRuntimeSpecifierMap(runtimeImports: ReactRuntimeImports, routerAdapter?: ReactRouterAdapter): Record<string, string>;
|
|
5
|
+
export declare function getReactRuntimeExternalSpecifiers(): string[];
|
|
6
|
+
export declare function getReactClientGraphAllowSpecifiers(runtimeSpecifiers: Iterable<string>, routerAdapter?: ReactRouterAdapter): string[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const REACT_RUNTIME_SPECIFIERS = [
|
|
2
|
+
"react",
|
|
3
|
+
"react-dom",
|
|
4
|
+
"react/jsx-runtime",
|
|
5
|
+
"react/jsx-dev-runtime",
|
|
6
|
+
"react-dom/client"
|
|
7
|
+
];
|
|
8
|
+
function buildReactRuntimeSpecifierMap(runtimeImports, routerAdapter) {
|
|
9
|
+
const map = {
|
|
10
|
+
react: runtimeImports.react,
|
|
11
|
+
"react/jsx-runtime": runtimeImports.reactJsxRuntime,
|
|
12
|
+
"react/jsx-dev-runtime": runtimeImports.reactJsxDevRuntime,
|
|
13
|
+
"react-dom": runtimeImports.reactDom,
|
|
14
|
+
"react-dom/client": runtimeImports.reactDomClient
|
|
15
|
+
};
|
|
16
|
+
if (routerAdapter && runtimeImports.router) {
|
|
17
|
+
map[routerAdapter.importMapKey] = runtimeImports.router;
|
|
18
|
+
}
|
|
19
|
+
return map;
|
|
20
|
+
}
|
|
21
|
+
function getReactRuntimeExternalSpecifiers() {
|
|
22
|
+
return [...REACT_RUNTIME_SPECIFIERS];
|
|
23
|
+
}
|
|
24
|
+
function getReactClientGraphAllowSpecifiers(runtimeSpecifiers, routerAdapter) {
|
|
25
|
+
return [
|
|
26
|
+
"@ecopages/core",
|
|
27
|
+
...REACT_RUNTIME_SPECIFIERS,
|
|
28
|
+
...routerAdapter ? [routerAdapter.importMapKey] : [],
|
|
29
|
+
...Array.from(runtimeSpecifiers)
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
REACT_RUNTIME_SPECIFIERS,
|
|
34
|
+
buildReactRuntimeSpecifierMap,
|
|
35
|
+
getReactClientGraphAllowSpecifiers,
|
|
36
|
+
getReactRuntimeExternalSpecifiers
|
|
37
|
+
};
|