0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

Add SmartyPants flag (#5769)

* feat: add smartypants flag

* test: smartypants in markdown and mdx

* docs: Smartypants -> SmartyPants

* chore: changeset

* chore: update changeset with 1.0 -> 2.0 in mind

* chore: bump to minor change
This commit is contained in:
Ben Holmes 2023-01-06 09:26:02 -05:00 committed by GitHub
parent 04bf679a5d
commit 93e633922c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 155 additions and 16 deletions

View file

@ -0,0 +1,34 @@
---
'astro': minor
'@astrojs/mdx': minor
'@astrojs/markdown-remark': minor
---
Introduce a `smartypants` flag to opt-out of Astro's default SmartyPants plugin.
```js
{
markdown: {
smartypants: false,
}
}
```
#### Migration
You may have disabled Astro's built-in plugins (GitHub-Flavored Markdown and Smartypants) with the `extendDefaultPlugins` option. This has now been split into 2 flags to disable each plugin individually:
- `markdown.gfm` to disable GitHub-Flavored Markdown
- `markdown.smartypants` to disable SmartyPants
```diff
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
- extendDefaultPlugins: false,
+ smartypants: false,
+ gfm: false,
}
});
```

View file

@ -785,6 +785,23 @@ export interface AstroUserConfig {
* ```
*/
gfm?: boolean;
/**
* @docs
* @name markdown.smartypants
* @type {boolean}
* @default `true`
* @description
* Astro uses the [SmartyPants formatter](https://daringfireball.net/projects/smartypants/) by default. To disable this, set the `smartypants` flag to `false`:
*
* ```js
* {
* markdown: {
* smartypants: false,
* }
* }
* ```
*/
smartypants?: boolean;
/**
* @docs
* @name markdown.remarkRehype

View file

@ -163,6 +163,7 @@ export const AstroConfigSchema = z.object({
.optional()
.default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype),
gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm),
smartypants: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.smartypants),
})
.default({}),
vite: z

View file

@ -47,25 +47,23 @@ describe('Astro Markdown plugins', () => {
});
// Asserts Astro 1.0 behavior is removed. Test can be removed in Astro 3.0.
it('Still applies GFM when user plugins are provided', async () => {
it('Still applies default plugins when user plugins are provided', async () => {
const fixture = await buildFixture({
markdown: {
remarkPlugins: [remarkExamplePlugin],
rehypePlugins: [[addClasses, { 'h1,h2,h3': 'title' }]],
},
});
const html = await fixture.readFile('/with-gfm/index.html');
const $ = cheerio.load(html);
const gfmHtml = await fixture.readFile('/with-gfm/index.html');
const $1 = cheerio.load(gfmHtml);
expect($1('a[href="https://example.com"]')).to.have.lengthOf(1);
// test 1: GFM autolink applied correctly
expect($('a[href="https://example.com"]')).to.have.lengthOf(1);
const smartypantsHtml = await fixture.readFile('/with-smartypants/index.html');
const $2 = cheerio.load(smartypantsHtml);
expect($2('p').html()).to.equal('“Smartypants” is — awesome');
// test 2: remark plugins still applied
expect(html).to.include('Remark plugin applied!');
// test 3: rehype plugins still applied
expect($('#github-flavored-markdown-test')).to.have.lengthOf(1);
expect($('#github-flavored-markdown-test').hasClass('title')).to.equal(true);
testRemark(gfmHtml);
testRehype(gfmHtml, '#github-flavored-markdown-test');
});
for (const gfm of [true, false]) {
@ -87,12 +85,42 @@ describe('Astro Markdown plugins', () => {
expect($('a[href="https://example.com"]')).to.have.lengthOf(0);
}
// test 2: remark plugins still applied
expect(html).to.include('Remark plugin applied!');
testRemark(html);
testRehype(html, '#github-flavored-markdown-test');
});
}
// test 3: rehype plugins still applied
expect($('#github-flavored-markdown-test')).to.have.lengthOf(1);
expect($('#github-flavored-markdown-test').hasClass('title')).to.equal(true);
for (const smartypants of [true, false]) {
it(`Handles SmartyPants when smartypants = ${smartypants}`, async () => {
const fixture = await buildFixture({
markdown: {
remarkPlugins: [remarkExamplePlugin],
rehypePlugins: [[addClasses, { 'h1,h2,h3': 'title' }]],
smartypants,
},
});
const html = await fixture.readFile('/with-smartypants/index.html');
const $ = cheerio.load(html);
// test 1: GFM autolink applied correctly
if (smartypants === true) {
expect($('p').html()).to.equal('“Smartypants” is — awesome');
} else {
expect($('p').html()).to.equal('"Smartypants" is -- awesome');
}
testRemark(html);
testRehype(html, '#smartypants-test');
});
}
});
function testRehype(html, headingId) {
const $ = cheerio.load(html);
expect($(headingId)).to.have.lengthOf(1);
expect($(headingId).hasClass('title')).to.equal(true);
}
function testRemark(html) {
expect(html).to.include('Remark plugin applied!');
}

View file

@ -0,0 +1,3 @@
# Smartypants test
"Smartypants" is -- awesome

View file

@ -43,6 +43,7 @@
"rehype-raw": "^6.1.1",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"remark-smartypants": "^2.0.0",
"shiki": "^0.11.1",
"unist-util-visit": "^4.1.0",
"vfile": "^5.3.2"

View file

@ -186,6 +186,7 @@ function applyDefaultOptions({
recmaPlugins: options.recmaPlugins ?? defaults.recmaPlugins,
remarkRehype: options.remarkRehype ?? defaults.remarkRehype,
gfm: options.gfm ?? defaults.gfm,
smartypants: options.smartypants ?? defaults.smartypants,
remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins,
rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins,
shikiConfig: options.shikiConfig ?? defaults.shikiConfig,

View file

@ -14,6 +14,7 @@ import type { Image } from 'mdast';
import { pathToFileURL } from 'node:url';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import remarkSmartypants from 'remark-smartypants';
import { visit } from 'unist-util-visit';
import type { VFile } from 'vfile';
import { MdxOptions } from './index.js';
@ -153,6 +154,9 @@ export async function getRemarkPlugins(
if (mdxOptions.gfm) {
remarkPlugins.push(remarkGfm);
}
if (mdxOptions.smartypants) {
remarkPlugins.push(remarkSmartypants);
}
remarkPlugins = [...remarkPlugins, ...ignoreStringPlugins(mdxOptions.remarkPlugins)];

View file

@ -21,3 +21,5 @@ Oh cool, more text!
And section 2, with a hyperlink to check GFM is preserved: https://handle-me-gfm.com
<div data-recma-plugin-works={recmaPluginWorking}></div>
> "Smartypants" is -- awesome

View file

@ -36,6 +36,19 @@ describe('MDX plugins', () => {
expect(selectGfmLink(document)).to.not.be.null;
});
it('Applies SmartyPants by default', async () => {
const fixture = await buildFixture({
integrations: [mdx()],
});
const html = await fixture.readFile(FILE);
const { document } = parseHTML(html);
const quote = selectSmartypantsQuote(document);
expect(quote).to.not.be.null;
expect(quote.textContent).to.contain('“Smartypants” is — awesome');
});
it('supports custom rehype plugins', async () => {
const fixture = await buildFixture({
integrations: [
@ -88,6 +101,7 @@ describe('MDX plugins', () => {
markdown: {
remarkPlugins: [remarkToc],
gfm: false,
smartypants: false,
},
integrations: [
mdx({
@ -129,6 +143,23 @@ describe('MDX plugins', () => {
expect(selectGfmLink(document), 'Respects `markdown.gfm` unexpectedly.').to.not.be.null;
}
});
it('Handles smartypants', async () => {
const html = await fixture.readFile(FILE);
const { document } = parseHTML(html);
const quote = selectSmartypantsQuote(document);
if (extendMarkdownConfig === true) {
expect(quote.textContent, 'Does not respect `markdown.smartypants` option.').to.contain(
'"Smartypants" is -- awesome'
);
} else {
expect(quote.textContent, 'Respects `markdown.smartypants` unexpectedly.').to.contain(
'“Smartypants” is — awesome'
);
}
});
});
}
@ -202,6 +233,10 @@ function selectGfmLink(document) {
return document.querySelector('a[href="https://handle-me-gfm.com"]');
}
function selectSmartypantsQuote(document) {
return document.querySelector('blockquote');
}
function selectRemarkExample(document) {
return document.querySelector('div[data-remark-plugin-works]');
}

View file

@ -43,6 +43,7 @@
"remark-gfm": "^3.0.1",
"remark-parse": "^10.0.1",
"remark-rehype": "^10.1.0",
"remark-smartypants": "^2.0.0",
"shiki": "^0.11.1",
"unified": "^10.1.2",
"unist-util-map": "^3.1.1",

View file

@ -24,6 +24,7 @@ import remarkUnwrap from './remark-unwrap.js';
import rehypeRaw from 'rehype-raw';
import rehypeStringify from 'rehype-stringify';
import remarkGfm from 'remark-gfm';
import remarkSmartypants from 'remark-smartypants';
import markdown from 'remark-parse';
import markdownToHtml from 'remark-rehype';
import { unified } from 'unified';
@ -43,6 +44,7 @@ export const markdownConfigDefaults: Omit<Required<AstroMarkdownOptions>, 'draft
rehypePlugins: [],
remarkRehype: {},
gfm: true,
smartypants: true,
};
/** Shared utility for rendering markdown */
@ -58,6 +60,7 @@ export async function renderMarkdown(
rehypePlugins = markdownConfigDefaults.rehypePlugins,
remarkRehype = markdownConfigDefaults.remarkRehype,
gfm = markdownConfigDefaults.gfm,
smartypants = markdownConfigDefaults.smartypants,
isAstroFlavoredMd = false,
isExperimentalContentCollections = false,
contentDir,
@ -75,6 +78,10 @@ export async function renderMarkdown(
parser.use(remarkGfm);
}
if (smartypants) {
parser.use(remarkSmartypants);
}
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));

View file

@ -48,6 +48,7 @@ export interface AstroMarkdownOptions {
rehypePlugins?: RehypePlugins;
remarkRehype?: RemarkRehype;
gfm?: boolean;
smartypants?: boolean;
}
export interface MarkdownRenderingOptions extends AstroMarkdownOptions {

View file

@ -2917,6 +2917,7 @@ importers:
remark-gfm: ^3.0.1
remark-rehype: ^10.1.0
remark-shiki-twoslash: ^3.1.0
remark-smartypants: ^2.0.0
remark-toc: ^8.0.1
shiki: ^0.11.1
unist-util-visit: ^4.1.0
@ -2936,6 +2937,7 @@ importers:
rehype-raw: 6.1.1
remark-frontmatter: 4.0.1
remark-gfm: 3.0.1
remark-smartypants: 2.0.0
shiki: 0.11.1
unist-util-visit: 4.1.1
vfile: 5.3.6
@ -3520,6 +3522,7 @@ importers:
remark-gfm: ^3.0.1
remark-parse: ^10.0.1
remark-rehype: ^10.1.0
remark-smartypants: ^2.0.0
shiki: ^0.11.1
unified: ^10.1.2
unist-util-map: ^3.1.1
@ -3544,6 +3547,7 @@ importers:
remark-gfm: 3.0.1
remark-parse: 10.0.1
remark-rehype: 10.1.0
remark-smartypants: 2.0.0
shiki: 0.11.1
unified: 10.1.2
unist-util-map: 3.1.2