@ecopages/core 0.2.0-beta.27 → 0.2.0-beta.29
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 -1
- package/package.json +42 -26
- package/src/adapters/abstract/application-adapter.d.ts +22 -4
- package/src/adapters/abstract/application-adapter.js +27 -4
- package/src/adapters/abstract/server-adapter.d.ts +10 -0
- package/src/adapters/bun/create-app.d.ts +1 -0
- package/src/adapters/bun/create-app.js +11 -4
- package/src/adapters/bun/server-adapter.js +1 -0
- package/src/adapters/node/create-app.d.ts +1 -0
- package/src/adapters/node/create-app.js +10 -3
- package/src/adapters/node/server-adapter.js +1 -0
- package/src/adapters/shared/fs-server-response-factory.d.ts +5 -0
- package/src/adapters/shared/fs-server-response-factory.js +20 -0
- package/src/adapters/shared/fs-server-response-matcher.d.ts +18 -0
- package/src/adapters/shared/fs-server-response-matcher.js +75 -15
- package/src/config/config-builder.d.ts +12 -2
- package/src/config/config-builder.js +41 -2
- package/src/config/constants.d.ts +1 -0
- package/src/config/constants.js +2 -1
- package/src/eco/eco.js +2 -2
- package/src/eco/eco.utils.d.ts +1 -1
- package/src/eco/eco.utils.js +1 -1
- package/src/hmr/client/hmr-runtime.js +1 -1
- package/src/hmr/hmr-file-change-prep.d.ts +1 -1
- package/src/hmr/hmr-file-change-prep.js +1 -1
- package/src/plugins/processor.d.ts +8 -0
- package/src/route-renderer/GRAPH.md +11 -11
- package/src/route-renderer/README.md +11 -6
- package/src/route-renderer/orchestration/{document-shell-render.service.d.ts → document-shell/document-shell-render.service.d.ts} +19 -2
- package/src/route-renderer/orchestration/{document-shell-render.service.js → document-shell/document-shell-render.service.js} +28 -0
- package/src/route-renderer/orchestration/{layout-shell-props.service.d.ts → document-shell/layout-shell-props.service.d.ts} +1 -1
- package/src/route-renderer/orchestration/{component-render-context.d.ts → foreign-child/component-render-context.d.ts} +1 -1
- package/src/route-renderer/orchestration/{component-render-context.js → foreign-child/component-render-context.js} +1 -1
- package/src/route-renderer/orchestration/{render-output.utils.d.ts → foreign-child/foreign-child-output.utils.d.ts} +1 -7
- package/src/route-renderer/orchestration/{render-output.utils.js → foreign-child/foreign-child-output.utils.js} +1 -26
- package/src/route-renderer/orchestration/{foreign-subtree-execution.service.d.ts → foreign-child/foreign-subtree-execution.service.d.ts} +5 -5
- package/src/route-renderer/orchestration/{foreign-subtree-execution.service.js → foreign-child/foreign-subtree-execution.service.js} +2 -2
- package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.d.ts +19 -0
- package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +35 -0
- package/src/route-renderer/orchestration/integration-renderer.d.ts +4 -46
- package/src/route-renderer/orchestration/integration-renderer.js +53 -157
- package/src/route-renderer/orchestration/integration-renderer.test-fixtures.d.ts +75 -0
- package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +177 -0
- package/src/route-renderer/orchestration/{component-graph-collectors.d.ts → ownership-graph/component-graph-collectors.d.ts} +4 -4
- package/src/route-renderer/orchestration/{component-graph-collectors.js → ownership-graph/component-graph-collectors.js} +1 -1
- package/src/route-renderer/orchestration/{component-graph.d.ts → ownership-graph/component-graph.d.ts} +1 -3
- package/src/route-renderer/orchestration/{component-graph.js → ownership-graph/component-graph.js} +0 -2
- package/src/route-renderer/orchestration/{ownership-validation.service.d.ts → ownership-graph/ownership-validation.service.d.ts} +2 -2
- package/src/route-renderer/orchestration/{ownership-validation.service.js → ownership-graph/ownership-validation.service.js} +3 -4
- package/src/route-renderer/orchestration/{global-injector-assets.service.d.ts → page-browser-graph/global-injector-assets.service.d.ts} +3 -3
- package/src/route-renderer/orchestration/{global-injector-assets.service.js → page-browser-graph/global-injector-assets.service.js} +2 -2
- package/src/route-renderer/orchestration/{page-browser-graph-contribution.loader.d.ts → page-browser-graph/page-browser-graph-contribution.loader.d.ts} +1 -1
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.d.ts +12 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +29 -0
- package/src/route-renderer/orchestration/{page-browser-graph-session.d.ts → page-browser-graph/page-browser-graph-session.d.ts} +3 -3
- package/src/route-renderer/orchestration/{page-browser-graph-session.js → page-browser-graph/page-browser-graph-session.js} +1 -1
- package/src/route-renderer/orchestration/{page-browser-graph.service.d.ts → page-browser-graph/page-browser-graph.service.d.ts} +3 -3
- package/src/route-renderer/orchestration/{page-browser-graph.service.js → page-browser-graph/page-browser-graph.service.js} +3 -3
- package/src/route-renderer/orchestration/{processed-asset-dedupe.d.ts → page-browser-graph/processed-asset-dedupe.d.ts} +1 -1
- package/src/route-renderer/orchestration/{integration-route-render-adapter.d.ts → route-pipeline/integration-route-render-adapter.d.ts} +3 -3
- package/src/route-renderer/orchestration/{integration-route-render-adapter.js → route-pipeline/integration-route-render-adapter.js} +1 -1
- package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.d.ts +6 -0
- package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +27 -0
- package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.d.ts +15 -0
- package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +36 -0
- package/src/route-renderer/orchestration/{route-html-finalization.service.d.ts → route-pipeline/route-html-finalization.service.d.ts} +6 -2
- package/src/route-renderer/orchestration/{route-html-finalization.service.js → route-pipeline/route-html-finalization.service.js} +5 -2
- package/src/route-renderer/orchestration/{route-prepared-options.builder.d.ts → route-pipeline/route-prepared-options.builder.d.ts} +3 -3
- package/src/route-renderer/orchestration/{route-prepared-options.builder.js → route-pipeline/route-prepared-options.builder.js} +8 -4
- package/src/route-renderer/orchestration/{route-prepared-options.utils.js → route-pipeline/route-prepared-options.utils.js} +1 -1
- package/src/route-renderer/orchestration/{route-render-orchestrator.d.ts → route-pipeline/route-render-orchestrator.d.ts} +6 -6
- package/src/route-renderer/orchestration/{route-render-orchestrator.js → route-pipeline/route-render-orchestrator.js} +8 -5
- package/src/route-renderer/orchestration/string-markup-renderer.js +1 -1
- package/src/services/html/html-transformer.service.js +1 -1
- package/src/services/invalidation/development-invalidation.service.d.ts +5 -0
- package/src/services/invalidation/development-invalidation.service.js +8 -9
- package/src/static-site-generator/README.md +9 -1
- package/src/static-site-generator/production-page-browser-graph-prebuild.js +1 -1
- package/src/static-site-generator/sitemap-routes.d.ts +14 -0
- package/src/static-site-generator/sitemap-routes.js +31 -0
- package/src/static-site-generator/sitemap.d.ts +15 -0
- package/src/static-site-generator/sitemap.js +33 -0
- package/src/static-site-generator/static-export-context.d.ts +9 -1
- package/src/static-site-generator/static-site-generator.d.ts +27 -6
- package/src/static-site-generator/static-site-generator.js +200 -50
- package/src/types/internal-types.d.ts +9 -2
- package/src/types/public-types.d.ts +78 -2
- package/src/utils/ecopages-route-info.d.ts +26 -0
- package/src/utils/ecopages-route-info.js +26 -0
- package/src/utils/html-escaping.d.ts +7 -0
- package/src/utils/html-escaping.js +5 -1
- package/src/utils/path-pattern.d.ts +23 -0
- package/src/utils/path-pattern.js +27 -0
- package/src/watchers/project-watcher.d.ts +3 -3
- package/src/watchers/project-watcher.js +4 -4
- package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +0 -1
- package/src/route-renderer/orchestration/declared-ownership-graph.js +0 -8
- package/src/route-renderer/orchestration/template-serialization.d.ts +0 -38
- package/src/route-renderer/orchestration/template-serialization.js +0 -45
- /package/src/route-renderer/orchestration/{layout-shell-props.service.js → document-shell/layout-shell-props.service.js} +0 -0
- /package/src/route-renderer/orchestration/{page-browser-graph-contribution.loader.js → page-browser-graph/page-browser-graph-contribution.loader.js} +0 -0
- /package/src/route-renderer/orchestration/{processed-asset-dedupe.js → page-browser-graph/processed-asset-dedupe.js} +0 -0
- /package/src/route-renderer/orchestration/{route-prepared-options.utils.d.ts → route-pipeline/route-prepared-options.utils.d.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createPagePackage } from "../../../services/assets/asset-processing-service/index.js";
|
|
2
|
+
import { buildProcessedAssetDedupeKey, dedupeProcessedAssets } from "./processed-asset-dedupe.js";
|
|
3
|
+
function mergePageBrowserGraph(currentPagePackage, processedDependencies, pageBrowserGraph) {
|
|
4
|
+
const mergedGraph = currentPagePackage?.pageBrowserGraph ? {
|
|
5
|
+
entryAssets: dedupeProcessedAssets([
|
|
6
|
+
...currentPagePackage.pageBrowserGraph.entryAssets,
|
|
7
|
+
...pageBrowserGraph.entryAssets
|
|
8
|
+
]),
|
|
9
|
+
chunkAssets: dedupeProcessedAssets([
|
|
10
|
+
...currentPagePackage.pageBrowserGraph.chunkAssets,
|
|
11
|
+
...pageBrowserGraph.chunkAssets
|
|
12
|
+
])
|
|
13
|
+
} : pageBrowserGraph;
|
|
14
|
+
const pageBrowserGraphAssetKeys = new Set(
|
|
15
|
+
[...mergedGraph.entryAssets, ...mergedGraph.chunkAssets].map((asset) => buildProcessedAssetDedupeKey(asset))
|
|
16
|
+
);
|
|
17
|
+
const baseAssets = currentPagePackage ? currentPagePackage.assets.filter(
|
|
18
|
+
(asset) => !pageBrowserGraphAssetKeys.has(buildProcessedAssetDedupeKey(asset))
|
|
19
|
+
) : processedDependencies;
|
|
20
|
+
return {
|
|
21
|
+
pagePackage: createPagePackage(dedupeProcessedAssets(baseAssets), {
|
|
22
|
+
pageBrowserGraph: mergedGraph
|
|
23
|
+
}),
|
|
24
|
+
mergedGraph
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
mergePageBrowserGraph
|
|
29
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { EcoPagesAppConfig } from '
|
|
2
|
-
import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '
|
|
3
|
-
import type { ProcessedAsset } from '
|
|
1
|
+
import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
2
|
+
import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../../types/public-types.js';
|
|
3
|
+
import type { ProcessedAsset } from '../../../services/assets/asset-processing-service/assets.types.js';
|
|
4
4
|
export type GraphPolicy = 'development' | 'production';
|
|
5
5
|
export type GraphKey = {
|
|
6
6
|
integrationName: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { appLogger } from "
|
|
3
|
+
import { appLogger } from "../../../global/app-logger.js";
|
|
4
4
|
function isSkipCacheGroupedOutcome(outcome) {
|
|
5
5
|
return "skipCache" in outcome;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { EcoPagesAppConfig } from '
|
|
2
|
-
import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '
|
|
3
|
-
import { type AssetProcessingService } from '
|
|
1
|
+
import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
2
|
+
import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../../types/public-types.js';
|
|
3
|
+
import { type AssetProcessingService } from '../../../services/assets/asset-processing-service/index.js';
|
|
4
4
|
import { type GraphPolicy } from './page-browser-graph-session.js';
|
|
5
5
|
export type PageBrowserGraphResolveInput = {
|
|
6
6
|
routeFile: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { fileSystem } from "@ecopages/file-system";
|
|
3
3
|
import {
|
|
4
|
-
} from "
|
|
5
|
-
import { appLogger } from "
|
|
6
|
-
import { startupTrace } from "
|
|
4
|
+
} from "../../../services/assets/asset-processing-service/index.js";
|
|
5
|
+
import { appLogger } from "../../../global/app-logger.js";
|
|
6
|
+
import { startupTrace } from "../../../diagnostics/startup-trace.js";
|
|
7
7
|
import {
|
|
8
8
|
collectPageBrowserGraphDependencyPaths,
|
|
9
9
|
createPageBrowserGraphEntryFingerprint,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { ProcessedAsset } from '
|
|
1
|
+
import type { ProcessedAsset } from '../../../services/assets/asset-processing-service/index.js';
|
|
2
2
|
export declare function buildProcessedAssetDedupeKey(asset: ProcessedAsset): string;
|
|
3
3
|
export declare function dedupeProcessedAssets(assets: ProcessedAsset[]): ProcessedAsset[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ProcessedAsset } from '
|
|
2
|
-
import type { HtmlDocumentContribution } from '
|
|
3
|
-
import type { EcoComponent, EcoPageFile, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PagePackageResult, RouteRendererBody, RouteRendererOptions } from '
|
|
1
|
+
import type { ProcessedAsset } from '../../../services/assets/asset-processing-service/index.js';
|
|
2
|
+
import type { HtmlDocumentContribution } from '../../../services/html/html-transformer.service.js';
|
|
3
|
+
import type { EcoComponent, EcoPageFile, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PagePackageResult, RouteRendererBody, RouteRendererOptions } from '../../../types/public-types.js';
|
|
4
4
|
import type { RouteHtmlFinalization, RouteRenderOrchestratorAdapter, RouteRenderOrchestratorResolvedInputs } from './route-render-orchestrator.js';
|
|
5
5
|
/**
|
|
6
6
|
* Host surface required to build the orchestrator adapter for one integration renderer.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadPageBrowserGraphContribution } from "
|
|
1
|
+
import { loadPageBrowserGraphContribution } from "../page-browser-graph/page-browser-graph-contribution.loader.js";
|
|
2
2
|
import { buildRouteHtmlFinalization } from "./route-html-finalization.service.js";
|
|
3
3
|
function createIntegrationRouteRenderAdapter(host) {
|
|
4
4
|
return {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function decodeHtmlEntities(value: string): string;
|
|
2
|
+
export declare function normalizeUnresolvedMarkerArtifactHtml(html: string): string;
|
|
3
|
+
export declare function inspectUnresolvedMarkerArtifactHtml(html: string): {
|
|
4
|
+
hasUnresolvedMarkerArtifacts: boolean;
|
|
5
|
+
normalizedHtml: string;
|
|
6
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function decodeHtmlEntities(value) {
|
|
2
|
+
let decoded = value;
|
|
3
|
+
let previous;
|
|
4
|
+
do {
|
|
5
|
+
previous = decoded;
|
|
6
|
+
decoded = decoded.replaceAll(""", '"').replaceAll("'", "'").replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
7
|
+
} while (decoded !== previous);
|
|
8
|
+
return decoded;
|
|
9
|
+
}
|
|
10
|
+
function normalizeUnresolvedMarkerArtifactHtml(html) {
|
|
11
|
+
return html.replace(
|
|
12
|
+
/&(?:amp;)?lt;eco-marker\b[\s\S]*?&(?:amp;)?gt;&(?:amp;)?lt;\/eco-marker&(?:amp;)?gt;/g,
|
|
13
|
+
(marker) => decodeHtmlEntities(marker)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function inspectUnresolvedMarkerArtifactHtml(html) {
|
|
17
|
+
const normalizedHtml = normalizeUnresolvedMarkerArtifactHtml(html);
|
|
18
|
+
return {
|
|
19
|
+
normalizedHtml,
|
|
20
|
+
hasUnresolvedMarkerArtifacts: normalizedHtml.includes("<eco-marker")
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
decodeHtmlEntities,
|
|
25
|
+
inspectUnresolvedMarkerArtifactHtml,
|
|
26
|
+
normalizeUnresolvedMarkerArtifactHtml
|
|
27
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HtmlDocumentContribution } from '../../../services/html/html-transformer.service.js';
|
|
2
|
+
import type { PageMetadataProps, PageRobotsMetadata } from '../../../types/public-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds a head contribution for non-default `metadata.robots` values.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* When `robots` is omitted or matches the default `index,follow` behaviour,
|
|
8
|
+
* no tag is emitted (Next.js parity). Otherwise a single
|
|
9
|
+
* `<meta name="robots" content="...">` contribution is returned.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildRobotsMetaContribution(metadata: Pick<PageMetadataProps, 'robots'>): HtmlDocumentContribution | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Formats robots directives for a meta tag, or `undefined` when defaults apply.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatRobotsMetaContent(robots: PageRobotsMetadata | undefined): string | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { escapeHtmlAttribute } from "../../../utils/html-escaping.js";
|
|
2
|
+
function buildRobotsMetaContribution(metadata) {
|
|
3
|
+
const content = formatRobotsMetaContent(metadata.robots);
|
|
4
|
+
if (!content) {
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
placement: "head-append",
|
|
9
|
+
html: `<meta name="robots" content="${escapeHtmlAttribute(content)}">`
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function formatRobotsMetaContent(robots) {
|
|
13
|
+
if (!robots) {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
const index = robots.index !== false;
|
|
17
|
+
const follow = robots.follow !== false;
|
|
18
|
+
const directives = [];
|
|
19
|
+
if (!index) {
|
|
20
|
+
directives.push("noindex");
|
|
21
|
+
}
|
|
22
|
+
if (!follow) {
|
|
23
|
+
directives.push("nofollow");
|
|
24
|
+
}
|
|
25
|
+
if (robots.nocache) {
|
|
26
|
+
directives.push("nocache");
|
|
27
|
+
}
|
|
28
|
+
if (directives.length === 0) {
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
return directives.join(", ");
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
buildRobotsMetaContribution,
|
|
35
|
+
formatRobotsMetaContent
|
|
36
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { HtmlDocumentContribution } from '
|
|
2
|
-
import type { IntegrationRendererRenderOptions } from '
|
|
1
|
+
import type { HtmlDocumentContribution } from '../../../services/html/html-transformer.service.js';
|
|
2
|
+
import type { IntegrationRendererRenderOptions } from '../../../types/public-types.js';
|
|
3
3
|
import type { RouteHtmlFinalization } from './route-render-orchestrator.js';
|
|
4
4
|
export type RouteHtmlFinalizationContext<C> = {
|
|
5
5
|
renderOptions: IntegrationRendererRenderOptions<C>;
|
|
@@ -12,5 +12,9 @@ export type RouteHtmlFinalizationContext<C> = {
|
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
14
|
* Builds the structural HTML finalization plan for one prepared route render.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Merges core robots meta contributions with integration-owned document
|
|
18
|
+
* contributions so page-level `metadata.robots` works across templates.
|
|
15
19
|
*/
|
|
16
20
|
export declare function buildRouteHtmlFinalization<C>(context: RouteHtmlFinalizationContext<C>): RouteHtmlFinalization;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { buildRobotsMetaContribution } from "./robots-meta.contribution.js";
|
|
1
2
|
function buildRouteHtmlFinalization(context) {
|
|
2
3
|
const { renderOptions } = context;
|
|
3
4
|
const documentAttributes = context.getDocumentAttributes(renderOptions);
|
|
4
|
-
const
|
|
5
|
+
const integrationContributions = context.getHtmlDocumentContributions({ renderOptions, partial: false }) ?? [];
|
|
6
|
+
const robotsContribution = buildRobotsMetaContribution(renderOptions.metadata ?? {});
|
|
7
|
+
const htmlContributions = robotsContribution ? [robotsContribution, ...integrationContributions] : integrationContributions;
|
|
5
8
|
const hasStructuralFinalization = documentAttributes && Object.keys(documentAttributes).length > 0;
|
|
6
|
-
if (!hasStructuralFinalization &&
|
|
9
|
+
if (!hasStructuralFinalization && htmlContributions.length === 0) {
|
|
7
10
|
return {};
|
|
8
11
|
}
|
|
9
12
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { EcoPagesAppConfig } from '
|
|
2
|
-
import type { EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphResult, PageMetadataProps, EcoPageLayoutEntry, RouteRendererOptions } from '
|
|
3
|
-
import { type ProcessedAsset } from '
|
|
1
|
+
import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
2
|
+
import type { EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphResult, PageMetadataProps, EcoPageLayoutEntry, RouteRendererOptions } from '../../../types/public-types.js';
|
|
3
|
+
import { type ProcessedAsset } from '../../../services/assets/asset-processing-service/index.js';
|
|
4
4
|
type PreparedRenderInputs = {
|
|
5
5
|
Page: EcoPageFile['default'] | EcoPageComponent<any>;
|
|
6
6
|
HtmlTemplate: EcoComponent<HtmlTemplateProps>;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { createPagePackage } from "
|
|
2
|
-
import { dedupeProcessedAssets } from "
|
|
1
|
+
import { createPagePackage } from "../../../services/assets/asset-processing-service/index.js";
|
|
2
|
+
import { dedupeProcessedAssets } from "../page-browser-graph/processed-asset-dedupe.js";
|
|
3
3
|
import { createPageLocalsProxy } from "./route-prepared-options.utils.js";
|
|
4
4
|
function buildPreparedRenderOptions(input) {
|
|
5
5
|
const { routeOptions, resolvedInputs, resolvedDependencies, allDependencies, pageBrowserGraph, appConfig } = input;
|
|
6
6
|
const { Page, HtmlTemplate, Layouts, Layout, layoutEntries, props, metadata, integrationSpecificProps } = resolvedInputs;
|
|
7
7
|
const dedupedDependencies = dedupeProcessedAssets(allDependencies);
|
|
8
8
|
const pagePackage = createPagePackage(dedupedDependencies, { pageBrowserGraph });
|
|
9
|
-
const
|
|
9
|
+
const resolvedProps = {
|
|
10
10
|
...props,
|
|
11
|
+
...routeOptions.props ?? {}
|
|
12
|
+
};
|
|
13
|
+
const pageProps = {
|
|
14
|
+
...resolvedProps,
|
|
11
15
|
params: routeOptions.params || {},
|
|
12
16
|
query: routeOptions.query || {}
|
|
13
17
|
};
|
|
@@ -25,7 +29,7 @@ function buildPreparedRenderOptions(input) {
|
|
|
25
29
|
Layouts,
|
|
26
30
|
Layout,
|
|
27
31
|
layoutEntries,
|
|
28
|
-
props,
|
|
32
|
+
props: resolvedProps,
|
|
29
33
|
Page,
|
|
30
34
|
metadata,
|
|
31
35
|
params: routeOptions.params || {},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LocalsAccessError } from "
|
|
1
|
+
import { LocalsAccessError } from "../../../errors/locals-access-error.js";
|
|
2
2
|
function createPageLocalsProxy(filePath) {
|
|
3
3
|
const errorMessage = `[ecopages] Request locals are only available during request-time rendering with cache: 'dynamic'. Page: ${filePath}. If you meant to use locals here, set cache: 'dynamic' and provide locals from route middleware/handlers.`;
|
|
4
4
|
return new Proxy(
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { EcoPagesAppConfig } from '
|
|
2
|
-
import type { EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult, EcoPageLayoutEntry } from '
|
|
3
|
-
import type { AssetProcessingService, ProcessedAsset } from '
|
|
4
|
-
import type { HtmlDocumentContribution } from '
|
|
5
|
-
import { OwnershipValidationService } from '
|
|
6
|
-
import { PageBrowserGraphService } from '
|
|
1
|
+
import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
|
|
2
|
+
import type { EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult, EcoPageLayoutEntry } from '../../../types/public-types.js';
|
|
3
|
+
import type { AssetProcessingService, ProcessedAsset } from '../../../services/assets/asset-processing-service/index.js';
|
|
4
|
+
import type { HtmlDocumentContribution } from '../../../services/html/html-transformer.service.js';
|
|
5
|
+
import { OwnershipValidationService } from '../ownership-graph/ownership-validation.service.js';
|
|
6
|
+
import { PageBrowserGraphService } from '../page-browser-graph/page-browser-graph.service.js';
|
|
7
7
|
export type RouteRenderOrchestratorResolvedInputs = {
|
|
8
8
|
Page: EcoPageFile['default'] | EcoPageComponent<any>;
|
|
9
9
|
HtmlTemplate: EcoComponent<HtmlTemplateProps>;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { inspectUnresolvedMarkerArtifactHtml } from "./
|
|
2
|
-
import {
|
|
1
|
+
import { inspectUnresolvedMarkerArtifactHtml } from "./marker-artifact.utils.js";
|
|
2
|
+
import {
|
|
3
|
+
OwnershipValidationService,
|
|
4
|
+
throwIfOwnershipInvalid
|
|
5
|
+
} from "../ownership-graph/ownership-validation.service.js";
|
|
3
6
|
import {
|
|
4
7
|
buildEagerSsrLazyAssetsFromGraph,
|
|
5
8
|
collectResolvedLazyTriggersFromGraph,
|
|
6
9
|
collectUsedIntegrationDependenciesFromGraph
|
|
7
|
-
} from "
|
|
8
|
-
import { buildGlobalInjectorAssets } from "
|
|
9
|
-
import { PageBrowserGraphService } from "
|
|
10
|
+
} from "../ownership-graph/component-graph-collectors.js";
|
|
11
|
+
import { buildGlobalInjectorAssets } from "../page-browser-graph/global-injector-assets.service.js";
|
|
12
|
+
import { PageBrowserGraphService } from "../page-browser-graph/page-browser-graph.service.js";
|
|
10
13
|
import { buildPreparedRenderOptions } from "./route-prepared-options.builder.js";
|
|
11
14
|
class RouteRenderOrchestrator {
|
|
12
15
|
appConfig;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IntegrationRenderer } from "./integration-renderer.js";
|
|
2
|
-
import { resolveInnermostPageLayout } from "./layout-shell-props.service.js";
|
|
2
|
+
import { resolveInnermostPageLayout } from "./document-shell/layout-shell-props.service.js";
|
|
3
3
|
class StringMarkupRenderer extends IntegrationRenderer {
|
|
4
4
|
async renderComponent(input) {
|
|
5
5
|
if (typeof input.component !== "function") {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
buildProcessedAssetDedupeKey,
|
|
6
6
|
dedupeProcessedAssets
|
|
7
|
-
} from "../../route-renderer/orchestration/processed-asset-dedupe.js";
|
|
7
|
+
} from "../../route-renderer/orchestration/page-browser-graph/processed-asset-dedupe.js";
|
|
8
8
|
class HtmlTransformerService {
|
|
9
9
|
processedDependencies = [];
|
|
10
10
|
pagePackage;
|
|
@@ -87,6 +87,11 @@ export declare class DevelopmentInvalidationService {
|
|
|
87
87
|
isServerModuleSourceFile(filePath: string): boolean;
|
|
88
88
|
/**
|
|
89
89
|
* Returns whether a processor owns the changed file as an asset input.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* Watch config drives processor notifications only. Asset ownership requires
|
|
93
|
+
* declared capabilities so dependency-only watches (for example content
|
|
94
|
+
* collection MDX scans) do not skip server invalidation and HMR.
|
|
90
95
|
*/
|
|
91
96
|
isProcessorOwnedAsset(filePath: string): boolean;
|
|
92
97
|
}
|
|
@@ -218,21 +218,20 @@ class DevelopmentInvalidationService {
|
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
220
|
* Returns whether a processor owns the changed file as an asset input.
|
|
221
|
+
*
|
|
222
|
+
* @remarks
|
|
223
|
+
* Watch config drives processor notifications only. Asset ownership requires
|
|
224
|
+
* declared capabilities so dependency-only watches (for example content
|
|
225
|
+
* collection MDX scans) do not skip server invalidation and HMR.
|
|
221
226
|
*/
|
|
222
227
|
isProcessorOwnedAsset(filePath) {
|
|
223
228
|
for (const processor of this.appConfig.processors.values()) {
|
|
224
229
|
const capabilities = processor.getAssetCapabilities?.() ?? [];
|
|
225
|
-
if (capabilities.length
|
|
226
|
-
const matchesConfiguredAsset = typeof processor.matchesFileFilter !== "function" || processor.matchesFileFilter(filePath);
|
|
227
|
-
if (matchesConfiguredAsset && capabilities.some((capability) => processor.canProcessAsset?.(capability.kind, filePath))) {
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
+
if (capabilities.length === 0) {
|
|
230
231
|
continue;
|
|
231
232
|
}
|
|
232
|
-
const
|
|
233
|
-
if (
|
|
234
|
-
const { extensions = [] } = watchConfig;
|
|
235
|
-
if (extensions.length && extensions.some((ext) => filePath.endsWith(ext))) {
|
|
233
|
+
const matchesConfiguredAsset = typeof processor.matchesFileFilter !== "function" || processor.matchesFileFilter(filePath);
|
|
234
|
+
if (matchesConfiguredAsset && capabilities.some((capability) => processor.canProcessAsset?.(capability.kind, filePath))) {
|
|
236
235
|
return true;
|
|
237
236
|
}
|
|
238
237
|
}
|
|
@@ -29,8 +29,10 @@ It should not invent a parallel rendering stack just for build mode.
|
|
|
29
29
|
|
|
30
30
|
| File | Role |
|
|
31
31
|
| ------------------------------ | ---------------------------------------------------------------------------- |
|
|
32
|
-
| `static-site-generator.ts` | Route enumeration, HTML artifact writes, integration export hooks
|
|
32
|
+
| `static-site-generator.ts` | Route enumeration, HTML artifact writes, integration export hooks, sitemap |
|
|
33
33
|
| `static-export-context.ts` | Hook context type for `beforeStaticExport` / `afterStaticExport` |
|
|
34
|
+
| `sitemap.ts` | Pure sitemap.xml renderer |
|
|
35
|
+
| `sitemap-routes.ts` | Sitemap location assembly (`exclude`, `extraUrls`, dedupe) |
|
|
34
36
|
| `static-build-invalidation.ts` | `dist/` reset policy, production cache clearing, static-render cache context |
|
|
35
37
|
|
|
36
38
|
Build-input fingerprinting (`hashAppConfigFile`, `createBuildInputsFingerprint`) lives in `packages/core/src/build/cache/build-input-fingerprint.ts` and is shared with server-entry and unified-graph caches.
|
|
@@ -56,4 +58,10 @@ Integrations may implement:
|
|
|
56
58
|
- `beforeStaticExport(context)` — runs after unified-graph prebuild, before page rendering
|
|
57
59
|
- `afterStaticExport(context)` — runs in a `finally` block after generation completes
|
|
58
60
|
|
|
61
|
+
When `appConfig.sitemap.enabled` is true, `sitemap.xml` is written **after** `afterStaticExport` so integration-generated URLs can be listed via `extraUrls`.
|
|
62
|
+
|
|
63
|
+
Sitemap eligibility is decided during successful page export (`activeStaticPathnames` plus `robots.index !== false`, fail-closed on metadata errors). `resolveSitemapLocations` then applies `exclude` and appends `extraUrls`.
|
|
64
|
+
|
|
65
|
+
`StaticExportContext.routes` is the unfiltered static-generation route list. Sitemap filtering is applied separately.
|
|
66
|
+
|
|
59
67
|
See `StaticExportContext` in `static-export-context.ts`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { getAppPageBrowserGraphSession } from "../route-renderer/orchestration/page-browser-graph-session.js";
|
|
2
|
+
import { getAppPageBrowserGraphSession } from "../route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js";
|
|
3
3
|
function shouldPrebuildProductionPageBrowserGraphs() {
|
|
4
4
|
return process.env.NODE_ENV === "production";
|
|
5
5
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SitemapConfig } from '../types/public-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the ordered absolute locations for sitemap.xml.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `eligiblePathnames` must already be successfully exported and robots-indexable.
|
|
7
|
+
* Filtering here is only `sitemap.exclude`, then `extraUrls` are appended.
|
|
8
|
+
* `extraUrls` always include (deduped) and are not subject to `exclude` or page robots.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveSitemapLocations(input: {
|
|
11
|
+
eligiblePathnames: readonly string[];
|
|
12
|
+
sitemap: SitemapConfig;
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
}): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { matchesAnyPathPattern, normalizePathname } from "../utils/path-pattern.js";
|
|
2
|
+
import { buildSitemapLocation } from "./sitemap.js";
|
|
3
|
+
function resolveSitemapLocations(input) {
|
|
4
|
+
const exclude = input.sitemap.exclude ?? [];
|
|
5
|
+
const locations = [];
|
|
6
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7
|
+
for (const pathname of input.eligiblePathnames) {
|
|
8
|
+
const normalized = normalizePathname(pathname);
|
|
9
|
+
if (matchesAnyPathPattern(normalized, exclude)) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const location = buildSitemapLocation(input.baseUrl, normalized);
|
|
13
|
+
if (seen.has(location)) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
seen.add(location);
|
|
17
|
+
locations.push(location);
|
|
18
|
+
}
|
|
19
|
+
for (const extraUrl of input.sitemap.extraUrls ?? []) {
|
|
20
|
+
const location = buildSitemapLocation(input.baseUrl, extraUrl);
|
|
21
|
+
if (seen.has(location)) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
seen.add(location);
|
|
25
|
+
locations.push(location);
|
|
26
|
+
}
|
|
27
|
+
return locations;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
resolveSitemapLocations
|
|
31
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joins a base origin with a pathname or absolute URL into a sitemap `<loc>` value.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Trailing slashes are stripped except for the site root (`/`). Absolute `http(s)`
|
|
6
|
+
* URLs in `extraUrls` are returned as-is (still slash-normalized).
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildSitemapLocation(baseUrl: string, pathnameOrUrl: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Renders a sitemap.org 0.9 urlset document from absolute location URLs.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* Callers are responsible for deduplication; this serializer preserves input order.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderSitemap(locations: readonly string[]): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { escapeXmlText } from "../utils/html-escaping.js";
|
|
2
|
+
import { normalizePathname } from "../utils/path-pattern.js";
|
|
3
|
+
function buildSitemapLocation(baseUrl, pathnameOrUrl) {
|
|
4
|
+
const trimmed = pathnameOrUrl.trim();
|
|
5
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
6
|
+
return normalizeLocation(trimmed);
|
|
7
|
+
}
|
|
8
|
+
const origin = baseUrl.replace(/\/+$/, "");
|
|
9
|
+
const pathname = normalizePathname(trimmed);
|
|
10
|
+
return pathname === "/" ? `${origin}/` : `${origin}${pathname}`;
|
|
11
|
+
}
|
|
12
|
+
function renderSitemap(locations) {
|
|
13
|
+
const urls = locations.map((loc) => ` <url>
|
|
14
|
+
<loc>${escapeXmlText(normalizeLocation(loc))}</loc>
|
|
15
|
+
</url>`).join("\n");
|
|
16
|
+
return [
|
|
17
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
18
|
+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
19
|
+
urls,
|
|
20
|
+
"</urlset>",
|
|
21
|
+
""
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
function normalizeLocation(location) {
|
|
25
|
+
if (/^https?:\/\/[^/]+\/?$/i.test(location)) {
|
|
26
|
+
return `${location.replace(/\/+$/, "")}/`;
|
|
27
|
+
}
|
|
28
|
+
return location.replace(/\/+$/, "");
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
buildSitemapLocation,
|
|
32
|
+
renderSitemap
|
|
33
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EcoPagesAppConfig } from '../types/internal-types.js';
|
|
2
|
-
import type { StaticRoute } from '../types/public-types.js';
|
|
2
|
+
import type { EcopagesRouteInfo, StaticRoute } from '../types/public-types.js';
|
|
3
3
|
import type { StaticGenerationRendererResolver } from '../route-renderer/route-renderer.js';
|
|
4
4
|
import type { StaticGenerationRoute } from '../router/server/route-registry.js';
|
|
5
5
|
type StaticGenerationRouteSource = {
|
|
@@ -18,5 +18,13 @@ export interface StaticExportContext {
|
|
|
18
18
|
staticRoutes?: StaticRoute[];
|
|
19
19
|
force: boolean;
|
|
20
20
|
preserveExportDirectory: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Unfiltered static-generation routes (exact + expanded dynamic).
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* Sitemap filtering is applied separately; integrations that build custom
|
|
26
|
+
* artifacts should start from this full list.
|
|
27
|
+
*/
|
|
28
|
+
routes: EcopagesRouteInfo[];
|
|
21
29
|
}
|
|
22
30
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EcoPagesAppConfig } from '../types/internal-types.js';
|
|
2
|
-
import type { StaticRoute } from '../types/public-types.js';
|
|
2
|
+
import type { SitemapConfig, StaticRoute } from '../types/public-types.js';
|
|
3
3
|
import type { PageRendererResolver, StaticGenerationRendererResolver } from '../route-renderer/route-renderer.js';
|
|
4
4
|
import type { StaticGenerationRoute } from '../router/server/route-registry.js';
|
|
5
5
|
import type { RouteModuleBuildCache } from '../services/module-loading/route-module-build-cache.store.js';
|
|
@@ -31,6 +31,7 @@ export declare class StaticSiteGenerator {
|
|
|
31
31
|
private readonly routeModuleBuildCacheOverride?;
|
|
32
32
|
private staticRenderCacheContext;
|
|
33
33
|
private forceFullStaticGeneration;
|
|
34
|
+
private pageModuleLoader?;
|
|
34
35
|
/**
|
|
35
36
|
* Creates the static-site generator for one app config.
|
|
36
37
|
*/
|
|
@@ -46,6 +47,10 @@ export declare class StaticSiteGenerator {
|
|
|
46
47
|
* Writes the robots.txt file declared by the app config.
|
|
47
48
|
*/
|
|
48
49
|
generateRobotsTxt(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Writes the configured sitemap file to the export directory.
|
|
52
|
+
*/
|
|
53
|
+
generateSitemap(locations: readonly string[], sitemap: SitemapConfig): void;
|
|
49
54
|
/**
|
|
50
55
|
* Returns whether the input path points at the root directory.
|
|
51
56
|
*/
|
|
@@ -56,6 +61,18 @@ export declare class StaticSiteGenerator {
|
|
|
56
61
|
getDirectories(routes: string[]): string[];
|
|
57
62
|
private writeStaticOutput;
|
|
58
63
|
private writeStaticPageArtifact;
|
|
64
|
+
/**
|
|
65
|
+
* Marks a successfully exported pathname as sitemap-eligible when robots allow indexing.
|
|
66
|
+
*
|
|
67
|
+
* @remarks
|
|
68
|
+
* Fail-closed: metadata resolution errors omit the URL. `robots.index === false`
|
|
69
|
+
* also omits it. Eligibility is decided during generation, not by reloading modules
|
|
70
|
+
* after `afterStaticExport`.
|
|
71
|
+
*/
|
|
72
|
+
private considerSitemapEligibility;
|
|
73
|
+
private getPageModuleLoader;
|
|
74
|
+
private resolveFilesystemPageMetadata;
|
|
75
|
+
private resolveExplicitViewMetadata;
|
|
59
76
|
private pruneStaleStaticOutputs;
|
|
60
77
|
private createFilesystemStaticContents;
|
|
61
78
|
/**
|
|
@@ -64,7 +81,15 @@ export declare class StaticSiteGenerator {
|
|
|
64
81
|
* @remarks
|
|
65
82
|
* Routes are rendered through the normal route renderer directly.
|
|
66
83
|
*/
|
|
67
|
-
generateStaticPages(
|
|
84
|
+
generateStaticPages(input: {
|
|
85
|
+
router: StaticGenerationRouteSource;
|
|
86
|
+
baseUrl: string;
|
|
87
|
+
routeRendererFactory?: StaticPageRouteRendererFactory;
|
|
88
|
+
skipped?: string[];
|
|
89
|
+
activeStaticPathnames?: Set<string>;
|
|
90
|
+
preloadedRoutes?: readonly StaticGenerationRoute[];
|
|
91
|
+
sitemapEligiblePathnames?: Set<string>;
|
|
92
|
+
}): Promise<void>;
|
|
68
93
|
private createStaticExportContext;
|
|
69
94
|
private invokeStaticExportHook;
|
|
70
95
|
/**
|
|
@@ -78,10 +103,6 @@ export declare class StaticSiteGenerator {
|
|
|
78
103
|
force?: boolean;
|
|
79
104
|
preserveExportDirectory?: boolean;
|
|
80
105
|
}): Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* Generates static pages from explicit static routes registered via app.static().
|
|
83
|
-
* These routes use eco.page views via loader functions for HMR support.
|
|
84
|
-
*/
|
|
85
106
|
private generateExplicitStaticPages;
|
|
86
107
|
private resolveExplicitViewSourceFile;
|
|
87
108
|
private generateExplicitStaticRoute;
|