/** Safe for double-quoted HTML attributes */ const escAttr = (str) => String(str ?? "") .replace(/&/g, "&") .replace(/"/g, """) .replace(/ { const root = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; const categoryUrl = `${root}/Kategorie/${category.seoName}`; const name = category.name || `Kategorie ${category.seoName}`; const site = config.siteName || config.brandName; const desc = `${name} bei ${config.brandName}: Growshop-Sortiment online kaufen. Schnelle Lieferung, Laden Dresden.`; const descShort = desc.length > 160 ? `${desc.slice(0, 157)}...` : desc; const e = escAttr; const logoUrl = config.images && config.images.logo ? `${root}${config.images.logo}` : `${root}/assets/images/nopicture.jpg`; return ` `; }; const generateCategoryJsonLd = (category, products = [], baseUrl, config) => { // Category IDs to skip (seeds, plants, headshop items) const skipCategoryIds = [689, 706, 709, 711, 714, 748, 749, 896, 710, 924, 923, 922, 921, 916, 278, 259, 258]; // Check if category ID is in skip list if (category.id && skipCategoryIds.includes(parseInt(category.id))) { return ''; } const root = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; const categoryUrl = `${root}/Kategorie/${category.seoName}`; const id = { business: `${root}#business`, website: `${root}#website`, breadcrumb: `${categoryUrl}#breadcrumb`, itemList: `${categoryUrl}#itemlist`, }; const logoUrl = config.images && config.images.logo ? `${root}${config.images.logo}` : undefined; const businessNode = { "@id": id.business, "@type": ["GardenStore", "LocalBusiness", "Organization"], name: config.brandName, url: root, ...(logoUrl && { logo: { "@type": "ImageObject", url: logoUrl }, image: { "@type": "ImageObject", url: logoUrl }, }), }; const websiteNode = { "@id": id.website, "@type": "WebSite", name: config.siteName || config.brandName, url: root, publisher: { "@id": id.business }, }; const breadcrumbNode = { "@id": id.breadcrumb, "@type": "BreadcrumbList", itemListElement: [ { "@type": "ListItem", position: 1, name: "Home", item: root, }, { "@type": "ListItem", position: 2, name: category.name, item: categoryUrl, }, ], }; const collectionPageNode = { "@id": categoryUrl, "@type": "CollectionPage", name: category.name, url: categoryUrl, description: `${category.name} - Entdecken Sie unsere Auswahl an hochwertigen Produkten`, isPartOf: { "@id": id.website }, breadcrumb: { "@id": id.breadcrumb }, }; const graph = [businessNode, websiteNode, breadcrumbNode, collectionPageNode]; // ItemList: URLs only — full Product/Offer markup belongs on each /Artikel/… page (Google guidelines). const withUrls = (products || []).filter((p) => p && p.seoName); if (withUrls.length > 0) { collectionPageNode.mainEntity = { "@id": id.itemList }; graph.push({ "@id": id.itemList, "@type": "ItemList", numberOfItems: withUrls.length, itemListElement: withUrls.map((product, index) => ({ "@type": "ListItem", position: index + 1, item: `${root}/Artikel/${product.seoName}`, })), }); } const categoryGraph = { "@context": "https://schema.org", "@graph": graph, }; return ``; }; module.exports = { generateCategoryMetaTags, generateCategoryJsonLd, };