@ecopages/react 0.2.0-alpha.9 → 0.2.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +30 -13
  2. package/package.json +23 -12
  3. package/src/eco-embed.d.ts +11 -0
  4. package/src/eco-embed.js +11 -0
  5. package/src/react-hmr-strategy.d.ts +102 -18
  6. package/src/react-hmr-strategy.js +427 -50
  7. package/src/react-renderer.d.ts +100 -92
  8. package/src/react-renderer.js +356 -340
  9. package/src/react.constants.d.ts +1 -0
  10. package/src/react.constants.js +4 -0
  11. package/src/react.plugin.d.ts +25 -107
  12. package/src/react.plugin.js +109 -61
  13. package/src/react.types.d.ts +88 -0
  14. package/src/react.types.js +0 -0
  15. package/src/router-adapter.d.ts +7 -14
  16. package/src/runtime/use-sync-external-store-with-selector.d.ts +3 -0
  17. package/src/runtime/use-sync-external-store-with-selector.js +56 -0
  18. package/src/services/pages-index.d.ts +64 -0
  19. package/src/services/pages-index.js +73 -0
  20. package/src/services/react-bundle.service.d.ts +24 -9
  21. package/src/services/react-bundle.service.js +35 -24
  22. package/src/services/react-hmr-page-metadata-cache.d.ts +10 -1
  23. package/src/services/react-hmr-page-metadata-cache.js +18 -2
  24. package/src/services/react-hydration-asset.service.d.ts +28 -19
  25. package/src/services/react-hydration-asset.service.js +83 -64
  26. package/src/services/react-mdx-config-dependency.service.d.ts +36 -0
  27. package/src/services/react-mdx-config-dependency.service.js +122 -0
  28. package/src/services/react-page-module.service.d.ts +8 -3
  29. package/src/services/react-page-module.service.js +33 -26
  30. package/src/services/react-page-payload.service.d.ts +46 -0
  31. package/src/services/react-page-payload.service.js +67 -0
  32. package/src/services/react-runtime-bundle.service.d.ts +9 -2
  33. package/src/services/react-runtime-bundle.service.js +77 -16
  34. package/src/utils/client-graph-boundary-cache.d.ts +108 -0
  35. package/src/utils/client-graph-boundary-cache.js +116 -0
  36. package/src/utils/client-graph-boundary-plugin.d.ts +13 -5
  37. package/src/utils/client-graph-boundary-plugin.js +63 -5
  38. package/src/utils/component-config-traversal.d.ts +36 -0
  39. package/src/utils/component-config-traversal.js +54 -0
  40. package/src/utils/declared-modules.d.ts +1 -1
  41. package/src/utils/declared-modules.js +7 -16
  42. package/src/utils/dynamic.test.browser.d.ts +1 -0
  43. package/src/utils/dynamic.test.browser.js +33 -0
  44. package/src/utils/hydration-scripts.d.ts +9 -5
  45. package/src/utils/hydration-scripts.js +119 -34
  46. package/src/utils/hydration-scripts.test.browser.d.ts +1 -0
  47. package/src/utils/hydration-scripts.test.browser.js +198 -0
  48. package/src/utils/react-dom-runtime-interop-plugin.d.ts +1 -1
  49. package/src/utils/react-dom-runtime-interop-plugin.js +9 -0
  50. package/src/utils/react-mdx-loader-plugin.d.ts +1 -1
  51. package/src/utils/{react-runtime-specifier-map.d.ts → react-runtime-alias-map.d.ts} +3 -1
  52. package/src/utils/react-runtime-alias-map.js +90 -0
  53. package/CHANGELOG.md +0 -27
  54. package/src/react-hmr-strategy.ts +0 -386
  55. package/src/react-renderer.ts +0 -803
  56. package/src/react.plugin.ts +0 -276
  57. package/src/router-adapter.ts +0 -95
  58. package/src/services/react-bundle.service.ts +0 -108
  59. package/src/services/react-hmr-page-metadata-cache.ts +0 -24
  60. package/src/services/react-hydration-asset.service.ts +0 -263
  61. package/src/services/react-page-module.service.ts +0 -224
  62. package/src/services/react-runtime-bundle.service.ts +0 -172
  63. package/src/utils/client-graph-boundary-plugin.ts +0 -831
  64. package/src/utils/client-only.ts +0 -27
  65. package/src/utils/declared-modules.ts +0 -99
  66. package/src/utils/dynamic.ts +0 -27
  67. package/src/utils/hmr-scripts.ts +0 -47
  68. package/src/utils/html-boundary.ts +0 -66
  69. package/src/utils/hydration-scripts.ts +0 -459
  70. package/src/utils/reachability-analyzer.ts +0 -593
  71. package/src/utils/react-dom-runtime-interop-plugin.ts +0 -33
  72. package/src/utils/react-mdx-loader-plugin.ts +0 -63
  73. package/src/utils/react-runtime-specifier-map.js +0 -37
  74. package/src/utils/react-runtime-specifier-map.ts +0 -45
  75. package/src/utils/use-sync-external-store-shim-plugin.d.ts +0 -5
  76. package/src/utils/use-sync-external-store-shim-plugin.js +0 -41
  77. package/src/utils/use-sync-external-store-shim-plugin.ts +0 -45
