@ecopages/react 0.2.0-alpha.5 → 0.2.0-alpha.51
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 +152 -29
- package/package.json +24 -12
- package/src/eco-embed.d.ts +11 -0
- package/src/eco-embed.js +11 -0
- package/src/react-hmr-strategy.d.ts +65 -43
- package/src/react-hmr-strategy.js +298 -145
- package/src/react-renderer.d.ts +169 -42
- package/src/react-renderer.js +484 -164
- package/src/react.constants.d.ts +1 -0
- package/src/react.constants.js +4 -0
- package/src/react.plugin.d.ts +40 -111
- package/src/react.plugin.js +136 -61
- package/src/react.types.d.ts +88 -0
- package/src/react.types.js +0 -0
- package/src/router-adapter.d.ts +7 -14
- package/src/runtime/use-sync-external-store-with-selector.d.ts +3 -0
- package/src/runtime/use-sync-external-store-with-selector.js +56 -0
- package/src/services/react-bundle.service.d.ts +22 -35
- package/src/services/react-bundle.service.js +41 -105
- 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 +28 -19
- package/src/services/react-hydration-asset.service.js +85 -66
- 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 +10 -2
- package/src/services/react-page-module.service.js +47 -39
- 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 +20 -13
- package/src/services/react-runtime-bundle.service.js +146 -179
- package/src/utils/client-graph-boundary-plugin.d.ts +1 -1
- package/src/utils/client-graph-boundary-plugin.js +80 -3
- 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 +27 -6
- package/src/utils/hydration-scripts.js +177 -44
- package/src/utils/hydration-scripts.test.browser.d.ts +1 -0
- package/src/utils/hydration-scripts.test.browser.js +198 -0
- package/src/utils/react-dom-runtime-interop-plugin.d.ts +5 -0
- package/src/utils/react-dom-runtime-interop-plugin.js +38 -0
- package/src/utils/react-mdx-loader-plugin.d.ts +1 -1
- package/src/utils/react-mdx-loader-plugin.js +13 -5
- package/src/utils/react-runtime-alias-map.d.ts +8 -0
- package/src/utils/react-runtime-alias-map.js +90 -0
- package/CHANGELOG.md +0 -67
- package/src/react-hmr-strategy.ts +0 -455
- 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 -217
- 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 -710
- 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 -593
- package/src/utils/react-mdx-loader-plugin.ts +0 -40
package/src/router-adapter.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router adapter interface for React integration.
|
|
3
|
-
* Allows pluggable SPA routers to be used with the React plugin.
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Configuration for a React router adapter.
|
|
9
|
-
* Implement this interface to create custom router integrations.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* const myRouter: ReactRouterAdapter = {
|
|
14
|
-
* name: 'my-router',
|
|
15
|
-
* bundle: {
|
|
16
|
-
* importPath: '@my/router/browser.ts',
|
|
17
|
-
* outputName: 'my-router',
|
|
18
|
-
* externals: ['react', 'react-dom'],
|
|
19
|
-
* },
|
|
20
|
-
* importMapKey: '@my/router',
|
|
21
|
-
* components: {
|
|
22
|
-
* router: 'MyRouter',
|
|
23
|
-
* pageContent: 'PageOutlet',
|
|
24
|
-
* },
|
|
25
|
-
* getRouterProps: (page, props) => `{ page: ${page}, pageProps: ${props} }`,
|
|
26
|
-
* };
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export interface ReactRouterAdapter {
|
|
30
|
-
/**
|
|
31
|
-
* Unique identifier for caching and debugging.
|
|
32
|
-
*/
|
|
33
|
-
name: string;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Runtime bundle configuration.
|
|
37
|
-
*/
|
|
38
|
-
bundle: {
|
|
39
|
-
/**
|
|
40
|
-
* Node module import path for the browser-compatible entry.
|
|
41
|
-
* @example '@ecopages/react-router/browser.ts'
|
|
42
|
-
*/
|
|
43
|
-
importPath: string;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Output filename (without extension).
|
|
47
|
-
* @example 'react-router-esm'
|
|
48
|
-
*/
|
|
49
|
-
outputName: string;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Packages to externalize when bundling.
|
|
53
|
-
* These should be available via import map.
|
|
54
|
-
* @example ['react', 'react-dom', 'react/jsx-runtime']
|
|
55
|
-
*/
|
|
56
|
-
externals: string[];
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Bare specifier for the import map entry.
|
|
61
|
-
* This is what the hydration script will import from.
|
|
62
|
-
* @example '@ecopages/react-router'
|
|
63
|
-
*/
|
|
64
|
-
importMapKey: string;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Component names to import from the router package.
|
|
68
|
-
*/
|
|
69
|
-
components: {
|
|
70
|
-
/**
|
|
71
|
-
* The router component that wraps the layout.
|
|
72
|
-
* @example 'EcoRouter'
|
|
73
|
-
*/
|
|
74
|
-
router: string;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The component that renders the current page content.
|
|
78
|
-
* @example 'PageContent'
|
|
79
|
-
*/
|
|
80
|
-
pageContent: string;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Generate the props object for the router component.
|
|
85
|
-
* @param page - Variable name holding the page component
|
|
86
|
-
* @param props - Variable name holding the page props
|
|
87
|
-
* @returns Code string for the router props
|
|
88
|
-
* @example
|
|
89
|
-
* ```ts
|
|
90
|
-
* getRouterProps: (page, props) => `{ page: ${page}, pageProps: ${props} }`
|
|
91
|
-
* // Results in: { page: Component, pageProps: props }
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
getRouterProps(page: string, props: string): string;
|
|
95
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bundle configuration service for React integration.
|
|
3
|
-
*
|
|
4
|
-
* Encapsulates all esbuild plugin creation and bundle options
|
|
5
|
-
* for client-side React component builds.
|
|
6
|
-
*
|
|
7
|
-
* @module
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { createClientGraphBoundaryPlugin } from '../utils/client-graph-boundary-plugin.ts';
|
|
11
|
-
import type { ReactRouterAdapter } from '../router-adapter.ts';
|
|
12
|
-
import type { CompileOptions } from '@mdx-js/mdx';
|
|
13
|
-
import { ReactRuntimeBundleService, type ReactRuntimeImports } from './react-runtime-bundle.service.ts';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Configuration for the ReactBundleService.
|
|
17
|
-
*/
|
|
18
|
-
export interface ReactBundleServiceConfig {
|
|
19
|
-
rootDir: string;
|
|
20
|
-
routerAdapter?: ReactRouterAdapter;
|
|
21
|
-
mdxCompilerOptions?: CompileOptions;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Manages esbuild bundle configuration and plugin creation for React page/component builds.
|
|
26
|
-
*/
|
|
27
|
-
export class ReactBundleService {
|
|
28
|
-
private readonly runtimeBundleService: ReactRuntimeBundleService;
|
|
29
|
-
|
|
30
|
-
constructor(private readonly config: ReactBundleServiceConfig) {
|
|
31
|
-
this.runtimeBundleService = new ReactRuntimeBundleService({
|
|
32
|
-
routerAdapter: config.routerAdapter,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Returns resolved runtime import paths for the React runtime.
|
|
38
|
-
*/
|
|
39
|
-
getRuntimeImports(): ReactRuntimeImports {
|
|
40
|
-
return this.runtimeBundleService.getRuntimeImports();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Creates esbuild bundle options for a page or component entry.
|
|
45
|
-
*
|
|
46
|
-
* @param componentName - Generated unique component name for output naming
|
|
47
|
-
* @param isMdx - Whether the source file is an MDX file
|
|
48
|
-
* @param declaredModules - Explicitly declared browser module specifiers
|
|
49
|
-
* @returns Bundle options object for the build adapter
|
|
50
|
-
*/
|
|
51
|
-
async createBundleOptions(
|
|
52
|
-
componentName: string,
|
|
53
|
-
isMdx: boolean,
|
|
54
|
-
declaredModules: string[],
|
|
55
|
-
): Promise<Record<string, unknown>> {
|
|
56
|
-
const runtimeImports = this.getRuntimeImports();
|
|
57
|
-
const options: Record<string, unknown> = {
|
|
58
|
-
external: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom/client'],
|
|
59
|
-
mainFields: ['module', 'browser', 'main'],
|
|
60
|
-
naming: `${componentName}.[ext]`,
|
|
61
|
-
...(import.meta.env?.NODE_ENV === 'production' && {
|
|
62
|
-
minify: true,
|
|
63
|
-
splitting: false,
|
|
64
|
-
treeshaking: true,
|
|
65
|
-
}),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const graphBoundaryPlugin = createClientGraphBoundaryPlugin({
|
|
69
|
-
absWorkingDir: this.config.rootDir,
|
|
70
|
-
declaredModules,
|
|
71
|
-
alwaysAllowSpecifiers: [
|
|
72
|
-
'@ecopages/core',
|
|
73
|
-
'react',
|
|
74
|
-
'react-dom',
|
|
75
|
-
'react/jsx-runtime',
|
|
76
|
-
'react/jsx-dev-runtime',
|
|
77
|
-
'react-dom/client',
|
|
78
|
-
...(this.config.routerAdapter ? [this.config.routerAdapter.importMapKey] : []),
|
|
79
|
-
],
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const runtimeAliasPlugin = this.createRuntimeAliasPlugin(runtimeImports);
|
|
83
|
-
const useSyncExternalStoreShimPlugin = this.createSyncExternalStorePlugin();
|
|
84
|
-
|
|
85
|
-
if (isMdx && this.config.mdxCompilerOptions) {
|
|
86
|
-
const { createReactMdxLoaderPlugin } = await import('../utils/react-mdx-loader-plugin.ts');
|
|
87
|
-
const mdxPlugin = createReactMdxLoaderPlugin(this.config.mdxCompilerOptions);
|
|
88
|
-
options.plugins = [graphBoundaryPlugin, runtimeAliasPlugin, mdxPlugin, useSyncExternalStoreShimPlugin];
|
|
89
|
-
} else {
|
|
90
|
-
options.plugins = [graphBoundaryPlugin, runtimeAliasPlugin, useSyncExternalStoreShimPlugin];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return options;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Creates the esbuild plugin that rewrites bare React specifiers
|
|
98
|
-
* to their runtime asset URLs.
|
|
99
|
-
*/
|
|
100
|
-
createRuntimeAliasPlugin(runtimeImports: ReactRuntimeImports) {
|
|
101
|
-
const aliases = new Map<string, string>([
|
|
102
|
-
['react', runtimeImports.react],
|
|
103
|
-
['react-dom/client', runtimeImports.reactDomClient],
|
|
104
|
-
['react/jsx-runtime', runtimeImports.reactJsxRuntime],
|
|
105
|
-
['react/jsx-dev-runtime', runtimeImports.reactJsxDevRuntime],
|
|
106
|
-
['react-dom', runtimeImports.reactDom],
|
|
107
|
-
]);
|
|
108
|
-
|
|
109
|
-
if (this.config.routerAdapter && runtimeImports.router) {
|
|
110
|
-
aliases.set(this.config.routerAdapter.importMapKey, runtimeImports.router);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
114
|
-
const pattern = new RegExp(
|
|
115
|
-
`^(${Array.from(aliases.keys())
|
|
116
|
-
.map((key) => escapeRegExp(key))
|
|
117
|
-
.join('|')})$`,
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
name: 'react-runtime-import-alias',
|
|
122
|
-
setup(build: {
|
|
123
|
-
onResolve: (
|
|
124
|
-
options: { filter: RegExp; namespace?: string },
|
|
125
|
-
callback: (args: {
|
|
126
|
-
path: string;
|
|
127
|
-
importer: string;
|
|
128
|
-
namespace: string;
|
|
129
|
-
}) => { path?: string; namespace?: string; external?: boolean } | undefined,
|
|
130
|
-
) => void;
|
|
131
|
-
}) {
|
|
132
|
-
build.onResolve({ filter: pattern }, (args) => {
|
|
133
|
-
const mappedPath = aliases.get(args.path);
|
|
134
|
-
if (!mappedPath) {
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
path: mappedPath,
|
|
139
|
-
external: true,
|
|
140
|
-
};
|
|
141
|
-
});
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Redirects `use-sync-external-store/shim` imports to React's built-in
|
|
148
|
-
* `useSyncExternalStore`.
|
|
149
|
-
*
|
|
150
|
-
* Libraries like React Aria still list `use-sync-external-store` as a
|
|
151
|
-
* dependency to support React 16/17. On React 18+ the `/shim` export is
|
|
152
|
-
* already a pass-through, but without this plugin esbuild would bundle
|
|
153
|
-
* the full CJS shim (including `process.env` branching) into the browser
|
|
154
|
-
* bundle. The plugin short-circuits the resolution so only a single clean
|
|
155
|
-
* ESM re-export is emitted.
|
|
156
|
-
*/
|
|
157
|
-
private createSyncExternalStorePlugin() {
|
|
158
|
-
return {
|
|
159
|
-
name: 'react-renderer-use-sync-external-store-shim',
|
|
160
|
-
setup(build: {
|
|
161
|
-
onResolve: (
|
|
162
|
-
options: { filter: RegExp; namespace?: string },
|
|
163
|
-
callback: (args: {
|
|
164
|
-
path: string;
|
|
165
|
-
importer: string;
|
|
166
|
-
namespace: string;
|
|
167
|
-
}) => { path?: string; namespace?: string } | undefined,
|
|
168
|
-
) => void;
|
|
169
|
-
onLoad: (
|
|
170
|
-
options: { filter: RegExp; namespace?: string },
|
|
171
|
-
callback: (args: {
|
|
172
|
-
path: string;
|
|
173
|
-
namespace: string;
|
|
174
|
-
}) => { contents?: string; loader?: 'js' } | undefined,
|
|
175
|
-
) => void;
|
|
176
|
-
}) {
|
|
177
|
-
build.onResolve({ filter: /^use-sync-external-store\/shim(?:\/index\.js)?$/ }, () => ({
|
|
178
|
-
path: 'use-sync-external-store/shim',
|
|
179
|
-
namespace: 'ecopages-react-renderer-shim',
|
|
180
|
-
}));
|
|
181
|
-
|
|
182
|
-
build.onLoad(
|
|
183
|
-
{ filter: /^use-sync-external-store\/shim$/, namespace: 'ecopages-react-renderer-shim' },
|
|
184
|
-
() => ({
|
|
185
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
186
|
-
loader: 'js',
|
|
187
|
-
}),
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
build.onLoad({ filter: /[\\/]use-sync-external-store[\\/]shim[\\/]index\.js$/ }, () => ({
|
|
191
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
192
|
-
loader: 'js',
|
|
193
|
-
}));
|
|
194
|
-
|
|
195
|
-
build.onLoad(
|
|
196
|
-
{
|
|
197
|
-
filter: /[\\/]use-sync-external-store[\\/]cjs[\\/]use-sync-external-store-shim\.development\.js$/,
|
|
198
|
-
},
|
|
199
|
-
() => ({
|
|
200
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
201
|
-
loader: 'js',
|
|
202
|
-
}),
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
build.onLoad(
|
|
206
|
-
{
|
|
207
|
-
filter: /[\\/]use-sync-external-store[\\/]cjs[\\/]use-sync-external-store-shim\.production\.js$/,
|
|
208
|
-
},
|
|
209
|
-
() => ({
|
|
210
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
211
|
-
loader: 'js',
|
|
212
|
-
}),
|
|
213
|
-
);
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React-only cache for page metadata that HMR rebuilds need during development.
|
|
3
|
-
*
|
|
4
|
-
* This keeps React Fast Refresh optimizations local to the React integration so
|
|
5
|
-
* core HMR interfaces do not need React-specific metadata hooks.
|
|
6
|
-
*/
|
|
7
|
-
export class ReactHmrPageMetadataCache {
|
|
8
|
-
private readonly declaredModulesByEntrypoint = new Map<string, string[]>();
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Stores the declared browser modules for a page entrypoint.
|
|
12
|
-
*/
|
|
13
|
-
setDeclaredModules(entrypointPath: string, declaredModules: string[]): void {
|
|
14
|
-
this.declaredModulesByEntrypoint.set(entrypointPath, [...declaredModules]);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Returns the last known declared browser modules for a page entrypoint.
|
|
19
|
-
*/
|
|
20
|
-
getDeclaredModules(entrypointPath: string): string[] | undefined {
|
|
21
|
-
const declaredModules = this.declaredModulesByEntrypoint.get(entrypointPath);
|
|
22
|
-
return declaredModules ? [...declaredModules] : undefined;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hydration asset creation service for React integration.
|
|
3
|
-
*
|
|
4
|
-
* Builds the asset definitions (bundled component scripts + hydration bootstrap scripts)
|
|
5
|
-
* required for client-side React rendering — both at the page level and the component
|
|
6
|
-
* island level.
|
|
7
|
-
*
|
|
8
|
-
* @module
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import path from 'node:path';
|
|
12
|
-
import type { EcoComponentConfig } from '@ecopages/core';
|
|
13
|
-
import { rapidhash } from '@ecopages/core/hash';
|
|
14
|
-
import { RESOLVED_ASSETS_DIR } from '@ecopages/core/constants';
|
|
15
|
-
import {
|
|
16
|
-
AssetFactory,
|
|
17
|
-
type AssetDefinition,
|
|
18
|
-
type ProcessedAsset,
|
|
19
|
-
} from '@ecopages/core/services/asset-processing-service';
|
|
20
|
-
import type { AssetProcessingService } from '@ecopages/core/services/asset-processing-service';
|
|
21
|
-
import { createHydrationScript } from '../utils/hydration-scripts.ts';
|
|
22
|
-
import { createIslandHydrationScript } from '../utils/hydration-scripts.ts';
|
|
23
|
-
import { collectDeclaredModulesInConfig } from '../utils/declared-modules.ts';
|
|
24
|
-
import type { ReactBundleService } from './react-bundle.service.ts';
|
|
25
|
-
import type { ReactHmrPageMetadataCache } from './react-hmr-page-metadata-cache.ts';
|
|
26
|
-
import type { ReactRouterAdapter } from '../router-adapter.ts';
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Configuration for the ReactHydrationAssetService.
|
|
30
|
-
*/
|
|
31
|
-
export interface ReactHydrationAssetServiceConfig {
|
|
32
|
-
srcDir: string;
|
|
33
|
-
routerAdapter?: ReactRouterAdapter;
|
|
34
|
-
assetProcessingService: AssetProcessingService;
|
|
35
|
-
bundleService: ReactBundleService;
|
|
36
|
-
hmrPageMetadataCache?: ReactHmrPageMetadataCache;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Manages the creation of client-side hydration assets for React pages and component islands.
|
|
41
|
-
*/
|
|
42
|
-
export class ReactHydrationAssetService {
|
|
43
|
-
constructor(private readonly config: ReactHydrationAssetServiceConfig) {}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Resolves the import path for the bundled page component.
|
|
47
|
-
* Uses HMR manager for development or constructs static path for production.
|
|
48
|
-
*
|
|
49
|
-
* @param pagePath - Absolute path to the page source file
|
|
50
|
-
* @param componentName - Generated unique component name
|
|
51
|
-
* @returns The resolved import path for the bundled component
|
|
52
|
-
*/
|
|
53
|
-
async resolveAssetImportPath(pagePath: string, componentName: string): Promise<string> {
|
|
54
|
-
const hmrManager = this.config.assetProcessingService?.getHmrManager();
|
|
55
|
-
|
|
56
|
-
if (hmrManager?.isEnabled()) {
|
|
57
|
-
return hmrManager.registerEntrypoint(pagePath);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return `/${path
|
|
61
|
-
.join(RESOLVED_ASSETS_DIR, path.relative(this.config.srcDir, pagePath))
|
|
62
|
-
.replace(path.basename(pagePath), `${componentName}.js`)
|
|
63
|
-
.replace(/\\/g, '/')}`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Creates the asset dependencies for a page: the bundled component and hydration script.
|
|
68
|
-
*
|
|
69
|
-
* @param pagePath - Absolute path to the page source file
|
|
70
|
-
* @param componentName - Generated unique component name
|
|
71
|
-
* @param importPath - Resolved import path for the bundled component
|
|
72
|
-
* @param bundleOptions - Bundle configuration options
|
|
73
|
-
* @param isDevelopment - Whether running in development mode with HMR
|
|
74
|
-
* @param isMdx - Whether the source file is an MDX file
|
|
75
|
-
* @param props - Optional page props for client serialization
|
|
76
|
-
* @returns Array of asset definitions for processing
|
|
77
|
-
*/
|
|
78
|
-
createPageDependencies(
|
|
79
|
-
pagePath: string,
|
|
80
|
-
componentName: string,
|
|
81
|
-
importPath: string,
|
|
82
|
-
bundleOptions: Record<string, unknown>,
|
|
83
|
-
isDevelopment: boolean,
|
|
84
|
-
isMdx: boolean,
|
|
85
|
-
props?: Record<string, unknown>,
|
|
86
|
-
): AssetDefinition[] {
|
|
87
|
-
const runtimeImports = this.config.bundleService.getRuntimeImports();
|
|
88
|
-
const dependencies: AssetDefinition[] = [
|
|
89
|
-
AssetFactory.createFileScript({
|
|
90
|
-
position: 'head',
|
|
91
|
-
filepath: pagePath,
|
|
92
|
-
name: componentName,
|
|
93
|
-
excludeFromHtml: true,
|
|
94
|
-
bundle: true,
|
|
95
|
-
bundleOptions,
|
|
96
|
-
attributes: {
|
|
97
|
-
type: 'module',
|
|
98
|
-
defer: '',
|
|
99
|
-
'data-eco-persist': 'true',
|
|
100
|
-
},
|
|
101
|
-
}),
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
if (props && Object.keys(props).length > 0) {
|
|
105
|
-
dependencies.push(
|
|
106
|
-
AssetFactory.createContentScript({
|
|
107
|
-
position: 'head',
|
|
108
|
-
content: `window.__ECO_PAGE__={module:"${importPath}",props:${JSON.stringify(props)}};`,
|
|
109
|
-
name: `${componentName}-props`,
|
|
110
|
-
bundle: false,
|
|
111
|
-
attributes: {
|
|
112
|
-
type: 'module',
|
|
113
|
-
},
|
|
114
|
-
}),
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
dependencies.push(
|
|
119
|
-
AssetFactory.createContentScript({
|
|
120
|
-
position: 'head',
|
|
121
|
-
content: createHydrationScript({
|
|
122
|
-
importPath,
|
|
123
|
-
reactImportPath: runtimeImports.react,
|
|
124
|
-
reactDomClientImportPath: runtimeImports.reactDomClient,
|
|
125
|
-
routerImportPath: runtimeImports.router,
|
|
126
|
-
isDevelopment,
|
|
127
|
-
isMdx,
|
|
128
|
-
router: this.config.routerAdapter,
|
|
129
|
-
}),
|
|
130
|
-
name: `${componentName}-hydration`,
|
|
131
|
-
bundle: false,
|
|
132
|
-
attributes: {
|
|
133
|
-
type: 'module',
|
|
134
|
-
defer: '',
|
|
135
|
-
'data-eco-rerun': 'true',
|
|
136
|
-
'data-eco-script-id': `${componentName}-hydration`,
|
|
137
|
-
'data-eco-persist': 'true',
|
|
138
|
-
},
|
|
139
|
-
}),
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return dependencies;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Builds client-side assets for a React component island.
|
|
147
|
-
*
|
|
148
|
-
* Includes the bundled component entry and an inline hydration bootstrap script.
|
|
149
|
-
*
|
|
150
|
-
* @param componentFile - Absolute path to the component source file
|
|
151
|
-
* @param componentInstanceId - Unique instance ID for DOM targeting
|
|
152
|
-
* @param props - Serialized props for client-side hydration
|
|
153
|
-
* @param config - Optional component config with `__eco` metadata
|
|
154
|
-
* @returns Processed assets ready for injection
|
|
155
|
-
*/
|
|
156
|
-
async buildComponentRenderAssets(
|
|
157
|
-
componentFile: string,
|
|
158
|
-
componentInstanceId: string,
|
|
159
|
-
props: Record<string, unknown>,
|
|
160
|
-
config?: EcoComponentConfig,
|
|
161
|
-
): Promise<ProcessedAsset[]> {
|
|
162
|
-
const componentName = `ecopages-react-island-${rapidhash(`${componentFile}:${componentInstanceId}`)}`;
|
|
163
|
-
const importPath = await this.resolveAssetImportPath(componentFile, componentName);
|
|
164
|
-
const hmrManager = this.config.assetProcessingService?.getHmrManager();
|
|
165
|
-
const isDevelopment = hmrManager?.isEnabled() ?? false;
|
|
166
|
-
const declaredModules = collectDeclaredModulesInConfig(config);
|
|
167
|
-
const bundleOptions = await this.config.bundleService.createBundleOptions(
|
|
168
|
-
componentName,
|
|
169
|
-
false,
|
|
170
|
-
declaredModules,
|
|
171
|
-
);
|
|
172
|
-
const runtimeImports = this.config.bundleService.getRuntimeImports();
|
|
173
|
-
|
|
174
|
-
const dependencies: AssetDefinition[] = [
|
|
175
|
-
AssetFactory.createFileScript({
|
|
176
|
-
position: 'head',
|
|
177
|
-
filepath: componentFile,
|
|
178
|
-
name: componentName,
|
|
179
|
-
excludeFromHtml: true,
|
|
180
|
-
bundle: true,
|
|
181
|
-
bundleOptions,
|
|
182
|
-
attributes: {
|
|
183
|
-
type: 'module',
|
|
184
|
-
defer: '',
|
|
185
|
-
'data-eco-persist': 'true',
|
|
186
|
-
},
|
|
187
|
-
}),
|
|
188
|
-
AssetFactory.createContentScript({
|
|
189
|
-
position: 'head',
|
|
190
|
-
content: createIslandHydrationScript({
|
|
191
|
-
importPath,
|
|
192
|
-
reactImportPath: runtimeImports.react,
|
|
193
|
-
reactDomClientImportPath: runtimeImports.reactDomClient,
|
|
194
|
-
targetSelector: `[data-eco-component-id="${componentInstanceId}"]`,
|
|
195
|
-
props,
|
|
196
|
-
componentRef: config?.__eco?.id,
|
|
197
|
-
componentFile,
|
|
198
|
-
isDevelopment,
|
|
199
|
-
}),
|
|
200
|
-
name: `${componentName}-hydration`,
|
|
201
|
-
bundle: false,
|
|
202
|
-
attributes: {
|
|
203
|
-
type: 'module',
|
|
204
|
-
defer: '',
|
|
205
|
-
'data-eco-rerun': 'true',
|
|
206
|
-
'data-eco-script-id': `${componentName}-hydration`,
|
|
207
|
-
'data-eco-persist': 'true',
|
|
208
|
-
},
|
|
209
|
-
}),
|
|
210
|
-
];
|
|
211
|
-
|
|
212
|
-
if (!this.config.assetProcessingService) {
|
|
213
|
-
return [];
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return this.config.assetProcessingService.processDependencies(dependencies, componentName);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Builds all client-side route assets for a page.
|
|
221
|
-
*
|
|
222
|
-
* @param pagePath - Absolute file path of the page
|
|
223
|
-
* @param isMdx - Whether the page is an MDX file
|
|
224
|
-
* @param declaredModules - Explicitly declared browser module specifiers
|
|
225
|
-
* @returns Processed assets for the route
|
|
226
|
-
*/
|
|
227
|
-
async buildRouteRenderAssets(
|
|
228
|
-
pagePath: string,
|
|
229
|
-
isMdx: boolean,
|
|
230
|
-
declaredModules: string[],
|
|
231
|
-
): Promise<ProcessedAsset[]> {
|
|
232
|
-
const componentName = `ecopages-react-${rapidhash(pagePath)}`;
|
|
233
|
-
const hmrManager = this.config.assetProcessingService?.getHmrManager();
|
|
234
|
-
const isDevelopment = hmrManager?.isEnabled() ?? false;
|
|
235
|
-
if (isDevelopment) {
|
|
236
|
-
this.config.hmrPageMetadataCache?.setDeclaredModules(pagePath, declaredModules);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const importPath = await this.resolveAssetImportPath(pagePath, componentName);
|
|
240
|
-
const bundleOptions = await this.config.bundleService.createBundleOptions(
|
|
241
|
-
componentName,
|
|
242
|
-
isMdx,
|
|
243
|
-
declaredModules,
|
|
244
|
-
);
|
|
245
|
-
const dependencies = this.createPageDependencies(
|
|
246
|
-
pagePath,
|
|
247
|
-
componentName,
|
|
248
|
-
importPath,
|
|
249
|
-
bundleOptions,
|
|
250
|
-
isDevelopment,
|
|
251
|
-
isMdx,
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
if (!this.config.assetProcessingService) {
|
|
255
|
-
throw new Error('AssetProcessingService is not set');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return this.config.assetProcessingService.processDependencies(dependencies, componentName);
|
|
259
|
-
}
|
|
260
|
-
}
|