@ecopages/react 0.2.0-alpha.1

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/LICENSE +21 -0
  3. package/README.md +65 -0
  4. package/package.json +76 -0
  5. package/src/declarations.d.ts +6 -0
  6. package/src/react-hmr-strategy.d.ts +143 -0
  7. package/src/react-hmr-strategy.js +332 -0
  8. package/src/react-hmr-strategy.ts +444 -0
  9. package/src/react-renderer.d.ts +106 -0
  10. package/src/react-renderer.js +302 -0
  11. package/src/react-renderer.ts +403 -0
  12. package/src/react.plugin.d.ts +147 -0
  13. package/src/react.plugin.js +126 -0
  14. package/src/react.plugin.ts +241 -0
  15. package/src/router-adapter.d.ts +87 -0
  16. package/src/router-adapter.js +0 -0
  17. package/src/router-adapter.ts +95 -0
  18. package/src/services/react-bundle.service.d.ts +68 -0
  19. package/src/services/react-bundle.service.js +145 -0
  20. package/src/services/react-bundle.service.ts +212 -0
  21. package/src/services/react-hmr-page-metadata-cache.d.ts +17 -0
  22. package/src/services/react-hmr-page-metadata-cache.js +19 -0
  23. package/src/services/react-hmr-page-metadata-cache.ts +24 -0
  24. package/src/services/react-hydration-asset.service.d.ts +75 -0
  25. package/src/services/react-hydration-asset.service.js +198 -0
  26. package/src/services/react-hydration-asset.service.ts +260 -0
  27. package/src/services/react-page-module.service.d.ts +80 -0
  28. package/src/services/react-page-module.service.js +155 -0
  29. package/src/services/react-page-module.service.ts +214 -0
  30. package/src/services/react-runtime-bundle.service.d.ts +38 -0
  31. package/src/services/react-runtime-bundle.service.js +207 -0
  32. package/src/services/react-runtime-bundle.service.ts +271 -0
  33. package/src/utils/client-graph-boundary-plugin.d.ts +43 -0
  34. package/src/utils/client-graph-boundary-plugin.js +356 -0
  35. package/src/utils/client-graph-boundary-plugin.ts +590 -0
  36. package/src/utils/client-only.d.ts +8 -0
  37. package/src/utils/client-only.js +19 -0
  38. package/src/utils/client-only.ts +27 -0
  39. package/src/utils/declared-modules.d.ts +42 -0
  40. package/src/utils/declared-modules.js +56 -0
  41. package/src/utils/declared-modules.ts +99 -0
  42. package/src/utils/dynamic.d.ts +15 -0
  43. package/src/utils/dynamic.js +12 -0
  44. package/src/utils/dynamic.ts +27 -0
  45. package/src/utils/hmr-scripts.d.ts +18 -0
  46. package/src/utils/hmr-scripts.js +31 -0
  47. package/src/utils/hmr-scripts.ts +47 -0
  48. package/src/utils/html-boundary.d.ts +7 -0
  49. package/src/utils/html-boundary.js +55 -0
  50. package/src/utils/html-boundary.ts +66 -0
  51. package/src/utils/hydration-scripts.d.ts +71 -0
  52. package/src/utils/hydration-scripts.js +222 -0
  53. package/src/utils/hydration-scripts.ts +338 -0
  54. package/src/utils/reachability-analyzer.d.ts +55 -0
  55. package/src/utils/reachability-analyzer.js +243 -0
  56. package/src/utils/reachability-analyzer.ts +440 -0
  57. package/src/utils/react-mdx-loader-plugin.d.ts +3 -0
  58. package/src/utils/react-mdx-loader-plugin.js +37 -0
  59. package/src/utils/react-mdx-loader-plugin.ts +40 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * This module contains the react plugin for Ecopages
