@nolebase/vitepress-plugin-meta
Version:
A vitepress plugin to add <meta> (excerpts, author, authors, etc.) to your rendered pages to maximize SEO and social media sharing capabilities.
1 lines • 7.79 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../../src/vitepress/index.ts"],"sourcesContent":["import type { Nodes } from 'hast'\n\nimport type { HeadConfig, TransformContext } from 'vitepress'\nimport { defu } from 'defu'\nimport { select, selectAll } from 'hast-util-select'\nimport { toText } from 'hast-util-to-text'\nimport RehypeParse from 'rehype-parse'\nimport RetextStringify from 'retext-stringify'\nimport { unified } from 'unified'\nimport { remove } from 'unist-util-remove'\nimport { removePosition } from 'unist-util-remove-position'\n\nfunction RehypeRetext(option: { selector: string, removeSelectors: string[] }): (tree: Nodes) => void {\n return (nodes) => {\n const vpDocElement = select(option.selector, nodes)\n if (!vpDocElement)\n return\n if (vpDocElement.children.length === 0)\n return\n\n for (const selector of option.removeSelectors) {\n const elements = selectAll(selector, vpDocElement)\n if (elements)\n remove(vpDocElement, elements)\n }\n\n removePosition(vpDocElement)\n if (nodes.type !== 'root' && nodes.type !== 'element')\n return\n\n const text = toText(vpDocElement)\n .replaceAll(/(\\n){2,}/g, ' ')\n\n nodes.children = [{ type: 'text', value: text }]\n }\n}\n\ninterface TransformHeadMetaOptions {\n /**\n * Max length of the excerpt in characters for the meta description.\n *\n * @default 200\n */\n length?: number\n /**\n * CSS selector for the content element.\n *\n * @default '#VPContent div.content main .vp-doc div'\n */\n contentSelector?: string\n /**\n * CSS selector for the content element to remove.\n *\n * @default ['h1','.nolebase-page-properties-container']\n */\n removeContentSelector?: string[]\n /**\n * Whether to use the tagline from the frontmatter for the home layout.\n */\n useTaglineForHomeLayout?: boolean\n /**\n * Handle the excerpt before adding it to the head.\n */\n handleExcerpt?: (excerpt: string, context: Readonly<TransformContext>) => Promise<string>\n}\n\nfunction getMeta(head: HeadConfig[], fromKey: string, withValue: string): HeadConfig | undefined {\n return head.find(([key, attrs]) => key === 'meta' && attrs[fromKey] === withValue)\n}\n\nfunction updateMetaOrCreateMeta(head: HeadConfig[], fromKey: string, withValue: string, asContent: string): HeadConfig[] {\n const meta = head.find(([key, attrs]) => key === 'meta' && attrs[fromKey] === withValue)\n if (meta) {\n meta[1].content = asContent\n return head\n }\n\n head.push(['meta', { [fromKey]: withValue, content: asContent }])\n return head\n}\n\nexport function transformHeadMeta(options?: TransformHeadMetaOptions): (head: HeadConfig[], context: Readonly<TransformContext>) => Promise<HeadConfig[] | void> {\n const opts = defu(options, {\n length: 200,\n contentSelector: '#VPContent div.content main .vp-doc div',\n removeContentSelector: [\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n '.vp-nolebase-page-properties-container',\n '.vp-nolebase-git-changelog-history-container',\n '.vp-nolebase-git-changelog-contributors-container',\n ],\n useTaglineForHomeLayout: true,\n })\n\n return async (head: HeadConfig[], context: Readonly<TransformContext>) => {\n const result = (await unified()\n .data({ settings: { fragment: true } })\n .use(RehypeParse)\n .use(RehypeRetext, {\n selector: opts.contentSelector,\n removeSelectors: opts.removeContentSelector,\n })\n .use(RetextStringify)\n .process(context.content))\n .toString()\n\n let excerpt = result.slice(0, opts.length).trim()\n // if result is longer than 200 characters, add ellipsis\n if (result.length > opts.length)\n excerpt += '...'\n\n if (context.pageData.frontmatter?.layout === 'home' && opts.useTaglineForHomeLayout)\n excerpt = context.pageData.frontmatter?.hero?.tagline ?? context.siteConfig.site.description\n if (opts.handleExcerpt && typeof opts.handleExcerpt === 'function') {\n const handledResult = opts.handleExcerpt(excerpt, context)\n if (handledResult && typeof handledResult === 'string')\n excerpt = handledResult\n else if (handledResult instanceof Promise)\n excerpt = await handledResult\n }\n\n const ogTitle = getMeta(head, 'property', 'og:title')\n if (!ogTitle && context.pageData.title)\n head = updateMetaOrCreateMeta(head, 'property', 'og:title', context.pageData.title)\n\n head = updateMetaOrCreateMeta(head, 'name', 'description', excerpt)\n head = updateMetaOrCreateMeta(head, 'property', 'og:description', excerpt)\n head = updateMetaOrCreateMeta(head, 'property', 'twitter:description', excerpt)\n\n return head\n }\n}\n"],"names":[],"mappings":"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"}