Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@
"./website/matchers/*": "./website/matchers/*.ts",
"./website/flags/*": "./website/flags/*.ts",
"./website/flags/multivariate/*": "./website/flags/multivariate/*.ts",
"./website/utils/*": "./website/utils/*.ts"
"./website/utils/*": "./website/utils/*.ts",
"./website/sections/*": "./website/sections/*.tsx",
"./website/sections/Seo/*": "./website/sections/Seo/*.tsx"
},
"scripts": {
"generate:manifests": "tsx scripts/generate-manifests.ts",
Expand Down
11 changes: 6 additions & 5 deletions website/client.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
/**
* Website app singleton configuration.
*
* Follows the same pattern as vtex/client.ts and resend/client.ts.
* Uses globalThis to survive Vite module duplication (optimized deps
* vs raw source imports can create separate module instances).
*/

import type { WebsiteConfig } from "./types";

let _config: WebsiteConfig | null = null;
const G = globalThis as unknown as { __decoWebsiteConfig?: WebsiteConfig };

export function configureWebsite(config: WebsiteConfig): void {
_config = config;
G.__decoWebsiteConfig = config;
}

export function getWebsiteConfig(): WebsiteConfig {
if (!_config) {
if (!G.__decoWebsiteConfig) {
throw new Error("Website app not configured. Call configureWebsite() first.");
}
return _config;
return G.__decoWebsiteConfig;
}
17 changes: 15 additions & 2 deletions website/sections/Seo/SeoV2.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getWebsiteConfig } from "../../client";
import SeoComponent, {
renderTemplateString,
type SEOSection,
Expand All @@ -12,16 +13,28 @@ type Props = Pick<

/**
* Loader that merges page-level SEO props with app-level defaults.
* The framework calls this with the WebsiteConfig from the app state.
* When called by the framework, `seo` comes from the app state.
* When called standalone (e.g. asJson handler), falls back to the
* globalThis-backed singleton set by `configureWebsite()`.
*/
export function loader(props: Props, seo?: WebsiteConfig["seo"]) {
const resolvedSeo =
seo ??
(() => {
try {
return getWebsiteConfig().seo;
} catch {
return undefined;
}
})();

const {
titleTemplate = "",
descriptionTemplate = "",
title: appTitle = "",
description: appDescription = "",
...seoSiteProps
} = seo ?? {};
} = resolvedSeo ?? {};

const { title: _title, description: _description, ...seoProps } = props;

Expand Down
Loading