@ecopages/core 0.2.0-beta.37 → 0.2.0-beta.39
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 +1 -0
- package/package.json +35 -3
- package/src/adapters/bun/server-adapter.js +1 -0
- package/src/adapters/node/server-adapter.js +1 -0
- package/src/adapters/shared/http/explicit-static-render-preparation.js +1 -1
- package/src/adapters/shared/http/explicit-static-route-matcher.js +1 -1
- package/src/adapters/shared/http/fs-server-response-matcher.js +15 -7
- package/src/adapters/shared/runtime/collect-dev-prewarm-plan.d.ts +11 -0
- package/src/adapters/shared/runtime/collect-dev-prewarm-plan.js +29 -0
- package/src/adapters/shared/runtime/dev-static-route-prewarm.d.ts +16 -0
- package/src/adapters/shared/runtime/dev-static-route-prewarm.js +66 -0
- package/src/adapters/shared/runtime/render-context.js +1 -1
- package/src/adapters/shared/runtime/server-adapter.d.ts +7 -1
- package/src/adapters/shared/runtime/server-adapter.js +64 -5
- package/src/build/README.md +1 -1
- package/src/build/browser/browser-runtime-plugin.js +2 -5
- package/src/build/cache/production-build-cache.d.ts +3 -2
- package/src/build/cache/production-build-cache.js +2 -2
- package/src/build/contracts/content-virtual-modules.d.ts +12 -0
- package/src/build/contracts/content-virtual-modules.js +29 -0
- package/src/build/rolldown/rolldown-adapter-helpers.js +1 -1
- package/src/build/runtime/build-request-policy.d.ts +3 -4
- package/src/build/runtime/build-request-policy.js +4 -4
- package/src/cache/index.d.ts +2 -1
- package/src/cache/index.js +2 -1
- package/src/cache/module-parse-cache.d.ts +5 -0
- package/src/cache/module-parse-cache.js +27 -3
- package/src/cache/module-transform-profiler.d.ts +8 -0
- package/src/cache/module-transform-profiler.js +8 -0
- package/src/client/view-transitions.d.ts +17 -1
- package/src/client/view-transitions.js +45 -1
- package/src/client/view-transitions.test.browser.d.ts +1 -0
- package/src/client/view-transitions.test.browser.js +56 -0
- package/src/config/config-builder.d.ts +16 -9
- package/src/config/config-builder.js +24 -14
- package/src/dev/transform-server/dev-transform-vendor-registry.d.ts +1 -0
- package/src/dev/transform-server/dev-transform-vendor-registry.js +15 -2
- package/src/diagnostics/request-pipeline-metrics.d.ts +38 -0
- package/src/diagnostics/request-pipeline-metrics.js +121 -0
- package/src/eco/component-identity.d.ts +13 -0
- package/src/eco/component-identity.js +18 -0
- package/src/eco/eco-declared-component.d.ts +3 -2
- package/src/eco/eco-declared-component.js +6 -4
- package/src/eco/eco.browser.js +11 -4
- package/src/eco/eco.js +15 -8
- package/src/eco/eco.types.d.ts +34 -6
- package/src/errors/http-error.d.ts +8 -0
- package/src/errors/http-error.js +16 -0
- package/src/errors/undeclared-component-dependency-error.d.ts +1 -1
- package/src/errors/undeclared-component-dependency-error.js +2 -2
- package/src/index.browser.d.ts +1 -0
- package/src/index.browser.js +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/plugins/README.md +2 -1
- package/src/plugins/eco-component-meta-plugin.d.ts +6 -102
- package/src/plugins/eco-component-meta-plugin.js +110 -391
- package/src/plugins/processor.d.ts +10 -0
- package/src/plugins/processor.js +9 -0
- package/src/route-renderer/README.md +12 -9
- package/src/route-renderer/orchestration/document-shell/document-shell-render.service.js +1 -1
- package/src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js +1 -1
- package/src/route-renderer/orchestration/integration-renderer.d.ts +19 -11
- package/src/route-renderer/orchestration/integration-renderer.js +95 -27
- package/src/route-renderer/orchestration/ownership-graph/component-graph-collectors.js +5 -4
- package/src/route-renderer/orchestration/ownership-graph/component-graph.js +6 -4
- package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.d.ts +0 -1
- package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.js +10 -19
- package/src/route-renderer/orchestration/page-browser-graph/grouped-graph-build-plan.d.ts +15 -0
- package/src/route-renderer/orchestration/page-browser-graph/grouped-graph-build-plan.js +9 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.merge.d.ts +5 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.merge.js +30 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +23 -6
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +156 -67
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.d.ts +18 -2
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +63 -39
- package/src/route-renderer/orchestration/page-browser-graph/route-instance-key.d.ts +29 -0
- package/src/route-renderer/orchestration/page-browser-graph/route-instance-key.js +54 -0
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.d.ts +3 -0
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.js +3 -2
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.d.ts +1 -0
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +3 -5
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.d.ts +14 -4
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +91 -32
- package/src/route-renderer/page-loading/component-dependency-collection.js +3 -2
- package/src/route-renderer/page-loading/ecopages-virtual-imports.d.ts +0 -5
- package/src/route-renderer/page-loading/ecopages-virtual-imports.js +4 -10
- package/src/route-renderer/page-loading/file-scoped-dependency-components.d.ts +42 -0
- package/src/route-renderer/page-loading/file-scoped-dependency-components.js +94 -0
- package/src/route-renderer/page-loading/lazy-trigger-planning.js +2 -1
- package/src/route-renderer/page-loading/page-module-loader.d.ts +2 -1
- package/src/route-renderer/page-loading/page-module-loader.js +9 -3
- package/src/route-renderer/page-loading/resolved-page-dependencies.d.ts +12 -0
- package/src/route-renderer/page-loading/resolved-page-dependencies.js +38 -0
- package/src/router/README.md +8 -0
- package/src/services/README.md +5 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.d.ts +1 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.js +22 -5
- package/src/services/cache/cache.types.d.ts +4 -2
- package/src/services/cache/html-page-cache-dependency-index.d.ts +34 -0
- package/src/services/cache/html-page-cache-dependency-index.js +88 -0
- package/src/services/cache/index.d.ts +2 -0
- package/src/services/cache/index.js +2 -0
- package/src/services/cache/page-cache-service.d.ts +31 -1
- package/src/services/cache/page-cache-service.js +79 -3
- package/src/services/cache/page-request-cache-coordinator.service.js +4 -0
- package/src/services/module-loading/README.md +9 -6
- package/src/services/module-loading/app-server-module-transpiler.service.js +0 -3
- package/src/services/module-loading/collection-server-module-build.service.d.ts +19 -0
- package/src/services/module-loading/collection-server-module-build.service.js +83 -0
- package/src/services/module-loading/page-module-import.service.d.ts +5 -11
- package/src/services/module-loading/page-module-import.service.js +56 -73
- package/src/services/module-loading/route-module-build-cache.store.js +6 -6
- package/src/services/module-loading/route-module-build-manifest.d.ts +8 -5
- package/src/services/module-loading/route-module-build-manifest.js +20 -32
- package/src/services/module-loading/server-module-transpiler.service.d.ts +0 -2
- package/src/services/module-loading/server-module-transpiler.service.js +0 -3
- package/src/static-site-generator/production-page-browser-graph-prebuild.d.ts +10 -6
- package/src/static-site-generator/production-page-browser-graph-prebuild.js +29 -10
- package/src/static-site-generator/static-site-generator.js +6 -3
- package/src/types/internal-types.d.ts +4 -0
- package/src/types/public-types.d.ts +27 -19
- package/src/watchers/project-watcher.js +14 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.d.ts +0 -9
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.js +0 -11
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import type { EcoPagesAppConfig, IHmrManager } from '../../types/internal-types.js';
|
|
7
|
-
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPageFile, EcoPagesElement, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
7
|
+
import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoComponentDependencies, EcoPageFile, EcoPagesElement, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
8
8
|
import { type AssetProcessingService, type ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
|
|
9
9
|
import { HtmlTransformerService } from '../../services/html/html-transformer.service.js';
|
|
10
10
|
import type { HtmlDocumentContribution } from '../../services/html/html-transformer.service.js';
|
|
@@ -12,18 +12,15 @@ import { HttpError } from '../../errors/http-error.js';
|
|
|
12
12
|
import { DependencyResolverService } from '../page-loading/dependency-resolver.js';
|
|
13
13
|
import { PageModuleLoaderService } from '../page-loading/page-module-loader.js';
|
|
14
14
|
import { RouteRenderOrchestrator, type RouteRenderOrchestratorAdapter, type RouteRenderOrchestratorResolvedInputs } from './route-pipeline/route-render-orchestrator.js';
|
|
15
|
+
import { type ResolvedPageDependencies } from '../page-loading/resolved-page-dependencies.js';
|
|
16
|
+
import { type GroupedGraphBuildPlan } from './page-browser-graph/grouped-graph-build-plan.js';
|
|
15
17
|
import type { ForeignChildRuntime } from './foreign-child/component-render-context.js';
|
|
16
18
|
import { ForeignSubtreeExecutionService } from './foreign-child/foreign-subtree-execution.service.js';
|
|
17
19
|
/**
|
|
18
20
|
* Controls how one route module is loaded outside the normal render path.
|
|
19
|
-
*
|
|
20
|
-
* Request-time metadata inspection and static-generation probes use these
|
|
21
|
-
* options to isolate their module identity from the main render cache while
|
|
22
|
-
* still going through the owning integration's import setup.
|
|
23
21
|
*/
|
|
24
22
|
export type RouteModuleLoadOptions = {
|
|
25
23
|
bypassCache?: boolean;
|
|
26
|
-
cacheScope?: string;
|
|
27
24
|
};
|
|
28
25
|
/**
|
|
29
26
|
* Context for renderToResponse method.
|
|
@@ -72,9 +69,19 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
|
|
|
72
69
|
*/
|
|
73
70
|
protected ensureIntegrationRuntimeActivated(): Promise<void>;
|
|
74
71
|
/**
|
|
75
|
-
* Prebuilds the production Page Browser Graph for one route file.
|
|
72
|
+
* Prebuilds the production Page Browser Graph for one route file and optional params.
|
|
76
73
|
*/
|
|
77
|
-
prebuildProductionPageBrowserGraph(routeFile: string
|
|
74
|
+
prebuildProductionPageBrowserGraph(routeFile: string, options?: Pick<RouteRendererOptions, 'params' | 'query'> & {
|
|
75
|
+
groupedBuildPlan?: GroupedGraphBuildPlan;
|
|
76
|
+
}): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Builds one production grouped graph plan for the supplied static route instances.
|
|
79
|
+
*/
|
|
80
|
+
buildGroupedGraphBuildPlan(instances: ReadonlyArray<{
|
|
81
|
+
routeFile: string;
|
|
82
|
+
params?: RouteRendererOptions['params'];
|
|
83
|
+
query?: RouteRendererOptions['query'];
|
|
84
|
+
}>): Promise<GroupedGraphBuildPlan>;
|
|
78
85
|
/**
|
|
79
86
|
* Loads one route module through the owning renderer's import path.
|
|
80
87
|
*
|
|
@@ -118,6 +125,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
|
|
|
118
125
|
*/
|
|
119
126
|
protected prepareViewDependencies(view: EcoComponent, layout?: EcoComponent): Promise<ProcessedAsset[]>;
|
|
120
127
|
protected resolvePageBrowserGraphForFile(filePath: string): Promise<PageBrowserGraphResult | undefined>;
|
|
128
|
+
protected resolvePageBrowserGraphForRoute(filePath: string, routeOptions?: Pick<RouteRendererOptions, 'params' | 'query'>, groupedBuildPlan?: GroupedGraphBuildPlan): Promise<PageBrowserGraphResult | undefined>;
|
|
121
129
|
/**
|
|
122
130
|
* Merges component-scoped assets into the active HTML transformer state.
|
|
123
131
|
*
|
|
@@ -226,12 +234,9 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
|
|
|
226
234
|
* @returns The HTML template component.
|
|
227
235
|
*/
|
|
228
236
|
protected getHtmlTemplate(): Promise<EcoComponent<HtmlTemplateProps>>;
|
|
229
|
-
protected usesIntegrationPageImporter(_file: string): boolean;
|
|
230
|
-
protected importIntegrationPageFile(_file: string, _options?: RouteModuleLoadOptions): Promise<EcoPageFile>;
|
|
231
237
|
protected normalizeImportedPageFile<TPageModule extends EcoPageFile>(_file: string, pageModule: TPageModule): TPageModule;
|
|
232
238
|
/**
|
|
233
239
|
* Imports the page file from the specified path.
|
|
234
|
-
* It uses dynamic import to load the file and returns the imported module.
|
|
235
240
|
*
|
|
236
241
|
* @param file - The file path to import.
|
|
237
242
|
* @returns The imported module.
|
|
@@ -270,6 +275,9 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
|
|
|
270
275
|
*/
|
|
271
276
|
protected createRouteRenderOrchestratorAdapter(): RouteRenderOrchestratorAdapter<C>;
|
|
272
277
|
protected resolveRouteRenderInputs(routeOptions: RouteRendererOptions): Promise<RouteRenderOrchestratorResolvedInputs>;
|
|
278
|
+
protected buildPageBrowserGraphContributionContext(routeFile: string, routeOptions?: Pick<RouteRendererOptions, 'params' | 'query'>): Promise<PageBrowserGraphContributionContext>;
|
|
279
|
+
protected resolvePageDependencies(context: PageBrowserGraphContributionContext): Promise<ResolvedPageDependencies | undefined>;
|
|
280
|
+
protected resolvePageBrowserGraphContributionFromDependencies(dependencies: EcoComponentDependencies, ownerFile: string): Promise<PageBrowserGraphContribution | undefined>;
|
|
273
281
|
protected resolveRouteDependencies(input: {
|
|
274
282
|
components: (EcoComponent | Partial<EcoComponent>)[];
|
|
275
283
|
}): Promise<{
|
|
@@ -13,7 +13,11 @@ import { OwnershipValidationService } from './ownership-graph/ownership-validati
|
|
|
13
13
|
import { hasForeignChildDescendantsInGraph } from './ownership-graph/component-graph-collectors.js';
|
|
14
14
|
import { RouteRenderOrchestrator, } from './route-pipeline/route-render-orchestrator.js';
|
|
15
15
|
import { createIntegrationRouteRenderAdapter } from './route-pipeline/integration-route-render-adapter.js';
|
|
16
|
-
import {
|
|
16
|
+
import { createPageDependencyInstanceKey } from './page-browser-graph/route-instance-key.js';
|
|
17
|
+
import { mergePageBrowserGraphContributions } from './page-browser-graph/page-browser-graph-contribution.merge.js';
|
|
18
|
+
import { collectDependencyWatchPaths, collectFileScopedDependencyComponents, } from '../page-loading/file-scoped-dependency-components.js';
|
|
19
|
+
import { resolvePageDependenciesFromContext, } from '../page-loading/resolved-page-dependencies.js';
|
|
20
|
+
import { createGroupedGraphBuildPlanKey, } from './page-browser-graph/grouped-graph-build-plan.js';
|
|
17
21
|
import { normalizeUnresolvedMarkerArtifactHtml } from './route-pipeline/marker-artifact.utils.js';
|
|
18
22
|
import { isMarkupNodeLike } from './foreign-child/foreign-child-output.utils.js';
|
|
19
23
|
import { getForeignSubtreeResolutionContextKey, getForeignSubtreeTokenPrefix, resolveOwningIntegrationRenderer, } from './foreign-child/owning-renderer-resolution.js';
|
|
@@ -66,11 +70,30 @@ export class IntegrationRenderer {
|
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
69
|
-
* Prebuilds the production Page Browser Graph for one route file.
|
|
73
|
+
* Prebuilds the production Page Browser Graph for one route file and optional params.
|
|
70
74
|
*/
|
|
71
|
-
async prebuildProductionPageBrowserGraph(routeFile) {
|
|
75
|
+
async prebuildProductionPageBrowserGraph(routeFile, options) {
|
|
72
76
|
await this.ensureIntegrationRuntimeActivated();
|
|
73
|
-
await this.
|
|
77
|
+
await this.resolvePageBrowserGraphForRoute(routeFile, options, options?.groupedBuildPlan);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Builds one production grouped graph plan for the supplied static route instances.
|
|
81
|
+
*/
|
|
82
|
+
async buildGroupedGraphBuildPlan(instances) {
|
|
83
|
+
const builtInstances = await Promise.all(instances.map(async (routeInstance) => {
|
|
84
|
+
const context = await this.buildPageBrowserGraphContributionContext(routeInstance.routeFile, routeInstance);
|
|
85
|
+
const resolvedPageDependencies = await this.resolvePageDependencies(context);
|
|
86
|
+
return {
|
|
87
|
+
routeFile: routeInstance.routeFile,
|
|
88
|
+
dependencyInstanceKey: context.dependencyInstanceKey ?? '',
|
|
89
|
+
contribution: mergePageBrowserGraphContributions(await this.collectPageBrowserGraphContribution(context), resolvedPageDependencies?.contribution),
|
|
90
|
+
};
|
|
91
|
+
}));
|
|
92
|
+
return {
|
|
93
|
+
integrationName: this.name,
|
|
94
|
+
planKey: createGroupedGraphBuildPlanKey(this.name, builtInstances),
|
|
95
|
+
instances: builtInstances,
|
|
96
|
+
};
|
|
74
97
|
}
|
|
75
98
|
/**
|
|
76
99
|
* Loads one route module through the owning renderer's import path.
|
|
@@ -173,10 +196,23 @@ export class IntegrationRenderer {
|
|
|
173
196
|
return resolvedDependencies;
|
|
174
197
|
}
|
|
175
198
|
async resolvePageBrowserGraphForFile(filePath) {
|
|
199
|
+
/**
|
|
200
|
+
* @remarks
|
|
201
|
+
* Integration renderers such as React still call this file-only entry point
|
|
202
|
+
* during explicit view rendering. Route-instance-aware callers should prefer
|
|
203
|
+
* `resolvePageBrowserGraphForRoute`.
|
|
204
|
+
*/
|
|
205
|
+
return this.resolvePageBrowserGraphForRoute(filePath);
|
|
206
|
+
}
|
|
207
|
+
async resolvePageBrowserGraphForRoute(filePath, routeOptions, groupedBuildPlan) {
|
|
208
|
+
const graphContext = await this.buildPageBrowserGraphContributionContext(filePath, routeOptions);
|
|
209
|
+
const resolvedPageDependencies = await this.resolvePageDependencies(graphContext);
|
|
176
210
|
return await this.routeRenderOrchestrator.resolveDeclaredPageBrowserGraph({
|
|
177
211
|
routeFile: filePath,
|
|
212
|
+
dependencyInstanceKey: graphContext.dependencyInstanceKey,
|
|
178
213
|
integrationName: this.name,
|
|
179
|
-
|
|
214
|
+
groupedBuildPlan,
|
|
215
|
+
collectContribution: async () => mergePageBrowserGraphContributions(await this.collectPageBrowserGraphContribution(graphContext), resolvedPageDependencies?.contribution),
|
|
180
216
|
});
|
|
181
217
|
}
|
|
182
218
|
/**
|
|
@@ -330,14 +366,14 @@ export class IntegrationRenderer {
|
|
|
330
366
|
? input.children.outerHTML
|
|
331
367
|
: undefined;
|
|
332
368
|
if (input.children !== undefined && serializedChildren === undefined) {
|
|
333
|
-
const componentFile = input.component.config?.
|
|
369
|
+
const componentFile = input.component.config?.identity?.file ?? 'unknown component';
|
|
334
370
|
const childTag = Object.prototype.toString.call(input.children);
|
|
335
371
|
throw new TypeError(`[ecopages] ${this.name} renderer expected serialized children for ${componentFile}, received ${childTag}.`);
|
|
336
372
|
}
|
|
337
373
|
const props = serializedChildren === undefined ? input.props : { ...input.props, children: serializedChildren };
|
|
338
374
|
const content = await component(props);
|
|
339
375
|
if (typeof content !== 'string') {
|
|
340
|
-
const componentFile = input.component.config?.
|
|
376
|
+
const componentFile = input.component.config?.identity?.file ?? 'unknown component';
|
|
341
377
|
const contentTag = Object.prototype.toString.call(content);
|
|
342
378
|
throw new TypeError(`[ecopages] ${this.name} renderer expected a string render result for ${componentFile}, received ${contentTag}.`);
|
|
343
379
|
}
|
|
@@ -417,33 +453,19 @@ export class IntegrationRenderer {
|
|
|
417
453
|
invariant(false, `Error importing HtmlTemplate: ${error}`);
|
|
418
454
|
}
|
|
419
455
|
}
|
|
420
|
-
usesIntegrationPageImporter(_file) {
|
|
421
|
-
return false;
|
|
422
|
-
}
|
|
423
|
-
async importIntegrationPageFile(_file, _options) {
|
|
424
|
-
invariant(false, 'Integration page importer must be implemented when enabled');
|
|
425
|
-
}
|
|
426
456
|
normalizeImportedPageFile(_file, pageModule) {
|
|
427
457
|
return pageModule;
|
|
428
458
|
}
|
|
429
459
|
/**
|
|
430
460
|
* Imports the page file from the specified path.
|
|
431
|
-
* It uses dynamic import to load the file and returns the imported module.
|
|
432
461
|
*
|
|
433
462
|
* @param file - The file path to import.
|
|
434
463
|
* @returns The imported module.
|
|
435
464
|
*/
|
|
436
465
|
async importPageFile(file, options) {
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
bypassCache,
|
|
441
|
-
cacheScope: options?.cacheScope,
|
|
442
|
-
})
|
|
443
|
-
: await this.pageModuleLoaderService.importPageFile(file, {
|
|
444
|
-
bypassCache,
|
|
445
|
-
cacheScope: options?.cacheScope,
|
|
446
|
-
});
|
|
466
|
+
const pageModule = await this.pageModuleLoaderService.importPageFile(file, {
|
|
467
|
+
bypassCache: options?.bypassCache,
|
|
468
|
+
});
|
|
447
469
|
return this.normalizeImportedPageFile(file, pageModule);
|
|
448
470
|
}
|
|
449
471
|
/**
|
|
@@ -494,6 +516,8 @@ export class IntegrationRenderer {
|
|
|
494
516
|
resolveRouteDependencies: (input) => this.resolveRouteDependencies(input),
|
|
495
517
|
importPageFile: (file) => this.importPageFile(file),
|
|
496
518
|
collectPageBrowserGraphContribution: (context) => this.collectPageBrowserGraphContribution(context),
|
|
519
|
+
resolvePageDependencies: (context) => this.resolvePageDependencies(context),
|
|
520
|
+
buildPageBrowserGraphContributionContext: (routeFile, routeOptions) => this.buildPageBrowserGraphContributionContext(routeFile, routeOptions),
|
|
497
521
|
renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
|
|
498
522
|
getDocumentAttributes: (renderOptions) => this.getDocumentAttributes(renderOptions),
|
|
499
523
|
getHtmlDocumentContributions: (options) => this.getHtmlDocumentContributions(options),
|
|
@@ -506,20 +530,22 @@ export class IntegrationRenderer {
|
|
|
506
530
|
});
|
|
507
531
|
}
|
|
508
532
|
async resolveRouteRenderInputs(routeOptions) {
|
|
509
|
-
const
|
|
533
|
+
const resolvedPageModule = await this.pageModuleLoaderService.resolvePageModule({
|
|
510
534
|
file: routeOptions.file,
|
|
535
|
+
pageModule: routeOptions.pageModule,
|
|
511
536
|
importPageFileFn: (targetFile) => this.importPageFile(targetFile),
|
|
512
537
|
});
|
|
513
|
-
const { Page, integrationSpecificProps } =
|
|
538
|
+
const { Page, integrationSpecificProps, module: pageModule } = resolvedPageModule;
|
|
514
539
|
const HtmlTemplate = await this.getHtmlTemplate();
|
|
515
540
|
const Layouts = resolvePageLayoutComponents(Page.config?.layouts);
|
|
516
541
|
const Layout = resolveInnermostPageLayout(Layouts);
|
|
517
542
|
const { props, metadata } = await this.pageModuleLoaderService.resolvePageData({
|
|
518
|
-
pageModule,
|
|
543
|
+
pageModule: resolvedPageModule,
|
|
519
544
|
routeOptions,
|
|
520
545
|
});
|
|
521
546
|
return {
|
|
522
547
|
Page,
|
|
548
|
+
pageModule,
|
|
523
549
|
HtmlTemplate: HtmlTemplate,
|
|
524
550
|
Layouts,
|
|
525
551
|
Layout,
|
|
@@ -529,6 +555,48 @@ export class IntegrationRenderer {
|
|
|
529
555
|
integrationSpecificProps,
|
|
530
556
|
};
|
|
531
557
|
}
|
|
558
|
+
async buildPageBrowserGraphContributionContext(routeFile, routeOptions) {
|
|
559
|
+
const { module: pageModule, ...resolvedPageModule } = await this.pageModuleLoaderService.resolvePageModule({
|
|
560
|
+
file: routeFile,
|
|
561
|
+
importPageFileFn: (targetFile) => this.importPageFile(targetFile),
|
|
562
|
+
});
|
|
563
|
+
const { props } = await this.pageModuleLoaderService.resolvePageData({
|
|
564
|
+
pageModule: resolvedPageModule,
|
|
565
|
+
routeOptions: {
|
|
566
|
+
file: routeFile,
|
|
567
|
+
params: routeOptions?.params,
|
|
568
|
+
query: routeOptions?.query,
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
return {
|
|
572
|
+
file: routeFile,
|
|
573
|
+
pageModule,
|
|
574
|
+
props,
|
|
575
|
+
params: routeOptions?.params,
|
|
576
|
+
query: routeOptions?.query,
|
|
577
|
+
dependencyInstanceKey: createPageDependencyInstanceKey({
|
|
578
|
+
params: routeOptions?.params,
|
|
579
|
+
query: routeOptions?.query,
|
|
580
|
+
}),
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
async resolvePageDependencies(context) {
|
|
584
|
+
return resolvePageDependenciesFromContext(context, this.name, (dependencies, ownerFile) => this.resolvePageBrowserGraphContributionFromDependencies(dependencies, ownerFile));
|
|
585
|
+
}
|
|
586
|
+
async resolvePageBrowserGraphContributionFromDependencies(dependencies, ownerFile) {
|
|
587
|
+
const components = collectFileScopedDependencyComponents({
|
|
588
|
+
ownerFile,
|
|
589
|
+
integrationName: this.name,
|
|
590
|
+
dependencies,
|
|
591
|
+
});
|
|
592
|
+
if (components.length === 0) {
|
|
593
|
+
return undefined;
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
assets: await this.processComponentDependencies(components),
|
|
597
|
+
watchPaths: collectDependencyWatchPaths(ownerFile, components),
|
|
598
|
+
};
|
|
599
|
+
}
|
|
532
600
|
async resolveRouteDependencies(input) {
|
|
533
601
|
return {
|
|
534
602
|
resolvedDependencies: await this.resolveDependencies(input.components),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { AssetFactory } from '../../../services/assets/asset-processing-service/index.js';
|
|
3
3
|
import { walkComponentGraph } from './component-graph.js';
|
|
4
|
+
import { getComponentIdentity } from '../../../eco/component-identity.js';
|
|
4
5
|
function toGraphRoots(components) {
|
|
5
6
|
return components.filter(Boolean).map((component) => ({ component }));
|
|
6
7
|
}
|
|
@@ -10,7 +11,7 @@ export function collectIntegrationNamesFromGraph(components, currentIntegrationN
|
|
|
10
11
|
roots: toGraphRoots(components),
|
|
11
12
|
currentIntegrationName,
|
|
12
13
|
onComponent: ({ component }) => {
|
|
13
|
-
const integrationName = component
|
|
14
|
+
const integrationName = getComponentIdentity(component)?.integration ?? component.config?.integration;
|
|
14
15
|
if (integrationName) {
|
|
15
16
|
integrationNames.add(integrationName);
|
|
16
17
|
}
|
|
@@ -41,7 +42,7 @@ export function hasForeignChildDescendantsInGraph(component, currentIntegrationN
|
|
|
41
42
|
if (foundForeign) {
|
|
42
43
|
return false;
|
|
43
44
|
}
|
|
44
|
-
const integrationName = currentComponent
|
|
45
|
+
const integrationName = getComponentIdentity(currentComponent)?.integration ?? currentComponent.config?.integration;
|
|
45
46
|
if (integrationName && integrationName !== currentIntegrationName) {
|
|
46
47
|
foundForeign = true;
|
|
47
48
|
return false;
|
|
@@ -63,8 +64,8 @@ export function collectEagerSsrLazyScriptDefinitions(components) {
|
|
|
63
64
|
currentIntegrationName: '',
|
|
64
65
|
visitLayout: true,
|
|
65
66
|
onConfig: (config) => {
|
|
66
|
-
const componentFile = config?.
|
|
67
|
-
if (!componentFile) {
|
|
67
|
+
const componentFile = getComponentIdentity(config)?.file;
|
|
68
|
+
if (!componentFile || !config) {
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
71
|
const componentDir = path.dirname(componentFile);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getComponentIdentity } from '../../../eco/component-identity.js';
|
|
1
2
|
export function walkComponentGraph(input) {
|
|
2
3
|
const seenComponents = new Set();
|
|
3
4
|
const seenConfigs = new Set();
|
|
@@ -10,7 +11,8 @@ export function walkComponentGraph(input) {
|
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
13
|
seenComponents.add(ecoComponent);
|
|
13
|
-
const
|
|
14
|
+
const identity = getComponentIdentity(ecoComponent);
|
|
15
|
+
const integrationName = ecoComponent.config?.integration ?? identity?.integration ?? parentIntegrationName;
|
|
14
16
|
if (input.onConfig && ecoComponent.config && !seenConfigs.has(ecoComponent.config)) {
|
|
15
17
|
seenConfigs.add(ecoComponent.config);
|
|
16
18
|
input.onConfig(ecoComponent.config);
|
|
@@ -62,10 +64,10 @@ export function walkComponentGraph(input) {
|
|
|
62
64
|
export function mapComponentGraph(input) {
|
|
63
65
|
let nextSyntheticId = 0;
|
|
64
66
|
const mapComponent = (component, source, parentIntegrationName, lineage) => {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
+
const identity = getComponentIdentity(component);
|
|
68
|
+
const integrationName = component.config?.integration ?? identity?.integration ?? parentIntegrationName;
|
|
67
69
|
const isForeignToParent = integrationName !== parentIntegrationName;
|
|
68
|
-
const componentId =
|
|
70
|
+
const componentId = identity?.id ?? identity?.file ?? `${source}:${(nextSyntheticId += 1)}`;
|
|
69
71
|
const nextLineage = new Set(lineage);
|
|
70
72
|
nextLineage.add(component);
|
|
71
73
|
const children = (component.config?.dependencies?.components ?? []).flatMap((child) => {
|
|
@@ -24,7 +24,6 @@ export declare class OwnershipValidationService {
|
|
|
24
24
|
* Validates foreign ownership edges reachable from the supplied route roots.
|
|
25
25
|
*/
|
|
26
26
|
validate(input: OwnershipValidationInput): OwnershipValidationError[];
|
|
27
|
-
private assertDeclaredComponentDependencies;
|
|
28
27
|
private isRegisteredIntegration;
|
|
29
28
|
}
|
|
30
29
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assertEcoDeclaredComponent } from '../../../eco/eco-declared-component.js';
|
|
2
|
-
import {
|
|
2
|
+
import { getComponentIdentity } from '../../../eco/component-identity.js';
|
|
3
|
+
import { mapComponentGraph } from './component-graph.js';
|
|
3
4
|
/**
|
|
4
5
|
* Validates foreign ownership as soon as the route root graph is loaded.
|
|
5
6
|
*
|
|
@@ -19,12 +20,17 @@ export class OwnershipValidationService {
|
|
|
19
20
|
* Validates foreign ownership edges reachable from the supplied route roots.
|
|
20
21
|
*/
|
|
21
22
|
validate(input) {
|
|
22
|
-
this.assertDeclaredComponentDependencies(input);
|
|
23
23
|
return mapComponentGraph({
|
|
24
24
|
roots: input.roots,
|
|
25
25
|
currentIntegrationName: input.currentIntegrationName,
|
|
26
26
|
mapNode: ({ component, integrationName, componentId, isForeignToParent }, children) => {
|
|
27
|
-
const
|
|
27
|
+
const parentFile = getComponentIdentity(component)?.file;
|
|
28
|
+
for (const child of component.config?.dependencies?.components ?? []) {
|
|
29
|
+
if (child) {
|
|
30
|
+
assertEcoDeclaredComponent(child, { parentComponentFile: parentFile });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const componentMeta = getComponentIdentity(component);
|
|
28
34
|
const errors = children.flat();
|
|
29
35
|
if (!isForeignToParent) {
|
|
30
36
|
return errors;
|
|
@@ -35,7 +41,7 @@ export class OwnershipValidationService {
|
|
|
35
41
|
if (!componentMeta) {
|
|
36
42
|
errors.push({
|
|
37
43
|
code: 'MISSING_COMPONENT_METADATA',
|
|
38
|
-
message: `[ecopages] Foreign child "${componentId}" must provide stable
|
|
44
|
+
message: `[ecopages] Foreign child "${componentId}" must provide stable component identity so ownership diagnostics stay actionable. Declared dependencies must include all possible foreign children.`,
|
|
39
45
|
componentId,
|
|
40
46
|
integrationName,
|
|
41
47
|
});
|
|
@@ -53,21 +59,6 @@ export class OwnershipValidationService {
|
|
|
53
59
|
},
|
|
54
60
|
}).flat();
|
|
55
61
|
}
|
|
56
|
-
assertDeclaredComponentDependencies(input) {
|
|
57
|
-
walkComponentGraph({
|
|
58
|
-
roots: input.roots,
|
|
59
|
-
currentIntegrationName: input.currentIntegrationName,
|
|
60
|
-
onComponent: ({ component }) => {
|
|
61
|
-
const parentFile = component.config?.__eco?.file;
|
|
62
|
-
for (const child of component.config?.dependencies?.components ?? []) {
|
|
63
|
-
if (!child) {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
assertEcoDeclaredComponent(child, { parentComponentFile: parentFile });
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
62
|
isRegisteredIntegration(integrationName, currentIntegrationName) {
|
|
72
63
|
if (integrationName === currentIntegrationName) {
|
|
73
64
|
return true;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PageBrowserGraphContribution } from '../../../types/public-types.js';
|
|
2
|
+
export type GroupedGraphBuildPlanInstance = {
|
|
3
|
+
routeFile: string;
|
|
4
|
+
dependencyInstanceKey: string;
|
|
5
|
+
contribution?: PageBrowserGraphContribution;
|
|
6
|
+
};
|
|
7
|
+
export type GroupedGraphBuildPlan = {
|
|
8
|
+
integrationName: string;
|
|
9
|
+
planKey: string;
|
|
10
|
+
instances: ReadonlyArray<GroupedGraphBuildPlanInstance>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Builds a stable grouped-cache identity for one integration static export plan.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createGroupedGraphBuildPlanKey(integrationName: string, instances: ReadonlyArray<Pick<GroupedGraphBuildPlanInstance, 'routeFile' | 'dependencyInstanceKey'>>): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a stable grouped-cache identity for one integration static export plan.
|
|
3
|
+
*/
|
|
4
|
+
export function createGroupedGraphBuildPlanKey(integrationName, instances) {
|
|
5
|
+
const canonicalInstances = [...instances]
|
|
6
|
+
.map((instance) => [instance.routeFile, instance.dependencyInstanceKey])
|
|
7
|
+
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
8
|
+
return JSON.stringify(['grouped-plan', integrationName, canonicalInstances]);
|
|
9
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PageBrowserGraphContribution } from '../../../types/public-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Merges multiple Page Browser Graph contributions into one declarative payload.
|
|
4
|
+
*/
|
|
5
|
+
export declare function mergePageBrowserGraphContributions(...contributions: Array<PageBrowserGraphContribution | undefined>): PageBrowserGraphContribution | undefined;
|
package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.merge.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges multiple Page Browser Graph contributions into one declarative payload.
|
|
3
|
+
*/
|
|
4
|
+
export function mergePageBrowserGraphContributions(...contributions) {
|
|
5
|
+
const dependencies = [];
|
|
6
|
+
const assets = [];
|
|
7
|
+
const watchPaths = new Set();
|
|
8
|
+
for (const contribution of contributions) {
|
|
9
|
+
if (!contribution) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (contribution.dependencies?.length) {
|
|
13
|
+
dependencies.push(...contribution.dependencies);
|
|
14
|
+
}
|
|
15
|
+
if (contribution.assets?.length) {
|
|
16
|
+
assets.push(...contribution.assets);
|
|
17
|
+
}
|
|
18
|
+
for (const watchPath of contribution.watchPaths ?? []) {
|
|
19
|
+
watchPaths.add(watchPath);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (dependencies.length === 0 && assets.length === 0 && watchPaths.size === 0) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
...(dependencies.length > 0 ? { dependencies } : {}),
|
|
27
|
+
...(assets.length > 0 ? { assets } : {}),
|
|
28
|
+
...(watchPaths.size > 0 ? { watchPaths: Array.from(watchPaths) } : {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { GroupedGraphScope } from './route-instance-key.js';
|
|
1
2
|
import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
2
3
|
import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../../types/public-types.js';
|
|
3
4
|
import type { ProcessedAsset } from '../../../services/assets/asset-processing-service/assets.types.js';
|
|
@@ -5,6 +6,7 @@ export type GraphPolicy = 'development' | 'production';
|
|
|
5
6
|
export type GraphKey = {
|
|
6
7
|
integrationName: string;
|
|
7
8
|
routeFile: string;
|
|
9
|
+
dependencyInstanceKey: string;
|
|
8
10
|
entryFingerprint: string;
|
|
9
11
|
policy: GraphPolicy;
|
|
10
12
|
};
|
|
@@ -18,6 +20,11 @@ export type GraphRecord = {
|
|
|
18
20
|
export type GraphBuildResult = {
|
|
19
21
|
result: PageBrowserGraphResult;
|
|
20
22
|
dependencyPaths: ReadonlySet<string>;
|
|
23
|
+
/**
|
|
24
|
+
* @remarks
|
|
25
|
+
* When false, the build result is returned to callers but not committed to the session cache.
|
|
26
|
+
*/
|
|
27
|
+
cacheable?: boolean;
|
|
21
28
|
};
|
|
22
29
|
type GroupedGraphRecord = {
|
|
23
30
|
assetsByRoute: Map<string, ProcessedAsset[]>;
|
|
@@ -32,6 +39,7 @@ export type GroupedGraphBuildOutcome = GroupedGraphRecord | {
|
|
|
32
39
|
export type AffectedGraphIdentity = {
|
|
33
40
|
integrationName: string;
|
|
34
41
|
routeFile: string;
|
|
42
|
+
dependencyInstanceKey: string;
|
|
35
43
|
policy: GraphPolicy;
|
|
36
44
|
entryFingerprint: string;
|
|
37
45
|
};
|
|
@@ -42,8 +50,10 @@ export declare class GraphDependencyIndex {
|
|
|
42
50
|
private readonly dependencyToGraphKeys;
|
|
43
51
|
bindRecord(record: GraphRecord): void;
|
|
44
52
|
unbindRecord(record: GraphRecord): void;
|
|
45
|
-
bindGroupedRecord(
|
|
46
|
-
unbindGroupedRecord(
|
|
53
|
+
bindGroupedRecord(cacheKey: string, record: GroupedGraphRecord): void;
|
|
54
|
+
unbindGroupedRecord(cacheKey: string, record: GroupedGraphRecord): void;
|
|
55
|
+
bindPaths(serializedKey: string, dependencyPaths: ReadonlySet<string>): void;
|
|
56
|
+
unbindPaths(serializedKey: string, dependencyPaths: ReadonlySet<string>): void;
|
|
47
57
|
getAffectedKeys(filePath: string): Set<string>;
|
|
48
58
|
resetForTests(): void;
|
|
49
59
|
private addDependencyBinding;
|
|
@@ -55,21 +65,24 @@ export declare class GraphDependencyIndex {
|
|
|
55
65
|
export declare class SessionPageBrowserGraphCache {
|
|
56
66
|
private readonly records;
|
|
57
67
|
private readonly inFlight;
|
|
68
|
+
private readonly inFlightDependencyPaths;
|
|
69
|
+
private readonly inFlightGenerations;
|
|
58
70
|
private readonly groupedRecords;
|
|
59
71
|
private readonly groupedInFlight;
|
|
60
72
|
private readonly dependencyIndex;
|
|
61
73
|
private readonly graphGenerations;
|
|
62
74
|
private readonly groupedGenerations;
|
|
63
75
|
private buildCount;
|
|
64
|
-
getGraphByRoute(integrationName: string, routeFile: string, policy: GraphPolicy): PageBrowserGraphResult | undefined;
|
|
65
76
|
/**
|
|
66
77
|
* Page Browser Graph compilations in this dev-server session.
|
|
67
78
|
*/
|
|
68
79
|
getBuildCount(): number;
|
|
80
|
+
getGraphByRoute(integrationName: string, routeFile: string, policy: GraphPolicy, dependencyInstanceKey?: string): PageBrowserGraphResult | undefined;
|
|
81
|
+
getDependencyPathsByRoute(integrationName: string, routeFile: string, policy: GraphPolicy, dependencyInstanceKey?: string): ReadonlySet<string> | undefined;
|
|
69
82
|
getAffectedGraphIdentities(filePath: string): AffectedGraphIdentity[];
|
|
70
|
-
resolveGraph(key: GraphKey, build: () => Promise<GraphBuildResult | undefined>): Promise<PageBrowserGraphResult | undefined>;
|
|
71
|
-
resolveGroupedGraph(
|
|
72
|
-
peekGroupedGraph(
|
|
83
|
+
resolveGraph(key: GraphKey, provisionalDependencyPaths: ReadonlySet<string>, build: () => Promise<GraphBuildResult | undefined>): Promise<PageBrowserGraphResult | undefined>;
|
|
84
|
+
resolveGroupedGraph(scope: GroupedGraphScope, build: () => Promise<GroupedGraphBuildOutcome | undefined>): Promise<Map<string, ProcessedAsset[]>>;
|
|
85
|
+
peekGroupedGraph(scope: GroupedGraphScope): Map<string, ProcessedAsset[]> | undefined;
|
|
73
86
|
invalidateByFilePath(filePath: string): number;
|
|
74
87
|
invalidateRoute(integrationName: string, routeFile: string): void;
|
|
75
88
|
invalidateByRouteFile(routeFile: string): void;
|
|
@@ -85,6 +98,10 @@ export declare class SessionPageBrowserGraphCache {
|
|
|
85
98
|
private runGroupedBuild;
|
|
86
99
|
private canCommitGraphBuild;
|
|
87
100
|
private canCommitGroupedBuild;
|
|
101
|
+
private bindDependencyPaths;
|
|
102
|
+
private unbindDependencyPaths;
|
|
103
|
+
private clearInFlightBuild;
|
|
104
|
+
private releaseInFlightDependencyPaths;
|
|
88
105
|
}
|
|
89
106
|
/**
|
|
90
107
|
* Returns the session graph cache for one app instance.
|