diff --git a/docs/plugins/Description.md b/docs/plugins/Description.md index 6849b3ca4..526bb0175 100644 --- a/docs/plugins/Description.md +++ b/docs/plugins/Description.md @@ -14,6 +14,7 @@ If the frontmatter contains a `description` property, it is used (see [[authorin This plugin accepts the following configuration options: - `descriptionLength`: the maximum length of the generated description. Default is 150 characters. The cut off happens after the first _sentence_ that ends after the given length. +- `replaceExternalLinks`: If `true` (default), replace external links with their domain and path in the description (e.g. `https://domain.tld/some_page/another_page?query=hello&target=world` is replaced with `domain.tld/some_page/another_page`). ## API diff --git a/quartz/plugins/transformers/description.ts b/quartz/plugins/transformers/description.ts index 884d5b189..0f8cd8d39 100644 --- a/quartz/plugins/transformers/description.ts +++ b/quartz/plugins/transformers/description.ts @@ -5,12 +5,19 @@ import { escapeHTML } from "../../util/escape" export interface Options { descriptionLength: number + replaceExternalLinks: boolean } const defaultOptions: Options = { descriptionLength: 150, + replaceExternalLinks: true, } +const urlRegex = new RegExp( + /(https?:\/\/)?(?([\da-z\.-]+)\.([a-z\.]{2,6})(:\d+)?)(?[\/\w\.-]*)(\?[\/\w\.=&;-]*)?/, + "g", +) + export const Description: QuartzTransformerPlugin | undefined> = (userOpts) => { const opts = { ...defaultOptions, ...userOpts } return { @@ -19,19 +26,39 @@ export const Description: QuartzTransformerPlugin | undefined> return [ () => { return async (tree: HTMLRoot, file) => { - const frontMatterDescription = file.data.frontmatter?.description - const text = escapeHTML(toString(tree)) + let frontMatterDescription = file.data.frontmatter?.description + let text = escapeHTML(toString(tree)) + + if (opts.replaceExternalLinks) { + frontMatterDescription = frontMatterDescription?.replace( + urlRegex, + "$" + "$", + ) + text = text.replace(urlRegex, "$" + "$") + } const desc = frontMatterDescription ?? text - const sentences = desc.replace(/\s+/g, " ").split(".") + const sentences = desc.replace(/\s+/g, " ").split(/\.\s/) let finalDesc = "" let sentenceIdx = 0 const len = opts.descriptionLength - while (finalDesc.length < len) { - const sentence = sentences[sentenceIdx] - if (!sentence) break - finalDesc += sentence + "." - sentenceIdx++ + + if (sentences[0] !== undefined && sentences[0].length >= len) { + const firstSentence = sentences[0].split(" ") + while (finalDesc.length < len) { + const sentence = firstSentence[sentenceIdx] + if (!sentence) break + finalDesc += sentence + " " + sentenceIdx++ + } + finalDesc = finalDesc.trimEnd() + "..." + } else { + while (finalDesc.length < len) { + const sentence = sentences[sentenceIdx] + if (!sentence) break + finalDesc += sentence.endsWith(".") ? sentence : sentence + "." + sentenceIdx++ + } } file.data.description = finalDesc