From a77b9adbad1697444453845d4278a3e08a4f9186 Mon Sep 17 00:00:00 2001 From: marcoferreiradev Date: Mon, 5 Jan 2026 17:16:13 -0300 Subject: [PATCH] fix(commerce): correctly exclude structured data for bots when disabled - Replace boolean `ignoreStructuredData` with explicit `structuredDataControl` options - Fix bug where bots still received JSON-LD even when `ignoreStructuredData=true` - Add "always include" (default), "disable for users", and "disable for all" modes - Deprecate `ignoreStructuredData` with backward compatibility - Extract shared logic to `shouldIncludeStructuredData` utility --- commerce/sections/Seo/SeoPDPV2.tsx | 22 +++++++++++++++++++--- commerce/sections/Seo/SeoPLPV2.tsx | 22 +++++++++++++++++++--- commerce/utils/structuredData.ts | 16 ++++++++++++++++ 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 commerce/utils/structuredData.ts diff --git a/commerce/sections/Seo/SeoPDPV2.tsx b/commerce/sections/Seo/SeoPDPV2.tsx index 573f359c7..b1c4e334e 100644 --- a/commerce/sections/Seo/SeoPDPV2.tsx +++ b/commerce/sections/Seo/SeoPDPV2.tsx @@ -6,6 +6,10 @@ import { import { ProductDetailsPage } from "../../types.ts"; import { canonicalFromBreadcrumblist } from "../../utils/canonical.ts"; import { AppContext } from "../../mod.ts"; +import { + shouldIncludeStructuredData, + StructuredDataControl, +} from "../../utils/structuredData.ts"; export interface Props { /** @title Data Source */ @@ -23,8 +27,15 @@ export interface Props { /** * @title Ignore Structured Data * @description By default, Structured Data is sent to everyone. Use this to prevent Structured Data from being sent to your customers, it will still be sent to crawlers and bots. Be aware that some integrations may not work if Structured Data is not sent. + * @deprecated Use `structuredDataControl` instead. */ ignoreStructuredData?: boolean; + /** + * @title Structured Data Control + * @description Choose when to include JSON-LD structured data. Default sends to everyone. "disable for users" shows only to bots/crawlers. "disable for all" removes completely. Note: some third-party integrations may require structured data to function properly. + * @default "always include" + */ + structuredDataControl?: StructuredDataControl; } /** @title Product details */ @@ -41,6 +52,7 @@ export function loader(_props: Props, _req: Request, ctx: AppContext) { jsonLD, omitVariants, ignoreStructuredData, + structuredDataControl, } = props; const title = renderTemplateString( @@ -63,9 +75,13 @@ export function loader(_props: Props, _req: Request, ctx: AppContext) { jsonLD.product.isVariantOf.hasVariant = []; } - const jsonLDs = (ignoreStructuredData && !ctx.isBot) || !jsonLD - ? [] - : [jsonLD]; + // Handle deprecated prop + const mode = structuredDataControl ?? + (ignoreStructuredData ? "disable for users" : "always include"); + + const jsonLDs = shouldIncludeStructuredData(jsonLD, mode, ctx.isBot) + ? [jsonLD] + : []; return { ...seoSiteProps, diff --git a/commerce/sections/Seo/SeoPLPV2.tsx b/commerce/sections/Seo/SeoPLPV2.tsx index 0eaac0b49..d8d8ad7c1 100644 --- a/commerce/sections/Seo/SeoPLPV2.tsx +++ b/commerce/sections/Seo/SeoPLPV2.tsx @@ -6,6 +6,10 @@ import { import { ProductListingPage } from "../../types.ts"; import { canonicalFromBreadcrumblist } from "../../utils/canonical.ts"; import { AppContext } from "../../mod.ts"; +import { + shouldIncludeStructuredData, + StructuredDataControl, +} from "../../utils/structuredData.ts"; export interface ConfigJsonLD { /** @@ -16,8 +20,15 @@ export interface ConfigJsonLD { /** * @title Ignore Structured Data * @description By default, Structured Data is sent to everyone. Use this to prevent Structured Data from being sent to your customers, it will still be sent to crawlers and bots. Be aware that some integrations may not work if Structured Data is not sent. + * @deprecated Use `structuredDataControl` instead. */ ignoreStructuredData?: boolean; + /** + * @title Structured Data Control + * @description Choose when to include JSON-LD structured data. Default sends to everyone. "disable for users" shows only to bots/crawlers. "disable for all" removes completely. Note: some third-party integrations may require structured data to function properly. + * @default "always include" + */ + structuredDataControl?: StructuredDataControl; } export interface Props { @@ -82,9 +93,14 @@ export function loader(_props: Props, _req: Request, ctx: AppContext) { }); } - const jsonLDs = (configJsonLD?.ignoreStructuredData && !ctx.isBot) || !jsonLD - ? [] - : [jsonLD]; + const mode = configJsonLD?.structuredDataControl ?? + (configJsonLD?.ignoreStructuredData + ? "disable for users" + : "always include"); + + const jsonLDs = shouldIncludeStructuredData(jsonLD, mode, ctx.isBot) + ? [jsonLD] + : []; return { ...seoSiteProps, diff --git a/commerce/utils/structuredData.ts b/commerce/utils/structuredData.ts new file mode 100644 index 000000000..a83c759fd --- /dev/null +++ b/commerce/utils/structuredData.ts @@ -0,0 +1,16 @@ +// commerce/utils/structuredData.ts +export type StructuredDataControl = + | "disable for users" + | "disable for all" + | "always include"; + +export function shouldIncludeStructuredData( + jsonLD: unknown, + control: StructuredDataControl = "always include", + isBot: boolean, +): boolean { + if (!jsonLD) return false; + + return control !== "disable for all" && + (control !== "disable for users" || isBot); +}