diff --git a/packages/site-kit/src/lib/markdown/renderer.ts b/packages/site-kit/src/lib/markdown/renderer.ts index 8eb687e31a..dcf321ea60 100644 --- a/packages/site-kit/src/lib/markdown/renderer.ts +++ b/packages/site-kit/src/lib/markdown/renderer.ts @@ -3,7 +3,7 @@ import { createHash } from 'node:crypto'; import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises'; import path from 'node:path'; import ts from 'typescript'; -import { SHIKI_LANGUAGE_MAP, escape, normalizeSlugify, transform } from './utils'; +import { SHIKI_LANGUAGE_MAP, escape, normalizeSlugify, smart_quotes, transform } from './utils'; import type { Declaration, TypeElement, Modules } from './index'; type MetadataKeys = 'file' | 'link' | 'copy'; @@ -235,6 +235,7 @@ async function parse({ /** @type {string} */ const content = await transform(body, { + text: smart_quotes, heading(html, level, raw) { const title = html .replace(/<\/?code>/g, '') diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 750816cb81..7b19e0731c 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -78,6 +78,20 @@ export const normalizeSlugify = (str: string) => { return slugify(removeHTMLEntities(removeMarkdown(str))).replace(/(<([^>]+)>)/gi, ''); }; +export function smart_quotes(str: string) { + // replace dumb quotes with smart quotes. This isn't a perfect algorithm — it + // wouldn't correctly handle `That '70s show` or `My country 'tis of thee` + // but a) it's very unlikely they'll occur in our docs, and + // b) they can be dealt with manually + return str.replace(/(.|^)('|")(.|$)/g, (m, before, quote, after) => { + const left = !before.trim(); + const double = quote === '"'; + const entity = `&${left ? 'l' : 'r'}${double ? 'd' : 's'}quo;`; + + return (before ?? '') + entity + (after ?? ''); + }); +} + const default_renderer: Partial = { code(code, infostring, escaped) { const lang = infostring?.match(/\S*/)?.[0]; diff --git a/packages/site-kit/src/lib/server/content/index.ts b/packages/site-kit/src/lib/server/content/index.ts index 2b31976cb9..49392ec36d 100644 --- a/packages/site-kit/src/lib/server/content/index.ts +++ b/packages/site-kit/src/lib/server/content/index.ts @@ -1,4 +1,4 @@ -import { extract_frontmatter, slugify } from '../../markdown/utils'; +import { extract_frontmatter, slugify, smart_quotes } from '../../markdown/utils'; import type { Document } from '../../types'; export async function create_index( @@ -24,6 +24,9 @@ export async function create_index( throw new Error(`Missing title in ${slug} frontmatter`); } + metadata.title = smart_quotes(metadata.title); + if (metadata.description) metadata.description = smart_quotes(metadata.description); + const sections = Array.from(body.matchAll(/^##\s+(.*)$/gm)).map((match) => { const title = match[1].replace(/`/g, '').replace(/</g, '<').replace(/>/g, '>'); const slug = slugify(title);