package/README.md CHANGED
@@ -5,7 +5,8 @@ First-class integration for [React 19](https://react.dev/) in Ecopages. This plu
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- bunx jsr add @ecopages/react
8
+ bun add @ecopages/react react react-dom
9
+ bun add -d @types/react @types/react-dom
9
10
  ```
10
11
 
11
12
  ## Usage
@@ -26,15 +27,15 @@ export default config;
26
27
 
27
28
  ## Component-Level Islands
28
29
 
29
- By default, Ecopages React acts in island mode:
30
+ For component-level islands, Ecopages React uses this contract:
30
31
 
31
32
  - SSR output preserves the authored DOM structure (no unnecessary wrapper elements).
32
33
  - A stable `data-eco-component-id` attribute is attached to the component SSR root.
33
- - The client bootstrap mounts the component via `createRoot()` strictly within that root boundary.
34
+ - The island runtime replaces the SSR host with a dedicated client-owned container and mounts it with `createRoot()`. Full-page hydration paths use `hydrateRoot()`.
34
35
 
35
36
  > [!TIP]
36
37
  > **Full React SPA Routing:**
37
- > If you are building full React pages and want client-side navigation (SPA), use [@ecopages/react-router](../react-router/README.md) and pass it to the react plugin: `reactPlugin({ router: ecoRouter() })`.
38
+ > If you are building full React pages and want client-side navigation (SPA), use [@ecopages/react-router](../../react-router/README.md) and pass it to the react plugin: `reactPlugin({ router: ecoRouter() })`.
38
39
 
39
40
  ## MDX Support
40
41
 
@@ -60,6 +61,22 @@ const config = await new ConfigBuilder()
60
61
  export default config;
61
62
  ```
62
63
 
64
+ ## Mixed Rendering
65
+
66
+ The React integration can participate in mixed-renderer apps in three ways:
67
+
68
+ - React can own the page or view directly.
69
+ - React can render nested foreign subtrees inside pages owned by another integration.
70
+ - React can render through non-React page, layout, or document shells when those shell components return strings.
71
+
72
+ When a non-React render pass reaches a React-owned foreign child, Ecopages hands that foreign subtree back to the React renderer. When React renders through a non-React shell, that shell must serialize to HTML so React can insert the result into the final response without escaping it.
73
+
74
+ Important:
75
+
76
+ - Components that may render foreign children must declare those children in `config.dependencies.components`.
77
+ - Ecopages validates mixed-renderer ownership from declared dependencies during render preparation. It does not infer every foreign subtree from rendered HTML alone.
78
+ - React still keeps its own child transport and hydration rules for React-owned subtrees.
79
+
63
80
  ## Server and Client Graph Contract
64
81
 
65
82
  The React integration supports Node.js modules and server-only code **only on the server execution graph**.
@@ -99,7 +116,7 @@ The client bundle keeps:
99
116
  - The page component render path.
100
117
  - Client-safe component dependencies reachable from render.
101
118
  - Layout wiring needed for hydration.
102
- - Router runtime state needed by [@ecopages/react-router](../react-router/README.md) when SPA mode is enabled.
119
+ - Router runtime state needed by [@ecopages/react-router](../../react-router/README.md) when SPA mode is enabled.
103
120
 
104
121
  The client bundle removes or excludes:
105
122
 
@@ -114,7 +131,7 @@ Important:
114
131
 
115
132
  ### AST Pipeline Order
116
133
 
117
- The browser-bound transform in [packages/integrations/react/src/utils/client-graph-boundary-plugin.ts](packages/integrations/react/src/utils/client-graph-boundary-plugin.ts) follows this order:
134
+ The browser-bound transform in [src/utils/client-graph-boundary-plugin.ts](src/utils/client-graph-boundary-plugin.ts) follows this order:
118
135
 
119
136
  1. Parse the module and build a reachability view of the client render graph.
120
137
  2. Remove imports that are not allowed or not reachable from the client graph.
@@ -149,7 +166,7 @@ The fix is to strip server-only `eco.page(...)` options after import pruning, wh
149
166
 
150
167
  The browser must not receive arbitrary request-scoped data.
151
168
 
152
- The React renderer in [packages/integrations/react/src/react-renderer.ts](packages/integrations/react/src/react-renderer.ts) serializes only the top-level `locals` keys explicitly declared by `Page.requires`. If a page does not declare `requires`, no `locals` are serialized for hydration.
169
+ The React renderer in [src/react-renderer.ts](src/react-renderer.ts) serializes only the top-level `locals` keys explicitly declared by `Page.requires`. If a page does not declare `requires`, no `locals` are serialized for hydration.
153
170
 
154
171
  Example:
155
172
 
@@ -174,8 +191,8 @@ Hydration must rebuild the same tree the server rendered.
174
191
 
175
192
  That applies to both:
176
193
 
177
- - non-router hydration scripts in [packages/integrations/react/src/utils/hydration-scripts.ts](packages/integrations/react/src/utils/hydration-scripts.ts)
178
- - router-backed hydration in [packages/react-router/src/router.ts](packages/react-router/src/router.ts)
194
+ - non-router hydration scripts in [src/utils/hydration-scripts.ts](src/utils/hydration-scripts.ts)
195
+ - router-backed hydration in [../../react-router/src/router.ts](../../react-router/src/router.ts)
179
196
 
180
197
  If the page render receives `locals` on the server and the layout also depends on those values, the client must pass the same serialized `locals` into the layout during hydration. Otherwise React will detect a mismatch.
181
198
 
@@ -183,9 +200,9 @@ If the page render receives `locals` on the server and the layout also depends o
183
200
 
184
201
  The main regression coverage lives in:
185
202
 
186
- - [packages/integrations/react/src/utils/client-graph-boundary-plugin.test.ts](packages/integrations/react/src/utils/client-graph-boundary-plugin.test.ts): verifies server-only `eco.page(...)` options are stripped from browser bundles.
187
- - [packages/integrations/react/src/react-renderer.locals.test.ts](packages/integrations/react/src/react-renderer.locals.test.ts): verifies only declared `requires` keys are serialized into hydration payloads.
188
- - [packages/integrations/react/src/utils/hydration-scripts.test.ts](packages/integrations/react/src/utils/hydration-scripts.test.ts): verifies non-router hydration passes serialized `locals` into layouts.
189
- - [packages/react-router/test/hmr-reload.test.browser.ts](packages/react-router/test/hmr-reload.test.browser.ts): verifies router-backed layout hydration receives `locals` with `persistLayouts` both enabled and disabled.
203
+ - [src/utils/client-graph-boundary-plugin.test.ts](src/utils/client-graph-boundary-plugin.test.ts): verifies server-only `eco.page(...)` options are stripped from browser bundles.
204
+ - [src/react-renderer.locals.test.ts](src/react-renderer.locals.test.ts): verifies only declared `requires` keys are serialized into hydration payloads.
205
+ - [src/utils/hydration-scripts.test.ts](src/utils/hydration-scripts.test.ts): verifies non-router hydration passes serialized `locals` into layouts.
206
+ - [../../react-router/test/hmr-reload.test.browser.ts](../../react-router/test/hmr-reload.test.browser.ts): verifies router-backed layout hydration receives `locals` with `persistLayouts` both enabled and disabled.
190
207
 
191
208
  If you change the AST transform or hydration flow, update the corresponding tests in the same change.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/react",
3
- "version": "0.2.0-alpha.9",
3
+ "version": "0.2.1-beta.0",
4
4
  "description": "React integration for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -16,6 +16,10 @@
16
16
  "default": "./src/react.plugin.js",
17
17
  "types": "./src/react.plugin.d.ts"
18
18
  },
