UNPKG

@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.93 kB
{"version":3,"file":"index.cjs","sources":["../../src/vitepress/index.ts"],"sourcesContent":["import type { Nodes } from 'hast'\nimport type { HeadConfig, TransformContext } from 'vitepress'\n\nimport RehypeParse from 'rehype-parse'\nimport RetextStringify from 'retext-stringify'\n\nimport { defu } from 'defu'\nimport { select, selectAll } from 'hast-util-select'\nimport { toText } from 'hast-util-to-text'\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: 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":["select","selectAll","remove","removePosition","toText","defu","unified","RehypeParse","RetextStringify"],"mappings":"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"}