@ecopages/react 0.2.0-alpha.5 → 0.2.0-alpha.7
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 +22 -41
- package/README.md +135 -29
- package/package.json +3 -3
- package/src/react-hmr-strategy.d.ts +22 -30
- package/src/react-hmr-strategy.js +57 -120
- package/src/react-hmr-strategy.ts +76 -145
- package/src/react-renderer.d.ts +130 -11
- package/src/react-renderer.js +368 -64
- package/src/react-renderer.ts +490 -90
- package/src/react.plugin.d.ts +17 -5
- package/src/react.plugin.js +44 -13
- package/src/react.plugin.ts +49 -14
- package/src/router-adapter.d.ts +2 -2
- package/src/router-adapter.ts +2 -2
- package/src/services/react-bundle.service.d.ts +2 -30
- package/src/services/react-bundle.service.js +19 -94
- package/src/services/react-bundle.service.ts +20 -129
- package/src/services/react-hydration-asset.service.js +3 -3
- package/src/services/react-hydration-asset.service.ts +7 -4
- package/src/services/react-page-module.service.d.ts +3 -0
- package/src/services/react-page-module.service.js +20 -16
- package/src/services/react-page-module.service.ts +27 -17
- package/src/services/react-runtime-bundle.service.d.ts +12 -12
- package/src/services/react-runtime-bundle.service.js +98 -180
- package/src/services/react-runtime-bundle.service.ts +112 -211
- package/src/utils/client-graph-boundary-plugin.js +78 -1
- package/src/utils/client-graph-boundary-plugin.ts +122 -1
- package/src/utils/hydration-scripts.d.ts +18 -1
- package/src/utils/hydration-scripts.js +83 -32
- package/src/utils/hydration-scripts.ts +159 -38
- 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-dom-runtime-interop-plugin.ts +33 -0
- package/src/utils/react-mdx-loader-plugin.js +13 -5
- package/src/utils/react-mdx-loader-plugin.ts +28 -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/react-runtime-specifier-map.ts +45 -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/utils/use-sync-external-store-shim-plugin.ts +45 -0
package/src/react.plugin.d.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import { IntegrationPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
6
6
|
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
7
7
|
import type { HmrStrategy } from '@ecopages/core/hmr/hmr-strategy';
|
|
8
|
-
import type { IHmrManager } from '@ecopages/core/internal-types';
|
|
9
8
|
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
10
9
|
import type { CompileOptions } from '@mdx-js/mdx';
|
|
11
10
|
import type React from 'react';
|
|
@@ -106,12 +105,28 @@ export declare class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
106
105
|
private mdxLoaderPlugin;
|
|
107
106
|
private runtimeBundleService;
|
|
108
107
|
private readonly hmrPageMetadataCache;
|
|
108
|
+
private runtimeDependenciesInitialized;
|
|
109
109
|
/**
|
|
110
110
|
* Indicates whether React explicit graph mode is enabled for renderer/HMR behavior.
|
|
111
111
|
*/
|
|
112
112
|
private explicitGraphEnabled;
|
|
113
113
|
constructor(options?: Omit<ReactPluginOptions, 'name'>);
|
|
114
|
+
private ensureRuntimeDependencies;
|
|
114
115
|
get plugins(): EcoBuildPlugin[];
|
|
116
|
+
/**
|
|
117
|
+
* Ensures the optional React MDX loader exists before either config-time
|
|
118
|
+
* manifest sealing or runtime setup needs it.
|
|
119
|
+
*/
|
|
120
|
+
private ensureMdxLoaderPlugin;
|
|
121
|
+
/**
|
|
122
|
+
* Prepares React's build-facing loader contributions before config build seals
|
|
123
|
+
* the app manifest.
|
|
124
|
+
*/
|
|
125
|
+
prepareBuildContributions(): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Performs runtime-only React setup after build contributions are already
|
|
128
|
+
* materialized.
|
|
129
|
+
*/
|
|
115
130
|
setup(): Promise<void>;
|
|
116
131
|
/**
|
|
117
132
|
* Provides React-specific HMR strategy with Fast Refresh support.
|
|
@@ -123,10 +138,7 @@ export declare class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
123
138
|
* @returns ReactHmrStrategy instance for handling React component updates
|
|
124
139
|
*/
|
|
125
140
|
getHmrStrategy(): HmrStrategy | undefined;
|
|
126
|
-
|
|
127
|
-
* Override to register React-specific specifier mappings for HMR.
|
|
128
|
-
*/
|
|
129
|
-
setHmrManager(hmrManager: IHmrManager): void;
|
|
141
|
+
getRuntimeSpecifierMap(): Record<string, string>;
|
|
130
142
|
/**
|
|
131
143
|
* Declares React's boundary deferral rule for cross-integration rendering.
|
|
132
144
|
*
|
package/src/react.plugin.js
CHANGED
|
@@ -15,15 +15,21 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
15
15
|
mdxLoaderPlugin;
|
|
16
16
|
runtimeBundleService;
|
|
17
17
|
hmrPageMetadataCache = new ReactHmrPageMetadataCache();
|
|
18
|
+
runtimeDependenciesInitialized = false;
|
|
18
19
|
/**
|
|
19
20
|
* Indicates whether React explicit graph mode is enabled for renderer/HMR behavior.
|
|
20
21
|
*/
|
|
21
22
|
explicitGraphEnabled;
|
|
22
23
|
constructor(options) {
|
|
23
|
-
const extensions =
|
|
24
|
+
const { extensions: _ignoredExtensions, ...restOptions } = options ?? {};
|
|
25
|
+
const extensions = [...options?.extensions ?? [".tsx"]];
|
|
24
26
|
const mdxExtensions = options?.mdx?.extensions ?? [".mdx"];
|
|
25
27
|
if (options?.mdx?.enabled) {
|
|
26
|
-
|
|
28
|
+
for (const extension of mdxExtensions) {
|
|
29
|
+
if (!extensions.includes(extension)) {
|
|
30
|
+
extensions.push(extension);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
27
33
|
} else if (options?.mdx?.extensions?.length) {
|
|
28
34
|
appLogger.warn(
|
|
29
35
|
"MDX extensions provided but MDX is disabled. MDX files will not be processed. Set mdx.enabled to true to enable MDX support."
|
|
@@ -32,7 +38,7 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
32
38
|
super({
|
|
33
39
|
name: PLUGIN_NAME,
|
|
34
40
|
extensions,
|
|
35
|
-
...
|
|
41
|
+
...restOptions
|
|
36
42
|
});
|
|
37
43
|
this.mdxEnabled = options?.mdx?.enabled ?? false;
|
|
38
44
|
this.mdxExtensions = mdxExtensions;
|
|
@@ -59,7 +65,13 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
59
65
|
ReactRenderer.mdxExtensions = this.mdxExtensions;
|
|
60
66
|
ReactRenderer.explicitGraphEnabled = this.explicitGraphEnabled;
|
|
61
67
|
ReactRenderer.hmrPageMetadataCache = this.hmrPageMetadataCache;
|
|
68
|
+
}
|
|
69
|
+
ensureRuntimeDependencies() {
|
|
70
|
+
if (this.runtimeDependenciesInitialized) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
62
73
|
this.integrationDependencies.unshift(...this.runtimeBundleService.getDependencies());
|
|
74
|
+
this.runtimeDependenciesInitialized = true;
|
|
63
75
|
}
|
|
64
76
|
get plugins() {
|
|
65
77
|
if (this.mdxLoaderPlugin) {
|
|
@@ -67,11 +79,32 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
67
79
|
}
|
|
68
80
|
return [];
|
|
69
81
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Ensures the optional React MDX loader exists before either config-time
|
|
84
|
+
* manifest sealing or runtime setup needs it.
|
|
85
|
+
*/
|
|
86
|
+
async ensureMdxLoaderPlugin() {
|
|
87
|
+
if (!this.mdxEnabled || !this.mdxCompilerOptions || this.mdxLoaderPlugin) {
|
|
88
|
+
return;
|
|
74
89
|
}
|
|
90
|
+
const { createReactMdxLoaderPlugin } = await import("./utils/react-mdx-loader-plugin.js");
|
|
91
|
+
this.mdxLoaderPlugin = createReactMdxLoaderPlugin(this.mdxCompilerOptions);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Prepares React's build-facing loader contributions before config build seals
|
|
95
|
+
* the app manifest.
|
|
96
|
+
*/
|
|
97
|
+
async prepareBuildContributions() {
|
|
98
|
+
this.ensureRuntimeDependencies();
|
|
99
|
+
await this.ensureMdxLoaderPlugin();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Performs runtime-only React setup after build contributions are already
|
|
103
|
+
* materialized.
|
|
104
|
+
*/
|
|
105
|
+
async setup() {
|
|
106
|
+
this.ensureRuntimeDependencies();
|
|
107
|
+
await this.ensureMdxLoaderPlugin();
|
|
75
108
|
await super.setup();
|
|
76
109
|
}
|
|
77
110
|
/**
|
|
@@ -92,15 +125,13 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
92
125
|
context,
|
|
93
126
|
this.hmrPageMetadataCache,
|
|
94
127
|
this.mdxCompilerOptions,
|
|
128
|
+
this.extensions,
|
|
129
|
+
this.appConfig.templatesExt,
|
|
95
130
|
this.explicitGraphEnabled
|
|
96
131
|
);
|
|
97
132
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*/
|
|
101
|
-
setHmrManager(hmrManager) {
|
|
102
|
-
super.setHmrManager(hmrManager);
|
|
103
|
-
hmrManager.registerSpecifierMap(this.runtimeBundleService.getSpecifierMap());
|
|
133
|
+
getRuntimeSpecifierMap() {
|
|
134
|
+
return this.runtimeBundleService.getSpecifierMap();
|
|
104
135
|
}
|
|
105
136
|
/**
|
|
106
137
|
* Declares React's boundary deferral rule for cross-integration rendering.
|
package/src/react.plugin.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import { IntegrationPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
6
6
|
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
7
7
|
import type { HmrStrategy } from '@ecopages/core/hmr/hmr-strategy';
|
|
8
|
-
import type { IHmrManager } from '@ecopages/core/internal-types';
|
|
9
8
|
import type { AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
10
9
|
import { Logger } from '@ecopages/logger';
|
|
11
10
|
import type { CompileOptions } from '@mdx-js/mdx';
|
|
@@ -116,17 +115,23 @@ export class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
116
115
|
private mdxLoaderPlugin: EcoBuildPlugin | undefined;
|
|
117
116
|
private runtimeBundleService: ReactRuntimeBundleService;
|
|
118
117
|
private readonly hmrPageMetadataCache = new ReactHmrPageMetadataCache();
|
|
118
|
+
private runtimeDependenciesInitialized = false;
|
|
119
119
|
/**
|
|
120
120
|
* Indicates whether React explicit graph mode is enabled for renderer/HMR behavior.
|
|
121
121
|
*/
|
|
122
122
|
private explicitGraphEnabled: boolean;
|
|
123
123
|
|
|
124
124
|
constructor(options?: Omit<ReactPluginOptions, 'name'>) {
|
|
125
|
-
const extensions =
|
|
125
|
+
const { extensions: _ignoredExtensions, ...restOptions } = options ?? {};
|
|
126
|
+
const extensions = [...(options?.extensions ?? ['.tsx'])];
|
|
126
127
|
const mdxExtensions = options?.mdx?.extensions ?? ['.mdx'];
|
|
127
128
|
|
|
128
129
|
if (options?.mdx?.enabled) {
|
|
129
|
-
|
|
130
|
+
for (const extension of mdxExtensions) {
|
|
131
|
+
if (!extensions.includes(extension)) {
|
|
132
|
+
extensions.push(extension);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
130
135
|
} else if (options?.mdx?.extensions?.length) {
|
|
131
136
|
appLogger.warn(
|
|
132
137
|
'MDX extensions provided but MDX is disabled. MDX files will not be processed. Set mdx.enabled to true to enable MDX support.',
|
|
@@ -136,7 +141,7 @@ export class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
136
141
|
super({
|
|
137
142
|
name: PLUGIN_NAME,
|
|
138
143
|
extensions,
|
|
139
|
-
...
|
|
144
|
+
...restOptions,
|
|
140
145
|
});
|
|
141
146
|
|
|
142
147
|
this.mdxEnabled = options?.mdx?.enabled ?? false;
|
|
@@ -166,7 +171,15 @@ export class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
166
171
|
ReactRenderer.mdxExtensions = this.mdxExtensions;
|
|
167
172
|
ReactRenderer.explicitGraphEnabled = this.explicitGraphEnabled;
|
|
168
173
|
ReactRenderer.hmrPageMetadataCache = this.hmrPageMetadataCache;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private ensureRuntimeDependencies(): void {
|
|
177
|
+
if (this.runtimeDependenciesInitialized) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
169
181
|
this.integrationDependencies.unshift(...this.runtimeBundleService.getDependencies());
|
|
182
|
+
this.runtimeDependenciesInitialized = true;
|
|
170
183
|
}
|
|
171
184
|
|
|
172
185
|
override get plugins(): EcoBuildPlugin[] {
|
|
@@ -176,11 +189,35 @@ export class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
176
189
|
return [];
|
|
177
190
|
}
|
|
178
191
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Ensures the optional React MDX loader exists before either config-time
|
|
194
|
+
* manifest sealing or runtime setup needs it.
|
|
195
|
+
*/
|
|
196
|
+
private async ensureMdxLoaderPlugin(): Promise<void> {
|
|
197
|
+
if (!this.mdxEnabled || !this.mdxCompilerOptions || this.mdxLoaderPlugin) {
|
|
198
|
+
return;
|
|
183
199
|
}
|
|
200
|
+
|
|
201
|
+
const { createReactMdxLoaderPlugin } = await import('./utils/react-mdx-loader-plugin.ts');
|
|
202
|
+
this.mdxLoaderPlugin = createReactMdxLoaderPlugin(this.mdxCompilerOptions);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Prepares React's build-facing loader contributions before config build seals
|
|
207
|
+
* the app manifest.
|
|
208
|
+
*/
|
|
209
|
+
override async prepareBuildContributions(): Promise<void> {
|
|
210
|
+
this.ensureRuntimeDependencies();
|
|
211
|
+
await this.ensureMdxLoaderPlugin();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Performs runtime-only React setup after build contributions are already
|
|
216
|
+
* materialized.
|
|
217
|
+
*/
|
|
218
|
+
override async setup(): Promise<void> {
|
|
219
|
+
this.ensureRuntimeDependencies();
|
|
220
|
+
await this.ensureMdxLoaderPlugin();
|
|
184
221
|
await super.setup();
|
|
185
222
|
}
|
|
186
223
|
|
|
@@ -204,16 +241,14 @@ export class ReactPlugin extends IntegrationPlugin<React.JSX.Element> {
|
|
|
204
241
|
context,
|
|
205
242
|
this.hmrPageMetadataCache,
|
|
206
243
|
this.mdxCompilerOptions,
|
|
244
|
+
this.extensions,
|
|
245
|
+
this.appConfig.templatesExt,
|
|
207
246
|
this.explicitGraphEnabled,
|
|
208
247
|
);
|
|
209
248
|
}
|
|
210
249
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
*/
|
|
214
|
-
override setHmrManager(hmrManager: IHmrManager): void {
|
|
215
|
-
super.setHmrManager(hmrManager);
|
|
216
|
-
hmrManager.registerSpecifierMap(this.runtimeBundleService.getSpecifierMap());
|
|
250
|
+
override getRuntimeSpecifierMap(): Record<string, string> {
|
|
251
|
+
return this.runtimeBundleService.getSpecifierMap();
|
|
217
252
|
}
|
|
218
253
|
|
|
219
254
|
/**
|
package/src/router-adapter.d.ts
CHANGED
|
@@ -46,13 +46,13 @@ export interface ReactRouterAdapter {
|
|
|
46
46
|
outputName: string;
|
|
47
47
|
/**
|
|
48
48
|
* Packages to externalize when bundling.
|
|
49
|
-
* These should be available
|
|
49
|
+
* These should be available through the runtime bare-specifier map.
|
|
50
50
|
* @example ['react', 'react-dom', 'react/jsx-runtime']
|
|
51
51
|
*/
|
|
52
52
|
externals: string[];
|
|
53
53
|
};
|
|
54
54
|
/**
|
|
55
|
-
* Bare specifier for the
|
|
55
|
+
* Bare specifier for the runtime mapping entry.
|
|
56
56
|
* This is what the hydration script will import from.
|
|
57
57
|
* @example '@ecopages/react-router'
|
|
58
58
|
*/
|
package/src/router-adapter.ts
CHANGED
|
@@ -50,14 +50,14 @@ export interface ReactRouterAdapter {
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Packages to externalize when bundling.
|
|
53
|
-
* These should be available
|
|
53
|
+
* These should be available through the runtime bare-specifier map.
|
|
54
54
|
* @example ['react', 'react-dom', 'react/jsx-runtime']
|
|
55
55
|
*/
|
|
56
56
|
externals: string[];
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* Bare specifier for the
|
|
60
|
+
* Bare specifier for the runtime mapping entry.
|
|
61
61
|
* This is what the hydration script will import from.
|
|
62
62
|
* @example '@ecopages/react-router'
|
|
63
63
|
*/
|
|
@@ -21,8 +21,8 @@ export interface ReactBundleServiceConfig {
|
|
|
21
21
|
* Manages esbuild bundle configuration and plugin creation for React page/component builds.
|
|
22
22
|
*/
|
|
23
23
|
export declare class ReactBundleService {
|
|
24
|
-
private readonly config;
|
|
25
24
|
private readonly runtimeBundleService;
|
|
25
|
+
private readonly config;
|
|
26
26
|
constructor(config: ReactBundleServiceConfig);
|
|
27
27
|
/**
|
|
28
28
|
* Returns resolved runtime import paths for the React runtime.
|
|
@@ -41,33 +41,5 @@ export declare class ReactBundleService {
|
|
|
41
41
|
* Creates the esbuild plugin that rewrites bare React specifiers
|
|
42
42
|
* to their runtime asset URLs.
|
|
43
43
|
*/
|
|
44
|
-
createRuntimeAliasPlugin(
|
|
45
|
-
name: string;
|
|
46
|
-
setup(build: {
|
|
47
|
-
onResolve: (options: {
|
|
48
|
-
filter: RegExp;
|
|
49
|
-
namespace?: string;
|
|
50
|
-
}, callback: (args: {
|
|
51
|
-
path: string;
|
|
52
|
-
importer: string;
|
|
53
|
-
namespace: string;
|
|
54
|
-
}) => {
|
|
55
|
-
path?: string;
|
|
56
|
-
namespace?: string;
|
|
57
|
-
external?: boolean;
|
|
58
|
-
} | undefined) => void;
|
|
59
|
-
}): void;
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Redirects `use-sync-external-store/shim` imports to React's built-in
|
|
63
|
-
* `useSyncExternalStore`.
|
|
64
|
-
*
|
|
65
|
-
* Libraries like React Aria still list `use-sync-external-store` as a
|
|
66
|
-
* dependency to support React 16/17. On React 18+ the `/shim` export is
|
|
67
|
-
* already a pass-through, but without this plugin esbuild would bundle
|
|
68
|
-
* the full CJS shim (including `process.env` branching) into the browser
|
|
69
|
-
* bundle. The plugin short-circuits the resolution so only a single clean
|
|
70
|
-
* ESM re-export is emitted.
|
|
71
|
-
*/
|
|
72
|
-
private createSyncExternalStorePlugin;
|
|
44
|
+
createRuntimeAliasPlugin(runtimeSpecifierMap: Record<string, string>): import("packages/core/src/build/build-types.ts").EcoBuildPlugin | null;
|
|
73
45
|
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { createClientGraphBoundaryPlugin } from "../utils/client-graph-boundary-plugin.js";
|
|
2
|
+
import {
|
|
3
|
+
buildReactRuntimeSpecifierMap,
|
|
4
|
+
getReactClientGraphAllowSpecifiers,
|
|
5
|
+
getReactRuntimeExternalSpecifiers
|
|
6
|
+
} from "../utils/react-runtime-specifier-map.js";
|
|
7
|
+
import { createUseSyncExternalStoreShimPlugin } from "../utils/use-sync-external-store-shim-plugin.js";
|
|
8
|
+
import { createRuntimeSpecifierAliasPlugin } from "@ecopages/core/build/runtime-specifier-alias-plugin";
|
|
2
9
|
import { ReactRuntimeBundleService } from "./react-runtime-bundle.service.js";
|
|
3
10
|
class ReactBundleService {
|
|
11
|
+
runtimeBundleService;
|
|
12
|
+
config;
|
|
4
13
|
constructor(config) {
|
|
5
14
|
this.config = config;
|
|
6
15
|
this.runtimeBundleService = new ReactRuntimeBundleService({
|
|
7
16
|
routerAdapter: config.routerAdapter
|
|
8
17
|
});
|
|
9
18
|
}
|
|
10
|
-
runtimeBundleService;
|
|
11
19
|
/**
|
|
12
20
|
* Returns resolved runtime import paths for the React runtime.
|
|
13
21
|
*/
|
|
@@ -24,8 +32,9 @@ class ReactBundleService {
|
|
|
24
32
|
*/
|
|
25
33
|
async createBundleOptions(componentName, isMdx, declaredModules) {
|
|
26
34
|
const runtimeImports = this.getRuntimeImports();
|
|
35
|
+
const runtimeSpecifierMap = buildReactRuntimeSpecifierMap(runtimeImports, this.config.routerAdapter);
|
|
27
36
|
const options = {
|
|
28
|
-
external:
|
|
37
|
+
external: getReactRuntimeExternalSpecifiers(),
|
|
29
38
|
mainFields: ["module", "browser", "main"],
|
|
30
39
|
naming: `${componentName}.[ext]`,
|
|
31
40
|
...import.meta.env?.NODE_ENV === "production" && {
|
|
@@ -37,18 +46,13 @@ class ReactBundleService {
|
|
|
37
46
|
const graphBoundaryPlugin = createClientGraphBoundaryPlugin({
|
|
38
47
|
absWorkingDir: this.config.rootDir,
|
|
39
48
|
declaredModules,
|
|
40
|
-
alwaysAllowSpecifiers: [
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"react-dom/client",
|
|
47
|
-
...this.config.routerAdapter ? [this.config.routerAdapter.importMapKey] : []
|
|
48
|
-
]
|
|
49
|
+
alwaysAllowSpecifiers: getReactClientGraphAllowSpecifiers([], this.config.routerAdapter)
|
|
50
|
+
});
|
|
51
|
+
const runtimeAliasPlugin = this.createRuntimeAliasPlugin(runtimeSpecifierMap);
|
|
52
|
+
const useSyncExternalStoreShimPlugin = createUseSyncExternalStoreShimPlugin({
|
|
53
|
+
name: "react-renderer-use-sync-external-store-shim",
|
|
54
|
+
namespace: "ecopages-react-renderer-shim"
|
|
49
55
|
});
|
|
50
|
-
const runtimeAliasPlugin = this.createRuntimeAliasPlugin(runtimeImports);
|
|
51
|
-
const useSyncExternalStoreShimPlugin = this.createSyncExternalStorePlugin();
|
|
52
56
|
if (isMdx && this.config.mdxCompilerOptions) {
|
|
53
57
|
const { createReactMdxLoaderPlugin } = await import("../utils/react-mdx-loader-plugin.js");
|
|
54
58
|
const mdxPlugin = createReactMdxLoaderPlugin(this.config.mdxCompilerOptions);
|
|
@@ -62,87 +66,8 @@ class ReactBundleService {
|
|
|
62
66
|
* Creates the esbuild plugin that rewrites bare React specifiers
|
|
63
67
|
* to their runtime asset URLs.
|
|
64
68
|
*/
|
|
65
|
-
createRuntimeAliasPlugin(
|
|
66
|
-
|
|
67
|
-
["react", runtimeImports.react],
|
|
68
|
-
["react-dom/client", runtimeImports.reactDomClient],
|
|
69
|
-
["react/jsx-runtime", runtimeImports.reactJsxRuntime],
|
|
70
|
-
["react/jsx-dev-runtime", runtimeImports.reactJsxDevRuntime],
|
|
71
|
-
["react-dom", runtimeImports.reactDom]
|
|
72
|
-
]);
|
|
73
|
-
if (this.config.routerAdapter && runtimeImports.router) {
|
|
74
|
-
aliases.set(this.config.routerAdapter.importMapKey, runtimeImports.router);
|
|
75
|
-
}
|
|
76
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
77
|
-
const pattern = new RegExp(
|
|
78
|
-
`^(${Array.from(aliases.keys()).map((key) => escapeRegExp(key)).join("|")})$`
|
|
79
|
-
);
|
|
80
|
-
return {
|
|
81
|
-
name: "react-runtime-import-alias",
|
|
82
|
-
setup(build) {
|
|
83
|
-
build.onResolve({ filter: pattern }, (args) => {
|
|
84
|
-
const mappedPath = aliases.get(args.path);
|
|
85
|
-
if (!mappedPath) {
|
|
86
|
-
return void 0;
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
path: mappedPath,
|
|
90
|
-
external: true
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Redirects `use-sync-external-store/shim` imports to React's built-in
|
|
98
|
-
* `useSyncExternalStore`.
|
|
99
|
-
*
|
|
100
|
-
* Libraries like React Aria still list `use-sync-external-store` as a
|
|
101
|
-
* dependency to support React 16/17. On React 18+ the `/shim` export is
|
|
102
|
-
* already a pass-through, but without this plugin esbuild would bundle
|
|
103
|
-
* the full CJS shim (including `process.env` branching) into the browser
|
|
104
|
-
* bundle. The plugin short-circuits the resolution so only a single clean
|
|
105
|
-
* ESM re-export is emitted.
|
|
106
|
-
*/
|
|
107
|
-
createSyncExternalStorePlugin() {
|
|
108
|
-
return {
|
|
109
|
-
name: "react-renderer-use-sync-external-store-shim",
|
|
110
|
-
setup(build) {
|
|
111
|
-
build.onResolve({ filter: /^use-sync-external-store\/shim(?:\/index\.js)?$/ }, () => ({
|
|
112
|
-
path: "use-sync-external-store/shim",
|
|
113
|
-
namespace: "ecopages-react-renderer-shim"
|
|
114
|
-
}));
|
|
115
|
-
build.onLoad(
|
|
116
|
-
{ filter: /^use-sync-external-store\/shim$/, namespace: "ecopages-react-renderer-shim" },
|
|
117
|
-
() => ({
|
|
118
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
119
|
-
loader: "js"
|
|
120
|
-
})
|
|
121
|
-
);
|
|
122
|
-
build.onLoad({ filter: /[\\/]use-sync-external-store[\\/]shim[\\/]index\.js$/ }, () => ({
|
|
123
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
124
|
-
loader: "js"
|
|
125
|
-
}));
|
|
126
|
-
build.onLoad(
|
|
127
|
-
{
|
|
128
|
-
filter: /[\\/]use-sync-external-store[\\/]cjs[\\/]use-sync-external-store-shim\.development\.js$/
|
|
129
|
-
},
|
|
130
|
-
() => ({
|
|
131
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
132
|
-
loader: "js"
|
|
133
|
-
})
|
|
134
|
-
);
|
|
135
|
-
build.onLoad(
|
|
136
|
-
{
|
|
137
|
-
filter: /[\\/]use-sync-external-store[\\/]cjs[\\/]use-sync-external-store-shim\.production\.js$/
|
|
138
|
-
},
|
|
139
|
-
() => ({
|
|
140
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
141
|
-
loader: "js"
|
|
142
|
-
})
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
69
|
+
createRuntimeAliasPlugin(runtimeSpecifierMap) {
|
|
70
|
+
return createRuntimeSpecifierAliasPlugin(runtimeSpecifierMap, { name: "react-runtime-import-alias" });
|
|
146
71
|
}
|
|
147
72
|
}
|
|
148
73
|
export {
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { createClientGraphBoundaryPlugin } from '../utils/client-graph-boundary-plugin.ts';
|
|
11
|
+
import {
|
|
12
|
+
buildReactRuntimeSpecifierMap,
|
|
13
|
+
getReactClientGraphAllowSpecifiers,
|
|
14
|
+
getReactRuntimeExternalSpecifiers,
|
|
15
|
+
} from '../utils/react-runtime-specifier-map.ts';
|
|
16
|
+
import { createUseSyncExternalStoreShimPlugin } from '../utils/use-sync-external-store-shim-plugin.ts';
|
|
17
|
+
import { createRuntimeSpecifierAliasPlugin } from '@ecopages/core/build/runtime-specifier-alias-plugin';
|
|
11
18
|
import type { ReactRouterAdapter } from '../router-adapter.ts';
|
|
12
19
|
import type { CompileOptions } from '@mdx-js/mdx';
|
|
13
20
|
import { ReactRuntimeBundleService, type ReactRuntimeImports } from './react-runtime-bundle.service.ts';
|
|
@@ -26,8 +33,10 @@ export interface ReactBundleServiceConfig {
|
|
|
26
33
|
*/
|
|
27
34
|
export class ReactBundleService {
|
|
28
35
|
private readonly runtimeBundleService: ReactRuntimeBundleService;
|
|
36
|
+
private readonly config: ReactBundleServiceConfig;
|
|
29
37
|
|
|
30
|
-
constructor(
|
|
38
|
+
constructor(config: ReactBundleServiceConfig) {
|
|
39
|
+
this.config = config;
|
|
31
40
|
this.runtimeBundleService = new ReactRuntimeBundleService({
|
|
32
41
|
routerAdapter: config.routerAdapter,
|
|
33
42
|
});
|
|
@@ -54,8 +63,9 @@ export class ReactBundleService {
|
|
|
54
63
|
declaredModules: string[],
|
|
55
64
|
): Promise<Record<string, unknown>> {
|
|
56
65
|
const runtimeImports = this.getRuntimeImports();
|
|
66
|
+
const runtimeSpecifierMap = buildReactRuntimeSpecifierMap(runtimeImports, this.config.routerAdapter);
|
|
57
67
|
const options: Record<string, unknown> = {
|
|
58
|
-
external:
|
|
68
|
+
external: getReactRuntimeExternalSpecifiers(),
|
|
59
69
|
mainFields: ['module', 'browser', 'main'],
|
|
60
70
|
naming: `${componentName}.[ext]`,
|
|
61
71
|
...(import.meta.env?.NODE_ENV === 'production' && {
|
|
@@ -68,19 +78,14 @@ export class ReactBundleService {
|
|
|
68
78
|
const graphBoundaryPlugin = createClientGraphBoundaryPlugin({
|
|
69
79
|
absWorkingDir: this.config.rootDir,
|
|
70
80
|
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
|
-
],
|
|
81
|
+
alwaysAllowSpecifiers: getReactClientGraphAllowSpecifiers([], this.config.routerAdapter),
|
|
80
82
|
});
|
|
81
83
|
|
|
82
|
-
const runtimeAliasPlugin = this.createRuntimeAliasPlugin(
|
|
83
|
-
const useSyncExternalStoreShimPlugin =
|
|
84
|
+
const runtimeAliasPlugin = this.createRuntimeAliasPlugin(runtimeSpecifierMap);
|
|
85
|
+
const useSyncExternalStoreShimPlugin = createUseSyncExternalStoreShimPlugin({
|
|
86
|
+
name: 'react-renderer-use-sync-external-store-shim',
|
|
87
|
+
namespace: 'ecopages-react-renderer-shim',
|
|
88
|
+
});
|
|
84
89
|
|
|
85
90
|
if (isMdx && this.config.mdxCompilerOptions) {
|
|
86
91
|
const { createReactMdxLoaderPlugin } = await import('../utils/react-mdx-loader-plugin.ts');
|
|
@@ -97,121 +102,7 @@ export class ReactBundleService {
|
|
|
97
102
|
* Creates the esbuild plugin that rewrites bare React specifiers
|
|
98
103
|
* to their runtime asset URLs.
|
|
99
104
|
*/
|
|
100
|
-
createRuntimeAliasPlugin(
|
|
101
|
-
|
|
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
|
-
};
|
|
105
|
+
createRuntimeAliasPlugin(runtimeSpecifierMap: Record<string, string>) {
|
|
106
|
+
return createRuntimeSpecifierAliasPlugin(runtimeSpecifierMap, { name: 'react-runtime-import-alias' });
|
|
216
107
|
}
|
|
217
108
|
}
|