@ecopages/react 0.2.0-alpha.39 → 0.2.0-alpha.40
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/react",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.40",
|
|
4
4
|
"description": "React integration for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -76,9 +76,5 @@
|
|
|
76
76
|
"oxc-transform": "^0.124.0",
|
|
77
77
|
"source-map": "^0.7.6",
|
|
78
78
|
"vfile": "^6.0.3"
|
|
79
|
-
},
|
|
80
|
-
"overrides": {
|
|
81
|
-
"react": "^19",
|
|
82
|
-
"react-dom": "^19"
|
|
83
79
|
}
|
|
84
80
|
}
|
package/src/react-renderer.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPageFile, IntegrationRendererRenderOptions, RouteRendererBody } from '@ecopages/core';
|
|
6
|
-
import { IntegrationRenderer, type RenderToResponseContext, type RouteModuleLoadOptions } from '@ecopages/core/route-renderer/integration-renderer';
|
|
7
|
-
import type { ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
6
|
+
import { IntegrationRenderer, type HtmlDocumentContribution, type HtmlDocumentContributionContext, type PageBrowserGraphContributionContext, type RenderToResponseContext, type RouteModuleLoadOptions } from '@ecopages/core/route-renderer/integration-renderer';
|
|
7
|
+
import type { AssetDefinition, ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
8
8
|
import type { ReactNode } from 'react';
|
|
9
9
|
import type { ReactRendererConfig } from './react.types.js';
|
|
10
10
|
import { ReactBundleService } from './services/react-bundle.service.js';
|
|
@@ -158,12 +158,10 @@ export declare class ReactRenderer extends IntegrationRenderer<ReactNode> {
|
|
|
158
158
|
private resolveQueuedForeignSubtreeHtml;
|
|
159
159
|
private buildHydrationProps;
|
|
160
160
|
/**
|
|
161
|
-
* Builds
|
|
162
|
-
*
|
|
163
|
-
* Router-backed React pages still need to publish the canonical page-data script
|
|
164
|
-
* even when the outer document shell belongs to another integration.
|
|
161
|
+
* Builds shared document html contributions for router-backed React pages rendered
|
|
162
|
+
* through a non-React HTML shell.
|
|
165
163
|
*/
|
|
166
|
-
private
|
|
164
|
+
private buildNonReactDocumentContributions;
|
|
167
165
|
/**
|
|
168
166
|
* Renders a foreign integration component that participates in React composition.
|
|
169
167
|
*
|
|
@@ -208,8 +206,9 @@ export declare class ReactRenderer extends IntegrationRenderer<ReactNode> {
|
|
|
208
206
|
protected usesIntegrationPageImporter(file: string): boolean;
|
|
209
207
|
protected importIntegrationPageFile(file: string, options?: RouteModuleLoadOptions): Promise<EcoPageFile>;
|
|
210
208
|
protected normalizeImportedPageFile<TPageModule extends EcoPageFile>(file: string, pageModule: TPageModule): TPageModule;
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
protected collectPageBrowserGraphContribution(context: PageBrowserGraphContributionContext): Promise<{
|
|
210
|
+
dependencies?: AssetDefinition[];
|
|
211
|
+
assets?: ProcessedAsset[];
|
|
213
212
|
}>;
|
|
214
213
|
/**
|
|
215
214
|
* Renders a full route response for the filesystem page pipeline.
|
|
@@ -220,6 +219,7 @@ export declare class ReactRenderer extends IntegrationRenderer<ReactNode> {
|
|
|
220
219
|
* React shell tree, and hand the result back as a document body.
|
|
221
220
|
*/
|
|
222
221
|
render({ params, query, props, locals, pageLocals, metadata, Page, Layout, HtmlTemplate, pageProps, }: IntegrationRendererRenderOptions<ReactNode>): Promise<RouteRendererBody>;
|
|
222
|
+
protected getHtmlDocumentContributions(options: HtmlDocumentContributionContext<ReactNode>): HtmlDocumentContribution[] | undefined;
|
|
223
223
|
protected getDocumentAttributes(): Record<string, string> | undefined;
|
|
224
224
|
/**
|
|
225
225
|
* Renders an arbitrary React view through the application's HTML shell.
|
package/src/react-renderer.js
CHANGED
|
@@ -178,8 +178,8 @@ class ReactRenderer extends IntegrationRenderer {
|
|
|
178
178
|
if (!filePath) {
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
|
-
const pageBrowserGraph = await this.
|
|
182
|
-
this.
|
|
181
|
+
const pageBrowserGraph = await this.resolvePageBrowserGraphForFile(filePath);
|
|
182
|
+
this.mergePageBrowserGraphIntoPagePackage(pageBrowserGraph);
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
185
|
* Renders a non-React layout or HTML template and enforces that mixed shells
|
|
@@ -304,18 +304,19 @@ class ReactRenderer extends IntegrationRenderer {
|
|
|
304
304
|
return hydrationProps;
|
|
305
305
|
}
|
|
306
306
|
/**
|
|
307
|
-
* Builds
|
|
308
|
-
*
|
|
309
|
-
* Router-backed React pages still need to publish the canonical page-data script
|
|
310
|
-
* even when the outer document shell belongs to another integration.
|
|
307
|
+
* Builds shared document html contributions for router-backed React pages rendered
|
|
308
|
+
* through a non-React HTML shell.
|
|
311
309
|
*/
|
|
312
|
-
|
|
310
|
+
buildNonReactDocumentContributions(htmlTemplate, pageProps) {
|
|
313
311
|
if (this.isReactManagedComponent(htmlTemplate) || !this.routerAdapter) {
|
|
314
312
|
return void 0;
|
|
315
313
|
}
|
|
316
|
-
return
|
|
317
|
-
|
|
318
|
-
|
|
314
|
+
return [
|
|
315
|
+
{
|
|
316
|
+
placement: "head-append",
|
|
317
|
+
html: this.pagePayloadService.buildRouterPageDataScript(pageProps)
|
|
318
|
+
}
|
|
319
|
+
];
|
|
319
320
|
}
|
|
320
321
|
/**
|
|
321
322
|
* Renders a foreign integration component that participates in React composition.
|
|
@@ -458,29 +459,30 @@ class ReactRenderer extends IntegrationRenderer {
|
|
|
458
459
|
config
|
|
459
460
|
};
|
|
460
461
|
}
|
|
461
|
-
async
|
|
462
|
+
async collectPageBrowserGraphContribution(context) {
|
|
462
463
|
try {
|
|
463
|
-
const pageModule =
|
|
464
|
+
const { file: pagePath, pageModule } = context;
|
|
464
465
|
const shouldHydrate = this.explicitGraphEnabled ? true : this.pageModuleService.shouldHydratePage(pageModule);
|
|
465
466
|
if (!shouldHydrate) {
|
|
466
467
|
return { assets: [] };
|
|
467
468
|
}
|
|
468
469
|
const isMdx = this.pageModuleService.isMdxFile(pagePath);
|
|
469
470
|
const declaredModules = this.pageModuleService.collectPageDeclaredModules(pageModule);
|
|
470
|
-
const
|
|
471
|
+
const dependencies = await this.hydrationAssetService.createPageBrowserGraphDependencies(
|
|
471
472
|
pagePath,
|
|
472
473
|
isMdx,
|
|
473
474
|
declaredModules
|
|
474
475
|
);
|
|
476
|
+
const assets = [];
|
|
475
477
|
if (isMdx) {
|
|
476
478
|
const mdxConfigAssets = await this.mdxConfigDependencyService.processMdxConfigDependencies({
|
|
477
479
|
pagePath,
|
|
478
480
|
config: pageModule.config,
|
|
479
481
|
processComponentDependencies: async (components) => await this.processComponentDependencies(components)
|
|
480
482
|
});
|
|
481
|
-
|
|
483
|
+
assets.push(...mdxConfigAssets);
|
|
482
484
|
}
|
|
483
|
-
return { assets
|
|
485
|
+
return { dependencies, assets };
|
|
484
486
|
} catch (error) {
|
|
485
487
|
if (error instanceof BundleError) {
|
|
486
488
|
console.error("[ecopages] Bundle errors:", error.logs);
|
|
@@ -529,13 +531,28 @@ class ReactRenderer extends IntegrationRenderer {
|
|
|
529
531
|
} : void 0,
|
|
530
532
|
htmlTemplate: HtmlTemplate,
|
|
531
533
|
metadata,
|
|
532
|
-
pageProps: allPageProps
|
|
533
|
-
documentProps: this.buildNonReactDocumentProps(HtmlTemplate, allPageProps)
|
|
534
|
+
pageProps: allPageProps
|
|
534
535
|
});
|
|
535
536
|
} catch (error) {
|
|
536
537
|
throw this.createRenderError("Failed to render component", error);
|
|
537
538
|
}
|
|
538
539
|
}
|
|
540
|
+
getHtmlDocumentContributions(options) {
|
|
541
|
+
if (options.partial || !options.renderOptions) {
|
|
542
|
+
return void 0;
|
|
543
|
+
}
|
|
544
|
+
const safeLocals = this.pagePayloadService.getSerializableLocals(
|
|
545
|
+
options.renderOptions.locals,
|
|
546
|
+
this.getComponentRequires(options.renderOptions.Page)
|
|
547
|
+
);
|
|
548
|
+
const allPageProps = this.pagePayloadService.buildSerializedPageProps({
|
|
549
|
+
pageProps: options.renderOptions.pageProps,
|
|
550
|
+
params: options.renderOptions.params,
|
|
551
|
+
query: options.renderOptions.query,
|
|
552
|
+
safeLocals
|
|
553
|
+
});
|
|
554
|
+
return this.buildNonReactDocumentContributions(options.renderOptions.HtmlTemplate, allPageProps);
|
|
555
|
+
}
|
|
539
556
|
getDocumentAttributes() {
|
|
540
557
|
return this.getRouterDocumentAttributes();
|
|
541
558
|
}
|
|
@@ -581,8 +598,7 @@ class ReactRenderer extends IntegrationRenderer {
|
|
|
581
598
|
component: HtmlTemplate,
|
|
582
599
|
props: {
|
|
583
600
|
metadata,
|
|
584
|
-
pageProps: normalizedProps
|
|
585
|
-
...this.buildNonReactDocumentProps(HtmlTemplate, normalizedProps) ?? {}
|
|
601
|
+
pageProps: normalizedProps
|
|
586
602
|
},
|
|
587
603
|
children: layoutRender?.html ?? viewRender.html
|
|
588
604
|
});
|
|
@@ -590,7 +606,8 @@ class ReactRenderer extends IntegrationRenderer {
|
|
|
590
606
|
const transformedHtml = await this.finalizeResolvedHtml({
|
|
591
607
|
html: `${this.DOC_TYPE}${documentRender.html}`,
|
|
592
608
|
partial: false,
|
|
593
|
-
documentAttributes: this.getRouterDocumentAttributes()
|
|
609
|
+
documentAttributes: this.getRouterDocumentAttributes(),
|
|
610
|
+
htmlContributions: this.buildNonReactDocumentContributions(HtmlTemplate, normalizedProps)
|
|
594
611
|
});
|
|
595
612
|
return this.createHtmlResponse(transformedHtml, ctx);
|
|
596
613
|
} catch (error) {
|
|
@@ -63,12 +63,20 @@ export declare class ReactHydrationAssetService {
|
|
|
63
63
|
*/
|
|
64
64
|
buildComponentRenderAssets(componentFile: string, config?: EcoComponentConfig): Promise<ProcessedAsset[]>;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Creates the Page Browser Graph dependency declarations for a React page.
|
|
67
67
|
*
|
|
68
68
|
* @param pagePath - Absolute file path of the page
|
|
69
69
|
* @param isMdx - Whether the page is an MDX file
|
|
70
70
|
* @param declaredModules - Explicitly declared browser module specifiers
|
|
71
|
-
* @returns
|
|
71
|
+
* @returns Declarative assets for core-owned processing
|
|
72
|
+
*/
|
|
73
|
+
createPageBrowserGraphDependencies(pagePath: string, isMdx: boolean, declaredModules: string[]): Promise<AssetDefinition[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Builds the Page Browser Graph assets for a React page.
|
|
76
|
+
*
|
|
77
|
+
* @remarks
|
|
78
|
+
* Kept as a compatibility wrapper while callers migrate to core-owned page
|
|
79
|
+
* graph assembly.
|
|
72
80
|
*/
|
|
73
81
|
buildPageBrowserGraphAssets(pagePath: string, isMdx: boolean, declaredModules: string[]): Promise<ProcessedAsset[]>;
|
|
74
82
|
}
|
|
@@ -146,14 +146,14 @@ class ReactHydrationAssetService {
|
|
|
146
146
|
return this.config.assetProcessingService.processDependencies(dependencies, componentName);
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
149
|
-
*
|
|
149
|
+
* Creates the Page Browser Graph dependency declarations for a React page.
|
|
150
150
|
*
|
|
151
151
|
* @param pagePath - Absolute file path of the page
|
|
152
152
|
* @param isMdx - Whether the page is an MDX file
|
|
153
153
|
* @param declaredModules - Explicitly declared browser module specifiers
|
|
154
|
-
* @returns
|
|
154
|
+
* @returns Declarative assets for core-owned processing
|
|
155
155
|
*/
|
|
156
|
-
async
|
|
156
|
+
async createPageBrowserGraphDependencies(pagePath, isMdx, declaredModules) {
|
|
157
157
|
const componentName = `ecopages-react-${rapidhash(pagePath)}`;
|
|
158
158
|
const hmrManager = this.config.assetProcessingService?.getHmrManager();
|
|
159
159
|
const isDevelopment = hmrManager?.isEnabled() ?? false;
|
|
@@ -180,6 +180,18 @@ class ReactHydrationAssetService {
|
|
|
180
180
|
useBrowserRuntimeImports,
|
|
181
181
|
isMdx
|
|
182
182
|
);
|
|
183
|
+
return dependencies;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Builds the Page Browser Graph assets for a React page.
|
|
187
|
+
*
|
|
188
|
+
* @remarks
|
|
189
|
+
* Kept as a compatibility wrapper while callers migrate to core-owned page
|
|
190
|
+
* graph assembly.
|
|
191
|
+
*/
|
|
192
|
+
async buildPageBrowserGraphAssets(pagePath, isMdx, declaredModules) {
|
|
193
|
+
const componentName = `ecopages-react-${rapidhash(pagePath)}`;
|
|
194
|
+
const dependencies = await this.createPageBrowserGraphDependencies(pagePath, isMdx, declaredModules);
|
|
183
195
|
if (!this.config.assetProcessingService) {
|
|
184
196
|
throw new Error("AssetProcessingService is not set");
|
|
185
197
|
}
|
|
@@ -38,7 +38,8 @@ class ReactPageModuleService {
|
|
|
38
38
|
const fileHash = fileSystem.hash(filePath);
|
|
39
39
|
const cacheScopeSuffix = options?.cacheScope ? `-${sanitizeCacheScope(options.cacheScope)}` : "";
|
|
40
40
|
const cacheBuster = options?.bypassCache || process?.env?.NODE_ENV === "development" ? `-${Date.now()}` : "";
|
|
41
|
-
const outputFileName = `${fileBaseName}-${fileHash}${cacheScopeSuffix}${cacheBuster}.
|
|
41
|
+
const outputFileName = `${fileBaseName}-${fileHash}${cacheScopeSuffix}${cacheBuster}.mjs`;
|
|
42
|
+
const outputNamingTemplate = `${fileBaseName}-${fileHash}${cacheScopeSuffix}${cacheBuster}.[ext]`;
|
|
42
43
|
const buildResult = await build(
|
|
43
44
|
{
|
|
44
45
|
entrypoints: [filePath],
|
|
@@ -50,7 +51,7 @@ class ReactPageModuleService {
|
|
|
50
51
|
splitting: false,
|
|
51
52
|
minify: false,
|
|
52
53
|
treeshaking: false,
|
|
53
|
-
naming:
|
|
54
|
+
naming: outputNamingTemplate,
|
|
54
55
|
plugins: [mdxPlugin]
|
|
55
56
|
},
|
|
56
57
|
this.config.buildExecutor
|
|
@@ -60,7 +61,7 @@ class ReactPageModuleService {
|
|
|
60
61
|
throw new Error(`Failed to compile MDX page module: ${details}`);
|
|
61
62
|
}
|
|
62
63
|
const preferredOutputPath = path.join(outdir, outputFileName);
|
|
63
|
-
const compiledOutput = buildResult.outputs.find((output) => output.path === preferredOutputPath)?.path ?? buildResult.outputs.find((output) => output.path
|
|
64
|
+
const compiledOutput = buildResult.outputs.find((output) => output.path === preferredOutputPath)?.path ?? buildResult.outputs.find((output) => /\.(?:[cm]?js)$/u.test(output.path))?.path;
|
|
64
65
|
if (!compiledOutput) {
|
|
65
66
|
throw new Error(`No compiled MDX output generated for page: ${filePath}`);
|
|
66
67
|
}
|