19
+ "./eco-embed": {
20
+ "default": "./src/eco-embed.js",
21
+ "types": "./src/eco-embed.d.ts"
22
+ },
19
23
  "./declarations": {
20
24
  "types": "./src/declarations.d.ts"
21
25
  },
@@ -27,10 +31,18 @@
27
31
  "types": "./src/utils/client-only.d.ts",
28
32
  "default": "./src/utils/client-only.js"
29
33
  },
34
+ "./runtime/use-sync-external-store-with-selector": {
35
+ "types": "./src/runtime/use-sync-external-store-with-selector.d.ts",
36
+ "default": "./src/runtime/use-sync-external-store-with-selector.js"
37
+ },
30
38
  "./router-adapter": {
31
39
  "types": "./src/router-adapter.d.ts",
32
40
  "default": "./src/router-adapter.js"
33
41
  },
42
+ "./eco-embed.ts": {
43
+ "default": "./src/eco-embed.js",
44
+ "types": "./src/eco-embed.d.ts"
45
+ },
34
46
  "./declarations.ts": {
35
47
  "types": "./src/declarations.d.ts"
36
48
  },
@@ -42,6 +54,10 @@
42
54
  "types": "./src/utils/client-only.d.ts",
43
55
  "default": "./src/utils/client-only.js"
