From 709177886e1cfcee8913ab4eb11b90db2fe1a9fe Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 2 Nov 2024 14:36:09 -0400 Subject: [PATCH] fix smart quotes - closes #470 --- .../site-kit/src/lib/markdown/renderer.ts | 51 +++++++++---------- packages/site-kit/src/lib/markdown/utils.ts | 8 +-- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/renderer.ts b/packages/site-kit/src/lib/markdown/renderer.ts index 8f952595ec..bd79475332 100644 --- a/packages/site-kit/src/lib/markdown/renderer.ts +++ b/packages/site-kit/src/lib/markdown/renderer.ts @@ -281,34 +281,33 @@ export async function render_content_markdown( snippets.save(token.text, html); } - // ensure that `foo`/`bar` is transformed to `foo` / `bar` - // https://github.com/sveltejs/svelte.dev/pull/577 - const slash_index = - // @ts-expect-error - token.tokens?.findIndex((token) => token.type === 'text' && token.text === '/') ?? -1; - - if (slash_index !== -1) { - // @ts-expect-error - const before = token.tokens[slash_index - 1]; - // @ts-expect-error - const after = token.tokens[slash_index + 1]; - - if (before?.type === 'codespan' && after?.type === 'codespan') { - // @ts-expect-error - token.tokens[slash_index].raw = token.tokens[slash_index].text = ' / '; + const tokens = 'tokens' in token ? token.tokens : undefined; + + if (tokens) { + // ensure that `foo`/`bar` is transformed to `foo` / `bar` + // https://github.com/sveltejs/svelte.dev/pull/577 + const slash_index = + tokens.findIndex((token) => token.type === 'text' && token.text === '/') ?? -1; + + if (slash_index !== -1) { + const before = tokens[slash_index - 1]; + const after = tokens[slash_index + 1]; + + if (before?.type === 'codespan' && after?.type === 'codespan') { + // @ts-expect-error + tokens[slash_index].raw = tokens[slash_index].text = ' / '; + } } - } - }, - text(token) { - // @ts-expect-error I think this is a bug in marked — some text tokens have children, - // but that's not reflected in the types. In these cases we can't just use `token.tokens` - // because that will result in e.g. `` elements not being generated - if (token.tokens) { - // @ts-expect-error - return this.parser!.parseInline(token.tokens); - } - return smart_quotes(token.text, true); + // smart quotes + for (let i = 0; i < tokens.length; i += 1) { + const token = tokens[i]; + + if (token.type === 'text') { + token.text = smart_quotes(token.text, { first: i === 0, html: true }); + } + } + } }, heading({ tokens, depth }) { const text = this.parser!.parseInline(tokens); diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index e4a06084be..d12d453c42 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -42,7 +42,10 @@ export const slugify = (str: string) => { .replace(/-$/, ''); }; -export function smart_quotes(str: string, html: boolean = false) { +export function smart_quotes( + str: string, + { first = true, html = false }: { first?: boolean; html?: boolean } = {} +) { // 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 @@ -50,8 +53,7 @@ export function smart_quotes(str: string, html: boolean = false) { return str.replace( html ? /(.|^)('|")(.|$)/g : /(.|^)('|")(.|$)/g, (m, before, quote, after) => { - const trimmed = before.trim(); - const left = trimmed === '' || trimmed === '('; + const left = (first && before === '') || [' ', '\n', '('].includes(before); let replacement = ''; if (html) {