feat: support .md overrides for content collections

This commit is contained in:
bholmesdev 2023-02-14 12:56:01 -05:00
parent d049a48cf1
commit 5aebbd88ca
7 changed files with 35 additions and 3 deletions

View file

@ -979,6 +979,7 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
} }
export interface ContentEntryType { export interface ContentEntryType {
name: string;
extensions: string[]; extensions: string[];
getEntryInfo(params: { fileUrl: URL; contents: string }): Promise<{ getEntryInfo(params: { fileUrl: URL; contents: string }): Promise<{
data: Record<string, unknown>; data: Record<string, unknown>;

View file

@ -1,6 +1,11 @@
export { attachContentServerListeners } from './server-listeners.js'; export { attachContentServerListeners } from './server-listeners.js';
export { createContentTypesGenerator } from './types-generator.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 { astroContentAssetPropagationPlugin } from './vite-plugin-content-assets.js';
export { astroContentImportPlugin } from './vite-plugin-content-imports.js'; export { astroContentImportPlugin } from './vite-plugin-content-imports.js';
export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js'; export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';

View file

@ -7,6 +7,7 @@ import { ErrorPayload as ViteErrorPayload, normalizePath, ViteDevServer } from '
import { z } from 'zod'; import { z } from 'zod';
import { AstroConfig, AstroSettings } from '../@types/astro.js'; import { AstroConfig, AstroSettings } from '../@types/astro.js';
import { AstroError, AstroErrorData } from '../core/errors/index.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'; import { CONTENT_TYPES_FILE } from './consts.js';
export const collectionConfigParser = z.object({ export const collectionConfigParser = z.object({
@ -350,3 +351,11 @@ function search(fs: typeof fsMod, srcDir: URL) {
} }
return { exists: false, url: paths[0] }; return { exists: false, url: paths[0] };
} }
export function hasMdContentEntryTypeOverride(settings: Pick<AstroSettings, 'contentEntryTypes'>) {
return settings.contentEntryTypes.some(
(contentEntryType) =>
contentEntryType.name !== MARKDOWN_CONTENT_ENTRY_TYPE_NAME &&
contentEntryType.extensions.includes('.md')
);
}

View file

@ -2,7 +2,10 @@ import { fileURLToPath } from 'node:url';
import { ContentEntryType } from '../@types/astro.js'; import { ContentEntryType } from '../@types/astro.js';
import { parseFrontmatter } from '../content/utils.js'; import { parseFrontmatter } from '../content/utils.js';
export const MARKDOWN_CONTENT_ENTRY_TYPE_NAME = 'astro:markdown';
export const markdownContentEntryType: ContentEntryType = { export const markdownContentEntryType: ContentEntryType = {
name: MARKDOWN_CONTENT_ENTRY_TYPE_NAME,
extensions: ['.md'], extensions: ['.md'],
async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) { async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl)); const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));

View file

@ -10,6 +10,7 @@ import type { Plugin } from 'vite';
import { normalizePath } from 'vite'; import { normalizePath } from 'vite';
import type { AstroSettings } from '../@types/astro'; import type { AstroSettings } from '../@types/astro';
import { getContentPaths } from '../content/index.js'; import { getContentPaths } from '../content/index.js';
import { hasMdContentEntryTypeOverride } from '../content/index.js';
import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js'; import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js';
import type { LogOptions } from '../core/logger/core.js'; import type { LogOptions } from '../core/logger/core.js';
import { warn } 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) { async load(id) {
if (isMarkdownFile(id)) { if (isMarkdownFile(id)) {
const { fileId, fileUrl } = getFileInfo(id, settings.config); 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 rawFile = await fs.promises.readFile(fileId, 'utf-8');
const raw = safeMatter(rawFile, id); const raw = safeMatter(rawFile, id);
const renderResult = await renderMarkdown(raw.content, { const renderResult = await renderMarkdown(raw.content, {

View file

@ -6,13 +6,16 @@ import { parseFrontmatter } from './utils.js';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import fs from 'node:fs'; import fs from 'node:fs';
const DEFAULT_MARKDOC_EXTS = ['.mdoc', '.md'];
export default function markdoc(): AstroIntegration { export default function markdoc(): AstroIntegration {
return { return {
name: '@astrojs/markdoc', name: '@astrojs/markdoc',
hooks: { hooks: {
'astro:config:setup': async ({ updateConfig, config, addContentEntryType, command }: any) => { 'astro:config:setup': async ({ updateConfig, config, addContentEntryType, command }: any) => {
const contentEntryType = { const contentEntryType = {
extensions: ['.mdoc'], name: 'astro:markdoc',
extensions: DEFAULT_MARKDOC_EXTS,
async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) { async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl)); const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
return { return {
@ -34,7 +37,7 @@ export default function markdoc(): AstroIntegration {
{ {
name: '@astrojs/markdoc', name: '@astrojs/markdoc',
async transform(code, id) { 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( 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 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;`; )};\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;`;

View file

@ -35,6 +35,7 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
command, command,
}: any) => { }: any) => {
const contentEntryType = { const contentEntryType = {
name: 'astro:mdx',
extensions: ['.mdx'], extensions: ['.mdx'],
async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) { async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl)); const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));