From c06f83ef85146831457fde1f2126b338f656b093 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Tue, 14 Feb 2023 12:56:01 -0500 Subject: [PATCH] feat: support `.md` overrides for content collections --- packages/astro/src/@types/astro.ts | 1 + packages/astro/src/content/index.ts | 7 ++++++- packages/astro/src/content/utils.ts | 9 +++++++++ .../src/vite-plugin-markdown/content-entry-type.ts | 3 +++ packages/astro/src/vite-plugin-markdown/index.ts | 10 ++++++++++ packages/integrations/markdoc/src/index.ts | 7 +++++-- packages/integrations/mdx/src/index.ts | 1 + 7 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index b43a31dfec08..156bbbb58656 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -978,6 +978,7 @@ export interface AstroConfig extends z.output { } export interface ContentEntryType { + name: string; extensions: string[]; getEntryInfo(params: { fileUrl: URL; contents: string }): Promise<{ data: Record; diff --git a/packages/astro/src/content/index.ts b/packages/astro/src/content/index.ts index f12106b0aa31..67e55e48da4a 100644 --- a/packages/astro/src/content/index.ts +++ b/packages/astro/src/content/index.ts @@ -1,6 +1,11 @@ export { attachContentServerListeners } from './server-listeners.js'; export { createContentTypesGenerator } from './types-generator.js'; -export { contentObservable, getContentPaths, getDotAstroTypeReference } from './utils.js'; +export { + contentObservable, + getContentPaths, + getDotAstroTypeReference, + hasMdContentEntryTypeOverride, +} from './utils.js'; export { astroContentAssetPropagationPlugin } from './vite-plugin-content-assets.js'; export { astroContentImportPlugin } from './vite-plugin-content-imports.js'; export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js'; diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 81aab319e121..26e68834ee57 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -7,6 +7,7 @@ import { ErrorPayload as ViteErrorPayload, normalizePath, ViteDevServer } from ' import { z } from 'zod'; import { AstroConfig, AstroSettings } from '../@types/astro.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; +import { MARKDOWN_CONTENT_ENTRY_TYPE_NAME } from '../vite-plugin-markdown/content-entry-type.js'; import { CONTENT_TYPES_FILE } from './consts.js'; export const collectionConfigParser = z.object({ @@ -347,3 +348,11 @@ function search(fs: typeof fsMod, srcDir: URL) { } return { exists: false, url: paths[0] }; } + +export function hasMdContentEntryTypeOverride(settings: Pick) { + return settings.contentEntryTypes.some( + (contentEntryType) => + contentEntryType.name !== MARKDOWN_CONTENT_ENTRY_TYPE_NAME && + contentEntryType.extensions.includes('.md') + ); +} diff --git a/packages/astro/src/vite-plugin-markdown/content-entry-type.ts b/packages/astro/src/vite-plugin-markdown/content-entry-type.ts index 01a67e033bdf..b8ed28f36fa3 100644 --- a/packages/astro/src/vite-plugin-markdown/content-entry-type.ts +++ b/packages/astro/src/vite-plugin-markdown/content-entry-type.ts @@ -2,7 +2,10 @@ import { fileURLToPath } from 'node:url'; import { ContentEntryType } from '../@types/astro.js'; import { parseFrontmatter } from '../content/utils.js'; +export const MARKDOWN_CONTENT_ENTRY_TYPE_NAME = 'astro:markdown'; + export const markdownContentEntryType: ContentEntryType = { + name: MARKDOWN_CONTENT_ENTRY_TYPE_NAME, extensions: ['.md'], async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) { const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl)); diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index 746bfde36963..39f5d1903a3b 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -10,6 +10,7 @@ import type { Plugin } from 'vite'; import { normalizePath } from 'vite'; import type { AstroSettings } from '../@types/astro'; import { getContentPaths } from '../content/index.js'; +import { hasMdContentEntryTypeOverride } from '../content/index.js'; import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js'; import type { LogOptions } from '../core/logger/core.js'; import { warn } from '../core/logger/core.js'; @@ -66,6 +67,15 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu async load(id) { if (isMarkdownFile(id)) { const { fileId, fileUrl } = getFileInfo(id, settings.config); + if ( + // Integrations can override the Markdown parser for content collections. + // If an override is present, skip this file. + fileId.startsWith(getContentPaths(settings.config).contentDir.pathname) && + hasMdContentEntryTypeOverride(settings) + ) { + return; + } + const rawFile = await fs.promises.readFile(fileId, 'utf-8'); const raw = safeMatter(rawFile, id); const renderResult = await renderMarkdown(raw.content, { diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts index ed260563b05a..9778ef40e5d5 100644 --- a/packages/integrations/markdoc/src/index.ts +++ b/packages/integrations/markdoc/src/index.ts @@ -6,13 +6,16 @@ import { parseFrontmatter } from './utils.js'; import { fileURLToPath } from 'node:url'; import fs from 'node:fs'; +const DEFAULT_MARKDOC_EXTS = ['.mdoc', '.md']; + export default function markdoc(): AstroIntegration { return { name: '@astrojs/markdoc', hooks: { 'astro:config:setup': async ({ updateConfig, config, addContentEntryType, command }: any) => { const contentEntryType = { - extensions: ['.mdoc'], + name: 'astro:markdoc', + extensions: DEFAULT_MARKDOC_EXTS, async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) { const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl)); return { @@ -34,7 +37,7 @@ export default function markdoc(): AstroIntegration { { name: '@astrojs/markdoc', async transform(code, id) { - if (!id.endsWith('.mdoc')) return; + if (!DEFAULT_MARKDOC_EXTS.some((ext) => id.endsWith(ext))) return; return `import { jsx as h } from 'astro/jsx-runtime';\nimport { Markdoc } from '@astrojs/markdoc';\nimport { Renderer } from '@astrojs/markdoc/components';\nexport const body = ${JSON.stringify( code )};\nexport function getParsed() { return Markdoc.parse(body); }\nexport function getTransformed(inlineConfig) { return Markdoc.transform(getParsed(), inlineConfig) }\nexport async function Content ({ config, components }) { return h(Renderer, { content: getTransformed(config), components }); }\nContent[Symbol.for('astro.needsHeadRendering')] = true;`; diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index 4944db4b8efa..2d0b767b9852 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -35,6 +35,7 @@ export default function mdx(partialMdxOptions: Partial = {}): AstroI command, }: any) => { const contentEntryType = { + name: 'astro:mdx', extensions: ['.mdx'], async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) { const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));