From 1da7cbaaa0e96ca7f237dc536fada4f44ec7e79e Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:36:54 +0100 Subject: [PATCH 01/13] feat(markdown/remark): Smartypants config --- packages/markdown/remark/src/index.ts | 5 +++-- packages/markdown/remark/src/types.ts | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index 634a883ebe98..4fcd8d304b72 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -90,8 +90,9 @@ export async function createMarkdownProcessor( if (gfm) { parser.use(remarkGfm); } - if (smartypants) { - parser.use(remarkSmartypants); + if (smartypants !== false) { + const smartypantsConfig = typeof smartypants === 'object' ? smartypants : {}; + parser.use(remarkSmartypants, smartypantsConfig); } } diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index 490d23fea859..fd9849538f19 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -35,6 +35,20 @@ export type RehypePlugins = (string | [string, any] | RehypePlugin | [RehypePlug export type RemarkRehype = RemarkRehypeOptions; +export interface QuoteCharacterMap { + double: string; + single: string; +} + +export interface SmartypantsOptions { + backticks?: boolean | 'all'; + closingQuotes?: QuoteCharacterMap; + dashes?: boolean | 'oldschool' | 'inverted'; + ellipses?: boolean | 'spaced' | 'unspaced'; + openingQuotes?: QuoteCharacterMap; + quotes?: boolean; +} + export type ThemePresets = BuiltinTheme | 'css-variables'; export type SyntaxHighlightConfigType = 'shiki' | 'prism'; @@ -58,7 +72,7 @@ export interface AstroMarkdownOptions { rehypePlugins?: RehypePlugins; remarkRehype?: RemarkRehype; gfm?: boolean; - smartypants?: boolean; + smartypants?: boolean | SmartypantsOptions; } /** From 3181557f5baf822ad5bd41f7d0d70502b59b1902 Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:37:29 +0100 Subject: [PATCH 02/13] feat(astro): Smartypants config --- .../astro/src/core/config/schemas/base.ts | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/config/schemas/base.ts b/packages/astro/src/core/config/schemas/base.ts index 0f494bdac904..73d727d08178 100644 --- a/packages/astro/src/core/config/schemas/base.ts +++ b/packages/astro/src/core/config/schemas/base.ts @@ -111,6 +111,26 @@ const highlighterTypesSchema = z .union([z.literal('shiki'), z.literal('prism')]) .default(syntaxHighlightDefaults.type); +const quoteCharacterMapSchema = z.object({ + double: z.string(), + single: z.string(), +}); + +const smartypantsOptionsSchema = z.object({ + backticks: z.union([z.boolean(), z.literal('all')]).default(true), + closingQuotes: quoteCharacterMapSchema.default({ + double: '”', + single: '’', + }), + dashes: z.union([z.boolean(), z.literal('inverted'), z.literal('oldschool')]).default(true), + ellipses: z.union([z.boolean(), z.literal('spaced'), z.literal('unspaced')]).default(true), + openingQuotes: quoteCharacterMapSchema.default({ + double: '“', + single: '‘', + }), + quotes: z.boolean().default(true), +}); + export const AstroConfigSchema = z.object({ root: z .string() @@ -377,7 +397,13 @@ export const AstroConfigSchema = z.object({ .custom((data) => data instanceof Object && !Array.isArray(data)) .default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype), gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm), - smartypants: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.smartypants), + smartypants: z + .union([z.boolean(), smartypantsOptionsSchema]) + .default(ASTRO_CONFIG_DEFAULTS.markdown.smartypants) + .transform((val) => { + if (val === true) return smartypantsOptionsSchema.parse({}); + return val; + }), }) .prefault({}), vite: z From 9dd6f22f403296096a8db17a352d5e26b7d43d2c Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:37:54 +0100 Subject: [PATCH 03/13] test: add e2e Smartypants config tests --- .../astro/test/astro-markdown-plugins.test.js | 79 ++++++++++++++++++- .../src/pages/with-backticks.md | 3 + .../src/pages/with-smartypants.md | 2 +- 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-backticks.md diff --git a/packages/astro/test/astro-markdown-plugins.test.js b/packages/astro/test/astro-markdown-plugins.test.js index 78db0e6dcbf6..d5e0f66d2ba9 100644 --- a/packages/astro/test/astro-markdown-plugins.test.js +++ b/packages/astro/test/astro-markdown-plugins.test.js @@ -60,7 +60,7 @@ describe('Astro Markdown plugins', () => { const smartypantsHtml = await fixture.readFile('/with-smartypants/index.html'); const $2 = cheerio.load(smartypantsHtml); - assert.equal($2('p').html(), '“Smartypants” is — awesome'); + assert.equal($2('p').html(), '“Smartypants” is — awesome …'); testRemark(gfmHtml); testRehype(gfmHtml, '#github-flavored-markdown-test'); @@ -82,7 +82,7 @@ describe('Astro Markdown plugins', () => { const $ = cheerio.load(html); // test 1: smartypants applied correctly - assert.equal($('p').html(), '“Smartypants” is — awesome'); + assert.equal($('p').html(), '“Smartypants” is — awesome …'); testRemark(html); testRehype(html, '#smartypants-test'); @@ -115,7 +115,7 @@ describe('Astro Markdown plugins', () => { const html = await fixture.readFile('/with-smartypants/index.html'); const $ = cheerio.load(html); - assert.equal($('p').html(), '"Smartypants" is -- awesome'); + assert.equal($('p').html(), '"Smartypants" is -- awesome ...'); testRemark(html); testRehype(html, '#smartypants-test'); @@ -146,6 +146,79 @@ describe('Astro Markdown plugins', () => { ); }); }); + + describe('Advanced Smartypants configurations', () => { + it('Handles custom dashes (oldschool)', async () => { + const fixture = await loadFixture({ + root: './fixtures/astro-markdown-plugins/', + markdown: { + ...defaultMarkdownConfig, + smartypants: { dashes: 'oldschool' }, + }, + }); + await fixture.build(); + + const html = await fixture.readFile('/with-smartypants/index.html'); + const $ = cheerio.load(html); + + // In 'oldschool', -- becomes en-dash (–) instead of em-dash (—) + assert.equal($('p').html(), '“Smartypants” is – awesome …'); + }); + + it('Handles disabled ellipses', async () => { + const fixture = await loadFixture({ + root: './fixtures/astro-markdown-plugins/', + markdown: { + ...defaultMarkdownConfig, + smartypants: { ellipses: false }, + }, + }); + await fixture.build(); + + const html = await fixture.readFile('/with-smartypants/index.html'); + const $ = cheerio.load(html); + + // Dashes should still be smart (em-dash), but dots should remain dots + assert.equal($('p').html(), '“Smartypants” is — awesome ...'); + }); + + it('Handles custom opening and closing quotes', async () => { + const fixture = await loadFixture({ + root: './fixtures/astro-markdown-plugins/', + markdown: { + ...defaultMarkdownConfig, + smartypants: { + openingQuotes: { double: '«', single: '‹' }, + closingQuotes: { double: '»', single: '›' }, + }, + }, + }); + await fixture.build(); + + const html = await fixture.readFile('/with-smartypants/index.html'); + const $ = cheerio.load(html); + + // Verify the custom guillemets are used + assert.equal($('p').html(), '«Smartypants» is — awesome …'); + }); + + it('Handles backticks: "all"', async () => { + const fixture = await loadFixture({ + root: './fixtures/astro-markdown-plugins/', + markdown: { + ...defaultMarkdownConfig, + smartypants: { backticks: 'all', quotes: false }, + }, + }); + await fixture.build(); + + const html = await fixture.readFile('/with-backticks/index.html'); + const $ = cheerio.load(html); + + // With backticks: 'all', single and double backticks are transformed + assert.ok($('p').html().includes('“Smarty”')); + }); + }); }); function testRehype(html, headingId) { diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-backticks.md b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-backticks.md new file mode 100644 index 000000000000..514e476ca7bd --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-backticks.md @@ -0,0 +1,3 @@ +# Smartypants Backticks test + +``Smarty'' diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-smartypants.md b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-smartypants.md index 5d7a85ab1ac8..0538abc98008 100644 --- a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-smartypants.md +++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-smartypants.md @@ -1,3 +1,3 @@ # Smartypants test -"Smartypants" is -- awesome +"Smartypants" is -- awesome ... From e12dc7e700b19762efd24271f6db81b1ae064a47 Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:01:54 +0100 Subject: [PATCH 04/13] docs: add changesets --- .changeset/legal-rings-rhyme.md | 21 +++++++++++++++++++++ .changeset/red-heads-stare.md | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .changeset/legal-rings-rhyme.md create mode 100644 .changeset/red-heads-stare.md diff --git a/.changeset/legal-rings-rhyme.md b/.changeset/legal-rings-rhyme.md new file mode 100644 index 000000000000..e213f2c335b3 --- /dev/null +++ b/.changeset/legal-rings-rhyme.md @@ -0,0 +1,21 @@ +--- +'@astrojs/markdown-remark': minor +--- + +Updates `createMarkdownProcessor` to support advanced SmartyPants options. + +The `smartypants` property in `AstroMarkdownOptions` now accepts a `SmartypantsOptions` object, allowing fine-grained control over typography transformations (backticks, dashes, ellipses, and quotes). + +```ts +import { createMarkdownProcessor } from '@astrojs/markdown-remark'; + +const processor = await createMarkdownProcessor({ + smartypants: { + backticks: 'all', + dashes: 'oldschool', + ellipses: 'unspaced', + openingQuotes: { double: '«', single: '‹' }, + closingQuotes: { double: '»', single: '›' }, + quotes: false, + } +}); diff --git a/.changeset/red-heads-stare.md b/.changeset/red-heads-stare.md new file mode 100644 index 000000000000..78e101edc0c8 --- /dev/null +++ b/.changeset/red-heads-stare.md @@ -0,0 +1,27 @@ +--- +'astro': minor +--- + +Adds support for advanced configuration of SmartyPants in Markdown. + +You can now pass an options object to `markdown.smartypants` in your Astro configuration to fine-tune how punctuation, dashes, and quotes are transformed. + +This is helpful for projects that require specific typographic standards, such as "oldschool" dash handling or localized quotation marks. + +```js +// astro.config.mjs +export default defineConfig({ + markdown: { + smartypants: { + backticks: 'all', + dashes: 'oldschool', + ellipses: 'unspaced', + openingQuotes: { double: '«', single: '‹' }, + closingQuotes: { double: '»', single: '›' }, + quotes: false, + }, + }, +}); +``` + +See [the `markdown.smartypants` reference documentation](https://docs.astro.build/en/reference/configuration-reference/#markdownsmartypants) for more information. From 897dd46ccc35aee0bb65b2a16b7dea1f84a77db7 Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:02:28 +0100 Subject: [PATCH 05/13] docs: update public Astro config types docs this file is used to autogenerate the configuration reference documentation --- packages/astro/src/types/public/config.ts | 101 +++++++++++++++++++--- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 3c522f74f08c..46afacc81942 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -1,10 +1,12 @@ import type { OutgoingHttpHeaders } from 'node:http'; import type { RemotePattern } from '@astrojs/internal-helpers/remote'; import type { + QuoteCharacterMap, RehypePlugins, RemarkPlugins, RemarkRehype, ShikiConfig, + SmartypantsOptions, SyntaxHighlightConfigType, } from '@astrojs/markdown-remark'; import type { Config as SvgoConfig } from 'svgo'; @@ -1947,24 +1949,103 @@ export interface AstroUserConfig< * ``` */ gfm?: boolean; + /** * @docs * @name markdown.smartypants - * @type {boolean} + * @type {boolean | SmartypantsOptions} * @default `true` * @version 2.0.0 * @description - * Astro uses the [SmartyPants formatter](https://daringfireball.net/projects/smartypants/) by default. To disable this, set the `smartypants` flag to `false`: + * Whether to use the [SmartyPants formatter](https://daringfireball.net/projects/smartypants/) to transform straight quotes into smart quotes, dashes into en/em dashes, and triple dots into ellipses. * - * ```js - * { - * markdown: { - * smartypants: false, - * } - * } - * ``` + * To disable this, set the `smartypants` flag to `false`. + * + * For more control over typography, you can instead specify a configuration object with the properties listed below. */ - smartypants?: boolean; + smartypants?: + | boolean + | { + /** + * @docs + * @name markdown.smartypants.backticks + * @kind h4 + * @type {boolean | 'all'} + * @default `true` + * @version 5.18.0 + * @description + * Whether to transform backticks into smart quotes. + * When set to `'all'`, double backticks are converted to double quotes and single backticks are converted to single quotes. + */ + backticks?: boolean | 'all'; + + /** + * @docs + * @name markdown.smartypants.quotes + * @kind h4 + * @type {boolean} + * @default `true` + * @version 5.18.0 + * @description + * Whether to transform straight quotes into curly "smart" quotes. + * * Note: If `backticks` is set to `'all'`, this should typically be set to `false`. + */ + quotes?: boolean; + + /** + * @docs + * @name markdown.smartypants.dashes + * @kind h4 + * @type {boolean | 'oldschool' | 'inverted'} + * @default `true` + * @version 5.18.0 + * @description + * How to transform dashes. + * - `true`: turns two dashes into an em dash. + * - `'oldschool'`: turns three dashes into an em dash and two into an en dash. + * - `'inverted'`: turns three dashes into an en dash and two into an em dash. + */ + dashes?: boolean | 'oldschool' | 'inverted'; + + /** + * @docs + * @name markdown.smartypants.ellipses + * @kind h4 + * @type {boolean | 'spaced' | 'unspaced'} + * @default `true` + * @version 5.18.0 + * @description + * Whether to transform triple dots into ellipses. + * - `'spaced'`: only transforms triple dots with spaces (e.g. `. . .`). + * - `'unspaced'`: only transforms triple dots without spaces (e.g. `...`). + */ + ellipses?: boolean | 'spaced' | 'unspaced'; + + /** + * @docs + * @name markdown.smartypants.openingQuotes + * @kind h4 + * @type {QuoteCharacterMap} + * @default `{ double: '“', single: '‘' }` + * @version 5.18.0 + * @description + * The specific characters to use for opening double and single quotes. + */ + openingQuotes?: QuoteCharacterMap; + + /** + * @docs + * @name markdown.smartypants.closingQuotes + * @kind h4 + * @type {QuoteCharacterMap} + * @default `{ double: '”', single: '’' }` + * @version 5.18.0 + * @description + * The specific characters to use for closing double and single quotes. + */ + closingQuotes?: QuoteCharacterMap; + }; + /** * @docs * @name markdown.remarkRehype From 7ca04cfef23212a2aa6bdeb2c0948d539529d463 Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:28:51 +0100 Subject: [PATCH 06/13] fix: unused import --- packages/astro/src/types/public/config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 46afacc81942..320674680360 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -6,7 +6,6 @@ import type { RemarkPlugins, RemarkRehype, ShikiConfig, - SmartypantsOptions, SyntaxHighlightConfigType, } from '@astrojs/markdown-remark'; import type { Config as SvgoConfig } from 'svgo'; From 4f302de9f4c1c08976a7849c522b2959c526c92f Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Fri, 30 Jan 2026 08:23:31 +0100 Subject: [PATCH 07/13] feat: change docs version --- packages/astro/src/types/public/config.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 320674680360..dc7eccd0132a 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -1971,7 +1971,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean | 'all'} * @default `true` - * @version 5.18.0 + * @version 6.0.0 * @description * Whether to transform backticks into smart quotes. * When set to `'all'`, double backticks are converted to double quotes and single backticks are converted to single quotes. @@ -1984,7 +1984,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean} * @default `true` - * @version 5.18.0 + * @version 6.0.0 * @description * Whether to transform straight quotes into curly "smart" quotes. * * Note: If `backticks` is set to `'all'`, this should typically be set to `false`. @@ -1997,7 +1997,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean | 'oldschool' | 'inverted'} * @default `true` - * @version 5.18.0 + * @version 6.0.0 * @description * How to transform dashes. * - `true`: turns two dashes into an em dash. @@ -2012,7 +2012,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean | 'spaced' | 'unspaced'} * @default `true` - * @version 5.18.0 + * @version 6.0.0 * @description * Whether to transform triple dots into ellipses. * - `'spaced'`: only transforms triple dots with spaces (e.g. `. . .`). @@ -2026,7 +2026,7 @@ export interface AstroUserConfig< * @kind h4 * @type {QuoteCharacterMap} * @default `{ double: '“', single: '‘' }` - * @version 5.18.0 + * @version 6.0.0 * @description * The specific characters to use for opening double and single quotes. */ @@ -2038,7 +2038,7 @@ export interface AstroUserConfig< * @kind h4 * @type {QuoteCharacterMap} * @default `{ double: '”', single: '’' }` - * @version 5.18.0 + * @version 6.0.0 * @description * The specific characters to use for closing double and single quotes. */ From 92c89389c8d47ca526f29fdd9c44532cf8e6d60e Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Sat, 14 Mar 2026 08:27:21 +0100 Subject: [PATCH 08/13] feat: set since version in config to 6.1.0 --- packages/astro/src/types/public/config.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 069b3b99fae1..505cbeb80fb8 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -2064,7 +2064,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean | 'all'} * @default `true` - * @version 6.0.0 + * @version 6.1.0 * @description * Whether to transform backticks into smart quotes. * When set to `'all'`, double backticks are converted to double quotes and single backticks are converted to single quotes. @@ -2077,7 +2077,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean} * @default `true` - * @version 6.0.0 + * @version 6.1.0 * @description * Whether to transform straight quotes into curly "smart" quotes. * * Note: If `backticks` is set to `'all'`, this should typically be set to `false`. @@ -2090,7 +2090,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean | 'oldschool' | 'inverted'} * @default `true` - * @version 6.0.0 + * @version 6.1.0 * @description * How to transform dashes. * - `true`: turns two dashes into an em dash. @@ -2105,7 +2105,7 @@ export interface AstroUserConfig< * @kind h4 * @type {boolean | 'spaced' | 'unspaced'} * @default `true` - * @version 6.0.0 + * @version 6.1.0 * @description * Whether to transform triple dots into ellipses. * - `'spaced'`: only transforms triple dots with spaces (e.g. `. . .`). @@ -2119,7 +2119,7 @@ export interface AstroUserConfig< * @kind h4 * @type {QuoteCharacterMap} * @default `{ double: '“', single: '‘' }` - * @version 6.0.0 + * @version 6.1.0 * @description * The specific characters to use for opening double and single quotes. */ @@ -2131,7 +2131,7 @@ export interface AstroUserConfig< * @kind h4 * @type {QuoteCharacterMap} * @default `{ double: '”', single: '’' }` - * @version 6.0.0 + * @version 6.1.0 * @description * The specific characters to use for closing double and single quotes. */ From ae92fb28144fdc0f2f002ab71b0d3e88764c9801 Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Sat, 14 Mar 2026 09:35:07 +0100 Subject: [PATCH 09/13] fix: adapt default type definition --- packages/astro/src/core/config/schemas/base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/config/schemas/base.ts b/packages/astro/src/core/config/schemas/base.ts index 325c9bfeeecf..5efd593d38ab 100644 --- a/packages/astro/src/core/config/schemas/base.ts +++ b/packages/astro/src/core/config/schemas/base.ts @@ -407,11 +407,11 @@ export const AstroConfigSchema = z.object({ gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm), smartypants: z .union([z.boolean(), smartypantsOptionsSchema]) - .default(ASTRO_CONFIG_DEFAULTS.markdown.smartypants) .transform((val) => { if (val === true) return smartypantsOptionsSchema.parse({}); return val; - }), + }) + .default(smartypantsOptionsSchema.parse({})), }) .prefault({}), vite: z From 09778a80937b17ff9e771c323ce3c26494dfeb0e Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:00:48 +0100 Subject: [PATCH 10/13] refactor: use retext-smartypants options --- .changeset/legal-rings-rhyme.md | 5 +- .changeset/red-heads-stare.md | 2 +- .../astro/src/core/config/schemas/base.ts | 8 +- packages/astro/src/types/public/config.ts | 87 +------------------ packages/markdown/remark/package.json | 1 + packages/markdown/remark/src/types.ts | 15 +--- pnpm-lock.yaml | 3 + 7 files changed, 20 insertions(+), 101 deletions(-) diff --git a/.changeset/legal-rings-rhyme.md b/.changeset/legal-rings-rhyme.md index e213f2c335b3..f989a59e08cb 100644 --- a/.changeset/legal-rings-rhyme.md +++ b/.changeset/legal-rings-rhyme.md @@ -4,7 +4,7 @@ Updates `createMarkdownProcessor` to support advanced SmartyPants options. -The `smartypants` property in `AstroMarkdownOptions` now accepts a `SmartypantsOptions` object, allowing fine-grained control over typography transformations (backticks, dashes, ellipses, and quotes). +The `smartypants` property in `AstroMarkdownOptions` now accepts `Smartypants` options, allowing fine-grained control over typography transformations (backticks, dashes, ellipses, and quotes). ```ts import { createMarkdownProcessor } from '@astrojs/markdown-remark'; @@ -19,3 +19,6 @@ const processor = await createMarkdownProcessor({ quotes: false, } }); +``` + +For the up-to-date supported properties, check out [the `retext-smartypants` options](https://github.com/retextjs/retext-smartypants?tab=readme-ov-file#fields). diff --git a/.changeset/red-heads-stare.md b/.changeset/red-heads-stare.md index 78e101edc0c8..342f19737188 100644 --- a/.changeset/red-heads-stare.md +++ b/.changeset/red-heads-stare.md @@ -24,4 +24,4 @@ export default defineConfig({ }); ``` -See [the `markdown.smartypants` reference documentation](https://docs.astro.build/en/reference/configuration-reference/#markdownsmartypants) for more information. +See [the `retext-smartypants` options](https://github.com/retextjs/retext-smartypants?tab=readme-ov-file#fields) for more information. diff --git a/packages/astro/src/core/config/schemas/base.ts b/packages/astro/src/core/config/schemas/base.ts index 5efd593d38ab..49be664cb75f 100644 --- a/packages/astro/src/core/config/schemas/base.ts +++ b/packages/astro/src/core/config/schemas/base.ts @@ -4,6 +4,7 @@ import type { RemarkPlugin as _RemarkPlugin, RemarkRehype as _RemarkRehype, ShikiConfig, + Smartypants as _Smartypants, } from '@astrojs/markdown-remark'; import { markdownConfigDefaults, syntaxHighlightDefaults } from '@astrojs/markdown-remark'; import { type BuiltinTheme, bundledThemes } from 'shiki'; @@ -49,6 +50,7 @@ type RehypePlugin = ComplexifyWithUnion<_RehypePlugin>; type RemarkPlugin = ComplexifyWithUnion<_RemarkPlugin>; /** @lintignore */ export type RemarkRehype = ComplexifyWithOmit<_RemarkRehype>; +export type Smartypants = ComplexifyWithOmit<_Smartypants>; export const ASTRO_CONFIG_DEFAULTS = { root: '.', @@ -123,7 +125,7 @@ const quoteCharacterMapSchema = z.object({ single: z.string(), }); -const smartypantsOptionsSchema = z.object({ +const smartypantsOptionsSchema: z.ZodType = z.object({ backticks: z.union([z.boolean(), z.literal('all')]).default(true), closingQuotes: quoteCharacterMapSchema.default({ double: '”', @@ -407,11 +409,11 @@ export const AstroConfigSchema = z.object({ gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm), smartypants: z .union([z.boolean(), smartypantsOptionsSchema]) - .transform((val) => { + .transform((val): false | Smartypants => { if (val === true) return smartypantsOptionsSchema.parse({}); return val; }) - .default(smartypantsOptionsSchema.parse({})), + .prefault(ASTRO_CONFIG_DEFAULTS.markdown.smartypants), }) .prefault({}), vite: z diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 505cbeb80fb8..541199a0f055 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -1,11 +1,11 @@ import type { OutgoingHttpHeaders } from 'node:http'; import type { RemotePattern } from '@astrojs/internal-helpers/remote'; import type { - QuoteCharacterMap, RehypePlugins, RemarkPlugins, RemarkRehype, ShikiConfig, + Smartypants, SyntaxHighlightConfigType, } from '@astrojs/markdown-remark'; import type { Config as SvgoConfig } from 'svgo'; @@ -2045,7 +2045,7 @@ export interface AstroUserConfig< /** * @docs * @name markdown.smartypants - * @type {boolean | SmartypantsOptions} + * @type {boolean | Smartypants} * @default `true` * @version 2.0.0 * @description @@ -2053,90 +2053,11 @@ export interface AstroUserConfig< * * To disable this, set the `smartypants` flag to `false`. * - * For more control over typography, you can instead specify a configuration object with the properties listed below. + * For more control over typography, you can instead specify a configuration object with the properties [supported by `retext-smartypants`](https://github.com/retextjs/retext-smartypants?tab=readme-ov-file#fields). */ smartypants?: | boolean - | { - /** - * @docs - * @name markdown.smartypants.backticks - * @kind h4 - * @type {boolean | 'all'} - * @default `true` - * @version 6.1.0 - * @description - * Whether to transform backticks into smart quotes. - * When set to `'all'`, double backticks are converted to double quotes and single backticks are converted to single quotes. - */ - backticks?: boolean | 'all'; - - /** - * @docs - * @name markdown.smartypants.quotes - * @kind h4 - * @type {boolean} - * @default `true` - * @version 6.1.0 - * @description - * Whether to transform straight quotes into curly "smart" quotes. - * * Note: If `backticks` is set to `'all'`, this should typically be set to `false`. - */ - quotes?: boolean; - - /** - * @docs - * @name markdown.smartypants.dashes - * @kind h4 - * @type {boolean | 'oldschool' | 'inverted'} - * @default `true` - * @version 6.1.0 - * @description - * How to transform dashes. - * - `true`: turns two dashes into an em dash. - * - `'oldschool'`: turns three dashes into an em dash and two into an en dash. - * - `'inverted'`: turns three dashes into an en dash and two into an em dash. - */ - dashes?: boolean | 'oldschool' | 'inverted'; - - /** - * @docs - * @name markdown.smartypants.ellipses - * @kind h4 - * @type {boolean | 'spaced' | 'unspaced'} - * @default `true` - * @version 6.1.0 - * @description - * Whether to transform triple dots into ellipses. - * - `'spaced'`: only transforms triple dots with spaces (e.g. `. . .`). - * - `'unspaced'`: only transforms triple dots without spaces (e.g. `...`). - */ - ellipses?: boolean | 'spaced' | 'unspaced'; - - /** - * @docs - * @name markdown.smartypants.openingQuotes - * @kind h4 - * @type {QuoteCharacterMap} - * @default `{ double: '“', single: '‘' }` - * @version 6.1.0 - * @description - * The specific characters to use for opening double and single quotes. - */ - openingQuotes?: QuoteCharacterMap; - - /** - * @docs - * @name markdown.smartypants.closingQuotes - * @kind h4 - * @type {QuoteCharacterMap} - * @default `{ double: '”', single: '’' }` - * @version 6.1.0 - * @description - * The specific characters to use for closing double and single quotes. - */ - closingQuotes?: QuoteCharacterMap; - }; + | Smartypants; /** * @docs diff --git a/packages/markdown/remark/package.json b/packages/markdown/remark/package.json index 0ba0e4f23de8..d51a0693f7f3 100644 --- a/packages/markdown/remark/package.json +++ b/packages/markdown/remark/package.json @@ -50,6 +50,7 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", + "retext-smartypants": "^6.2.0", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index fd9849538f19..fb4d9d7a95c9 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -2,6 +2,7 @@ import type { RemotePattern } from '@astrojs/internal-helpers/remote'; import type * as hast from 'hast'; import type * as mdast from 'mdast'; import type { Options as RemarkRehypeOptions } from 'remark-rehype'; +import type { Options as SmartypantsOptions } from "retext-smartypants"; import type { BuiltinTheme } from 'shiki'; import type * as unified from 'unified'; import type { CreateShikiHighlighterOptions, ShikiHighlighterHighlightOptions } from './shiki.js'; @@ -35,19 +36,7 @@ export type RehypePlugins = (string | [string, any] | RehypePlugin | [RehypePlug export type RemarkRehype = RemarkRehypeOptions; -export interface QuoteCharacterMap { - double: string; - single: string; -} - -export interface SmartypantsOptions { - backticks?: boolean | 'all'; - closingQuotes?: QuoteCharacterMap; - dashes?: boolean | 'oldschool' | 'inverted'; - ellipses?: boolean | 'spaced' | 'unspaced'; - openingQuotes?: QuoteCharacterMap; - quotes?: boolean; -} +export type Smartypants = SmartypantsOptions; export type ThemePresets = BuiltinTheme | 'css-variables'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 445726cad266..66b7d31a3985 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6992,6 +6992,9 @@ importers: remark-smartypants: specifier: ^3.0.2 version: 3.0.2 + retext-smartypants: + specifier: ^6.2.0 + version: 6.2.0 shiki: specifier: ^4.0.0 version: 4.0.2 From 83ff9164e69d3750472f39d79bc5e781718466bf Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:04:22 +0100 Subject: [PATCH 11/13] fix: remove export of Smartypants type from astro core --- packages/astro/src/core/config/schemas/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/config/schemas/base.ts b/packages/astro/src/core/config/schemas/base.ts index 49be664cb75f..bc2e4b9bc312 100644 --- a/packages/astro/src/core/config/schemas/base.ts +++ b/packages/astro/src/core/config/schemas/base.ts @@ -50,7 +50,7 @@ type RehypePlugin = ComplexifyWithUnion<_RehypePlugin>; type RemarkPlugin = ComplexifyWithUnion<_RemarkPlugin>; /** @lintignore */ export type RemarkRehype = ComplexifyWithOmit<_RemarkRehype>; -export type Smartypants = ComplexifyWithOmit<_Smartypants>; +type Smartypants = ComplexifyWithOmit<_Smartypants>; export const ASTRO_CONFIG_DEFAULTS = { root: '.', From 559bc7fd566b616fc574fe7837688ad4952d0d13 Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:06:58 +0100 Subject: [PATCH 12/13] fix: reexport Smartypants type but lintignore it --- packages/astro/src/core/config/schemas/base.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/config/schemas/base.ts b/packages/astro/src/core/config/schemas/base.ts index bc2e4b9bc312..f9cc154f3f1e 100644 --- a/packages/astro/src/core/config/schemas/base.ts +++ b/packages/astro/src/core/config/schemas/base.ts @@ -50,7 +50,8 @@ type RehypePlugin = ComplexifyWithUnion<_RehypePlugin>; type RemarkPlugin = ComplexifyWithUnion<_RemarkPlugin>; /** @lintignore */ export type RemarkRehype = ComplexifyWithOmit<_RemarkRehype>; -type Smartypants = ComplexifyWithOmit<_Smartypants>; +/** @lintignore */ +export type Smartypants = ComplexifyWithOmit<_Smartypants>; export const ASTRO_CONFIG_DEFAULTS = { root: '.', From 179938e2769adaa6e0c2abae59b10b384307f26c Mon Sep 17 00:00:00 2001 From: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:47:30 +0100 Subject: [PATCH 13/13] Update packages/astro/src/types/public/config.ts Co-authored-by: Armand Philippot --- packages/astro/src/types/public/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 541199a0f055..d1cfb0ff44fe 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -2053,7 +2053,7 @@ export interface AstroUserConfig< * * To disable this, set the `smartypants` flag to `false`. * - * For more control over typography, you can instead specify a configuration object with the properties [supported by `retext-smartypants`](https://github.com/retextjs/retext-smartypants?tab=readme-ov-file#fields). + * For more control over typography, you can instead specify a configuration object with the [properties supported by `retext-smartypants`](https://github.com/retextjs/retext-smartypants?tab=readme-ov-file#fields). */ smartypants?: | boolean