From 8c9192e27afbd7df9e15d82d7b7142ff59909dc7 Mon Sep 17 00:00:00 2001 From: babayaga Date: Sat, 8 Mar 2025 09:14:54 +0100 Subject: [PATCH] seo:item keywords --- public/favicons/site.webmanifest | 20 ++++- src/app/config.json | 4 +- src/base/seo.ts | 45 +++++++++++ src/components/BaseHead.astro | 78 +++++++------------- src/components/polymech/GalleryK.astro | 8 +- src/components/src/base/index.ts | 65 ++++++++++++++++ src/components/src/components/BaseHead.astro | 21 ++++++ tsconfig.json | 1 + 8 files changed, 180 insertions(+), 62 deletions(-) create mode 100644 src/base/seo.ts create mode 100644 src/components/src/base/index.ts create mode 100644 src/components/src/components/BaseHead.astro diff --git a/public/favicons/site.webmanifest b/public/favicons/site.webmanifest index 45dc8a2..1640b15 100644 --- a/public/favicons/site.webmanifest +++ b/public/favicons/site.webmanifest @@ -1 +1,19 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/favicons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/src/app/config.json b/src/app/config.json index 496df58..27fa3da 100644 --- a/src/app/config.json +++ b/src/app/config.json @@ -88,8 +88,8 @@ "pages":{ "home":{ "hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg", - "_blog":{ - "store": "posts" + "blog":{ + "store": "resources" } } } diff --git a/src/base/seo.ts b/src/base/seo.ts new file mode 100644 index 0000000..9043aa6 --- /dev/null +++ b/src/base/seo.ts @@ -0,0 +1,45 @@ +import { unified } from "unified"; +import remarkParse from "remark-parse"; +import remarkRehype from "remark-rehype"; +import rehypeStringify from "rehype-stringify"; +import { createComponent } from "astro/runtime/server/astro-component.js"; +import { renderTemplate, unescapeHTML } from "astro/runtime/server/index.js"; + +import { findUp } from 'find-up' +import { createLogger } from '@polymech/log' +import { parse, IProfile, IComponentConfig } from '@polymech/commons' +import { renderMarkup } from "@/model/component.js"; +import { sync as read } from '@polymech/fs/read' +import { sync as exists } from '@polymech/fs/exists' + +import { + LOGGING_NAMESPACE, + OSRL_ENV, + OSRL_PRODUCT_PROFILE, + PRODUCT_ROOT, + I18N_SOURCE_LANGUAGE +} from 'config/config.js' + +import { translate } from '@/base/i18n.js' +import { item_defaults } from '@/base/index.js' +import config from "../app/config.json" with { "type": "json" } + +export const item_keywords = async (item: IComponentConfig, locale: string = I18N_SOURCE_LANGUAGE) => { + let system_keywords = "" + if (item.PRODUCT_ROOT) { + const defaultsJson = await item_defaults(item.PRODUCT_ROOT); + let defaults: Record = defaultsJson ? read(defaultsJson, 'json') as Record || {} : {} + const defaultsKeywords = (defaults.keywords || "").split(',').map(k => k.trim()).filter(Boolean) + const configKeywords = (config.metadata.keywords || "").split(',').map(k => k.trim()).filter(Boolean) + const itemKeywords = (item.keywords || "").split(',').map(k => k.trim()).filter(Boolean) + const allKeywords = Array.from(new Set([ + ...defaultsKeywords, + ...configKeywords, + ...itemKeywords + ])).join(','); + system_keywords = await translate(allKeywords, I18N_SOURCE_LANGUAGE, locale) + } + const keywordsArray = [...new Set([item.name, ...system_keywords.split(',')])] + const keywords = keywordsArray.join(',') + return keywords +} \ No newline at end of file diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro index d29f4ae..5f2cc44 100644 --- a/src/components/BaseHead.astro +++ b/src/components/BaseHead.astro @@ -1,23 +1,20 @@ --- import "../styles/flowbite.css" -import { I18N_SOURCE_LANGUAGE, isRTL } from "config/config.js" -import { translate } from '@/base/i18n.js' -import { item_defaults } from '@/base/index.js' - -import { LANGUAGES_PROD, PRODUCT_ROOT } from "config/config.js" +import { LANGUAGES_PROD } from "config/config.js" import config from "config/config.json" import { plainify } from "../base/strings.js" + import "../styles/global.css" import "../styles/custom.scss" -import { sync as read } from '@polymech/fs/read' -import { sync as exists } from '@polymech/fs/exists' - import { AstroSeo } from "@astrolib/seo" +import { item_keywords } from '@/base/seo.js' import StructuredData from './head/ArticleStructuredData.astro' + import Hreflang from '@/components/polymech/hreflang.astro' +import { IComponentConfig } from "@polymech/commons" export interface Props { title?: string; @@ -28,8 +25,6 @@ export interface Props { canonical?: string; view: string; path: string; - additionalKeywords?: string[]; - onKeywordsProcessed?: (keywords: string[]) => void; frontmatter?: { title?: string; description?: string; @@ -38,51 +33,27 @@ export interface Props { }; } const env = import.meta.env -const { frontmatter, view, path, additionalKeywords = [], onKeywordsProcessed } = Astro.props +const { frontmatter, view, path } = Astro.props as Props; +const item: IComponentConfig = frontmatter as IComponentConfig || {} const { url } = Astro.request -const REDIRECT = false //import.meta.env.I18N_REDIRECT +const REDIRECT = false const _url = Astro.url - const canonicalUrl = _url.origin const canonicalURL = new URL(Astro.url.pathname, Astro.site) -const locale = Astro.currentLocale || I18N_SOURCE_LANGUAGE - const hreflangs = LANGUAGES_PROD.filter((lang)=>lang!==Astro.currentLocale).map((lang) => ({ lang, url: `${canonicalUrl}/${lang}/${view}/${path}`, })) -const image = frontmatter?.image || config.site.image +const image = item?.image || config.site.image const image_url = image.url const image_alt = image.alt -const title = frontmatter?.title || config.site.title -const description = frontmatter?.description || config.metadata.description +const title = item?.title as string || config.site.title || "" +const description = item?.description as string || config.metadata.description -let system_keywords = "" -const item_config = frontmatter as any || {} -if(item_config.PRODUCT_ROOT){ - const defaultsJson = await item_defaults(item_config.PRODUCT_ROOT); - let defaults:Record = read(defaultsJson, 'json') || {} - const defaultsKeywords = (defaults.keywords || "").split(',').map(k => k.trim()).filter(Boolean) - const configKeywords = (config.metadata.keywords || "").split(',').map(k => k.trim()).filter(Boolean) - const itemKeywords = (item_config.keywords || "").split(',').map(k => k.trim()).filter(Boolean) - const allKeywords = Array.from(new Set([ - ...defaultsKeywords, - ...configKeywords, - ...itemKeywords, - ...additionalKeywords - ])).join(','); - system_keywords = await translate(allKeywords, I18N_SOURCE_LANGUAGE, locale) -} -const keywordsArray = [...new Set([item_config.name, ...system_keywords.split(','), ...additionalKeywords])] -const keywords = keywordsArray.join(',') - -// Call the callback with the processed keywords if it exists -if (typeof onKeywordsProcessed === 'function') { - onKeywordsProcessed(keywordsArray); -} +const keywords = await item_keywords(item, Astro.currentLocale) --- + + - + + - + - - + + - + - - - + + - + @@ -162,15 +135,16 @@ if (typeof onKeywordsProcessed === 'function') { /> + - + { REDIRECT &&