From 2b05b1d4280e9c29a34f976923d1ef43ed593973 Mon Sep 17 00:00:00 2001 From: Vitalii Perehonchuk Date: Tue, 20 Feb 2024 19:15:01 +0200 Subject: [PATCH] inject-link-classes into rehype --- src/plugins/rehype/index.ts | 2 + src/plugins/rehype/inject-link-classes.ts | 50 +++++++++++++++ src/plugins/remark/index.ts | 2 - src/plugins/remark/inject-link-classes.ts | 74 ----------------------- 4 files changed, 52 insertions(+), 76 deletions(-) create mode 100644 src/plugins/rehype/inject-link-classes.ts delete mode 100644 src/plugins/remark/inject-link-classes.ts diff --git a/src/plugins/rehype/index.ts b/src/plugins/rehype/index.ts index 08ec1dc..806217c 100644 --- a/src/plugins/rehype/index.ts +++ b/src/plugins/rehype/index.ts @@ -5,8 +5,10 @@ import rehypeRaw from 'rehype-raw'; import { checkReferencedAnchorsPlugin } from './check-referenced-anchors.ts'; import { injectDtIdsPlugin } from './inject-dt-ids.ts'; import { injectHeadingSlugsPlugin } from './inject-heading-slugs.ts'; +import { injectLinkClassesPlugin } from './inject-link-classes.ts'; const rehypePlugins: RehypePlugins = [ + injectLinkClassesPlugin, injectHeadingSlugsPlugin, [rehypeAutolinkHeadings, { behavior: 'append' }], rehypeRaw, diff --git a/src/plugins/rehype/inject-link-classes.ts b/src/plugins/rehype/inject-link-classes.ts new file mode 100644 index 0000000..fde0e16 --- /dev/null +++ b/src/plugins/rehype/inject-link-classes.ts @@ -0,0 +1,50 @@ +import type { Element } from 'hast'; +import type { Root } from 'hast'; +import { visit } from 'unist-util-visit'; + +import hasPage from '../registry/has-page.js'; +import { initRegistry } from '../registry/registry.js'; +import getSlugFromUrl from '../utils/get-slug-from-url.js'; + +const EXTERNAL_LINK_CLASS = 'wd-external'; +const MISSING_LINK_CLASS = 'wd-nav-link-not-translated'; + +function getClassesByUrl(url: string): string[] { + if (url.startsWith('#')) { + return []; + } + if (url.startsWith('http')) { + return [EXTERNAL_LINK_CLASS]; + } + const slug = getSlugFromUrl(url); + if (slug === '') { + // Link to the root + return []; + } + const isPresent = hasPage(slug); + if (!isPresent) { + return [MISSING_LINK_CLASS]; + } + return []; +} + +export default async function injectLinkClasses(tree: Root) { + await initRegistry(); + + visit(tree, 'element', (node: Element) => { + if (node.tagName !== 'a') return; + const url = node.properties?.href; + if (typeof url !== 'string') return; + const classes = getClassesByUrl(url); + node.properties.class = classes.join(' '); + if (classes.includes(EXTERNAL_LINK_CLASS)) { + node.properties.target = '_blank'; + node.properties.rel = 'noopener noreferrer'; + node.properties['data-astro-prefetch'] = 'false'; + } + }); +} + +export function injectLinkClassesPlugin() { + return injectLinkClasses; +} diff --git a/src/plugins/remark/index.ts b/src/plugins/remark/index.ts index 5615168..0a5419e 100644 --- a/src/plugins/remark/index.ts +++ b/src/plugins/remark/index.ts @@ -7,7 +7,6 @@ import { extractSectionPlugin } from './extract-section.ts'; import { fixLocalImagesPlugin } from './fix-local-images.ts'; import { fixNolintLanguagePlugin } from './fix-nolint-language.ts'; import injectHistoryPlugin from './inject-history/index.ts'; -import { injectLinkClassesPlugin } from './inject-link-classes.ts'; import expandMacrosPlugin from './macros/plugin.ts'; const remarkPlugins: RemarkPlugins = [ // headings, @@ -15,7 +14,6 @@ const remarkPlugins: RemarkPlugins = [ extractSectionPlugin, extractDescriptionPlugin, expandMacrosPlugin, - injectLinkClassesPlugin, fixLocalImagesPlugin, extractCoverPlugin, fixNolintLanguagePlugin, diff --git a/src/plugins/remark/inject-link-classes.ts b/src/plugins/remark/inject-link-classes.ts deleted file mode 100644 index ccc085d..0000000 --- a/src/plugins/remark/inject-link-classes.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { Element } from 'hast'; -import { fromHtml } from 'hast-util-from-html'; -import { toHtml } from 'hast-util-to-html'; -import type { Html, Root } from 'mdast'; -import { SKIP, visit } from 'unist-util-visit'; - -import hasPage from '../registry/has-page.js'; -import { initRegistry } from '../registry/registry.js'; -import getSlugFromUrl from '../utils/get-slug-from-url.js'; - -import stringifyMdastToHtml from './definition-list/stringify-mdast-to-html.js'; - -const EXTERNAL_LINK_CLASS = 'wd-external'; -const MISSING_LINK_CLASS = 'wd-nav-link-not-translated'; - -function getClassesByUrl(url: string): string[] { - if (url.startsWith('#')) { - return []; - } - if (url.startsWith('http')) { - return [EXTERNAL_LINK_CLASS]; - } - const slug = getSlugFromUrl(url); - if (slug === '') { - // Link to the root - return []; - } - const isPresent = hasPage(slug); - if (!isPresent) { - return [MISSING_LINK_CLASS]; - } - return []; -} - -function injectLinkClassesIntoHtml(node: Html) { - const htmlTree = fromHtml(node.value); - visit(htmlTree, 'element', (node: Element) => { - if (node.tagName !== 'a') return; - const url = node.properties?.href; - if (typeof url !== 'string') return; - const classes = getClassesByUrl(url); - node.properties.class = classes.join(' '); - if (classes.includes(EXTERNAL_LINK_CLASS)) { - node.properties.target = '_blank'; - node.properties.rel = 'noopener noreferrer'; - } - }); - node.value = toHtml(htmlTree); -} - -export default async function injectLinkClasses(tree: Root) { - await initRegistry(); - visit(tree, 'html', injectLinkClassesIntoHtml); - - visit(tree, 'link', (node, index, parent) => { - if (!node.url) { - return; - } - if (!parent) throw new Error('Parent not found'); - if (index === undefined) throw new Error('Index not found'); - const classes = getClassesByUrl(node.url); - const replacement: Html = { - type: 'html', - value: `${node.children.map((child) => stringifyMdastToHtml(child)).join('')}`, - }; - parent.children.splice(index, 1, replacement); - return [SKIP, index]; - }); - // process.exit(0); -} - -export function injectLinkClassesPlugin() { - return injectLinkClasses; -}