From 7c439868a3bc7d466418da9af669966014f3d9fe Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Mon, 27 Mar 2023 18:04:37 -0400 Subject: [PATCH] [Markdoc] New config format with runtime variable support! (#6653) * deps: esbuild * feat: support direct component imports for render! * deps: add devalue back * refactor: remove unused components prop * refactor: load experimental assets config separately * fix: upate Content type def to support props * refactor: replace astro stub with inline data * feat: pass through viteId to getRenderMod * fix: add back $entry var with defaults convention * chore: remove unneeded validateRenderProps * chore: remove uneeded validateComponents * fix: remove userMarkdocConfig prop * chore: add helpful error for legacy config * deps: kleur * fix: add back `isCapitalized` * fix: log instead of throw to avoid scary stacktrace * chore: delete more old logic (nice) * chore: delete MORE unused utils * chore: comment on separate assets config * chore: remove console.log * chore: general code cleanup * test: new render config * docs: new README * fix: add expect-error on astro:assets * feat: add defineMarkdocConfig helper * docs: update example README * test: add runtime variable * chore: lint * chore: changeset * chore: add component import deletion * docs: add notes on Vite fork * fix: astro check * chore: add `.mts` to markdoc config formats --- .changeset/metal-cameras-bow.md | 42 +++ examples/with-markdoc/README.md | 15 +- examples/with-markdoc/astro.config.mjs | 14 +- examples/with-markdoc/markdoc.config.mjs | 14 + examples/with-markdoc/package.json | 3 +- .../src/components/DocsContent.astro | 32 --- examples/with-markdoc/src/pages/index.astro | 19 +- packages/astro/src/@types/astro.ts | 1 + .../content/vite-plugin-content-imports.ts | 2 +- packages/integrations/markdoc/README.md | 250 ++++++++---------- .../markdoc/components/Renderer.astro | 19 +- .../markdoc/components/TreeNode.ts | 29 +- packages/integrations/markdoc/package.json | 7 +- packages/integrations/markdoc/src/config.ts | 5 + .../markdoc/src/default-config.ts | 18 ++ .../markdoc/src/experimental-assets-config.ts | 29 ++ packages/integrations/markdoc/src/index.ts | 208 +++++---------- .../integrations/markdoc/src/load-config.ts | 102 +++++++ packages/integrations/markdoc/src/utils.ts | 56 ---- .../template/content-module-types.d.ts | 4 +- .../markdoc/test/content-collections.test.js | 196 ++------------ .../fixtures/content-collections/package.json | 4 - .../src/content/blog/post-1.mdoc | 7 + .../src/content/blog/post-2.mdoc | 7 + .../src/content/blog/post-3.mdoc | 7 + .../src/pages/entry.json.js | 2 +- .../fixtures/render-simple/astro.config.mjs | 7 + .../test/fixtures/render-simple/package.json | 9 + .../src/content/blog/simple.mdoc | 0 .../src/pages/index.astro} | 3 +- .../render-with-components/astro.config.mjs | 7 + .../render-with-components/markdoc.config.mjs | 28 ++ .../render-with-components/package.json | 12 + .../src/components/Code.astro | 0 .../src/components/CustomMarquee.astro | 0 .../src/content/blog/with-components.mdoc | 0 .../src/pages/index.astro} | 8 +- .../render-with-config/astro.config.mjs | 7 + .../render-with-config/markdoc.config.mjs | 15 ++ .../fixtures/render-with-config/package.json | 9 + .../src/content/blog/with-config.mdoc | 4 + .../src/pages/index.astro} | 4 +- .../integrations/markdoc/test/render.test.js | 124 +++++++++ pnpm-lock.yaml | 38 ++- 44 files changed, 734 insertions(+), 633 deletions(-) create mode 100644 .changeset/metal-cameras-bow.md create mode 100644 examples/with-markdoc/markdoc.config.mjs delete mode 100644 examples/with-markdoc/src/components/DocsContent.astro create mode 100644 packages/integrations/markdoc/src/config.ts create mode 100644 packages/integrations/markdoc/src/default-config.ts create mode 100644 packages/integrations/markdoc/src/experimental-assets-config.ts create mode 100644 packages/integrations/markdoc/src/load-config.ts create mode 100644 packages/integrations/markdoc/test/fixtures/content-collections/src/content/blog/post-1.mdoc create mode 100644 packages/integrations/markdoc/test/fixtures/content-collections/src/content/blog/post-2.mdoc create mode 100644 packages/integrations/markdoc/test/fixtures/content-collections/src/content/blog/post-3.mdoc create mode 100644 packages/integrations/markdoc/test/fixtures/render-simple/astro.config.mjs create mode 100644 packages/integrations/markdoc/test/fixtures/render-simple/package.json rename packages/integrations/markdoc/test/fixtures/{content-collections => render-simple}/src/content/blog/simple.mdoc (100%) rename packages/integrations/markdoc/test/fixtures/{content-collections/src/pages/content-simple.astro => render-simple/src/pages/index.astro} (92%) create mode 100644 packages/integrations/markdoc/test/fixtures/render-with-components/astro.config.mjs create mode 100644 packages/integrations/markdoc/test/fixtures/render-with-components/markdoc.config.mjs create mode 100644 packages/integrations/markdoc/test/fixtures/render-with-components/package.json rename packages/integrations/markdoc/test/fixtures/{content-collections => render-with-components}/src/components/Code.astro (100%) rename packages/integrations/markdoc/test/fixtures/{content-collections => render-with-components}/src/components/CustomMarquee.astro (100%) rename packages/integrations/markdoc/test/fixtures/{content-collections => render-with-components}/src/content/blog/with-components.mdoc (100%) rename packages/integrations/markdoc/test/fixtures/{content-collections/src/pages/content-with-components.astro => render-with-components/src/pages/index.astro} (65%) create mode 100644 packages/integrations/markdoc/test/fixtures/render-with-config/astro.config.mjs create mode 100644 packages/integrations/markdoc/test/fixtures/render-with-config/markdoc.config.mjs create mode 100644 packages/integrations/markdoc/test/fixtures/render-with-config/package.json rename packages/integrations/markdoc/test/fixtures/{content-collections => render-with-config}/src/content/blog/with-config.mdoc (75%) rename packages/integrations/markdoc/test/fixtures/{content-collections/src/pages/content-with-config.astro => render-with-config/src/pages/index.astro} (85%) create mode 100644 packages/integrations/markdoc/test/render.test.js diff --git a/.changeset/metal-cameras-bow.md b/.changeset/metal-cameras-bow.md new file mode 100644 index 000000000..2275c4804 --- /dev/null +++ b/.changeset/metal-cameras-bow.md @@ -0,0 +1,42 @@ +--- +'@astrojs/markdoc': minor +'astro': patch +--- + +Simplify Markdoc configuration with a new `markdoc.config.mjs` file. This lets you import Astro components directly to render as Markdoc tags and nodes, without the need for the previous `components` property. This new configuration also unlocks passing variables to your Markdoc from the `Content` component ([see the new docs](https://docs.astro.build/en/guides/integrations-guide/markdoc/#pass-markdoc-variables)). + +## Migration + +Move any existing Markdoc config from your `astro.config` to a new `markdoc.config.mjs` file at the root of your project. This should be applied as a default export, with the optional `defineMarkdocConfig()` helper for autocomplete in your editor. + +This example configures an `aside` Markdoc tag. Note that components should be imported and applied to the `render` attribute _directly,_ instead of passing the name as a string: + +```js +// markdoc.config.mjs +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import Aside from './src/components/Aside.astro'; + +export default defineMarkdocConfig({ + tags: { + aside: { + render: Aside, + } + } +}); +``` + +You should also remove the `components` prop from your `Content` components. Since components are imported into your config directly, this is no longer needed. + +```diff +--- +- import Aside from '../components/Aside.astro'; +import { getEntryBySlug } from 'astro:content'; + +const entry = await getEntryBySlug('docs', 'why-markdoc'); +const { Content } = await entry.render(); +--- + + +``` diff --git a/examples/with-markdoc/README.md b/examples/with-markdoc/README.md index 62f7cbfc8..b5adbf27b 100644 --- a/examples/with-markdoc/README.md +++ b/examples/with-markdoc/README.md @@ -23,23 +23,20 @@ Inside of your Astro project, you'll see the following folders and files: └── docs/ │ └── intro.mdoc | └── config.ts -│ └── components/ -| ├── Aside.astro -│ └── DocsContent.astro -│ └── layouts/ -│ └── Layout.astro -│ └── pages/ -│ └── index.astro +│ └── components/Aside.astro +│ └── layouts/Layout.astro +│ └── pages/index.astro | └── env.d.ts ├── astro.config.mjs +├── markdoc.config.mjs ├── README.md ├── package.json └── tsconfig.json ``` -Markdoc (`.mdoc`) files can be used in content collections to author your Markdown content alongside Astro and server-rendered UI framework components (React, Vue, Svelte, and more). See `src/content/docs/` for an example file. +Markdoc (`.mdoc`) files can be used in content collections. See `src/content/docs/` for an example file. -You can also apply Astro components and server-rendered UI components (React, Vue, Svelte, etc) to your Markdoc files. See `src/content/DocsContent.astro` for an example. +You can also render Astro components from your Markdoc files using [tags](https://markdoc.dev/docs/tags). See the `markdoc.config.mjs` file for an example configuration. ## 🧞 Commands diff --git a/examples/with-markdoc/astro.config.mjs b/examples/with-markdoc/astro.config.mjs index d88ed2098..29d846359 100644 --- a/examples/with-markdoc/astro.config.mjs +++ b/examples/with-markdoc/astro.config.mjs @@ -3,17 +3,5 @@ import markdoc from '@astrojs/markdoc'; // https://astro.build/config export default defineConfig({ - integrations: [ - markdoc({ - tags: { - aside: { - render: 'Aside', - attributes: { - type: { type: String }, - title: { type: String }, - }, - }, - }, - }), - ], + integrations: [markdoc()], }); diff --git a/examples/with-markdoc/markdoc.config.mjs b/examples/with-markdoc/markdoc.config.mjs new file mode 100644 index 000000000..0ae63d4ee --- /dev/null +++ b/examples/with-markdoc/markdoc.config.mjs @@ -0,0 +1,14 @@ +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import Aside from './src/components/Aside.astro'; + +export default defineMarkdocConfig({ + tags: { + aside: { + render: Aside, + attributes: { + type: { type: String }, + title: { type: String }, + }, + }, + }, +}); diff --git a/examples/with-markdoc/package.json b/examples/with-markdoc/package.json index 9ca562fa3..f31392840 100644 --- a/examples/with-markdoc/package.json +++ b/examples/with-markdoc/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@astrojs/markdoc": "^0.0.5", - "astro": "^2.1.7" + "astro": "^2.1.7", + "kleur": "^4.1.5" } } diff --git a/examples/with-markdoc/src/components/DocsContent.astro b/examples/with-markdoc/src/components/DocsContent.astro deleted file mode 100644 index 162c1fc6d..000000000 --- a/examples/with-markdoc/src/components/DocsContent.astro +++ /dev/null @@ -1,32 +0,0 @@ ---- -import Aside from './Aside.astro'; -import type { CollectionEntry } from 'astro:content'; - -type Props = { - entry: CollectionEntry<'docs'>; -}; - -const { entry } = Astro.props; -const { Content } = await entry.render(); ---- - - - - diff --git a/examples/with-markdoc/src/pages/index.astro b/examples/with-markdoc/src/pages/index.astro index 01412cce1..7efcbeda8 100644 --- a/examples/with-markdoc/src/pages/index.astro +++ b/examples/with-markdoc/src/pages/index.astro @@ -1,18 +1,25 @@ --- import { getEntryBySlug } from 'astro:content'; -import DocsContent from '../components/DocsContent.astro'; import Layout from '../layouts/Layout.astro'; const intro = await getEntryBySlug('docs', 'intro'); +const { Content } = await intro.render(); ---

{intro.data.title}

- - - - - +
+ + diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index a496a5aa0..e1d817607 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1056,6 +1056,7 @@ export interface ContentEntryType { getRenderModule?( this: rollup.PluginContext, params: { + viteId: string; entry: ContentEntryModule; } ): rollup.LoadResult | Promise; diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index 7ad71b31e..4437f4fa0 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -139,7 +139,7 @@ export const _internal = { }); } - return contentRenderer.bind(this)({ entry }); + return contentRenderer.bind(this)({ entry, viteId }); }, }); } diff --git a/packages/integrations/markdoc/README.md b/packages/integrations/markdoc/README.md index 76291e763..85a657ea4 100644 --- a/packages/integrations/markdoc/README.md +++ b/packages/integrations/markdoc/README.md @@ -99,53 +99,46 @@ const { Content } = await entry.render(); ### Using components -You can add Astro and UI framework components (React, Vue, Svelte, etc.) to your Markdoc using both [Markdoc tags][markdoc-tags] and HTML element [nodes][markdoc-nodes]. +You can add Astro components to your Markdoc using both [Markdoc tags][markdoc-tags] and HTML element [nodes][markdoc-nodes]. #### Render Markdoc tags as Astro components -You may configure [Markdoc tags][markdoc-tags] that map to components. You can configure a new tag from your `astro.config` using the `tags` attribute. +You may configure [Markdoc tags][markdoc-tags] that map to components. You can configure a new tag by creating a `markdoc.config.mjs|ts` file at the root of your project and configuring the `tag` attribute. +This example renders an `Aside` component, and allows a `type` prop to be passed as a string: ```js -// astro.config.mjs -import { defineConfig } from 'astro/config'; -import markdoc from '@astrojs/markdoc'; +// markdoc.config.mjs +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import Aside from './src/components/Aside.astro'; -// https://astro.build/config -export default defineConfig({ - integrations: [ - markdoc({ - tags: { - aside: { - render: 'Aside', - attributes: { - // Component props as attribute definitions - // See Markdoc's documentation on defining attributes - // https://markdoc.dev/docs/attributes#defining-attributes - type: { type: String }, - } - }, - }, - }), - ], -}); +export default defineMarkdocConfig({ + tags: { + aside: { + render: Aside, + attributes: { + // Markdoc requires type defs for each attribute. + // These should mirror the `Props` type of the component + // you are rendering. + // See Markdoc's documentation on defining attributes + // https://markdoc.dev/docs/attributes#defining-attributes + type: { type: String }, + } + }, + }, +}) ``` -Then, you can wire this render name (`'Aside'`) to a component from the `components` prop via the `` component. Note the object key name (`Aside` in this case) should match the render name: +This component can now be used in your Markdoc files with the `{% aside %}` tag. Children will be passed to your component's default slot: +```md +# Welcome to Markdoc 👋 -```astro ---- -import { getEntryBySlug } from 'astro:content'; -import Aside from '../components/Aside.astro'; +{% aside type="tip" %} -const entry = await getEntryBySlug('docs', 'why-markdoc'); -const { Content } = await entry.render(); ---- +Use tags like this fancy "aside" to add some *flair* to your docs. - +{% /aside %} ``` #### Render Markdoc nodes / HTML elements as Astro components @@ -153,46 +146,22 @@ const { Content } = await entry.render(); You may also want to map standard HTML elements like headings and paragraphs to components. For this, you can configure a custom [Markdoc node][markdoc-nodes]. This example overrides Markdoc's `heading` node to render a `Heading` component, passing the built-in `level` attribute as a prop: ```js -// astro.config.mjs -import { defineConfig } from 'astro/config'; -import markdoc from '@astrojs/markdoc'; +// markdoc.config.mjs +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import Heading from './src/components/Heading.astro'; -// https://astro.build/config -export default defineConfig({ - integrations: [ - markdoc({ - nodes: { - heading: { - render: 'Heading', - // Markdoc requires type defs for each attribute. - // These should mirror the `Props` type of the component - // you are rendering. - // See Markdoc's documentation on defining attributes - // https://markdoc.dev/docs/attributes#defining-attributes - attributes: { - level: { type: String }, - } - }, - }, - }), - ], -}); -``` - -Now, you can map the string passed to render (`'Heading'` in this example) to a component import. This is configured from the `` component used to render your Markdoc using the `components` prop: - -```astro ---- -import { getEntryBySlug } from 'astro:content'; -import Heading from '../components/Heading.astro'; - -const entry = await getEntryBySlug('docs', 'why-markdoc'); -const { Content } = await entry.render(); ---- - - +export default defineMarkdocConfig({ + nodes: { + heading: { + render: Heading, + attributes: { + // Pass the attributes from Markdoc's default heading node + // as component props. + level: { type: String }, + } + }, + }, +}) ``` Now, all Markdown headings will render with the `Heading.astro` component. This example uses a level 3 heading, automatically passing `level: 3` as the component prop: @@ -215,26 +184,26 @@ This example wraps a `Aside.tsx` component with a `ClientAside.astro` wrapper: import Aside from './Aside'; --- -