44
56
  },
57
+ "./runtime/use-sync-external-store-with-selector.ts": {
58
+ "types": "./src/runtime/use-sync-external-store-with-selector.d.ts",
59
+ "default": "./src/runtime/use-sync-external-store-with-selector.js"
60
+ },
45
61
  "./router-adapter.ts": {
46
62
  "types": "./src/router-adapter.d.ts",
47
63
  "default": "./src/router-adapter.js"
@@ -53,24 +69,19 @@
53
69
  "directory": "packages/integrations/react"
54
70
  },
55
71
  "peerDependencies": {
56
- "@ecopages/core": "0.2.0-alpha.9",
72
+ "@ecopages/core": "0.2.1-beta.0",
57
73
  "@types/react": "^19",
58
74
  "@types/react-dom": "^19",
59
75
  "react": "^19",
60
76
  "react-dom": "^19"
61
77
  },
62
78
  "dependencies": {
63
- "@ecopages/file-system": "0.2.0-alpha.9",
64
- "@ecopages/logger": "latest",
65
- "@mdx-js/esbuild": "^3.0.1",
66
- "@mdx-js/mdx": "^3.1.0",
67
- "oxc-parser": "^0.114.0",
68
- "oxc-transform": "^0.114.0",
79
+ "@ecopages/file-system": "0.2.1-beta.0",
80
+ "@ecopages/logger": "^0.2.3",
81
+ "@mdx-js/mdx": "^3.1.1",
82
+ "oxc-parser": "^0.124.0",
83
+ "oxc-transform": "^0.124.0",
69
84
  "source-map": "^0.7.6",
70
85
  "vfile": "^6.0.3"
71
- },
72
- "overrides": {
73
- "react": "^19",
74
- "react-dom": "^19"
75
86
  }
