@ecopages/react 0.2.0-alpha.9 → 0.2.0-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.
- package/README.md +30 -13
- package/package.json +23 -12
- package/src/eco-embed.d.ts +11 -0
- package/src/eco-embed.js +11 -0
- package/src/react-hmr-strategy.d.ts +102 -18
- package/src/react-hmr-strategy.js +427 -50
- package/src/react-renderer.d.ts +100 -92
- package/src/react-renderer.js +356 -340
- package/src/react.constants.d.ts +1 -0
- package/src/react.constants.js +4 -0
- package/src/react.plugin.d.ts +25 -107
- package/src/react.plugin.js +109 -61
- package/src/react.types.d.ts +88 -0
- package/src/react.types.js +0 -0
- package/src/router-adapter.d.ts +7 -14
- package/src/runtime/use-sync-external-store-with-selector.d.ts +3 -0
- package/src/runtime/use-sync-external-store-with-selector.js +56 -0
- package/src/services/pages-index.d.ts +64 -0
- package/src/services/pages-index.js +73 -0
- package/src/services/react-bundle.service.d.ts +24 -9
- package/src/services/react-bundle.service.js +35 -24
- package/src/services/react-hmr-page-metadata-cache.d.ts +10 -1
- package/src/services/react-hmr-page-metadata-cache.js +18 -2
- package/src/services/react-hydration-asset.service.d.ts +28 -19
- package/src/services/react-hydration-asset.service.js +83 -64
- package/src/services/react-mdx-config-dependency.service.d.ts +36 -0
- package/src/services/react-mdx-config-dependency.service.js +122 -0
- package/src/services/react-page-module.service.d.ts +8 -3
- package/src/services/react-page-module.service.js +33 -26
- package/src/services/react-page-payload.service.d.ts +46 -0
- package/src/services/react-page-payload.service.js +67 -0
- package/src/services/react-runtime-bundle.service.d.ts +9 -2
- package/src/services/react-runtime-bundle.service.js +77 -16
- package/src/utils/client-graph-boundary-cache.d.ts +108 -0
- package/src/utils/client-graph-boundary-cache.js +116 -0
- package/src/utils/client-graph-boundary-plugin.d.ts +13 -5
- package/src/utils/client-graph-boundary-plugin.js +63 -5
- package/src/utils/component-config-traversal.d.ts +36 -0
- package/src/utils/component-config-traversal.js +54 -0
- package/src/utils/declared-modules.d.ts +1 -1
- package/src/utils/declared-modules.js +7 -16
- package/src/utils/dynamic.test.browser.d.ts +1 -0
- package/src/utils/dynamic.test.browser.js +33 -0
- package/src/utils/hydration-scripts.d.ts +9 -5
- package/src/utils/hydration-scripts.js +119 -34
- package/src/utils/hydration-scripts.test.browser.d.ts +1 -0
- package/src/utils/hydration-scripts.test.browser.js +198 -0
- package/src/utils/react-dom-runtime-interop-plugin.d.ts +1 -1
- package/src/utils/react-dom-runtime-interop-plugin.js +9 -0
- package/src/utils/react-mdx-loader-plugin.d.ts +1 -1
- package/src/utils/{react-runtime-specifier-map.d.ts → react-runtime-alias-map.d.ts} +3 -1
- package/src/utils/react-runtime-alias-map.js +90 -0
- package/CHANGELOG.md +0 -27
- package/src/react-hmr-strategy.ts +0 -386
- package/src/react-renderer.ts +0 -803
- package/src/react.plugin.ts +0 -276
- package/src/router-adapter.ts +0 -95
- package/src/services/react-bundle.service.ts +0 -108
- package/src/services/react-hmr-page-metadata-cache.ts +0 -24
- package/src/services/react-hydration-asset.service.ts +0 -263
- package/src/services/react-page-module.service.ts +0 -224
- package/src/services/react-runtime-bundle.service.ts +0 -172
- package/src/utils/client-graph-boundary-plugin.ts +0 -831
- package/src/utils/client-only.ts +0 -27
- package/src/utils/declared-modules.ts +0 -99
- package/src/utils/dynamic.ts +0 -27
- package/src/utils/hmr-scripts.ts +0 -47
- package/src/utils/html-boundary.ts +0 -66
- package/src/utils/hydration-scripts.ts +0 -459
- package/src/utils/reachability-analyzer.ts +0 -593
- package/src/utils/react-dom-runtime-interop-plugin.ts +0 -33
- package/src/utils/react-mdx-loader-plugin.ts +0 -63
- package/src/utils/react-runtime-specifier-map.js +0 -37
- package/src/utils/react-runtime-specifier-map.ts +0 -45
- 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/src/utils/use-sync-external-store-shim-plugin.ts +0 -45
package/src/utils/client-only.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
|
|
4
|
-
type ClientOnlyProps = {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
fallback?: ReactNode;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const useIsClient = (): boolean => {
|
|
10
|
-
const [isClient, setIsClient] = useState(false);
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
setIsClient(true);
|
|
14
|
-
}, []);
|
|
15
|
-
|
|
16
|
-
return isClient;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const ClientOnly = ({ children, fallback = null }: ClientOnlyProps): ReactNode => {
|
|
20
|
-
const isClient = useIsClient();
|
|
21
|
-
|
|
22
|
-
if (!isClient) {
|
|
23
|
-
return fallback;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return children;
|
|
27
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared utilities for collecting declared module sources from component configs.
|
|
3
|
-
* Used by both the production ReactRenderer and the HMR strategy to ensure
|
|
4
|
-
* the client-graph-boundary plugin receives a consistent set of allowed modules.
|
|
5
|
-
*
|
|
6
|
-
* @module
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { EcoComponentConfig } from '@ecopages/core';
|
|
10
|
-
|
|
11
|
-
type PageConfigModule = {
|
|
12
|
-
default?: { config?: EcoComponentConfig };
|
|
13
|
-
config?: EcoComponentConfig;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Extracts the module source (package name) from a declared module string,
|
|
18
|
-
* stripping any `{namedImport,...}` grammar.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* parseDeclaredModuleSource('@ecopages/image-processor/component/react{EcoImage}')
|
|
22
|
-
* // → '@ecopages/image-processor/component/react'
|
|
23
|
-
*/
|
|
24
|
-
export function parseDeclaredModuleSource(value: string): string | undefined {
|
|
25
|
-
const source = value.trim();
|
|
26
|
-
if (source.length === 0) return undefined;
|
|
27
|
-
const openBraceIndex = source.indexOf('{');
|
|
28
|
-
if (openBraceIndex < 0) return source;
|
|
29
|
-
const from = source.slice(0, openBraceIndex).trim();
|
|
30
|
-
return from.length > 0 ? from : undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Normalizes an array of declared module strings into unique source paths.
|
|
35
|
-
*/
|
|
36
|
-
export function normalizeDeclaredModuleSources(modules?: string[]): string[] {
|
|
37
|
-
const seen = new Set<string>();
|
|
38
|
-
for (const declaration of modules ?? []) {
|
|
39
|
-
const from = parseDeclaredModuleSource(declaration);
|
|
40
|
-
if (from) {
|
|
41
|
-
seen.add(from);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return Array.from(seen);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Recursively walks a component config tree (including layouts and nested
|
|
49
|
-
* `dependencies.components`) to collect all declared module sources.
|
|
50
|
-
*/
|
|
51
|
-
export function collectDeclaredModulesInConfig(
|
|
52
|
-
config: EcoComponentConfig | undefined,
|
|
53
|
-
visited = new Set<EcoComponentConfig>(),
|
|
54
|
-
): string[] {
|
|
55
|
-
if (!config || visited.has(config)) {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
visited.add(config);
|
|
60
|
-
|
|
61
|
-
const declarations = normalizeDeclaredModuleSources(config.dependencies?.modules);
|
|
62
|
-
|
|
63
|
-
if (config.layout?.config) {
|
|
64
|
-
declarations.push(...collectDeclaredModulesInConfig(config.layout.config, visited));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
for (const component of config.dependencies?.components ?? []) {
|
|
68
|
-
if (component.config) {
|
|
69
|
-
declarations.push(...collectDeclaredModulesInConfig(component.config, visited));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return declarations;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Collects declared module sources from an already imported page module.
|
|
78
|
-
*/
|
|
79
|
-
export function collectPageDeclaredModulesFromModule(pageModule: PageConfigModule): string[] {
|
|
80
|
-
const declarations = [
|
|
81
|
-
...collectDeclaredModulesInConfig(pageModule.default?.config),
|
|
82
|
-
...collectDeclaredModulesInConfig(pageModule.config),
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
return Array.from(new Set(declarations));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Imports a page entrypoint and collects all transitively declared module sources
|
|
90
|
-
* from its config, layout config, and nested component configs.
|
|
91
|
-
*/
|
|
92
|
-
export async function collectPageDeclaredModules(pagePath: string): Promise<string[]> {
|
|
93
|
-
try {
|
|
94
|
-
const pageModule = (await import(pagePath)) as PageConfigModule;
|
|
95
|
-
return collectPageDeclaredModulesFromModule(pageModule);
|
|
96
|
-
} catch {
|
|
97
|
-
return [];
|
|
98
|
-
}
|
|
99
|
-
}
|
package/src/utils/dynamic.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { type ComponentType, type LazyExoticComponent, lazy } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Dynamically loads a React component with optional SSR support.
|
|
5
|
-
*
|
|
6
|
-
* @param importFn - Function returning a promise that resolves to a React component.
|
|
7
|
-
* @param options - Options for SSR behavior.
|
|
8
|
-
* @returns Lazy loaded component or a null fallback for non-client environments.
|
|
9
|
-
*/
|
|
10
|
-
type DynamicLoaderOptions = {
|
|
11
|
-
ssr?: boolean;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const NullComponent: ComponentType = () => null;
|
|
15
|
-
|
|
16
|
-
export function dynamic(
|
|
17
|
-
importFn: () => Promise<{ default: ComponentType<any> }>,
|
|
18
|
-
options: DynamicLoaderOptions = {},
|
|
19
|
-
): LazyExoticComponent<ComponentType<any>> | ComponentType {
|
|
20
|
-
const { ssr = false } = options;
|
|
21
|
-
|
|
22
|
-
if (ssr || typeof window !== 'undefined') {
|
|
23
|
-
return lazy(importFn);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return NullComponent;
|
|
27
|
-
}
|
package/src/utils/hmr-scripts.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HMR script utilities for React components.
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** Marker comment to identify already-processed HMR code */
|
|
7
|
-
const HMR_MARKER = '/* [ecopages] react-hmr */';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Checks if code has already been processed with HMR marker.
|
|
11
|
-
* @param code - The bundled code to check
|
|
12
|
-
* @returns True if the code already contains the HMR marker
|
|
13
|
-
*/
|
|
14
|
-
export function hasHmrMarker(code: string): boolean {
|
|
15
|
-
return code.includes(HMR_MARKER);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Injects HMR acceptance handler into bundled code.
|
|
20
|
-
* When a module with React exports changes, it triggers a full invalidation
|
|
21
|
-
* to ensure the parent module re-imports and re-renders with the updated component.
|
|
22
|
-
* @param code - The bundled code to wrap
|
|
23
|
-
* @returns Code with HMR handler injected
|
|
24
|
-
*/
|
|
25
|
-
export function injectHmrHandler(code: string): string {
|
|
26
|
-
return `${HMR_MARKER}
|
|
27
|
-
${code}
|
|
28
|
-
if (import.meta.hot) {
|
|
29
|
-
import.meta.hot.accept((newModule) => {
|
|
30
|
-
if (newModule) {
|
|
31
|
-
const exports = Object.keys(newModule);
|
|
32
|
-
const hasReactExport = exports.some(key => {
|
|
33
|
-
const value = newModule[key];
|
|
34
|
-
return value && (
|
|
35
|
-
typeof value === 'function' ||
|
|
36
|
-
(typeof value === 'object' && value.$$typeof)
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (hasReactExport) {
|
|
41
|
-
import.meta.hot.invalidate();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
`;
|
|
47
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const VOID_TAGS = new Set([
|
|
2
|
-
'area',
|
|
3
|
-
'base',
|
|
4
|
-
'br',
|
|
5
|
-
'col',
|
|
6
|
-
'embed',
|
|
7
|
-
'hr',
|
|
8
|
-
'img',
|
|
9
|
-
'input',
|
|
10
|
-
'link',
|
|
11
|
-
'meta',
|
|
12
|
-
'param',
|
|
13
|
-
'source',
|
|
14
|
-
'track',
|
|
15
|
-
'wbr',
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Returns true when HTML contains exactly one root element node.
|
|
20
|
-
*
|
|
21
|
-
* Used by component-level React rendering to decide whether root attributes can
|
|
22
|
-
* be attached safely without introducing synthetic wrapper nodes.
|
|
23
|
-
*/
|
|
24
|
-
export function hasSingleRootElement(html: string): boolean {
|
|
25
|
-
const firstTagMatch = html.match(/^\s*<([a-zA-Z][a-zA-Z0-9:-]*)\b[^>]*>/);
|
|
26
|
-
if (!firstTagMatch) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const firstTag = firstTagMatch[1].toLowerCase();
|
|
31
|
-
const firstTagText = firstTagMatch[0];
|
|
32
|
-
const firstTagStart = firstTagMatch.index ?? 0;
|
|
33
|
-
const firstTagEnd = firstTagStart + firstTagText.length;
|
|
34
|
-
const isSelfClosing = /\/\s*>$/.test(firstTagText);
|
|
35
|
-
|
|
36
|
-
if (isSelfClosing || VOID_TAGS.has(firstTag)) {
|
|
37
|
-
return html.slice(firstTagEnd).trim().length === 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const tokenRegex = /<\/?([a-zA-Z][a-zA-Z0-9:-]*)\b[^>]*>/g;
|
|
41
|
-
tokenRegex.lastIndex = firstTagEnd;
|
|
42
|
-
let depth = 1;
|
|
43
|
-
|
|
44
|
-
for (let token = tokenRegex.exec(html); token; token = tokenRegex.exec(html)) {
|
|
45
|
-
const tagText = token[0];
|
|
46
|
-
const tagName = token[1].toLowerCase();
|
|
47
|
-
const isClosing = tagText.startsWith('</');
|
|
48
|
-
const tokenSelfClosing = /\/\s*>$/.test(tagText);
|
|
49
|
-
|
|
50
|
-
if (VOID_TAGS.has(tagName) || tokenSelfClosing) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (isClosing) {
|
|
55
|
-
depth--;
|
|
56
|
-
if (depth === 0) {
|
|
57
|
-
const afterRoot = html.slice(token.index + token[0].length).trim();
|
|
58
|
-
return afterRoot.length === 0;
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
depth++;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return false;
|
|
66
|
-
}
|