From ac03218247763e4782824e220a384fd20ae6d769 Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Thu, 25 Aug 2022 17:21:56 -0400 Subject: [PATCH] [MD] `extendDefaultPlugins` to preserve default remark plugins (#4474) * feat: add { extends } to markdown config * test: remark plugins with extends * deps: pnpm lock * chore: changeset * fix: remarkPlugins -> rehypePlugins * docs: update markdown config reference * Revert "feat: add { extends } to markdown config" This reverts commit 5d050bbcf9a2c0d470cae79c4d0a954d489f4e8c. * feat: new "extendDefaultPlugins" flag * docs: update config * nit: We -> Astro applies * fix: backticks on `false` * nit: Note -> REAL note Co-authored-by: Sarah Rainsberger * docs: note -> caution Co-authored-by: Sarah Rainsberger --- .changeset/cool-turtles-lay.md | 6 ++ packages/astro/src/@types/astro.ts | 49 +++++++++++----- packages/astro/src/core/config.ts | 1 + .../astro/test/astro-markdown-plugins.test.js | 58 +++++++++++++++---- .../astro-markdown-plugins/astro.config.mjs | 3 +- .../astro-markdown-plugins/package.json | 1 - .../src/pages/with-gfm.md | 3 + packages/markdown/remark/src/index.ts | 7 ++- packages/markdown/remark/src/types.ts | 1 + pnpm-lock.yaml | 2 - 10 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 .changeset/cool-turtles-lay.md create mode 100644 packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-gfm.md diff --git a/.changeset/cool-turtles-lay.md b/.changeset/cool-turtles-lay.md new file mode 100644 index 000000000..e58bc57c7 --- /dev/null +++ b/.changeset/cool-turtles-lay.md @@ -0,0 +1,6 @@ +--- +'astro': minor +'@astrojs/markdown-remark': minor +--- + +Add "extends" to markdown plugin config to preserve Astro defaults diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 64b246238..4b4948671 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -648,17 +648,19 @@ export interface AstroUserConfig { * @name markdown.remarkPlugins * @type {RemarkPlugins} * @description - * Pass a custom [Remark](https://github.com/remarkjs/remark) plugin to customize how your Markdown is built. + * Pass [remark plugins](https://github.com/remarkjs/remark) to customize how your Markdown is built. You can import and apply the plugin function (recommended), or pass the plugin name as a string. * - * **Note:** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support and [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired. + * :::caution + * Providing a list of plugins will **remove** our default plugins. To preserve these defaults, see the `extendDefaultPlugins` flag. + * ::: * * ```js + * import remarkToc from 'remark-toc'; * { * markdown: { - * // Example: The default set of remark plugins used by Astro - * remarkPlugins: ['remark-gfm', 'remark-smartypants'], - * }, - * }; + * remarkPlugins: [remarkToc] + * } + * } * ``` */ remarkPlugins?: RemarkPlugins; @@ -667,26 +669,47 @@ export interface AstroUserConfig { * @name markdown.rehypePlugins * @type {RehypePlugins} * @description - * Pass a custom [Rehype](https://github.com/remarkjs/remark-rehype) plugin to customize how your Markdown is built. + * Pass [rehype plugins](https://github.com/remarkjs/remark-rehype) to customize how your Markdown's output HTML is processed. You can import and apply the plugin function (recommended), or pass the plugin name as a string. * - * **Note:** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support and [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired. + * :::caution + * Providing a list of plugins will **remove** our default plugins. To preserve these defaults, see the `extendDefaultPlugins` flag. + * ::: * * ```js + * import rehypeMinifyHtml from 'rehype-minify'; * { * markdown: { - * // Example: The default set of rehype plugins used by Astro - * rehypePlugins: [], - * }, - * }; + * rehypePlugins: [rehypeMinifyHtml] + * } + * } * ``` */ rehypePlugins?: RehypePlugins; + /** + * @docs + * @name markdown.extendDefaultPlugins + * @type {boolean} + * @default `false` + * @description + * Astro applies the [GitHub-flavored Markdown](https://github.com/remarkjs/remark-gfm) and [Smartypants](https://github.com/silvenon/remark-smartypants) plugins by default. When adding your own remark or rehype plugins, you can preserve these defaults by setting the `extendDefaultPlugins` flag to `true`: + * + * ```js + * { + * markdown: { + * extendDefaultPlugins: true, + * remarkPlugins: [exampleRemarkPlugin], + * rehypePlugins: [exampleRehypePlugin], + * } + * } + * ``` + */ + extendDefaultPlugins?: boolean; /** * @docs * @name markdown.remarkRehype * @type {RemarkRehype} * @description - * Pass options to [remark-rehype](https://github.com/remarkjs/remark-rehype#api) . + * Pass options to [remark-rehype](https://github.com/remarkjs/remark-rehype#api). * * ```js * { diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index 6b9f83e13..aa8d701e7 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -219,6 +219,7 @@ export const AstroConfigSchema = z.object({ .custom((data) => data instanceof Object && !Array.isArray(data)) .optional() .default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype), + extendDefaultPlugins: z.boolean().default(false), }) .default({}), vite: z diff --git a/packages/astro/test/astro-markdown-plugins.test.js b/packages/astro/test/astro-markdown-plugins.test.js index 9a925bdf0..c65284177 100644 --- a/packages/astro/test/astro-markdown-plugins.test.js +++ b/packages/astro/test/astro-markdown-plugins.test.js @@ -3,12 +3,24 @@ import * as cheerio from 'cheerio'; import { loadFixture } from './test-utils.js'; import addClasses from './fixtures/astro-markdown-plugins/add-classes.mjs'; -describe('Astro Markdown plugins', () => { - let fixture; +async function buildFixture(config) { + const fixture = await loadFixture({ + root: './fixtures/astro-markdown-plugins/', + ...config, + }); + await fixture.build(); + return fixture; +} - before(async () => { - fixture = await loadFixture({ - root: './fixtures/astro-markdown-plugins/', +function remarkExamplePlugin() { + return (tree) => { + tree.children.push({ type: 'paragraph', children: [{ type: 'text', value: 'Remark plugin applied!' }] }) + } +} + +describe('Astro Markdown plugins', () => { + it('Can render markdown with plugins', async () => { + const fixture = await buildFixture({ markdown: { remarkPlugins: [ 'remark-code-titles', @@ -21,17 +33,43 @@ describe('Astro Markdown plugins', () => { ], }, }); - await fixture.build(); - }); - - it('Can render markdown with plugins', async () => { const html = await fixture.readFile('/index.html'); const $ = cheerio.load(html); // test 1: Added a TOC expect($('.toc')).to.have.lengthOf(1); - // teste 2: Added .title to h1 + // test 2: Added .title to h1 expect($('#hello-world').hasClass('title')).to.equal(true); }); + + for (const extendDefaultPlugins of [true, false]) { + it(`Handles default plugins when extendDefaultPlugins = ${extendDefaultPlugins}`, async () => { + const fixture = await buildFixture({ + markdown: { + remarkPlugins: [remarkExamplePlugin], + rehypePlugins: [ + [addClasses, { 'h1,h2,h3': 'title' }], + ], + extendDefaultPlugins, + }, + }); + const html = await fixture.readFile('/with-gfm/index.html'); + const $ = cheerio.load(html); + + // test 1: GFM autolink applied correctly + if (extendDefaultPlugins === true) { + expect($('a[href="https://example.com"]')).to.have.lengthOf(1); + } else { + expect($('a[href="https://example.com"]')).to.have.lengthOf(0); + } + + // test 2: (sanity check) remark plugins still applied + expect(html).to.include('Remark plugin applied!'); + + // test 3: (sanity check) rehype plugins still applied + expect($('#github-flavored-markdown-test')).to.have.lengthOf(1); + expect($('#github-flavored-markdown-test').hasClass('title')).to.equal(true); + }) + } }); diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs index 08916b1fe..db9a6db87 100644 --- a/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs @@ -1,7 +1,6 @@ import { defineConfig } from 'astro/config'; -import preact from '@astrojs/preact'; // https://astro.build/config export default defineConfig({ - integrations: [preact()], + integrations: [], }); diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/package.json b/packages/astro/test/fixtures/astro-markdown-plugins/package.json index bdad17d55..e3970aa45 100644 --- a/packages/astro/test/fixtures/astro-markdown-plugins/package.json +++ b/packages/astro/test/fixtures/astro-markdown-plugins/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "dependencies": { - "@astrojs/preact": "workspace:*", "astro": "workspace:*", "hast-util-select": "^5.0.2", "rehype-slug": "^5.0.1" diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-gfm.md b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-gfm.md new file mode 100644 index 000000000..bbb0e7399 --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/with-gfm.md @@ -0,0 +1,3 @@ +# GitHub-flavored Markdown test + +This should auto-gen a link: https://example.com diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index da64a5459..c64bdac0e 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -39,6 +39,7 @@ export async function renderMarkdown( remarkPlugins = [], rehypePlugins = [], remarkRehype = {}, + extendDefaultPlugins = false, isAstroFlavoredMd = false, } = opts; const input = new VFile({ value: content, path: fileURL }); @@ -50,9 +51,9 @@ export async function renderMarkdown( .use(remarkInitializeAstroData) .use(isAstroFlavoredMd ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []); - if (remarkPlugins.length === 0 && rehypePlugins.length === 0) { - remarkPlugins = [...DEFAULT_REMARK_PLUGINS]; - rehypePlugins = [...DEFAULT_REHYPE_PLUGINS]; + if (extendDefaultPlugins || (remarkPlugins.length === 0 && rehypePlugins.length === 0)) { + remarkPlugins = [...DEFAULT_REMARK_PLUGINS, ...remarkPlugins]; + rehypePlugins = [...DEFAULT_REHYPE_PLUGINS, ...rehypePlugins]; } const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins)); diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index 9df11d414..5b40c9f9d 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -43,6 +43,7 @@ export interface AstroMarkdownOptions { remarkPlugins?: RemarkPlugins; rehypePlugins?: RehypePlugins; remarkRehype?: RemarkRehype; + extendDefaultPlugins?: boolean; } export interface MarkdownRenderingOptions extends AstroMarkdownOptions { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a51eacbd..a6fa6af7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1286,12 +1286,10 @@ importers: packages/astro/test/fixtures/astro-markdown-plugins: specifiers: - '@astrojs/preact': workspace:* astro: workspace:* hast-util-select: ^5.0.2 rehype-slug: ^5.0.1 dependencies: - '@astrojs/preact': link:../../../../integrations/preact astro: link:../../.. hast-util-select: 5.0.2 rehype-slug: 5.0.1