76
87
  }
@@ -0,0 +1,11 @@
1
+ import type { EcoComponent, EcoEmbedProps as CoreEcoEmbedProps } from '@ecopages/core';
2
+ import type { ReactNode } from 'react';
3
+ /**
4
+ * Props for the React-owned `EcoEmbed` adapter.
5
+ */
6
+ export type EcoEmbedProps<TComponent extends EcoComponent> = CoreEcoEmbedProps<TComponent>;
7
+ /**
8
+ * Renders a foreign or same-integration eco component from a React JSX file
9
+ * without forcing inline mixed-JSX authoring.
10
+ */
11
+ export declare function EcoEmbed<TComponent extends EcoComponent>({ component, props, children, }: EcoEmbedProps<TComponent>): ReactNode;
@@ -0,0 +1,11 @@
1
+ import { eco } from "@ecopages/core";
2
+ function EcoEmbed({
3
+ component,
4
+ props,
5
+ children
6
+ }) {
7
+ return eco.embed(component, props, children);
8
+ }
9
+ export {
10
+ EcoEmbed
11
+ };
@@ -6,10 +6,28 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { HmrStrategy, HmrStrategyType, type HmrAction } from '@ecopages/core/hmr/hmr-strategy';
9
+ import { HmrStrategy, type HmrAction } from '@ecopages/core/hmr/hmr-strategy';
10
+ import type { BrowserRuntimeManifest } from '@ecopages/core/build/browser-runtime-manifest';
10
11
  import type { DefaultHmrContext } from '@ecopages/core';
11
12
  import type { CompileOptions } from '@mdx-js/mdx';
13
+ import { ClientGraphBoundaryCache } from './utils/client-graph-boundary-cache.js';
12
14
  import type { ReactHmrPageMetadataCache } from './services/react-hmr-page-metadata-cache.js';
15
+ export interface ReactHmrStrategyOptions {
16
+ context: DefaultHmrContext;
17
+ pageMetadataCache: ReactHmrPageMetadataCache;
18
+ runtimeManifest: BrowserRuntimeManifest;
19
+ mdxCompilerOptions?: CompileOptions;
20
+ ownedTemplateExtensions?: string[];
21
+ allTemplateExtensions?: string[];
22
+ explicitGraphEnabled?: boolean;
23
+ /**
24
+ * Per-app cache for client-graph-boundary transform results. Owned by
25
+ * the React plugin for the app's lifetime. When omitted, the strategy
26
+ * uses a fresh in-memory cache that does not persist across HMR
27
+ * rebuilds.
28
+ */
29
+ clientGraphBoundaryCache?: ClientGraphBoundaryCache;
30
+ }
13
31
  /**
14
32
  * Strategy for handling React component HMR updates.
15
33
  *
@@ -39,17 +57,20 @@ import type { ReactHmrPageMetadataCache } from './services/react-hmr-page-metada
39
57
  * ```typescript
40
58
  * const context = {
41
59
  * getWatchedFiles: () => watchedFilesMap,
42
- * getSpecifierMap: () => specifierMap,
43
60
  * getDistDir: () => '/path/to/dist/_hmr',
44
61
  * getPlugins: () => [],
45
62
  * getSrcDir: () => '/path/to/src',
46
63
  * getLayoutsDir: () => '/path/to/src/layouts'
47
64
  * };
48
- * const strategy = new ReactHmrStrategy(context);
65
+ * const strategy = new ReactHmrStrategy({
66
+ * context,
67
+ * pageMetadataCache,
68
+ * runtimeManifest
69
+ * });
49
70
  * ```
50
71
  */
