@decocms/start 1.4.2 → 1.4.3
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 +1 -1
- package/src/cms/loader.ts +3 -3
- package/src/cms/resolve.ts +4 -1
- package/src/sdk/workerEntry.ts +63 -95
package/package.json
CHANGED
package/src/cms/loader.ts
CHANGED
|
@@ -196,13 +196,13 @@ export function getSiteSeo(): {
|
|
|
196
196
|
|
|
197
197
|
export function findPageByPath(
|
|
198
198
|
targetPath: string,
|
|
199
|
-
): { page: DecoPage; params: Record<string, string
|
|
199
|
+
): { page: DecoPage; params: Record<string, string>; blockKey: string } | null {
|
|
200
200
|
const allPages = getAllPages();
|
|
201
201
|
|
|
202
|
-
for (const { page } of allPages) {
|
|
202
|
+
for (const { key, page } of allPages) {
|
|
203
203
|
if (!page.path) continue;
|
|
204
204
|
const params = matchPath(page.path, targetPath);
|
|
205
|
-
if (params !== null) return { page, params };
|
|
205
|
+
if (params !== null) return { page, params, blockKey: key };
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
return null;
|
package/src/cms/resolve.ts
CHANGED
|
@@ -1262,6 +1262,8 @@ export interface DecoPageResult {
|
|
|
1262
1262
|
name: string;
|
|
1263
1263
|
path: string;
|
|
1264
1264
|
params: Record<string, string>;
|
|
1265
|
+
/** CMS block key, e.g. "pages-Lista de cupons [APP]-397629" */
|
|
1266
|
+
blockKey?: string;
|
|
1265
1267
|
resolvedSections: ResolvedSection[];
|
|
1266
1268
|
deferredSections: DeferredSection[];
|
|
1267
1269
|
/**
|
|
@@ -1285,7 +1287,7 @@ export async function resolveDecoPage(
|
|
|
1285
1287
|
return null;
|
|
1286
1288
|
}
|
|
1287
1289
|
|
|
1288
|
-
const { page, params } = match;
|
|
1290
|
+
const { page, params, blockKey } = match;
|
|
1289
1291
|
const ctx: MatcherContext = { ...matcherCtx, path: targetPath };
|
|
1290
1292
|
const rctx: ResolveContext = { routeParams: params, matcherCtx: ctx, memo: new Map(), depth: 0 };
|
|
1291
1293
|
|
|
@@ -1403,6 +1405,7 @@ export async function resolveDecoPage(
|
|
|
1403
1405
|
name: page.name,
|
|
1404
1406
|
path: page.path || targetPath,
|
|
1405
1407
|
params,
|
|
1408
|
+
blockKey,
|
|
1406
1409
|
resolvedSections: allResults.flat(),
|
|
1407
1410
|
deferredSections,
|
|
1408
1411
|
seoSection,
|
package/src/sdk/workerEntry.ts
CHANGED
|
@@ -39,9 +39,9 @@ import { getRenderShellConfig } from "../admin/setup";
|
|
|
39
39
|
import { RequestContext } from "./requestContext";
|
|
40
40
|
import { getAppMiddleware } from "./setupApps";
|
|
41
41
|
import type { MatcherContext, ResolvedSection } from "../cms/resolve";
|
|
42
|
-
import { resolveDecoPage
|
|
42
|
+
import { resolveDecoPage } from "../cms/resolve";
|
|
43
43
|
import { runSectionLoaders, runSingleSectionLoader } from "../cms/sectionLoaders";
|
|
44
|
-
import {
|
|
44
|
+
import { loadBlocks } from "../cms/loader";
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Append Link preload headers for CSS and fonts so the browser starts
|
|
@@ -980,24 +980,52 @@ export function createDecoWorkerEntry(
|
|
|
980
980
|
}
|
|
981
981
|
const enrichedSections = await runSectionLoaders(page.resolvedSections, request);
|
|
982
982
|
|
|
983
|
-
//
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
//
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
}
|
|
983
|
+
// Run SEO section loader if registered
|
|
984
|
+
let seoResult = page.seoSection;
|
|
985
|
+
if (seoResult) {
|
|
986
|
+
try {
|
|
987
|
+
seoResult = await runSingleSectionLoader(seoResult, request);
|
|
988
|
+
} catch {
|
|
989
|
+
// use unloaded seoSection
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Merge site-wide SEO defaults into seo props
|
|
994
|
+
const blocks = loadBlocks();
|
|
995
|
+
const site = blocks["Site"] as Record<string, unknown> | undefined;
|
|
996
|
+
const fullSiteSeo = (site?.seo as Record<string, unknown>) ?? {};
|
|
997
|
+
|
|
998
|
+
// When SeoV2 loader ran, use its output as base (preserves key order)
|
|
999
|
+
// and only fill in missing fields from the site-wide SEO config.
|
|
1000
|
+
const loaderProps = seoResult?.props ?? {};
|
|
1001
|
+
const seoProps: Record<string, unknown> = { ...loaderProps };
|
|
1002
|
+
for (const [k, v] of Object.entries(fullSiteSeo)) {
|
|
1003
|
+
if (!(k in seoProps)) seoProps[k] = v;
|
|
1004
|
+
}
|
|
1005
|
+
// Strip internal template fields
|
|
1006
|
+
delete seoProps.titleTemplate;
|
|
1007
|
+
delete seoProps.descriptionTemplate;
|
|
1008
|
+
|
|
1009
|
+
// Build resolveChain statically to match legacy deco-cx/deco format.
|
|
1010
|
+
type FieldResolver = { type: string; value: string | number };
|
|
1011
|
+
const rawKey = page.blockKey ?? `pages-${page.name}`;
|
|
1012
|
+
const encodedKey = rawKey.replace(
|
|
1013
|
+
/^(pages-)(.+)$/,
|
|
1014
|
+
(_m, prefix, rest) => prefix + encodeURIComponent(rest),
|
|
1015
|
+
);
|
|
1016
|
+
const pageChain: FieldResolver[] = [
|
|
1017
|
+
{ type: "resolver", value: "website/handlers/fresh.ts" },
|
|
1018
|
+
{ type: "prop", value: "page" },
|
|
1019
|
+
{ type: "resolver", value: "resolved" },
|
|
1020
|
+
{ type: "resolvable", value: encodedKey },
|
|
1021
|
+
{ type: "resolver", value: "website/pages/Page.tsx" },
|
|
1022
|
+
];
|
|
1023
|
+
|
|
1024
|
+
const seoChain: FieldResolver[] = [
|
|
1025
|
+
...pageChain,
|
|
1026
|
+
{ type: "prop", value: "seo" },
|
|
1027
|
+
{ type: "resolver", value: seoResult?.component ?? "website/sections/Seo/SeoV2.tsx" },
|
|
1028
|
+
];
|
|
1001
1029
|
|
|
1002
1030
|
const result = {
|
|
1003
1031
|
props: {
|
|
@@ -1006,16 +1034,27 @@ export function createDecoWorkerEntry(
|
|
|
1006
1034
|
seo: {
|
|
1007
1035
|
props: seoProps,
|
|
1008
1036
|
metadata: {
|
|
1009
|
-
resolveChain:
|
|
1010
|
-
component:
|
|
1037
|
+
resolveChain: seoChain,
|
|
1038
|
+
component: seoResult?.component ?? "website/sections/Seo/SeoV2.tsx",
|
|
1011
1039
|
},
|
|
1012
1040
|
},
|
|
1013
|
-
sections,
|
|
1041
|
+
sections: enrichedSections.map((s, i) => ({
|
|
1042
|
+
props: s.props,
|
|
1043
|
+
metadata: {
|
|
1044
|
+
resolveChain: [
|
|
1045
|
+
...pageChain,
|
|
1046
|
+
{ type: "prop", value: "sections" },
|
|
1047
|
+
{ type: "prop", value: String(i) },
|
|
1048
|
+
{ type: "resolver", value: s.component },
|
|
1049
|
+
],
|
|
1050
|
+
component: s.component,
|
|
1051
|
+
},
|
|
1052
|
+
})),
|
|
1014
1053
|
devMode: false,
|
|
1015
1054
|
unindexedDomain: false,
|
|
1016
1055
|
},
|
|
1017
1056
|
metadata: {
|
|
1018
|
-
resolveChain:
|
|
1057
|
+
resolveChain: pageChain,
|
|
1019
1058
|
component: "website/pages/Page.tsx",
|
|
1020
1059
|
},
|
|
1021
1060
|
};
|
|
@@ -1455,74 +1494,3 @@ export function createDecoWorkerEntry(
|
|
|
1455
1494
|
}
|
|
1456
1495
|
}
|
|
1457
1496
|
|
|
1458
|
-
// ---------------------------------------------------------------------------
|
|
1459
|
-
// ?asJson SEO builder — mirrors buildPageSeo() from cmsRoute.ts
|
|
1460
|
-
// ---------------------------------------------------------------------------
|
|
1461
|
-
|
|
1462
|
-
/**
|
|
1463
|
-
* Build SEO props for the ?asJson response, matching the legacy deco-cx/deco
|
|
1464
|
-
* format. Runs the SEO section loader, merges with site-wide SEO defaults,
|
|
1465
|
-
* and extracts section-contributed SEO.
|
|
1466
|
-
*/
|
|
1467
|
-
async function buildAsJsonSeo(
|
|
1468
|
-
seoSection: ResolvedSection | null | undefined,
|
|
1469
|
-
enrichedSections: ResolvedSection[],
|
|
1470
|
-
request: Request,
|
|
1471
|
-
): Promise<Record<string, unknown>> {
|
|
1472
|
-
const siteSeo = getSiteSeo();
|
|
1473
|
-
const sectionSeo = extractSeoFromSections(enrichedSections);
|
|
1474
|
-
|
|
1475
|
-
if (!seoSection) {
|
|
1476
|
-
const merged: Record<string, unknown> = { ...sectionSeo };
|
|
1477
|
-
if (siteSeo.title && !merged.title) merged.title = siteSeo.title;
|
|
1478
|
-
if (siteSeo.description && !merged.description) merged.description = siteSeo.description;
|
|
1479
|
-
if (siteSeo.image && !merged.image) merged.image = siteSeo.image;
|
|
1480
|
-
if (siteSeo.favicon) merged.favicon = siteSeo.favicon;
|
|
1481
|
-
if (!merged.jsonLDs) merged.jsonLDs = [];
|
|
1482
|
-
return merged;
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
// Run the section loader if registered (e.g. SEOPDP)
|
|
1486
|
-
let enrichedProps = seoSection.props;
|
|
1487
|
-
try {
|
|
1488
|
-
const enriched = await runSingleSectionLoader(seoSection, request);
|
|
1489
|
-
if (enriched) enrichedProps = enriched.props;
|
|
1490
|
-
} catch {
|
|
1491
|
-
// Section loader failed — use raw resolved props
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
const pageSeo = extractSeoFromProps(enrichedProps);
|
|
1495
|
-
|
|
1496
|
-
// Merge site-wide SEO defaults (same as cmsRoute.ts buildPageSeo)
|
|
1497
|
-
if (!pageSeo.title && siteSeo.title) pageSeo.title = siteSeo.title;
|
|
1498
|
-
if (!pageSeo.description && siteSeo.description) pageSeo.description = siteSeo.description;
|
|
1499
|
-
if (!pageSeo.image && siteSeo.image) pageSeo.image = siteSeo.image;
|
|
1500
|
-
|
|
1501
|
-
// Apply title/description templates (mirrors buildPageSeo in cmsRoute.ts).
|
|
1502
|
-
// Priority: page-level template → site-level template → no-op.
|
|
1503
|
-
const rawProps = seoSection.props;
|
|
1504
|
-
const titleTemplate =
|
|
1505
|
-
effectiveTemplate(rawProps.titleTemplate as string | undefined) ??
|
|
1506
|
-
effectiveTemplate(siteSeo.titleTemplate);
|
|
1507
|
-
const descTemplate =
|
|
1508
|
-
effectiveTemplate(rawProps.descriptionTemplate as string | undefined) ??
|
|
1509
|
-
effectiveTemplate(siteSeo.descriptionTemplate);
|
|
1510
|
-
|
|
1511
|
-
if (titleTemplate && pageSeo.title) {
|
|
1512
|
-
pageSeo.title = titleTemplate.replace("%s", pageSeo.title);
|
|
1513
|
-
}
|
|
1514
|
-
if (descTemplate && pageSeo.description) {
|
|
1515
|
-
pageSeo.description = descTemplate.replace("%s", pageSeo.description);
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
const merged = { ...sectionSeo, ...pageSeo } as Record<string, unknown>;
|
|
1519
|
-
if (siteSeo.favicon) merged.favicon = siteSeo.favicon;
|
|
1520
|
-
if (!merged.jsonLDs) merged.jsonLDs = [];
|
|
1521
|
-
return merged;
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
/** Returns a non-trivial template string, or undefined for "%s" / empty / blank. */
|
|
1525
|
-
function effectiveTemplate(tmpl: string | undefined): string | undefined {
|
|
1526
|
-
if (!tmpl || tmpl.trim() === "" || tmpl.trim() === "%s") return undefined;
|
|
1527
|
-
return tmpl;
|
|
1528
|
-
}
|