@ecopages/react 0.2.0-alpha.50 → 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/package.json +11 -3
- package/src/react-hmr-strategy.d.ts +8 -3
- package/src/react-hmr-strategy.js +15 -15
- package/src/react.plugin.d.ts +2 -0
- package/src/react.plugin.js +5 -1
- 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 +4 -5
- package/src/services/react-bundle.service.js +12 -27
- package/src/services/react-runtime-bundle.service.d.ts +5 -0
- package/src/services/react-runtime-bundle.service.js +48 -4
- package/src/utils/react-dom-runtime-interop-plugin.js +9 -0
- package/src/utils/react-runtime-alias-map.d.ts +2 -0
- package/src/utils/react-runtime-alias-map.js +64 -7
- package/src/utils/use-sync-external-store-shim-plugin.d.ts +0 -5
- package/src/utils/use-sync-external-store-shim-plugin.js +0 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/react",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.51",
|
|
4
4
|
"description": "React integration for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"types": "./src/utils/client-only.d.ts",
|
|
32
32
|
"default": "./src/utils/client-only.js"
|
|
33
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
|
+
},
|
|
34
38
|
"./router-adapter": {
|
|
35
39
|
"types": "./src/router-adapter.d.ts",
|
|
36
40
|
"default": "./src/router-adapter.js"
|
|
@@ -50,6 +54,10 @@
|
|
|
50
54
|
"types": "./src/utils/client-only.d.ts",
|
|
51
55
|
"default": "./src/utils/client-only.js"
|
|
52
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
|
+
},
|
|
53
61
|
"./router-adapter.ts": {
|
|
54
62
|
"types": "./src/router-adapter.d.ts",
|
|
55
63
|
"default": "./src/router-adapter.js"
|
|
@@ -61,14 +69,14 @@
|
|
|
61
69
|
"directory": "packages/integrations/react"
|
|
62
70
|
},
|
|
63
71
|
"peerDependencies": {
|
|
64
|
-
"@ecopages/core": "0.2.0-alpha.
|
|
72
|
+
"@ecopages/core": "0.2.0-alpha.51",
|
|
65
73
|
"@types/react": "^19",
|
|
66
74
|
"@types/react-dom": "^19",
|
|
67
75
|
"react": "^19",
|
|
68
76
|
"react-dom": "^19"
|
|
69
77
|
},
|
|
70
78
|
"dependencies": {
|
|
71
|
-
"@ecopages/file-system": "0.2.0-alpha.
|
|
79
|
+
"@ecopages/file-system": "0.2.0-alpha.51",
|
|
72
80
|
"@ecopages/logger": "^0.2.3",
|
|
73
81
|
"@mdx-js/esbuild": "^3.1.1",
|
|
74
82
|
"@mdx-js/mdx": "^3.1.1",
|
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
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';
|
|
12
13
|
import type { ReactHmrPageMetadataCache } from './services/react-hmr-page-metadata-cache.js';
|
|
13
14
|
export interface ReactHmrStrategyOptions {
|
|
14
15
|
context: DefaultHmrContext;
|
|
15
16
|
pageMetadataCache: ReactHmrPageMetadataCache;
|
|
16
|
-
|
|
17
|
+
runtimeManifest: BrowserRuntimeManifest;
|
|
17
18
|
mdxCompilerOptions?: CompileOptions;
|
|
18
19
|
ownedTemplateExtensions?: string[];
|
|
19
20
|
allTemplateExtensions?: string[];
|
|
@@ -56,7 +57,7 @@ export interface ReactHmrStrategyOptions {
|
|
|
56
57
|
* const strategy = new ReactHmrStrategy({
|
|
57
58
|
* context,
|
|
58
59
|
* pageMetadataCache,
|
|
59
|
-
*
|
|
60
|
+
* runtimeManifest
|
|
60
61
|
* });
|
|
61
62
|
* ```
|
|
62
63
|
*/
|
|
@@ -74,13 +75,17 @@ export declare class ReactHmrStrategy extends HmrStrategy {
|
|
|
74
75
|
private context;
|
|
75
76
|
private pageMetadataCache;
|
|
76
77
|
private explicitGraphEnabled;
|
|
77
|
-
private readonly
|
|
78
|
+
private readonly runtimeManifest;
|
|
78
79
|
constructor(options: ReactHmrStrategyOptions);
|
|
79
80
|
/**
|
|
80
81
|
* Returns build plugins for React HMR bundling.
|
|
81
82
|
*
|
|
82
83
|
* Includes the client graph boundary plugin to prevent undeclared imports
|
|
83
84
|
* (including `node:*`) from breaking the browser bundle.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* HMR builds receive the React runtime manifest and rewrite manifest-owned
|
|
88
|
+
* runtime imports to concrete asset URLs before module resolution.
|
|
84
89
|
*/
|
|
85
90
|
private getBuildPlugins;
|
|
86
91
|
private isReactEntrypoint;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { HmrStrategy, HmrStrategyType } from "@ecopages/core/hmr/hmr-strategy";
|
|
3
3
|
import { RESOLVED_ASSETS_DIR } from "@ecopages/core/constants";
|
|
4
|
-
import {
|
|
5
|
-
import { createRuntimeSpecifierAliasPlugin } from "@ecopages/core/build/runtime-specifier-alias-plugin";
|
|
4
|
+
import { createBrowserRuntimeImportRewritePlugin } from "@ecopages/core/build/browser-runtime-import-rewrite-plugin";
|
|
6
5
|
import { FileNotFoundError, fileSystem } from "@ecopages/file-system";
|
|
7
6
|
import { Logger } from "@ecopages/logger";
|
|
8
7
|
import { injectHmrHandler } from "./utils/hmr-scripts.js";
|
|
@@ -10,7 +9,6 @@ import { createClientGraphBoundaryPlugin } from "./utils/client-graph-boundary-p
|
|
|
10
9
|
import { collectPageDeclaredModules, collectPageDeclaredModulesFromModule } from "./utils/declared-modules.js";
|
|
11
10
|
import { createReactMdxLoaderPlugin } from "./utils/react-mdx-loader-plugin.js";
|
|
12
11
|
import { getReactClientGraphAllowSpecifiers } from "./utils/react-runtime-alias-map.js";
|
|
13
|
-
import { createUseSyncExternalStoreShimPlugin } from "./utils/use-sync-external-store-shim-plugin.js";
|
|
14
12
|
const appLogger = new Logger("[ReactHmrStrategy]");
|
|
15
13
|
class ReactHmrStrategy extends HmrStrategy {
|
|
16
14
|
type = HmrStrategyType.INTEGRATION;
|
|
@@ -28,12 +26,12 @@ class ReactHmrStrategy extends HmrStrategy {
|
|
|
28
26
|
context;
|
|
29
27
|
pageMetadataCache;
|
|
30
28
|
explicitGraphEnabled;
|
|
31
|
-
|
|
29
|
+
runtimeManifest;
|
|
32
30
|
constructor(options) {
|
|
33
31
|
super();
|
|
34
32
|
this.context = options.context;
|
|
35
33
|
this.pageMetadataCache = options.pageMetadataCache;
|
|
36
|
-
this.
|
|
34
|
+
this.runtimeManifest = options.runtimeManifest;
|
|
37
35
|
this.explicitGraphEnabled = options.explicitGraphEnabled ?? false;
|
|
38
36
|
this.mdxCompilerOptions = options.mdxCompilerOptions;
|
|
39
37
|
this.ownedTemplateExtensions = new Set(options.ownedTemplateExtensions ?? [".tsx"]);
|
|
@@ -46,11 +44,18 @@ class ReactHmrStrategy extends HmrStrategy {
|
|
|
46
44
|
*
|
|
47
45
|
* Includes the client graph boundary plugin to prevent undeclared imports
|
|
48
46
|
* (including `node:*`) from breaking the browser bundle.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* HMR builds receive the React runtime manifest and rewrite manifest-owned
|
|
50
|
+
* runtime imports to concrete asset URLs before module resolution.
|
|
49
51
|
*/
|
|
50
52
|
getBuildPlugins(declaredModules) {
|
|
51
|
-
const allowSpecifiers = getReactClientGraphAllowSpecifiers(
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
const allowSpecifiers = getReactClientGraphAllowSpecifiers(
|
|
54
|
+
this.runtimeManifest.assets.map((asset) => asset.specifier)
|
|
55
|
+
);
|
|
56
|
+
const runtimeRewritePlugin = createBrowserRuntimeImportRewritePlugin({
|
|
57
|
+
name: "react-hmr-runtime-import-rewrite",
|
|
58
|
+
manifest: this.runtimeManifest
|
|
54
59
|
});
|
|
55
60
|
return [
|
|
56
61
|
createClientGraphBoundaryPlugin({
|
|
@@ -58,12 +63,8 @@ class ReactHmrStrategy extends HmrStrategy {
|
|
|
58
63
|
alwaysAllowSpecifiers: allowSpecifiers,
|
|
59
64
|
declaredModules
|
|
60
65
|
}),
|
|
61
|
-
...
|
|
62
|
-
...this.context.getPlugins()
|
|
63
|
-
createUseSyncExternalStoreShimPlugin({
|
|
64
|
-
name: "react-hmr-use-sync-external-store-shim",
|
|
65
|
-
namespace: "ecopages-react-hmr-shim"
|
|
66
|
-
})
|
|
66
|
+
...runtimeRewritePlugin ? [runtimeRewritePlugin] : [],
|
|
67
|
+
...this.context.getPlugins()
|
|
67
68
|
];
|
|
68
69
|
}
|
|
69
70
|
isReactEntrypoint(filePath) {
|
|
@@ -469,7 +470,6 @@ class ReactHmrStrategy extends HmrStrategy {
|
|
|
469
470
|
}
|
|
470
471
|
try {
|
|
471
472
|
let code = await fileSystem.readFile(tempPath);
|
|
472
|
-
code = rewriteRuntimeSpecifierAliases(code, this.runtimeAliasMap);
|
|
473
473
|
code = this.rewriteChunkImportUrls(code);
|
|
474
474
|
code = injectHmrHandler(code);
|
|
475
475
|
await fileSystem.writeAsync(finalPath, code);
|
package/src/react.plugin.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IntegrationPlugin, type EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
2
|
+
import type { BrowserRuntimeManifest } from '@ecopages/core/build/browser-runtime-manifest';
|
|
2
3
|
import type { HmrStrategy } from '@ecopages/core/hmr/hmr-strategy';
|
|
3
4
|
import type React from 'react';
|
|
4
5
|
import { ReactRenderer } from './react-renderer.js';
|
|
@@ -40,6 +41,7 @@ export declare class ReactPlugin extends IntegrationPlugin<React.ReactNode> {
|
|
|
40
41
|
}): ReactRenderer;
|
|
41
42
|
private ensureRuntimeDependencies;
|
|
42
43
|
get plugins(): EcoBuildPlugin[];
|
|
44
|
+
get browserRuntimeManifest(): BrowserRuntimeManifest;
|
|
43
45
|
/**
|
|
44
46
|
* Ensures the optional React MDX loader exists before either config-time
|
|
45
47
|
* manifest sealing or runtime setup needs it.
|
package/src/react.plugin.js
CHANGED
|
@@ -135,6 +135,10 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
135
135
|
}
|
|
136
136
|
return [];
|
|
137
137
|
}
|
|
138
|
+
get browserRuntimeManifest() {
|
|
139
|
+
this.ensureRuntimeDependencies();
|
|
140
|
+
return this.runtimeBundleService.getRuntimeManifest();
|
|
141
|
+
}
|
|
138
142
|
/**
|
|
139
143
|
* Ensures the optional React MDX loader exists before either config-time
|
|
140
144
|
* manifest sealing or runtime setup needs it.
|
|
@@ -179,7 +183,7 @@ class ReactPlugin extends IntegrationPlugin {
|
|
|
179
183
|
return new ReactHmrStrategy({
|
|
180
184
|
context,
|
|
181
185
|
pageMetadataCache: this.hmrPageMetadataCache,
|
|
182
|
-
|
|
186
|
+
runtimeManifest: this.runtimeBundleService.getRuntimeManifest("development"),
|
|
183
187
|
mdxCompilerOptions: this.mdxCompilerOptions,
|
|
184
188
|
ownedTemplateExtensions: this.extensions,
|
|
185
189
|
allTemplateExtensions: this.appConfig.templatesExt,
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
type Subscribe = (onStoreChange: () => void) => () => void;
|
|
2
|
+
export declare function useSyncExternalStoreWithSelector<Snapshot, Selection>(subscribe: Subscribe, getSnapshot: () => Snapshot, getServerSnapshot: (() => Snapshot) | undefined, selector: (snapshot: Snapshot) => Selection, isEqual?: (left: Selection, right: Selection) => boolean): Selection;
|
|
3
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useDebugValue, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
2
|
+
const objectIs = Object.is;
|
|
3
|
+
function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
|
|
4
|
+
const instRef = useRef({ hasValue: false, value: void 0 });
|
|
5
|
+
const memoizedSelectionRef = useMemo(() => {
|
|
6
|
+
let hasMemo = false;
|
|
7
|
+
let memoizedSnapshot;
|
|
8
|
+
let memoizedSelection;
|
|
9
|
+
const maybeGetServerSnapshot = getServerSnapshot === void 0 ? null : getServerSnapshot;
|
|
10
|
+
const memoizedSelector = (nextSnapshot) => {
|
|
11
|
+
if (!hasMemo) {
|
|
12
|
+
hasMemo = true;
|
|
13
|
+
memoizedSnapshot = nextSnapshot;
|
|
14
|
+
const nextSelection2 = selector(nextSnapshot);
|
|
15
|
+
const inst = instRef.current;
|
|
16
|
+
if (isEqual !== void 0 && inst.hasValue) {
|
|
17
|
+
const currentSelection2 = inst.value;
|
|
18
|
+
if (isEqual(currentSelection2, nextSelection2)) {
|
|
19
|
+
memoizedSelection = currentSelection2;
|
|
20
|
+
return currentSelection2;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
memoizedSelection = nextSelection2;
|
|
24
|
+
return nextSelection2;
|
|
25
|
+
}
|
|
26
|
+
const currentSelection = memoizedSelection;
|
|
27
|
+
if (objectIs(memoizedSnapshot, nextSnapshot)) {
|
|
28
|
+
return currentSelection;
|
|
29
|
+
}
|
|
30
|
+
const nextSelection = selector(nextSnapshot);
|
|
31
|
+
if (isEqual !== void 0 && isEqual(currentSelection, nextSelection)) {
|
|
32
|
+
memoizedSnapshot = nextSnapshot;
|
|
33
|
+
return currentSelection;
|
|
34
|
+
}
|
|
35
|
+
memoizedSnapshot = nextSnapshot;
|
|
36
|
+
memoizedSelection = nextSelection;
|
|
37
|
+
return nextSelection;
|
|
38
|
+
};
|
|
39
|
+
return [
|
|
40
|
+
() => memoizedSelector(getSnapshot()),
|
|
41
|
+
maybeGetServerSnapshot === null ? void 0 : () => memoizedSelector(maybeGetServerSnapshot())
|
|
42
|
+
];
|
|
43
|
+
}, [getSnapshot, getServerSnapshot, selector, isEqual]);
|
|
44
|
+
const value = useSyncExternalStore(subscribe, memoizedSelectionRef[0], memoizedSelectionRef[1]);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
instRef.current = {
|
|
47
|
+
hasValue: true,
|
|
48
|
+
value
|
|
49
|
+
};
|
|
50
|
+
}, [value]);
|
|
51
|
+
useDebugValue(value);
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
useSyncExternalStoreWithSelector
|
|
56
|
+
};
|
|
@@ -47,15 +47,14 @@ export declare class ReactBundleService {
|
|
|
47
47
|
/**
|
|
48
48
|
* Creates esbuild bundle options for a page or component entry.
|
|
49
49
|
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* React derives runtime specifier mappings from the core browser runtime manifest
|
|
52
|
+
* so ESM imports resolve to concrete runtime asset URLs during module loading.
|
|
53
|
+
*
|
|
50
54
|
* @param componentName - Generated unique component name for output naming
|
|
51
55
|
* @param isMdx - Whether the source file is an MDX file
|
|
52
56
|
* @param declaredModules - Explicitly declared browser module specifiers
|
|
53
57
|
* @returns Bundle options object for the build adapter
|
|
54
58
|
*/
|
|
55
59
|
createBundleOptions(componentName: string, isMdx: boolean, declaredModules: string[], bundleOptions?: ReactClientBundleOptions): Promise<Record<string, unknown>>;
|
|
56
|
-
/**
|
|
57
|
-
* Creates the esbuild plugin that rewrites bare React specifiers
|
|
58
|
-
* to their runtime asset URLs.
|
|
59
|
-
*/
|
|
60
|
-
createRuntimeAliasPlugin(runtimeAliasMap: Record<string, string>): import("@ecopages/core/build/build-types").EcoBuildPlugin | null;
|
|
61
60
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { createClientGraphBoundaryPlugin } from "../utils/client-graph-boundary-plugin.js";
|
|
2
2
|
import {
|
|
3
|
-
buildReactRuntimeAliasMap,
|
|
4
3
|
getReactClientGraphAllowSpecifiers,
|
|
5
4
|
getReactRuntimeExternalSpecifiers
|
|
6
5
|
} from "../utils/react-runtime-alias-map.js";
|
|
7
|
-
import {
|
|
8
|
-
import { createRuntimeSpecifierAliasPlugin } from "@ecopages/core/build/runtime-specifier-alias-plugin";
|
|
6
|
+
import { createBrowserRuntimeImportRewritePlugin } from "@ecopages/core/build/browser-runtime-import-rewrite-plugin";
|
|
9
7
|
import { createForeignJsxOverridePlugin } from "@ecopages/core/plugins/foreign-jsx-override-plugin";
|
|
10
8
|
import { ReactRuntimeBundleService } from "./react-runtime-bundle.service.js";
|
|
11
9
|
import { createReactMdxLoaderPlugin } from "../utils/react-mdx-loader-plugin.js";
|
|
@@ -28,6 +26,10 @@ class ReactBundleService {
|
|
|
28
26
|
/**
|
|
29
27
|
* Creates esbuild bundle options for a page or component entry.
|
|
30
28
|
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* React derives runtime specifier mappings from the core browser runtime manifest
|
|
31
|
+
* so ESM imports resolve to concrete runtime asset URLs during module loading.
|
|
32
|
+
*
|
|
31
33
|
* @param componentName - Generated unique component name for output naming
|
|
32
34
|
* @param isMdx - Whether the source file is an MDX file
|
|
33
35
|
* @param declaredModules - Explicitly declared browser module specifiers
|
|
@@ -64,37 +66,20 @@ class ReactBundleService {
|
|
|
64
66
|
hostJsxImportSource: this.config.jsxImportSource ?? "react",
|
|
65
67
|
foreignExtensions: this.config.nonReactExtensions ?? []
|
|
66
68
|
});
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
const runtimeManifest = this.runtimeBundleService.getRuntimeManifest();
|
|
70
|
+
const runtimeRewritePlugin = createBrowserRuntimeImportRewritePlugin({
|
|
71
|
+
name: "react-renderer-runtime-import-rewrite",
|
|
72
|
+
manifest: runtimeManifest
|
|
70
73
|
});
|
|
71
|
-
const runtimePlugins = bundleOptions.includeRuntime ? [] : [
|
|
74
|
+
const runtimePlugins = bundleOptions.includeRuntime ? [] : [runtimeRewritePlugin].filter((plugin) => plugin !== null);
|
|
72
75
|
if (isMdx && this.config.mdxCompilerOptions) {
|
|
73
76
|
const mdxPlugin = createReactMdxLoaderPlugin(this.config.mdxCompilerOptions);
|
|
74
|
-
options.plugins = [
|
|
75
|
-
foreignJsxOverridePlugin,
|
|
76
|
-
graphBoundaryPlugin,
|
|
77
|
-
...runtimePlugins,
|
|
78
|
-
mdxPlugin,
|
|
79
|
-
useSyncExternalStoreShimPlugin
|
|
80
|
-
];
|
|
77
|
+
options.plugins = [foreignJsxOverridePlugin, graphBoundaryPlugin, ...runtimePlugins, mdxPlugin];
|
|
81
78
|
} else {
|
|
82
|
-
options.plugins = [
|
|
83
|
-
foreignJsxOverridePlugin,
|
|
84
|
-
graphBoundaryPlugin,
|
|
85
|
-
...runtimePlugins,
|
|
86
|
-
useSyncExternalStoreShimPlugin
|
|
87
|
-
];
|
|
79
|
+
options.plugins = [foreignJsxOverridePlugin, graphBoundaryPlugin, ...runtimePlugins];
|
|
88
80
|
}
|
|
89
81
|
return options;
|
|
90
82
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Creates the esbuild plugin that rewrites bare React specifiers
|
|
93
|
-
* to their runtime asset URLs.
|
|
94
|
-
*/
|
|
95
|
-
createRuntimeAliasPlugin(runtimeAliasMap) {
|
|
96
|
-
return createRuntimeSpecifierAliasPlugin(runtimeAliasMap, { name: "react-runtime-import-alias" });
|
|
97
|
-
}
|
|
98
83
|
}
|
|
99
84
|
export {
|
|
100
85
|
ReactBundleService
|
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
import type { EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
10
10
|
import { type AssetDefinition } from '@ecopages/core/services/asset-processing-service';
|
|
11
11
|
import type { ReactRouterAdapter } from '../router-adapter.js';
|
|
12
|
+
import { type BrowserRuntimeManifest } from '@ecopages/core/build/browser-runtime-manifest';
|
|
12
13
|
export type ReactRuntimeImports = {
|
|
13
14
|
react: string;
|
|
14
15
|
reactDomClient: string;
|
|
15
16
|
reactJsxRuntime: string;
|
|
16
17
|
reactJsxDevRuntime: string;
|
|
17
18
|
reactDom: string;
|
|
19
|
+
useSyncExternalStoreWithSelector: string;
|
|
18
20
|
router?: string;
|
|
19
21
|
};
|
|
20
22
|
export interface ReactRuntimeBundleServiceConfig {
|
|
@@ -32,8 +34,11 @@ export declare class ReactRuntimeBundleService {
|
|
|
32
34
|
private getReactVendorFileName;
|
|
33
35
|
private getReactDomVendorFileName;
|
|
34
36
|
private getRouterVendorFileName;
|
|
37
|
+
private getUseSyncExternalStoreWithSelectorVendorFileName;
|
|
38
|
+
private createReactVendorImportRewritePlugin;
|
|
35
39
|
getRuntimeImports(mode?: RuntimeMode): ReactRuntimeImports;
|
|
36
40
|
getRuntimeAliasMap(mode?: RuntimeMode): Record<string, string>;
|
|
41
|
+
getRuntimeManifest(mode?: RuntimeMode): BrowserRuntimeManifest;
|
|
37
42
|
getDependencies(): AssetDefinition[];
|
|
38
43
|
createRuntimeAliasPlugin(mode?: RuntimeMode): EcoBuildPlugin;
|
|
39
44
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBrowserRuntimeImportRewritePlugin,
|
|
3
|
+
DEFAULT_BROWSER_RUNTIME_IMPORT_REWRITE_PLUGIN_NAME
|
|
4
|
+
} from "@ecopages/core/build/browser-runtime-import-rewrite-plugin";
|
|
1
5
|
import { createRuntimeSpecifierAliasPlugin } from "@ecopages/core/build/runtime-specifier-alias-plugin";
|
|
2
6
|
import {
|
|
3
7
|
buildBrowserRuntimeAssetUrl,
|
|
@@ -5,7 +9,10 @@ import {
|
|
|
5
9
|
createBrowserRuntimeScriptAsset
|
|
6
10
|
} from "@ecopages/core/services/asset-processing-service";
|
|
7
11
|
import { createReactDomRuntimeInteropPlugin } from "../utils/react-dom-runtime-interop-plugin.js";
|
|
8
|
-
import { buildReactRuntimeAliasMap } from "../utils/react-runtime-alias-map.js";
|
|
12
|
+
import { buildReactRuntimeAliasMap, buildReactRuntimeManifest } from "../utils/react-runtime-alias-map.js";
|
|
13
|
+
import {
|
|
14
|
+
createBrowserRuntimeManifest
|
|
15
|
+
} from "@ecopages/core/build/browser-runtime-manifest";
|
|
9
16
|
class ReactRuntimeBundleService {
|
|
10
17
|
config;
|
|
11
18
|
constructor(config) {
|
|
@@ -39,6 +46,22 @@ class ReactRuntimeBundleService {
|
|
|
39
46
|
}
|
|
40
47
|
return mode === "development" ? `${this.config.routerAdapter.bundle.outputName}.development.js` : `${this.config.routerAdapter.bundle.outputName}.js`;
|
|
41
48
|
}
|
|
49
|
+
getUseSyncExternalStoreWithSelectorVendorFileName(mode) {
|
|
50
|
+
return mode === "development" ? "use-sync-external-store-with-selector.development.js" : "use-sync-external-store-with-selector.js";
|
|
51
|
+
}
|
|
52
|
+
createReactVendorImportRewritePlugin(mode) {
|
|
53
|
+
return createBrowserRuntimeImportRewritePlugin({
|
|
54
|
+
name: `react-plugin-vendor-runtime-import-rewrite-${mode}`,
|
|
55
|
+
manifest: createBrowserRuntimeManifest([
|
|
56
|
+
{
|
|
57
|
+
specifier: "react",
|
|
58
|
+
owner: "@ecopages/react",
|
|
59
|
+
importPath: "react",
|
|
60
|
+
publicPath: buildBrowserRuntimeAssetUrl(this.getReactVendorFileName(mode))
|
|
61
|
+
}
|
|
62
|
+
])
|
|
63
|
+
});
|
|
64
|
+
}
|
|
42
65
|
getRuntimeImports(mode = this.getCurrentRuntimeMode()) {
|
|
43
66
|
const reactVendorFileName = this.getReactVendorFileName(mode);
|
|
44
67
|
const reactDomVendorFileName = this.getReactDomVendorFileName(mode);
|
|
@@ -47,7 +70,10 @@ class ReactRuntimeBundleService {
|
|
|
47
70
|
reactDomClient: buildBrowserRuntimeAssetUrl(reactDomVendorFileName),
|
|
48
71
|
reactJsxRuntime: buildBrowserRuntimeAssetUrl(reactVendorFileName),
|
|
49
72
|
reactJsxDevRuntime: buildBrowserRuntimeAssetUrl(reactVendorFileName),
|
|
50
|
-
reactDom: buildBrowserRuntimeAssetUrl(reactDomVendorFileName)
|
|
73
|
+
reactDom: buildBrowserRuntimeAssetUrl(reactDomVendorFileName),
|
|
74
|
+
useSyncExternalStoreWithSelector: buildBrowserRuntimeAssetUrl(
|
|
75
|
+
this.getUseSyncExternalStoreWithSelectorVendorFileName(mode)
|
|
76
|
+
)
|
|
51
77
|
};
|
|
52
78
|
if (this.config.routerAdapter) {
|
|
53
79
|
runtimeImports.router = buildBrowserRuntimeAssetUrl(this.getRouterVendorFileName(mode));
|
|
@@ -57,10 +83,16 @@ class ReactRuntimeBundleService {
|
|
|
57
83
|
getRuntimeAliasMap(mode = this.getCurrentRuntimeMode()) {
|
|
58
84
|
return buildReactRuntimeAliasMap(this.getRuntimeImports(mode));
|
|
59
85
|
}
|
|
86
|
+
getRuntimeManifest(mode = this.getCurrentRuntimeMode()) {
|
|
87
|
+
return buildReactRuntimeManifest(this.getRuntimeImports(mode));
|
|
88
|
+
}
|
|
60
89
|
getDependencies() {
|
|
61
|
-
const reactDomRuntimeInteropPlugin = createReactDomRuntimeInteropPlugin();
|
|
62
90
|
const dependencies = [];
|
|
63
91
|
for (const mode of ["production", "development"]) {
|
|
92
|
+
const reactVendorImportRewritePlugin = this.createReactVendorImportRewritePlugin(mode);
|
|
93
|
+
const reactDomRuntimeInteropPlugin = createReactDomRuntimeInteropPlugin({
|
|
94
|
+
reactSpecifier: buildBrowserRuntimeAssetUrl(this.getReactVendorFileName(mode))
|
|
95
|
+
});
|
|
64
96
|
const reactRuntimeAliasPlugin = createRuntimeSpecifierAliasPlugin(
|
|
65
97
|
{
|
|
66
98
|
react: buildBrowserRuntimeAssetUrl(this.getReactVendorFileName(mode))
|
|
@@ -84,7 +116,8 @@ class ReactRuntimeBundleService {
|
|
|
84
116
|
cacheDirName: `ecopages-react-runtime-${mode}`,
|
|
85
117
|
rootDir: this.config.rootDir,
|
|
86
118
|
bundleOptions: {
|
|
87
|
-
define: this.createRuntimeDefines(mode)
|
|
119
|
+
define: this.createRuntimeDefines(mode),
|
|
120
|
+
excludeAppBuildPlugins: [DEFAULT_BROWSER_RUNTIME_IMPORT_REWRITE_PLUGIN_NAME]
|
|
88
121
|
}
|
|
89
122
|
}),
|
|
90
123
|
createBrowserRuntimeModuleAsset({
|
|
@@ -95,8 +128,19 @@ class ReactRuntimeBundleService {
|
|
|
95
128
|
rootDir: this.config.rootDir,
|
|
96
129
|
bundleOptions: {
|
|
97
130
|
define: this.createRuntimeDefines(mode),
|
|
131
|
+
excludeAppBuildPlugins: [DEFAULT_BROWSER_RUNTIME_IMPORT_REWRITE_PLUGIN_NAME],
|
|
98
132
|
plugins: reactDomBundlePlugins
|
|
99
133
|
}
|
|
134
|
+
}),
|
|
135
|
+
createBrowserRuntimeScriptAsset({
|
|
136
|
+
importPath: "@ecopages/react/runtime/use-sync-external-store-with-selector",
|
|
137
|
+
name: "use-sync-external-store-with-selector",
|
|
138
|
+
fileName: this.getUseSyncExternalStoreWithSelectorVendorFileName(mode),
|
|
139
|
+
bundleOptions: {
|
|
140
|
+
define: this.createRuntimeDefines(mode),
|
|
141
|
+
excludeAppBuildPlugins: [DEFAULT_BROWSER_RUNTIME_IMPORT_REWRITE_PLUGIN_NAME],
|
|
142
|
+
plugins: [reactVendorImportRewritePlugin]
|
|
143
|
+
}
|
|
100
144
|
})
|
|
101
145
|
);
|
|
102
146
|
if (this.config.routerAdapter) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
function escapeRegExp(value) {
|
|
4
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5
|
+
}
|
|
3
6
|
function createReactDomRuntimeInteropPlugin(options) {
|
|
4
7
|
const reactDomFileFilter = /[\\/]react-dom[\\/].*\.js$/;
|
|
5
8
|
const reactRequirePattern = /\brequire\((['"])react\1\)/g;
|
|
@@ -7,6 +10,12 @@ function createReactDomRuntimeInteropPlugin(options) {
|
|
|
7
10
|
return {
|
|
8
11
|
name: options?.name ?? "react-dom-runtime-interop",
|
|
9
12
|
setup(build) {
|
|
13
|
+
if (reactSpecifier.startsWith("/")) {
|
|
14
|
+
build.onResolve({ filter: new RegExp(`^${escapeRegExp(reactSpecifier)}$`) }, (args) => ({
|
|
15
|
+
path: args.path,
|
|
16
|
+
external: true
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
10
19
|
build.onLoad({ filter: reactDomFileFilter }, (args) => {
|
|
11
20
|
const content = fs.readFileSync(args.path, "utf-8");
|
|
12
21
|
if (!reactRequirePattern.test(content)) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ReactRouterAdapter } from '../router-adapter.js';
|
|
2
2
|
import type { ReactRuntimeImports } from '../services/react-runtime-bundle.service.js';
|
|
3
|
+
import { type BrowserRuntimeManifest } from '@ecopages/core/build/browser-runtime-manifest';
|
|
3
4
|
export declare const REACT_RUNTIME_SPECIFIERS: readonly ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom/client"];
|
|
4
5
|
export declare function buildReactRuntimeAliasMap(runtimeImports: ReactRuntimeImports): Record<string, string>;
|
|
6
|
+
export declare function buildReactRuntimeManifest(runtimeImports: ReactRuntimeImports): BrowserRuntimeManifest;
|
|
5
7
|
export declare function getReactRuntimeExternalSpecifiers(): string[];
|
|
6
8
|
export declare function getReactClientGraphAllowSpecifiers(runtimeSpecifiers: Iterable<string>, routerAdapter?: ReactRouterAdapter): string[];
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBrowserRuntimeManifest,
|
|
3
|
+
getBrowserRuntimeSpecifierMap
|
|
4
|
+
} from "@ecopages/core/build/browser-runtime-manifest";
|
|
1
5
|
const REACT_RUNTIME_SPECIFIERS = [
|
|
2
6
|
"react",
|
|
3
7
|
"react-dom",
|
|
@@ -6,13 +10,65 @@ const REACT_RUNTIME_SPECIFIERS = [
|
|
|
6
10
|
"react-dom/client"
|
|
7
11
|
];
|
|
8
12
|
function buildReactRuntimeAliasMap(runtimeImports) {
|
|
9
|
-
return
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return Object.fromEntries(getBrowserRuntimeSpecifierMap(buildReactRuntimeManifest(runtimeImports)));
|
|
14
|
+
}
|
|
15
|
+
function buildReactRuntimeManifest(runtimeImports) {
|
|
16
|
+
return createBrowserRuntimeManifest([
|
|
17
|
+
{
|
|
18
|
+
specifier: "react",
|
|
19
|
+
owner: "@ecopages/react",
|
|
20
|
+
importPath: "react",
|
|
21
|
+
publicPath: runtimeImports.react
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
specifier: "react/jsx-runtime",
|
|
25
|
+
owner: "@ecopages/react",
|
|
26
|
+
importPath: "react/jsx-runtime",
|
|
27
|
+
publicPath: runtimeImports.reactJsxRuntime
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
specifier: "react/jsx-dev-runtime",
|
|
31
|
+
owner: "@ecopages/react",
|
|
32
|
+
importPath: "react/jsx-dev-runtime",
|
|
33
|
+
publicPath: runtimeImports.reactJsxDevRuntime
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
specifier: "react-dom",
|
|
37
|
+
owner: "@ecopages/react",
|
|
38
|
+
importPath: "react-dom",
|
|
39
|
+
publicPath: runtimeImports.reactDom
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
specifier: "react-dom/client",
|
|
43
|
+
owner: "@ecopages/react",
|
|
44
|
+
importPath: "react-dom/client",
|
|
45
|
+
publicPath: runtimeImports.reactDomClient
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
specifier: "use-sync-external-store/shim",
|
|
49
|
+
owner: "@ecopages/react",
|
|
50
|
+
importPath: "use-sync-external-store/shim",
|
|
51
|
+
publicPath: runtimeImports.react
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
specifier: "use-sync-external-store/shim/index.js",
|
|
55
|
+
owner: "@ecopages/react",
|
|
56
|
+
importPath: "use-sync-external-store/shim/index.js",
|
|
57
|
+
publicPath: runtimeImports.react
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
specifier: "use-sync-external-store/shim/with-selector",
|
|
61
|
+
owner: "@ecopages/react",
|
|
62
|
+
importPath: "use-sync-external-store/shim/with-selector",
|
|
63
|
+
publicPath: runtimeImports.useSyncExternalStoreWithSelector
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
specifier: "use-sync-external-store/shim/with-selector.js",
|
|
67
|
+
owner: "@ecopages/react",
|
|
68
|
+
importPath: "use-sync-external-store/shim/with-selector.js",
|
|
69
|
+
publicPath: runtimeImports.useSyncExternalStoreWithSelector
|
|
70
|
+
}
|
|
71
|
+
]);
|
|
16
72
|
}
|
|
17
73
|
function getReactRuntimeExternalSpecifiers() {
|
|
18
74
|
return [...REACT_RUNTIME_SPECIFIERS];
|
|
@@ -28,6 +84,7 @@ function getReactClientGraphAllowSpecifiers(runtimeSpecifiers, routerAdapter) {
|
|
|
28
84
|
export {
|
|
29
85
|
REACT_RUNTIME_SPECIFIERS,
|
|
30
86
|
buildReactRuntimeAliasMap,
|
|
87
|
+
buildReactRuntimeManifest,
|
|
31
88
|
getReactClientGraphAllowSpecifiers,
|
|
32
89
|
getReactRuntimeExternalSpecifiers
|
|
33
90
|
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
function createUseSyncExternalStoreShimPlugin(options) {
|
|
2
|
-
const namespace = options?.namespace ?? "ecopages-react-use-sync-external-store-shim";
|
|
3
|
-
return {
|
|
4
|
-
name: options?.name ?? "react-use-sync-external-store-shim",
|
|
5
|
-
setup(build) {
|
|
6
|
-
build.onResolve({ filter: /^use-sync-external-store\/shim(?:\/index\.js)?$/ }, () => ({
|
|
7
|
-
path: "use-sync-external-store/shim",
|
|
8
|
-
namespace
|
|
9
|
-
}));
|
|
10
|
-
build.onLoad({ filter: /^use-sync-external-store\/shim$/, namespace }, () => ({
|
|
11
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
12
|
-
loader: "js"
|
|
13
|
-
}));
|
|
14
|
-
build.onLoad({ filter: /[\\/]use-sync-external-store[\\/]shim[\\/]index\.js$/ }, () => ({
|
|
15
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
16
|
-
loader: "js"
|
|
17
|
-
}));
|
|
18
|
-
build.onLoad(
|
|
19
|
-
{
|
|
20
|
-
filter: /[\\/]use-sync-external-store[\\/]cjs[\\/]use-sync-external-store-shim\.development\.js$/
|
|
21
|
-
},
|
|
22
|
-
() => ({
|
|
23
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
24
|
-
loader: "js"
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
build.onLoad(
|
|
28
|
-
{
|
|
29
|
-
filter: /[\\/]use-sync-external-store[\\/]cjs[\\/]use-sync-external-store-shim\.production\.js$/
|
|
30
|
-
},
|
|
31
|
-
() => ({
|
|
32
|
-
contents: "export { useSyncExternalStore } from 'react';",
|
|
33
|
-
loader: "js"
|
|
34
|
-
})
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
export {
|
|
40
|
-
createUseSyncExternalStoreShimPlugin
|
|
41
|
-
};
|