51
72
  export declare class ReactHmrStrategy extends HmrStrategy {
52
- readonly type = HmrStrategyType.INTEGRATION;
73
+ readonly type: 100;
53
74
  private mdxCompilerOptions?;
54
75
  private readonly ownedTemplateExtensions;
55
76
  private readonly allTemplateExtensions;
@@ -57,25 +78,24 @@ export declare class ReactHmrStrategy extends HmrStrategy {
57
78
  /**
58
79
  * Creates a new React HMR strategy instance.
59
80
  *
60
- * @param context - The HMR context providing access to watched files, plugins, build directories,
61
- * and the layouts directory for detecting layout file changes that require full
62
- * page reloads instead of module-level HMR updates.
63
- * @param pageMetadataCache - React-only cache of declared browser modules discovered during
64
- * server rendering. This avoids re-importing unchanged page modules
65
- * during save-time Fast Refresh rebuilds.
66
- * @param mdxCompilerOptions - Optional MDX compiler options for processing .mdx files
67
- * @param explicitGraphEnabled - Enables explicit graph mode for React HMR bundling.
68
- * In explicit mode, HMR builds omit AST server-only stripping plugins in React paths.
81
+ * @param options - React HMR runtime services and behavior flags.
69
82
  */
70
83
  private context;
71
84
  private pageMetadataCache;
72
85
  private explicitGraphEnabled;
73
- constructor(context: DefaultHmrContext, pageMetadataCache: ReactHmrPageMetadataCache, mdxCompilerOptions?: CompileOptions, ownedTemplateExtensions?: string[], allTemplateExtensions?: string[], explicitGraphEnabled?: boolean);
86
+ private readonly runtimeManifest;
87
+ private readonly clientGraphBoundaryCache;
88
+ private readonly pagesIndex;
89
+ constructor(options: ReactHmrStrategyOptions);
74
90
  /**
75
91
  * Returns build plugins for React HMR bundling.
76
92
  *
77
93
  * Includes the client graph boundary plugin to prevent undeclared imports
78
94
  * (including `node:*`) from breaking the browser bundle.
95
+ *
96
+ * @remarks
97
+ * HMR builds receive the React runtime manifest and rewrite manifest-owned
98
+ * runtime imports to concrete asset URLs before module resolution.
79
99
  */
80
100
  private getBuildPlugins;
81
101
  private isReactEntrypoint;
@@ -89,11 +109,22 @@ export declare class ReactHmrStrategy extends HmrStrategy {
89
109
  */
90
110
  private isRouteTemplate;
91
111
  private resolveTemplateExtension;
112
+ private ownsWatchedEntrypoint;
113
+ private configContainsFile;
114
+ private pageModuleRequiresLayoutRefresh;
115
+ private hasLayoutOwnedDependencyTarget;
92
116
  /**
93
117
  * Determines if the file is a React/MDX entrypoint that's registered for HMR.
94
118
  *
119
+ * Uses a three-way decision strategy for selective invalidation:
120
+ * 1. If the file is a watched entrypoint, check if React owns it
121
+ * 2. If the file is a dependency of watched entrypoints (via dependency graph),
122
+ * check if any affected entrypoints are React-owned. Returns false if hits
123
+ * exist but none are owned (prevents unnecessary rebuilds).
124
+ * 3. Otherwise, check if the file itself is a React entrypoint template
125
+ *
95
126
  * @param filePath - Absolute path to the changed file
96
- * @returns True if this is a registered React or MDX entrypoint
127
+ * @returns True if this file should trigger React HMR rebuilds
97
128
  */
98
129
  matches(filePath: string): boolean;
99
130
  /**
@@ -108,13 +139,33 @@ export declare class ReactHmrStrategy extends HmrStrategy {
108
139
  * @returns True if the file is in the layouts directory
109
140
  */
110
141
  private isLayoutFile;
142
+ private isPageEntrypoint;
143
+ private getEntrypointOutput;
144
+ private getRolldownEntryKey;
145
+ private getTempFileBasename;
146
+ private collectReactPageBuildTargets;
147
+ /**
148
+ * Expands one HMR request into the full React page build cohort when needed.
149
+ *
150
+ * @remarks
151
+ * Page and layout changes need one shared rebuild pass so sibling routes keep
152
+ * a consistent client module graph. Non-page changes that do not touch a page
153
+ * cohort can stay scoped to the originally requested targets.
154
+ */
155
+ private resolveBuildTargets;
156
+ private partitionBuildTargets;
111
157
  /**
112
- * Processes a React file change by rebuilding all React entrypoints.
158
+ * Processes a React file change by rebuilding affected React entrypoints.
159
+ *
160
+ * Uses a three-way decision strategy for selective invalidation:
161
+ * 1. Changed file is a watched entrypoint: rebuild only that entrypoint
162
+ * 2. Dependency graph has hits: rebuild only affected React-owned entrypoints.
163
+ * If hits exist but none map to React-owned entrypoints, return 'none' to
164
+ * prevent unnecessary rebuilds.
165
+ * 3. Dependency graph miss: fall back to rebuilding all watched entrypoints
113
166
  *
114
167
  * For layout files, broadcasts a 'layout-update' event to trigger full page reload.
115
168
  * For regular components/pages, broadcasts 'update' events for module-level HMR.
116
- * When a page entrypoint is first registered, only that entrypoint is built.
117
- * Subsequent file updates rebuild all watched React entrypoints as usual.
118
169
  *
119
170
  * @param _filePath - Absolute path to the changed file
120
171
  * @returns Action to broadcast update events (layout-update for layouts, update for components)
@@ -123,16 +174,49 @@ export declare class ReactHmrStrategy extends HmrStrategy {
123
174
  /**
124
175
  * Bundles a single React/MDX entrypoint with HMR support.
125
176
  *
177
+ * After successful bundling, populates the entrypoint dependency graph with
178
+ * the build's dependency metadata. This enables selective invalidation on
179
+ * subsequent file changes, so only entrypoints affected by a changed
180
+ * dependency are rebuilt.
181
+ *
126
182
  * @param entrypointPath - Absolute path to the source file
127
183
  * @param outputUrl - URL path for the bundled file
128
184
  * @returns True if bundling was successful
129
185
  */
130
186
  private bundleReactEntrypoint;
187
+ /**
188
+ * Bundles multiple React/MDX entrypoints in a single build pass.
189
+ *
190
+ * Uses code splitting to share common dependencies across entrypoints.
191
+ * After successful bundling, populates the entrypoint dependency graph with
192
+ * the build's dependency metadata for selective invalidation.
193
+ *
194
+ * @param entrypoints - Array of entrypoint paths and their output URLs
195
+ * @returns Array of output URLs that were successfully built
196
+ */
197
+ private bundleReactEntrypoints;
198
+ private resolveTempOutputPath;
199
+ /**
200
+ * Clears stale HMR output from a directory before a rebuild.
201
+ *
202
+ * Only removes:
203
+ * - `*.tmp.js` files (the per-build bundler output the strategy owns)
204
+ * - the `chunks/` subdirectory (the bundler's splitting target)
205
+ *
206
+ * The HMR runtime script (`_hmr_runtime.js`) and any user-authored
207
+ * assets in the outdir are preserved. This is the minimal set of
208
+ * files that, if left from a previous build, can cause the bundler
209
+ * to emit `ENOENT` for chunk references that point to entrypoints
210
+ * whose hash has since changed.
211
+ */
212
+ private clearHmrOutdir;
131
213
  /**
132
214
  * Encodes dynamic route segments (brackets) in file paths.
133
215
  * Converts `[slug]` to `_slug_` to avoid filesystem issues.
134
216
  */
135
217
  private encodeDynamicSegments;
218
+ private rewriteChunkImportUrls;
219
+ private isMissingTempOutputError;
136
220
  /**
137
221
  * Processes bundled output and injects the React HMR handler.
138
222
  * Writes to temp file first, then renames atomically to avoid conflicts.