wip: convert inline config to parser
This commit is contained in:
parent
6b94a279dd
commit
b9bf041e5b
9 changed files with 82 additions and 35 deletions
|
@ -977,21 +977,25 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
|
|||
integrations: AstroIntegration[];
|
||||
}
|
||||
|
||||
export interface ContentEntryType {
|
||||
extensions: string[];
|
||||
getEntryInfo(params: { fileUrl: URL }): Promise<{
|
||||
export interface ContentEntryParser {
|
||||
getEntryInfo(params: { contents: string; fileUrl: URL }): Promise<{
|
||||
data: Record<string, unknown>;
|
||||
body: string;
|
||||
slug: string;
|
||||
/**
|
||||
* Used for error hints to point to correct line and location
|
||||
* Should be the untouched data as read from the file,
|
||||
* including newlines
|
||||
*/
|
||||
rawData: string;
|
||||
body: string;
|
||||
slug: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ContentEntryType {
|
||||
extensions: string[];
|
||||
parser: string;
|
||||
}
|
||||
|
||||
export interface AstroSettings {
|
||||
config: AstroConfig;
|
||||
adapter: AstroAdapter | undefined;
|
||||
|
|
|
@ -5,7 +5,7 @@ import path from 'node:path';
|
|||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { ErrorPayload as ViteErrorPayload, normalizePath, ViteDevServer } from 'vite';
|
||||
import { z } from 'zod';
|
||||
import { AstroConfig, AstroSettings } from '../@types/astro.js';
|
||||
import { AstroConfig, AstroSettings, ContentEntryParser } from '../@types/astro.js';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
import { appendForwardSlash } from '../core/path.js';
|
||||
import { CONTENT_TYPES_FILE, defaultContentEntryExts } from './consts.js';
|
||||
|
@ -244,6 +244,28 @@ export function parseFrontmatter(fileContents: string, filePath: string) {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: Will require dev server restart to debug parser changes
|
||||
const contentEntryExtToParserCache = new Map<string, ContentEntryParser>();
|
||||
|
||||
export async function loadContentEntryParsers({
|
||||
settings,
|
||||
}: {
|
||||
settings: Pick<AstroSettings, 'contentEntryTypes'>;
|
||||
}): Promise<Map<string, ContentEntryParser>> {
|
||||
if (contentEntryExtToParserCache.size > 0) return contentEntryExtToParserCache;
|
||||
|
||||
for (const contentEntryType of settings.contentEntryTypes) {
|
||||
for (const extension of contentEntryType.extensions) {
|
||||
// TODO: explore SSR loading strategy if Content Collections processing
|
||||
// is moved from build to SSR
|
||||
const parser = await import(contentEntryType.parser);
|
||||
// TODO: Zod check that parser is a valid parser
|
||||
contentEntryExtToParserCache.set(extension, parser as ContentEntryParser);
|
||||
}
|
||||
}
|
||||
return contentEntryExtToParserCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* The content config is loaded separately from other `src/` files.
|
||||
* This global observable lets dependent plugins (like the content flag plugin)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as devalue from 'devalue';
|
||||
import type fsMod from 'node:fs';
|
||||
import { extname } from 'node:path';
|
||||
import { pathToFileURL } from 'url';
|
||||
import type { Plugin } from 'vite';
|
||||
import { AstroSettings } from '../@types/astro.js';
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
getEntrySlug,
|
||||
getEntryType,
|
||||
globalContentConfigObserver,
|
||||
loadContentEntryParsers,
|
||||
parseFrontmatter,
|
||||
} from './utils.js';
|
||||
|
||||
|
@ -65,16 +67,26 @@ export function astroContentImportPlugin({
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
const contentEntryParsers = await loadContentEntryParsers({ settings });
|
||||
const rawContents = await fs.promises.readFile(fileId, 'utf-8');
|
||||
const contentEntryType = settings.contentEntryTypes.find((entryType) =>
|
||||
entryType.extensions.some((ext) => fileId.endsWith(ext))
|
||||
);
|
||||
const fileExt = extname(fileId);
|
||||
const contentEntryParser = contentEntryParsers.get(fileExt);
|
||||
// if (!contentEntryParser) {
|
||||
// throw new AstroError({
|
||||
// ...AstroErrorData.UnknownContentCollectionError,
|
||||
// message: `No content parser found for file extension "${fileExt}".`,
|
||||
// });
|
||||
// }
|
||||
let body: string,
|
||||
unvalidatedData: Record<string, unknown>,
|
||||
unvalidatedSlug: string,
|
||||
rawData: string;
|
||||
if (contentEntryType) {
|
||||
const info = await contentEntryType.getEntryInfo({ fileUrl: pathToFileURL(fileId) });
|
||||
if (contentEntryParser) {
|
||||
const info = await contentEntryParser.getEntryInfo({
|
||||
fileUrl: pathToFileURL(fileId),
|
||||
contents: rawContents,
|
||||
});
|
||||
body = info.body;
|
||||
unvalidatedData = info.data;
|
||||
unvalidatedSlug = info.slug;
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
// @ts-ignore
|
||||
export { default as Renderer } from './Renderer.astro';
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"homepage": "https://docs.astro.build/en/guides/integrations-guide/markdoc/",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./content-type-parser": "./dist/content-type-parser.js",
|
||||
"./components": "./components/index.ts",
|
||||
"./content-types": "./content-types.d.ts",
|
||||
"./package.json": "./package.json"
|
||||
|
|
|
@ -1,11 +1,24 @@
|
|||
import matter from 'gray-matter';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { ErrorPayload as ViteErrorPayload } from 'vite';
|
||||
|
||||
export default {
|
||||
async getEntryInfo({ contents, fileUrl }: { contents: string; fileUrl: URL }) {
|
||||
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
|
||||
return {
|
||||
data: parsed.data,
|
||||
body: parsed.content,
|
||||
slug: parsed.data.slug,
|
||||
rawData: parsed.matter,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Match YAML exception handling from Astro core errors
|
||||
* @see 'astro/src/core/errors.ts'
|
||||
*/
|
||||
export function parseFrontmatter(fileContents: string, filePath: string) {
|
||||
function parseFrontmatter(fileContents: string, filePath: string) {
|
||||
try {
|
||||
// `matter` is empty string on cache results
|
||||
// clear cache to prevent this
|
|
@ -1,22 +1,10 @@
|
|||
import type { AstroIntegration } from 'astro';
|
||||
import type { InlineConfig } from 'vite';
|
||||
import _Markdoc from '@markdoc/markdoc';
|
||||
import fs from 'node:fs';
|
||||
import { parseFrontmatter } from './utils.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const contentEntryType = {
|
||||
extensions: ['.mdoc'],
|
||||
async getEntryInfo({ fileUrl }: { fileUrl: URL }) {
|
||||
const rawContents = await fs.promises.readFile(fileUrl, 'utf-8');
|
||||
const parsed = parseFrontmatter(rawContents, fileURLToPath(fileUrl));
|
||||
return {
|
||||
data: parsed.data,
|
||||
body: parsed.content,
|
||||
slug: parsed.data.slug,
|
||||
rawData: parsed.matter,
|
||||
};
|
||||
},
|
||||
parser: '@astrojs/markdoc/content-type-parser',
|
||||
};
|
||||
|
||||
export default function markdoc(partialOptions: {} = {}): AstroIntegration {
|
||||
|
@ -25,6 +13,7 @@ export default function markdoc(partialOptions: {} = {}): AstroIntegration {
|
|||
hooks: {
|
||||
'astro:config:setup': async ({ updateConfig, config, addContentEntryType, command }: any) => {
|
||||
addContentEntryType(contentEntryType);
|
||||
|
||||
console.log('Markdoc working!');
|
||||
const markdocConfigUrl = new URL('./markdoc.config.ts', config.srcDir);
|
||||
|
||||
|
|
14
packages/integrations/mdx/src/content-type-parser.ts
Normal file
14
packages/integrations/mdx/src/content-type-parser.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { parseFrontmatter } from './utils.js';
|
||||
|
||||
export default {
|
||||
async getEntryInfo({ contents, fileUrl }: { contents: string; fileUrl: URL }) {
|
||||
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
|
||||
return {
|
||||
data: parsed.data,
|
||||
body: parsed.content,
|
||||
slug: parsed.data.slug,
|
||||
rawData: parsed.matter,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -25,16 +25,7 @@ export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | '
|
|||
|
||||
const contentEntryType = {
|
||||
extensions: ['.mdx'],
|
||||
async getEntryInfo({ fileUrl }: { fileUrl: URL }) {
|
||||
const rawContents = await fs.readFile(fileUrl, 'utf-8');
|
||||
const parsed = parseFrontmatter(rawContents, fileURLToPath(fileUrl));
|
||||
return {
|
||||
data: parsed.data,
|
||||
body: parsed.content,
|
||||
slug: parsed.data.slug,
|
||||
rawData: parsed.matter,
|
||||
};
|
||||
},
|
||||
parser: '@astrojs/mdx/content-type-parser',
|
||||
};
|
||||
|
||||
export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroIntegration {
|
||||
|
|
Loading…
Reference in a new issue