3
+ * @module
4
+ */
5
+ import { IntegrationPlugin } from '@ecopages/core/plugins/integration-plugin';
6
+ import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
7
+ import type { HmrStrategy } from '@ecopages/core/hmr/hmr-strategy';
8
+ import type { IHmrManager } from '@ecopages/core/internal-types';
9
+ import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
10
+ import type { CompileOptions } from '@mdx-js/mdx';
11
+ import type React from 'react';
12
+ import { ReactRenderer } from './react-renderer.js';
13
+ import type { ReactRouterAdapter } from './router-adapter.js';
14
+ import type { ComponentBoundaryPolicyInput } from '@ecopages/core/plugins/integration-plugin';
15
+ /**
16
+ * MDX configuration options for the React plugin
17
+ */
18
+ export type ReactMdxOptions = {
19
+ /**
20
+ * Whether to enable MDX support.
21
+ * @default false
22
+ */
23
+ enabled: boolean;
24
+ /**
25
+ * Compiler options for MDX.
26
+ * @default undefined
27
+ */
28
+ compilerOptions?: Omit<CompileOptions, 'jsxImportSource' | 'jsxRuntime'>;
29
+ /**
30
+ * Remark plugins.
31
+ * @default undefined
32
+ */
33
+ remarkPlugins?: CompileOptions['remarkPlugins'];
34
+ /**
35
+ * Rehype plugins.
36
+ * @default undefined
37
+ */
38
+ rehypePlugins?: CompileOptions['rehypePlugins'];
39
+ /**
40
+ * Recma plugins.
41
+ * @default undefined
42
+ */
43
+ recmaPlugins?: CompileOptions['recmaPlugins'];
44
+ /**
45
+ * Custom extensions to be treated as MDX files.
46
+ * @default ['.mdx']
47
+ */
48
+ extensions?: string[];
49
+ };
50
+ /**
51
+ * Options for the React plugin
52
+ */
53
+ export type ReactPluginOptions = {
54
+ extensions?: string[];
55
+ dependencies?: AssetDefinition[];
56
+ /**
57
+ * Enables explicit client graph mode for React page entries.
58
+ *
59
+ * When enabled, React page-entry bundling relies on explicit dependency declarations
60
+ * and skips AST-based `middleware`/`requires` stripping in the React path.
61
+ * @default false
62
+ */
63
+ explicitGraph?: boolean;
64
+ /**
65
+ * Router adapter for SPA navigation.
66
+ * When provided, pages with layouts will be wrapped in the router for client-side navigation.
67
+ * @example
68
+ * ```ts
69
+ * import { ecoRouter } from '@ecopages/react-router';
70
+ * reactPlugin({ router: ecoRouter() })
71
+ * ```
72
+ */
73
+ router?: ReactRouterAdapter;
74
+ /**
75
+ * MDX configuration for handling .mdx files within the React plugin.
76
+ * When enabled, MDX files are treated as React pages with full router support.
77
+ * @example
78
+ * ```ts
79
+ * reactPlugin({
80
+ * router: ecoRouter(),
81
+ * mdx: {
82
+ * enabled: true,
83
+ * extensions: ['.mdx', '.md'],
84
+ * remarkPlugins: [remarkGfm],
85
+ * rehypePlugins: [[rehypePrettyCode, { theme: '...' }]],
86
+ * }
87
+ * })
88
+ * ```
89
+ */
90
+ mdx?: ReactMdxOptions;
91
+ };
92
+ /**
93
+ * The name of the React plugin
94
+ */
95
+ export declare const PLUGIN_NAME = "react";
96
+ /**
97
+ * The React plugin class
98
+ * This plugin provides support for React components in Ecopages
99
+ */
100
+ export declare class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
101
+ renderer: typeof ReactRenderer;
102
+ routerAdapter: ReactRouterAdapter | undefined;
103
+ private mdxEnabled;
104
+ private mdxCompilerOptions?;
105
+ private mdxExtensions;
106
+ private mdxLoaderPlugin;
107
+ private runtimeBundleService;
108
+ private readonly hmrPageMetadataCache;
109
+ /**
110
+ * Indicates whether React explicit graph mode is enabled for renderer/HMR behavior.
111
+ */
112
+ private explicitGraphEnabled;
113
+ constructor(options?: Omit<ReactPluginOptions, 'name'>);
114
+ get plugins(): EcoBuildPlugin[];
115
+ setup(): Promise<void>;
116
+ /**
117
+ * Provides React-specific HMR strategy with Fast Refresh support.
118
+ *
119
+ * The strategy shares a React-only page metadata cache with the renderer so
120
+ * save-time rebuilds can reuse declared-module analysis without expanding the
121
+ * core HMR interfaces.
122
+ *
123
+ * @returns ReactHmrStrategy instance for handling React component updates
124
+ */
125
+ getHmrStrategy(): HmrStrategy | undefined;
126
+ /**
127
+ * Override to register React-specific specifier mappings for HMR.
128
+ */
129
+ setHmrManager(hmrManager: IHmrManager): void;
130
+ /**
131
+ * Declares React's boundary deferral rule for cross-integration rendering.
132
+ *
133
+ * React defers when a render pass owned by another integration enters a React
134
+ * component boundary. That boundary is then resolved later through the marker
135
+ * graph stage using the React renderer.
136
+ *
137
+ * @param input Boundary metadata for the active render pass.
138
+ * @returns `true` when the boundary should be deferred into the marker pass.
139
+ */
140
+ shouldDeferComponentBoundary(input: ComponentBoundaryPolicyInput): boolean;
141
+ }
142
+ /**
143
+ * Factory function to create a React plugin instance
144
+ * @param options Configuration options for the React plugin
145
+ * @returns A new ReactPlugin instance
146
+ */
147
+ export declare function reactPlugin(options?: ReactPluginOptions): ReactPlugin;
@@ -0,0 +1,126 @@
1
+ import { IntegrationPlugin } from "@ecopages/core/plugins/integration-plugin";
2
+ import { Logger } from "@ecopages/logger";
3
+ import { ReactRenderer } from "./react-renderer.js";
4
+ import { ReactHmrStrategy } from "./react-hmr-strategy.js";
5
+ import { ReactRuntimeBundleService } from "./services/react-runtime-bundle.service.js";
6
+ import { ReactHmrPageMetadataCache } from "./services/react-hmr-page-metadata-cache.js";
7
+ const appLogger = new Logger("[ReactPlugin]");
8
+ const PLUGIN_NAME = "react";
9
+ class ReactPlugin extends IntegrationPlugin {
10
+ renderer = ReactRenderer;
11
+ routerAdapter;
12
+ mdxEnabled;
13
+ mdxCompilerOptions;
14
+ mdxExtensions;
15
+ mdxLoaderPlugin;
16
+ runtimeBundleService;
17
+ hmrPageMetadataCache = new ReactHmrPageMetadataCache();
18
+ /**
19
+ * Indicates whether React explicit graph mode is enabled for renderer/HMR behavior.
20
+ */
21
+ explicitGraphEnabled;
22
+ constructor(options) {
23
+ const extensions = [".tsx"];
24
+ const mdxExtensions = options?.mdx?.extensions ?? [".mdx"];
25
+ if (options?.mdx?.enabled) {
26
+ extensions.push(...mdxExtensions);
27
+ } else if (options?.mdx?.extensions?.length) {
28
+ appLogger.warn(
29
+ "MDX extensions provided but MDX is disabled. MDX files will not be processed. Set mdx.enabled to true to enable MDX support."
30
+ );
31
+ }
32
+ super({
33
+ name: PLUGIN_NAME,
34
+ extensions,
35
+ ...options
36
+ });
37
+ this.mdxEnabled = options?.mdx?.enabled ?? false;
38
+ this.mdxExtensions = mdxExtensions;
39
+ if (this.mdxEnabled) {
40
+ const { compilerOptions, remarkPlugins, rehypePlugins, recmaPlugins } = options?.mdx || {};
41
+ this.mdxCompilerOptions = {
42
+ ...compilerOptions,
43
+ remarkPlugins: [...compilerOptions?.remarkPlugins || [], ...remarkPlugins || []],
44
+ rehypePlugins: [...compilerOptions?.rehypePlugins || [], ...rehypePlugins || []],
45
+ recmaPlugins: [...compilerOptions?.recmaPlugins || [], ...recmaPlugins || []],
46
+ jsxImportSource: "react",
47
+ jsxRuntime: "automatic",
48
+ development: process.env.NODE_ENV === "development"
49
+ };
50
+ appLogger.debug("MDX mode enabled with React jsx runtime");
51
+ }
52
+ this.routerAdapter = options?.router;
53
+ this.runtimeBundleService = new ReactRuntimeBundleService({
54
+ routerAdapter: this.routerAdapter
55
+ });
56
+ this.explicitGraphEnabled = options?.explicitGraph ?? false;
57
+ ReactRenderer.routerAdapter = this.routerAdapter;
58
+ ReactRenderer.mdxCompilerOptions = this.mdxCompilerOptions;
59
+ ReactRenderer.mdxExtensions = this.mdxExtensions;
60
+ ReactRenderer.explicitGraphEnabled = this.explicitGraphEnabled;
61
+ ReactRenderer.hmrPageMetadataCache = this.hmrPageMetadataCache;
62
+ this.integrationDependencies.unshift(...this.runtimeBundleService.getDependencies());
63
+ }
64
+ get plugins() {
65
+ if (this.mdxLoaderPlugin) {
66
+ return [this.mdxLoaderPlugin];
67
+ }
68
+ return [];
69
+ }
70
+ async setup() {
71
+ if (this.mdxEnabled && this.mdxCompilerOptions) {
72
+ const { createReactMdxLoaderPlugin } = await import("./utils/react-mdx-loader-plugin.js");
73
+ this.mdxLoaderPlugin = createReactMdxLoaderPlugin(this.mdxCompilerOptions);
74
+ }
75
+ await super.setup();
76
+ }
77
+ /**
78
+ * Provides React-specific HMR strategy with Fast Refresh support.
79
+ *
80
+ * The strategy shares a React-only page metadata cache with the renderer so
81
+ * save-time rebuilds can reuse declared-module analysis without expanding the
82
+ * core HMR interfaces.
83
+ *
84
+ * @returns ReactHmrStrategy instance for handling React component updates
85
+ */
86
+ getHmrStrategy() {
87
+ if (!this.hmrManager || !this.appConfig) {
88
+ return void 0;
89
+ }
90
+ const context = this.hmrManager.getDefaultContext();
91
+ return new ReactHmrStrategy(
92
+ context,
93
+ this.hmrPageMetadataCache,
94
+ this.mdxCompilerOptions,
95
+ this.explicitGraphEnabled
96
+ );
97
+ }
98
+ /**
99
+ * Override to register React-specific specifier mappings for HMR.
100
+ */
101
+ setHmrManager(hmrManager) {
102
+ super.setHmrManager(hmrManager);
103
+ hmrManager.registerSpecifierMap(this.runtimeBundleService.getSpecifierMap());
104
+ }
105
+ /**
106
+ * Declares React's boundary deferral rule for cross-integration rendering.
107
+ *
108
+ * React defers when a render pass owned by another integration enters a React
109
+ * component boundary. That boundary is then resolved later through the marker
110
+ * graph stage using the React renderer.
111
+ *
112
+ * @param input Boundary metadata for the active render pass.
113
+ * @returns `true` when the boundary should be deferred into the marker pass.
114
+ */
115
+ shouldDeferComponentBoundary(input) {
116
+ return input.targetIntegration === this.name && input.currentIntegration !== this.name;
117
+ }
118
+ }
119
+ function reactPlugin(options) {
120
+ return new ReactPlugin(options);
121
+ }
122
+ export {
123
+ PLUGIN_NAME,
124
+ ReactPlugin,
125
+ reactPlugin
126
+ };
@@ -0,0 +1,241 @@
1
+ /**
2
+ * This module contains the react plugin for Ecopages
3
+ * @module
4
+ */
5
+ import { IntegrationPlugin } from '@ecopages/core/plugins/integration-plugin';
6
+ import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
7
+ import type { HmrStrategy } from '@ecopages/core/hmr/hmr-strategy';
8
+ import type { IHmrManager } from '@ecopages/core/internal-types';
9
+ import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
10
+ import { Logger } from '@ecopages/logger';
11
+ import type { CompileOptions } from '@mdx-js/mdx';
12
+ import type React from 'react';
13
+ import { ReactRenderer } from './react-renderer.ts';
14
+ import { ReactHmrStrategy } from './react-hmr-strategy.ts';
15
+ import type { ReactRouterAdapter } from './router-adapter.ts';
16
+ import type { ComponentBoundaryPolicyInput } from '@ecopages/core/plugins/integration-plugin';
17
+ import { ReactRuntimeBundleService } from './services/react-runtime-bundle.service.ts';
18
+ import { ReactHmrPageMetadataCache } from './services/react-hmr-page-metadata-cache.ts';
19
+
20
+ const appLogger = new Logger('[ReactPlugin]');
21
+
22
+ /**
23
+ * MDX configuration options for the React plugin
24
+ */
25
+ export type ReactMdxOptions = {
26
+ /**
27
+ * Whether to enable MDX support.
28
+ * @default false
29
+ */
30
+ enabled: boolean;
31
+ /**
32
+ * Compiler options for MDX.
33
+ * @default undefined
34
+ */
35
+ compilerOptions?: Omit<CompileOptions, 'jsxImportSource' | 'jsxRuntime'>;
36
+ /**
37
+ * Remark plugins.
38
+ * @default undefined
39
+ */
40
+ remarkPlugins?: CompileOptions['remarkPlugins'];
41
+ /**
42
+ * Rehype plugins.
43
+ * @default undefined
44
+ */
45
+ rehypePlugins?: CompileOptions['rehypePlugins'];
46
+ /**
47
+ * Recma plugins.
48
+ * @default undefined
49
+ */
50
+ recmaPlugins?: CompileOptions['recmaPlugins'];
51
+ /**
52
+ * Custom extensions to be treated as MDX files.
53
+ * @default ['.mdx']
54
+ */
55
+ extensions?: string[];
56
+ };
57
+
58
+ /**
59
+ * Options for the React plugin
60
+ */
61
+ export type ReactPluginOptions = {
62
+ extensions?: string[];
63
+ dependencies?: AssetDefinition[];
64
+ /**
65
+ * Enables explicit client graph mode for React page entries.
66
+ *
67
+ * When enabled, React page-entry bundling relies on explicit dependency declarations
68
+ * and skips AST-based `middleware`/`requires` stripping in the React path.
69
+ * @default false
70
+ */
71
+ explicitGraph?: boolean;
72
+ /**
73
+ * Router adapter for SPA navigation.
74
+ * When provided, pages with layouts will be wrapped in the router for client-side navigation.
75
+ * @example
76
+ * ```ts
77
+ * import { ecoRouter } from '@ecopages/react-router';
78
+ * reactPlugin({ router: ecoRouter() })
79
+ * ```
80
+ */
81
+ router?: ReactRouterAdapter;
82
+ /**
83
+ * MDX configuration for handling .mdx files within the React plugin.
84
+ * When enabled, MDX files are treated as React pages with full router support.
85
+ * @example
86
+ * ```ts
87
+ * reactPlugin({
88
+ * router: ecoRouter(),
89
+ * mdx: {
90
+ * enabled: true,
91
+ * extensions: ['.mdx', '.md'],
92
+ * remarkPlugins: [remarkGfm],
93
+ * rehypePlugins: [[rehypePrettyCode, { theme: '...' }]],
94
+ * }
95
+ * })
96
+ * ```
97
+ */
98
+ mdx?: ReactMdxOptions;
99
+ };
100
+
101
+ /**
102
+ * The name of the React plugin
103
+ */
104
+ export const PLUGIN_NAME = 'react';
105
+
106
+ /**
107
+ * The React plugin class
108
+ * This plugin provides support for React components in Ecopages
109
+ */
110
+ export class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
111
+ renderer = ReactRenderer;
112
+ routerAdapter: ReactRouterAdapter | undefined;
113
+ private mdxEnabled: boolean;
114
+ private mdxCompilerOptions?: CompileOptions;
115
+ private mdxExtensions: string[];
116
+ private mdxLoaderPlugin: EcoBuildPlugin | undefined;
117
+ private runtimeBundleService: ReactRuntimeBundleService;
118
+ private readonly hmrPageMetadataCache = new ReactHmrPageMetadataCache();
119
+ /**
120
+ * Indicates whether React explicit graph mode is enabled for renderer/HMR behavior.
121
+ */
122
+ private explicitGraphEnabled: boolean;
123
+
124
+ constructor(options?: Omit<ReactPluginOptions, 'name'>) {
125
+ const extensions = ['.tsx'];
126
+ const mdxExtensions = options?.mdx?.extensions ?? ['.mdx'];
127
+
128
+ if (options?.mdx?.enabled) {
129
+ extensions.push(...mdxExtensions);
130
+ } else if (options?.mdx?.extensions?.length) {
131
+ appLogger.warn(
132
+ 'MDX extensions provided but MDX is disabled. MDX files will not be processed. Set mdx.enabled to true to enable MDX support.',
133
+ );
134
+ }
135
+
136
+ super({
137
+ name: PLUGIN_NAME,
138
+ extensions,
139
+ ...options,
140
+ });
141
+
142
+ this.mdxEnabled = options?.mdx?.enabled ?? false;
143
+ this.mdxExtensions = mdxExtensions;
144
+
145
+ if (this.mdxEnabled) {
146
+ const { compilerOptions, remarkPlugins, rehypePlugins, recmaPlugins } = options?.mdx || {};
147
+ this.mdxCompilerOptions = {
148
+ ...compilerOptions,
149
+ remarkPlugins: [...(compilerOptions?.remarkPlugins || []), ...(remarkPlugins || [])],
150
+ rehypePlugins: [...(compilerOptions?.rehypePlugins || []), ...(rehypePlugins || [])],
151
+ recmaPlugins: [...(compilerOptions?.recmaPlugins || []), ...(recmaPlugins || [])],
152
+ jsxImportSource: 'react',
153
+ jsxRuntime: 'automatic',
154
+ development: process.env.NODE_ENV === 'development',
155
+ };
156
+ appLogger.debug('MDX mode enabled with React jsx runtime');
157
+ }
158
+
159
+ this.routerAdapter = options?.router;
160
+ this.runtimeBundleService = new ReactRuntimeBundleService({
161
+ routerAdapter: this.routerAdapter,
162
+ });
163
+ this.explicitGraphEnabled = options?.explicitGraph ?? false;
164
+ ReactRenderer.routerAdapter = this.routerAdapter;
165
+ ReactRenderer.mdxCompilerOptions = this.mdxCompilerOptions;
166
+ ReactRenderer.mdxExtensions = this.mdxExtensions;
167
+ ReactRenderer.explicitGraphEnabled = this.explicitGraphEnabled;
168
+ ReactRenderer.hmrPageMetadataCache = this.hmrPageMetadataCache;
169
+ this.integrationDependencies.unshift(...this.runtimeBundleService.getDependencies());
170
+ }
171
+
172
+ override get plugins(): EcoBuildPlugin[] {
173
+ if (this.mdxLoaderPlugin) {
174
+ return [this.mdxLoaderPlugin];
175
+ }
176
+ return [];
177
+ }
178
+
179
+ override async setup(): Promise<void> {
180
+ if (this.mdxEnabled && this.mdxCompilerOptions) {
181
+ const { createReactMdxLoaderPlugin } = await import('./utils/react-mdx-loader-plugin.ts');
182
+ this.mdxLoaderPlugin = createReactMdxLoaderPlugin(this.mdxCompilerOptions);
183
+ }
184
+ await super.setup();
185
+ }
186
+
187
+ /**
188
+ * Provides React-specific HMR strategy with Fast Refresh support.
189
+ *
190
+ * The strategy shares a React-only page metadata cache with the renderer so
191
+ * save-time rebuilds can reuse declared-module analysis without expanding the
192
+ * core HMR interfaces.
193
+ *
194
+ * @returns ReactHmrStrategy instance for handling React component updates
195
+ */
196
+ override getHmrStrategy(): HmrStrategy | undefined {
197
+ if (!this.hmrManager || !this.appConfig) {
198
+ return undefined;
199
+ }
200
+
201
+ const context = this.hmrManager.getDefaultContext();
202
+
203
+ return new ReactHmrStrategy(
204
+ context,
205
+ this.hmrPageMetadataCache,
206
+ this.mdxCompilerOptions,
207
+ this.explicitGraphEnabled,
208
+ );
209
+ }
210
+
211
+ /**
212
+ * Override to register React-specific specifier mappings for HMR.
213
+ */
214
+ override setHmrManager(hmrManager: IHmrManager): void {
215
+ super.setHmrManager(hmrManager);
216
+ hmrManager.registerSpecifierMap(this.runtimeBundleService.getSpecifierMap());
217
+ }
218
+
219
+ /**
220
+ * Declares React's boundary deferral rule for cross-integration rendering.
221
+ *
222
+ * React defers when a render pass owned by another integration enters a React
223
+ * component boundary. That boundary is then resolved later through the marker
224
+ * graph stage using the React renderer.
225
+ *
226
+ * @param input Boundary metadata for the active render pass.
227
+ * @returns `true` when the boundary should be deferred into the marker pass.
228
+ */
229
+ override shouldDeferComponentBoundary(input: ComponentBoundaryPolicyInput): boolean {
230
+ return input.targetIntegration === this.name && input.currentIntegration !== this.name;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Factory function to create a React plugin instance
236
+ * @param options Configuration options for the React plugin
237
+ * @returns A new ReactPlugin instance
238
+ */
239
+ export function reactPlugin(options?: ReactPluginOptions): ReactPlugin {
240
+ return new ReactPlugin(options);
241
+ }
@@ -0,0 +1,87 @@
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
+ * Configuration for a React router adapter.
8
+ * Implement this interface to create custom router integrations.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const myRouter: ReactRouterAdapter = {
13
+ * name: 'my-router',
14
+ * bundle: {
15
+ * importPath: '@my/router/browser.ts',
16
+ * outputName: 'my-router',
17
+ * externals: ['react', 'react-dom'],
18
+ * },
19
+ * importMapKey: '@my/router',
20
+ * components: {
21
+ * router: 'MyRouter',
22
+ * pageContent: 'PageOutlet',
23
+ * },
24
+ * getRouterProps: (page, props) => `{ page: ${page}, pageProps: ${props} }`,
25
+ * };
26
+ * ```
27
+ */
28
+ export interface ReactRouterAdapter {
29
+ /**
30
+ * Unique identifier for caching and debugging.
31
+ */
32
+ name: string;
33
+ /**
34
+ * Runtime bundle configuration.
35
+ */
36
+ bundle: {
37
+ /**
38
+ * Node module import path for the browser-compatible entry.
39
+ * @example '@ecopages/react-router/browser.ts'
40
+ */
41
+ importPath: string;
42
+ /**
43
+ * Output filename (without extension).
44
+ * @example 'react-router-esm'
45
+ */
46
+ outputName: string;
47
+ /**
48
+ * Packages to externalize when bundling.
49
+ * These should be available via import map.
50
+ * @example ['react', 'react-dom', 'react/jsx-runtime']
51
+ */
52
+ externals: string[];
53
+ };
54
+ /**
55
+ * Bare specifier for the import map entry.
56
+ * This is what the hydration script will import from.
57
+ * @example '@ecopages/react-router'
58
+ */
59
+ importMapKey: string;
60
+ /**
61
+ * Component names to import from the router package.
62
+ */
63
+ components: {
64
+ /**
65
+ * The router component that wraps the layout.
66
+ * @example 'EcoRouter'
67
+ */
68
+ router: string;
69
+ /**
70
+ * The component that renders the current page content.
71
+ * @example 'PageContent'
72
+ */
73
+ pageContent: string;
74
+ };
75
+ /**
76
+ * Generate the props object for the router component.
77
+ * @param page - Variable name holding the page component
78
+ * @param props - Variable name holding the page props
79
+ * @returns Code string for the router props
80
+ * @example
81
+ * ```ts
82
+ * getRouterProps: (page, props) => `{ page: ${page}, pageProps: ${props} }`
83
+ * // Results in: { page: Component, pageProps: props }
84
+ * ```
85
+ */
86
+ getRouterProps(page: string, props: string): string;
87
+ }
File without changes
@@ -0,0 +1,95 @@
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
+ }