diff --git a/package.json b/package.json index aec54c1..b22a1b3 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/website/client.ts b/website/client.ts index 312fa0e..5065095 100644 --- a/website/client.ts +++ b/website/client.ts @@ -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; } diff --git a/website/sections/Seo/SeoV2.tsx b/website/sections/Seo/SeoV2.tsx index ef964ef..f7cf41a 100644 --- a/website/sections/Seo/SeoV2.tsx +++ b/website/sections/Seo/SeoV2.tsx @@ -1,3 +1,4 @@ +import { getWebsiteConfig } from "../../client"; import SeoComponent, { renderTemplateString, type SEOSection, @@ -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;