@ecopages/ecopages-jsx 0.2.0-beta.4 → 0.2.0-beta.41
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 +2 -0
- package/package.json +6 -5
- package/src/dev-transform/ecopages-jsx-dev-transform-contributor.d.ts +17 -0
- package/src/dev-transform/ecopages-jsx-dev-transform-contributor.js +21 -0
- package/src/eco-embed.js +8 -10
- package/src/ecopages-jsx-hmr-ownership.d.ts +45 -0
- package/src/ecopages-jsx-hmr-ownership.js +77 -0
- package/src/ecopages-jsx-hmr-strategy.d.ts +19 -0
- package/src/ecopages-jsx-hmr-strategy.js +67 -0
- package/src/ecopages-jsx-mdx.d.ts +2 -4
- package/src/ecopages-jsx-mdx.js +63 -106
- package/src/ecopages-jsx-radiant-ssr-policy.d.ts +7 -5
- package/src/ecopages-jsx-radiant-ssr-policy.js +107 -73
- package/src/ecopages-jsx-render-session.d.ts +5 -9
- package/src/ecopages-jsx-render-session.js +104 -86
- package/src/ecopages-jsx-renderer.d.ts +22 -9
- package/src/ecopages-jsx-renderer.js +278 -234
- package/src/ecopages-jsx.constants.js +7 -4
- package/src/ecopages-jsx.plugin.d.ts +17 -0
- package/src/ecopages-jsx.plugin.js +162 -111
- package/src/ecopages-jsx.types.js +1 -0
- package/src/page-owned-content-script-grouping.d.ts +12 -0
- package/src/page-owned-content-script-grouping.js +63 -0
- package/src/radiant-registered-script-ssr-invalidation.d.ts +18 -0
- package/src/radiant-registered-script-ssr-invalidation.js +47 -0
- package/src/resolve-radiant-install-hydrator.d.ts +2 -0
- package/src/resolve-radiant-install-hydrator.js +3 -0
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ Radiant support is enabled by default. When `radiant: true`, the plugin keeps th
|
|
|
48
48
|
|
|
49
49
|
- Ecopages JSX owns page-level JSX SSR and container hydration.
|
|
50
50
|
- Radiant SSR is activated on the server through `@ecopages/radiant/server/render-component`.
|
|
51
|
+
- The server reuses Radiant's complete light-DOM surface, replacing partial globals before Radiant elements are imported.
|
|
51
52
|
- Radiant host hydration is activated on the client through an explicit head bootstrap that imports `@ecopages/radiant/client/install-hydrator` before intrinsic custom-element modules load.
|
|
52
53
|
|
|
53
54
|
That means server-rendered `RadiantElement` hosts hydrate in place only when both the SSR markers and the explicit client hydrator are present. Without the client hydrator, Radiant intentionally falls back to a fresh client render on first connect.
|
|
@@ -99,6 +100,7 @@ Ecopages JSX can own the outer page shell or just a nested foreign subtree. When
|
|
|
99
100
|
|
|
100
101
|
Important:
|
|
101
102
|
|
|
103
|
+
- Cross-integration JSX children must use `EcoEmbed`. Opaque plain objects fail fast at core foreign-child queue boundaries; they are not coerced with `String(object)`.
|
|
102
104
|
- Components that may render foreign children must declare those children in `config.dependencies.components`.
|
|
103
105
|
- Ecopages validates mixed-renderer ownership from declared dependencies during render preparation. It does not treat rendered HTML alone as the source of truth.
|
|
104
106
|
- Ecopages JSX keeps raw-markup preservation and asset collection inside the JSX renderer.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/ecopages-jsx",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.41",
|
|
4
4
|
"description": "JSX integration plugin for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -27,16 +27,17 @@
|
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/ecopages/ecopages.git",
|
|
30
|
+
"url": "git+https://github.com/ecopages/ecopages.git",
|
|
31
31
|
"directory": "packages/integrations/ecopages-jsx"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@ecopages/mdx": "0.2.0-beta.41",
|
|
34
35
|
"@mdx-js/mdx": "^3.1.1",
|
|
35
36
|
"vfile": "^6.0.3"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
|
-
"@ecopages/core": "0.2.0-beta.
|
|
39
|
-
"@ecopages/jsx": "0.3.0-
|
|
40
|
-
"@ecopages/radiant": "0.3.0-
|
|
39
|
+
"@ecopages/core": "0.2.0-beta.41",
|
|
40
|
+
"@ecopages/jsx": "0.3.0-beta.6",
|
|
41
|
+
"@ecopages/radiant": "0.3.0-beta.6"
|
|
41
42
|
}
|
|
42
43
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
2
|
+
import type { DevTransformBundleContributor } from '@ecopages/core/dev/transform-server';
|
|
3
|
+
import type { EcopagesJsxHmrStrategy } from '../ecopages-jsx-hmr-strategy.js';
|
|
4
|
+
export type EcopagesJsxDevTransformContributorOptions = {
|
|
5
|
+
strategy: EcopagesJsxHmrStrategy;
|
|
6
|
+
getMdxLoaderPlugin: () => EcoBuildPlugin | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Supplies Ecopages JSX module plugins for the core dev transform bundler.
|
|
10
|
+
*/
|
|
11
|
+
export declare class EcopagesJsxDevTransformContributor implements DevTransformBundleContributor {
|
|
12
|
+
private readonly strategy;
|
|
13
|
+
private readonly getMdxLoaderPlugin;
|
|
14
|
+
constructor(options: EcopagesJsxDevTransformContributorOptions);
|
|
15
|
+
ownsModule(sourcePath: string): boolean;
|
|
16
|
+
getModulePlugins(sourcePath: string): Promise<readonly EcoBuildPlugin[]>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supplies Ecopages JSX module plugins for the core dev transform bundler.
|
|
3
|
+
*/
|
|
4
|
+
export class EcopagesJsxDevTransformContributor {
|
|
5
|
+
strategy;
|
|
6
|
+
getMdxLoaderPlugin;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.strategy = options.strategy;
|
|
9
|
+
this.getMdxLoaderPlugin = options.getMdxLoaderPlugin;
|
|
10
|
+
}
|
|
11
|
+
ownsModule(sourcePath) {
|
|
12
|
+
return this.strategy.ownsDevTransformEntrypoint(sourcePath);
|
|
13
|
+
}
|
|
14
|
+
async getModulePlugins(sourcePath) {
|
|
15
|
+
if (!sourcePath.endsWith('.mdx')) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const mdxLoader = this.getMdxLoaderPlugin();
|
|
19
|
+
return mdxLoader ? [mdxLoader] : [];
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/eco-embed.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/** @jsxImportSource @ecopages/jsx */
|
|
2
|
+
import { eco } from '@ecopages/core';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a foreign or same-integration eco component from an Ecopages JSX
|
|
5
|
+
* file without forcing inline mixed-JSX authoring.
|
|
6
|
+
*/
|
|
7
|
+
export function EcoEmbed({ component, props, children, }) {
|
|
8
|
+
return eco.embed(component, props, children);
|
|
8
9
|
}
|
|
9
|
-
export {
|
|
10
|
-
EcoEmbed
|
|
11
|
-
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the source files that participate in the active SSR render tree.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* The JSX integration renderer calls {@link updateEjsxHmrOwnership} after each
|
|
6
|
+
* page, component, or view render so the HMR strategy can answer
|
|
7
|
+
* "does this changed file affect SSR HTML?" without re-walking the tree on
|
|
8
|
+
* every watcher event.
|
|
9
|
+
*
|
|
10
|
+
* The state is module-scoped because the HMR strategy and renderer live in
|
|
11
|
+
* different call sites (server strategy vs SSR renderer) and share no other
|
|
12
|
+
* lifecycle anchor. A fresh state is installed only when the source-file set
|
|
13
|
+
* changes, so the renderer's per-render hook stays O(1) in the steady state.
|
|
14
|
+
*/
|
|
15
|
+
import type { EcoComponent } from '@ecopages/core';
|
|
16
|
+
export type EjsxHmrOwnershipState = {
|
|
17
|
+
fileOwners: ReadonlySet<string>;
|
|
18
|
+
hash: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Returns the most recently recorded active render tree.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getEjsxHmrOwnership(): EjsxHmrOwnershipState;
|
|
24
|
+
/**
|
|
25
|
+
* Updates the active render tree from one or more root components.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Walks each root through `getComponentIdentity`, `config.dependencies.components[*].config`,
|
|
29
|
+
* and `config.layouts[*].config` recursively, collecting every file path. The
|
|
30
|
+
* state is replaced only when the resulting file set has a different hash, so
|
|
31
|
+
* the renderer's per-render hook is free in the steady state.
|
|
32
|
+
*/
|
|
33
|
+
export declare function updateEjsxHmrOwnership(components: ReadonlyArray<EcoComponent | Partial<EcoComponent> | undefined>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Merges one render tree into a pending ownership set during an active render.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mergeEjsxHmrOwnership(target: Set<string>, components: ReadonlyArray<EcoComponent | undefined>): void;
|
|
38
|
+
/**
|
|
39
|
+
* Publishes the merged ownership set for the completed render.
|
|
40
|
+
*/
|
|
41
|
+
export declare function publishEjsxHmrOwnership(files: ReadonlySet<string>): void;
|
|
42
|
+
/**
|
|
43
|
+
* Resets the state to empty. Used by tests and by HMR manager teardown.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resetEjsxHmrOwnership(): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the source files that participate in the active SSR render tree.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* The JSX integration renderer calls {@link updateEjsxHmrOwnership} after each
|
|
6
|
+
* page, component, or view render so the HMR strategy can answer
|
|
7
|
+
* "does this changed file affect SSR HTML?" without re-walking the tree on
|
|
8
|
+
* every watcher event.
|
|
9
|
+
*
|
|
10
|
+
* The state is module-scoped because the HMR strategy and renderer live in
|
|
11
|
+
* different call sites (server strategy vs SSR renderer) and share no other
|
|
12
|
+
* lifecycle anchor. A fresh state is installed only when the source-file set
|
|
13
|
+
* changes, so the renderer's per-render hook stays O(1) in the steady state.
|
|
14
|
+
*/
|
|
15
|
+
import { rapidhash } from '@ecopages/core/hash';
|
|
16
|
+
import { collectComponentConfigFilePaths } from '@ecopages/core/route-renderer/page-loading/file-scoped-dependency-components';
|
|
17
|
+
const EMPTY_STATE = {
|
|
18
|
+
fileOwners: new Set(),
|
|
19
|
+
hash: '',
|
|
20
|
+
};
|
|
21
|
+
let currentState = EMPTY_STATE;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the most recently recorded active render tree.
|
|
24
|
+
*/
|
|
25
|
+
export function getEjsxHmrOwnership() {
|
|
26
|
+
return currentState;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Updates the active render tree from one or more root components.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Walks each root through `getComponentIdentity`, `config.dependencies.components[*].config`,
|
|
33
|
+
* and `config.layouts[*].config` recursively, collecting every file path. The
|
|
34
|
+
* state is replaced only when the resulting file set has a different hash, so
|
|
35
|
+
* the renderer's per-render hook is free in the steady state.
|
|
36
|
+
*/
|
|
37
|
+
export function updateEjsxHmrOwnership(components) {
|
|
38
|
+
publishEjsxHmrOwnership(collectFileOwners(components));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Merges one render tree into a pending ownership set during an active render.
|
|
42
|
+
*/
|
|
43
|
+
export function mergeEjsxHmrOwnership(target, components) {
|
|
44
|
+
for (const file of collectFileOwners(components)) {
|
|
45
|
+
target.add(file);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Publishes the merged ownership set for the completed render.
|
|
50
|
+
*/
|
|
51
|
+
export function publishEjsxHmrOwnership(files) {
|
|
52
|
+
const hash = hashFileSet(files);
|
|
53
|
+
if (hash === currentState.hash) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
currentState = {
|
|
57
|
+
fileOwners: new Set(files),
|
|
58
|
+
hash,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resets the state to empty. Used by tests and by HMR manager teardown.
|
|
63
|
+
*/
|
|
64
|
+
export function resetEjsxHmrOwnership() {
|
|
65
|
+
currentState = EMPTY_STATE;
|
|
66
|
+
}
|
|
67
|
+
function collectFileOwners(components) {
|
|
68
|
+
return collectComponentConfigFilePaths(components, { includeLayouts: true });
|
|
69
|
+
}
|
|
70
|
+
function hashFileSet(files) {
|
|
71
|
+
if (files.size === 0) {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
const sorted = Array.from(files).sort();
|
|
75
|
+
const joined = sorted.join('\n');
|
|
76
|
+
return rapidhash(joined).toString(36);
|
|
77
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HmrStrategy, type HmrAction } from '@ecopages/core/hmr/hmr-strategy';
|
|
2
|
+
import type { HmrRegisteredEntrypointsContext } from '@ecopages/core/hmr/hmr-registered-entrypoints-context';
|
|
3
|
+
export type EcopagesJsxHmrStrategyContext = HmrRegisteredEntrypointsContext & {
|
|
4
|
+
getSrcDir(): string;
|
|
5
|
+
getPagesDir(): string;
|
|
6
|
+
getLayoutsDir(): string;
|
|
7
|
+
getIncludesDir(): string;
|
|
8
|
+
getTemplateExtensions(): ReadonlyArray<string>;
|
|
9
|
+
};
|
|
10
|
+
export declare class EcopagesJsxHmrStrategy extends HmrStrategy {
|
|
11
|
+
readonly type: 100;
|
|
12
|
+
private readonly context;
|
|
13
|
+
constructor(context: EcopagesJsxHmrStrategyContext);
|
|
14
|
+
matches(filePath: string): boolean;
|
|
15
|
+
ownsDevTransformEntrypoint(filePath: string): boolean;
|
|
16
|
+
process(filePath: string): Promise<HmrAction>;
|
|
17
|
+
private isPageOrLayoutSource;
|
|
18
|
+
private hasJsxTemplateExtension;
|
|
19
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { HmrStrategy, HmrStrategyType } from '@ecopages/core/hmr/hmr-strategy';
|
|
3
|
+
import { isRegisteredDevTransformEntrypoint } from '@ecopages/core/hmr/hmr-entrypoint-output';
|
|
4
|
+
import { getEjsxHmrOwnership } from './ecopages-jsx-hmr-ownership.js';
|
|
5
|
+
export class EcopagesJsxHmrStrategy extends HmrStrategy {
|
|
6
|
+
type = HmrStrategyType.INTEGRATION;
|
|
7
|
+
context;
|
|
8
|
+
constructor(context) {
|
|
9
|
+
super();
|
|
10
|
+
this.context = context;
|
|
11
|
+
}
|
|
12
|
+
matches(filePath) {
|
|
13
|
+
const resolvedPath = path.resolve(filePath);
|
|
14
|
+
if (isRegisteredDevTransformEntrypoint(this.context.getRegisteredEntrypoints(), resolvedPath)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const srcDir = path.resolve(this.context.getSrcDir());
|
|
18
|
+
if (!resolvedPath.startsWith(`${srcDir}${path.sep}`) && resolvedPath !== srcDir) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const includesDir = path.resolve(this.context.getIncludesDir());
|
|
22
|
+
if (includesDir && (resolvedPath === includesDir || resolvedPath.startsWith(`${includesDir}${path.sep}`))) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (this.isPageOrLayoutSource(resolvedPath)) {
|
|
26
|
+
return this.hasJsxTemplateExtension(resolvedPath);
|
|
27
|
+
}
|
|
28
|
+
const ownership = getEjsxHmrOwnership();
|
|
29
|
+
if (ownership.fileOwners.has(resolvedPath)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
ownsDevTransformEntrypoint(filePath) {
|
|
35
|
+
const resolvedPath = path.resolve(filePath);
|
|
36
|
+
if (!isRegisteredDevTransformEntrypoint(this.context.getRegisteredEntrypoints(), resolvedPath)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return this.isPageOrLayoutSource(resolvedPath) && this.hasJsxTemplateExtension(resolvedPath);
|
|
40
|
+
}
|
|
41
|
+
async process(filePath) {
|
|
42
|
+
return {
|
|
43
|
+
type: 'broadcast',
|
|
44
|
+
events: [
|
|
45
|
+
{
|
|
46
|
+
type: 'layout-update',
|
|
47
|
+
path: filePath,
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
isPageOrLayoutSource(resolvedPath) {
|
|
54
|
+
const pagesDir = path.resolve(this.context.getPagesDir());
|
|
55
|
+
const layoutsDir = path.resolve(this.context.getLayoutsDir());
|
|
56
|
+
if (pagesDir && (resolvedPath === pagesDir || resolvedPath.startsWith(`${pagesDir}${path.sep}`))) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (layoutsDir && (resolvedPath === layoutsDir || resolvedPath.startsWith(`${layoutsDir}${path.sep}`))) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
hasJsxTemplateExtension(resolvedPath) {
|
|
65
|
+
return this.context.getTemplateExtensions().some((extension) => resolvedPath.endsWith(extension));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CompileOptions } from '@mdx-js/mdx';
|
|
2
2
|
import type { EcoComponent, EcoComponentConfig, EcoFunctionComponent, EcoPageFile, GetMetadata } from '@ecopages/core';
|
|
3
3
|
import type { EcoBuildPlugin } from '@ecopages/core/plugins/integration-plugin';
|
|
4
|
+
import { appendMdxExtensions, createMdxExtensionFilter } from '@ecopages/mdx/core';
|
|
4
5
|
import type { JsxRenderable } from '@ecopages/jsx';
|
|
5
6
|
import type { EcopagesJsxMdxCompileOptions, EcopagesJsxMdxOptions } from './ecopages-jsx.types.js';
|
|
6
7
|
export type ResolvedMdxCompileOptions = EcopagesJsxMdxCompileOptions & Pick<CompileOptions, 'jsxImportSource' | 'jsxRuntime'>;
|
|
@@ -10,10 +11,7 @@ export type EcopagesJsxMdxPageModule = EcoPageFile<{
|
|
|
10
11
|
layout?: EcoComponent;
|
|
11
12
|
getMetadata?: GetMetadata;
|
|
12
13
|
}>;
|
|
13
|
-
export
|
|
14
|
-
allowQueryString?: boolean;
|
|
15
|
-
}) => RegExp;
|
|
16
|
-
export declare const appendMdxExtensions: (target: string[], mdxExtensions: string[]) => void;
|
|
14
|
+
export { appendMdxExtensions, createMdxExtensionFilter };
|
|
17
15
|
export declare const resolveMdxCompilerOptions: (mdxOptions: EcopagesJsxMdxOptions) => ResolvedMdxCompileOptions;
|
|
18
16
|
export declare const createMdxLoaderPlugin: (compilerOptions: ResolvedMdxCompileOptions, extensions: string[]) => EcoBuildPlugin;
|
|
19
17
|
export declare const registerBunMdxPlugin: (compilerOptions: ResolvedMdxCompileOptions, extensions: string[]) => Promise<void>;
|
package/src/ecopages-jsx-mdx.js
CHANGED
|
@@ -1,113 +1,70 @@
|
|
|
1
|
-
import { readFile } from
|
|
2
|
-
import
|
|
3
|
-
import { rapidhash } from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { ensurePageConfigLayouts } from '@ecopages/core/eco/page-layout-normalization';
|
|
3
|
+
import { rapidhash } from '@ecopages/core/hash';
|
|
4
|
+
import { appendMdxExtensions, createMdxExtensionFilter, createMdxLoaderPlugin as createMdxLoaderPluginCore, resolveMdxCompilerOptions as resolveMdxCompilerOptionsCore, } from '@ecopages/mdx/core';
|
|
5
|
+
import { VFile } from 'vfile';
|
|
6
|
+
import { ECOPAGES_JSX_PLUGIN_NAME } from './ecopages-jsx.constants.js';
|
|
7
|
+
export { appendMdxExtensions, createMdxExtensionFilter };
|
|
8
|
+
export const resolveMdxCompilerOptions = (mdxOptions) => resolveMdxCompilerOptionsCore(mdxOptions, {
|
|
9
|
+
jsxImportSource: '@ecopages/jsx',
|
|
10
|
+
defaults: {
|
|
11
|
+
format: 'detect',
|
|
12
|
+
outputFormat: 'program',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
export const createMdxLoaderPlugin = (compilerOptions, extensions) => createMdxLoaderPluginCore({
|
|
16
|
+
name: 'ecopages-jsx-mdx-loader',
|
|
17
|
+
compilerOptions,
|
|
18
|
+
extensions,
|
|
19
|
+
includeSourceMap: false,
|
|
20
|
+
loader: 'js',
|
|
21
|
+
});
|
|
22
|
+
export const registerBunMdxPlugin = async (compilerOptions, extensions) => {
|
|
23
|
+
if (typeof Bun === 'undefined') {
|
|
24
|
+
return;
|
|
20
25
|
}
|
|
21
|
-
|
|
26
|
+
const filter = createMdxExtensionFilter(extensions);
|
|
27
|
+
Bun.plugin({
|
|
28
|
+
name: 'ecopages-jsx-mdx',
|
|
29
|
+
setup(build) {
|
|
30
|
+
build.onLoad({ filter }, async (args) => {
|
|
31
|
+
const { compile } = await import('@mdx-js/mdx');
|
|
32
|
+
const source = await readFile(args.path, 'utf-8');
|
|
33
|
+
const compiled = await compile(new VFile({ value: source, path: args.path }), compilerOptions);
|
|
34
|
+
return { contents: String(compiled.value), loader: 'js' };
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
});
|
|
22
38
|
};
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const resolved = {
|
|
26
|
-
format: "detect",
|
|
27
|
-
outputFormat: "program",
|
|
28
|
-
...compilerOptions,
|
|
29
|
-
jsxImportSource: "@ecopages/jsx",
|
|
30
|
-
jsxRuntime: "automatic",
|
|
31
|
-
development: process.env.NODE_ENV === "development"
|
|
32
|
-
};
|
|
33
|
-
const mergedRemark = mergePluginLists(compilerOptions?.remarkPlugins, remarkPlugins);
|
|
34
|
-
const mergedRehype = mergePluginLists(compilerOptions?.rehypePlugins, rehypePlugins);
|
|
35
|
-
const mergedRecma = mergePluginLists(compilerOptions?.recmaPlugins, recmaPlugins);
|
|
36
|
-
if (mergedRemark) resolved.remarkPlugins = mergedRemark;
|
|
37
|
-
if (mergedRehype) resolved.rehypePlugins = mergedRehype;
|
|
38
|
-
if (mergedRecma) resolved.recmaPlugins = mergedRecma;
|
|
39
|
-
return resolved;
|
|
39
|
+
export const isMdxFile = (filePath, extensions) => {
|
|
40
|
+
return extensions.some((ext) => filePath.endsWith(ext));
|
|
40
41
|
};
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
name: "ecopages-jsx-mdx-loader",
|
|
45
|
-
setup(build) {
|
|
46
|
-
build.onLoad({ filter }, async (args) => {
|
|
47
|
-
const { compile } = await import("@mdx-js/mdx");
|
|
48
|
-
const filePath = args.path.includes("?") ? args.path.split("?")[0] : args.path;
|
|
49
|
-
const source = await readFile(filePath, "utf-8");
|
|
50
|
-
const compiled = await compile(new VFile({ value: source, path: filePath }), compilerOptions);
|
|
51
|
-
return {
|
|
52
|
-
contents: String(compiled.value),
|
|
53
|
-
loader: "js",
|
|
54
|
-
resolveDir: path.dirname(filePath)
|
|
55
|
-
};
|
|
56
|
-
});
|
|
42
|
+
export const normalizeMdxPageModule = (file, module) => {
|
|
43
|
+
if (typeof module.default !== 'function') {
|
|
44
|
+
throw new TypeError('MDX file must export a callable default component.');
|
|
57
45
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const source = await readFile(args.path, "utf-8");
|
|
71
|
-
const compiled = await compile(new VFile({ value: source, path: args.path }), compilerOptions);
|
|
72
|
-
return { contents: String(compiled.value), loader: "js" };
|
|
73
|
-
});
|
|
46
|
+
const Page = module.default;
|
|
47
|
+
const normalizedConfig = {
|
|
48
|
+
...(module.config ?? Page.config ?? {}),
|
|
49
|
+
identity: module.config?.identity ??
|
|
50
|
+
Page.config?.identity ?? {
|
|
51
|
+
id: String(rapidhash(file)),
|
|
52
|
+
file,
|
|
53
|
+
integration: ECOPAGES_JSX_PLUGIN_NAME,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
if (module.layout && !(normalizedConfig.layouts && normalizedConfig.layouts.length > 0)) {
|
|
57
|
+
normalizedConfig.layout = module.layout;
|
|
74
58
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const normalizeMdxPageModule = (file, module) => {
|
|
81
|
-
if (typeof module.default !== "function") {
|
|
82
|
-
throw new TypeError("MDX file must export a callable default component.");
|
|
83
|
-
}
|
|
84
|
-
const Page = module.default;
|
|
85
|
-
const normalizedConfig = {
|
|
86
|
-
...module.config ?? Page.config ?? {},
|
|
87
|
-
...module.layout ? { layout: module.layout } : {},
|
|
88
|
-
__eco: module.config?.__eco ?? Page.config?.__eco ?? {
|
|
89
|
-
id: String(rapidhash(file)),
|
|
90
|
-
file,
|
|
91
|
-
integration: ECOPAGES_JSX_PLUGIN_NAME
|
|
59
|
+
ensurePageConfigLayouts(normalizedConfig);
|
|
60
|
+
const wrappedPage = async (props) => await Page(props);
|
|
61
|
+
wrappedPage.config = normalizedConfig;
|
|
62
|
+
if (module.getMetadata ?? Page.metadata) {
|
|
63
|
+
wrappedPage.metadata = module.getMetadata ?? Page.metadata;
|
|
92
64
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
...module,
|
|
101
|
-
default: wrappedPage,
|
|
102
|
-
config: normalizedConfig
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
|
-
export {
|
|
106
|
-
appendMdxExtensions,
|
|
107
|
-
createMdxExtensionFilter,
|
|
108
|
-
createMdxLoaderPlugin,
|
|
109
|
-
isMdxFile,
|
|
110
|
-
normalizeMdxPageModule,
|
|
111
|
-
registerBunMdxPlugin,
|
|
112
|
-
resolveMdxCompilerOptions
|
|
65
|
+
return {
|
|
66
|
+
...module,
|
|
67
|
+
default: wrappedPage,
|
|
68
|
+
config: normalizedConfig,
|
|
69
|
+
};
|
|
113
70
|
};
|
|
@@ -4,13 +4,15 @@ import { createMarkupNodeLike } from '@ecopages/jsx';
|
|
|
4
4
|
*
|
|
5
5
|
* @remarks
|
|
6
6
|
* Radiant's server bridge and light-DOM shim are loaded lazily because most JSX
|
|
7
|
-
* renders do not need them.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* renders do not need them. Resolved runtime modules are cached on the policy
|
|
8
|
+
* instance so repeated renders on the same renderer reuse one load without
|
|
9
|
+
* process-wide mutable class state. Light-DOM constructor installation remains
|
|
10
|
+
* process-global and idempotent via {@link ensureRadiantLightDomGlobals}; the
|
|
11
|
+
* Radiant installer also repairs incomplete DOM globals left by another runtime.
|
|
10
12
|
*/
|
|
11
13
|
export declare class EcopagesJsxRadiantSsrPolicy {
|
|
12
|
-
private
|
|
13
|
-
private
|
|
14
|
+
private runtimeModules;
|
|
15
|
+
private runtimeModulesPromise;
|
|
14
16
|
private readonly enabled;
|
|
15
17
|
constructor(enabled: boolean);
|
|
16
18
|
/**
|