feat: allow Render type injection

This commit is contained in:
bholmesdev 2023-02-13 14:05:40 -05:00
parent 28520666fa
commit 576b481adb
5 changed files with 61 additions and 31 deletions

View file

@ -991,6 +991,7 @@ export interface ContentEntryType {
body: string; body: string;
slug: string; slug: string;
}>; }>;
contentModuleTypes?: string;
} }
export interface AstroSettings { export interface AstroSettings {

View file

@ -1,7 +1,17 @@
declare module 'astro:content' {
interface Render {
'.md': Promise<{
Content: import('astro').MarkdownInstance<{}>['Content'];
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}>;
}
}
declare module 'astro:content' { declare module 'astro:content' {
export { z } from 'astro/zod'; export { z } from 'astro/zod';
export type CollectionEntry<C extends keyof typeof entryMap> = export type CollectionEntry<C extends keyof typeof entryMap> =
(typeof entryMap)[C][keyof (typeof entryMap)[C]] & Render; (typeof entryMap)[C][keyof (typeof entryMap)[C]];
type BaseSchemaWithoutEffects = type BaseSchemaWithoutEffects =
| import('astro/zod').AnyZodObject | import('astro/zod').AnyZodObject
@ -57,14 +67,6 @@ declare module 'astro:content' {
Required<ContentConfig['collections'][C]>['schema'] Required<ContentConfig['collections'][C]>['schema']
>; >;
type Render = {
render(): Promise<{
Content: import('astro').MarkdownInstance<{}>['Content'];
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}>;
};
const entryMap: { const entryMap: {
// @@ENTRY_MAP@@ // @@ENTRY_MAP@@
}; };

View file

@ -4,7 +4,7 @@ import type fsMod from 'node:fs';
import * as path from 'node:path'; import * as path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url'; import { fileURLToPath, pathToFileURL } from 'node:url';
import { normalizePath, ViteDevServer } from 'vite'; import { normalizePath, ViteDevServer } from 'vite';
import type { AstroSettings } from '../@types/astro.js'; import type { AstroSettings, ContentEntryType } from '../@types/astro.js';
import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { info, LogOptions, warn } from '../core/logger/core.js'; import { info, LogOptions, warn } from '../core/logger/core.js';
import { isRelativePath } from '../core/path.js'; import { isRelativePath } from '../core/path.js';
@ -63,7 +63,7 @@ export async function createContentTypesGenerator({
let events: EventWithOptions[] = []; let events: EventWithOptions[] = [];
let debounceTimeout: NodeJS.Timeout | undefined; let debounceTimeout: NodeJS.Timeout | undefined;
const contentTypesBase = await fs.promises.readFile(contentPaths.typesTemplate, 'utf-8'); const typeTemplateContent = await fs.promises.readFile(contentPaths.typesTemplate, 'utf-8');
async function init(): Promise< async function init(): Promise<
{ typesGenerated: true } | { typesGenerated: false; reason: 'no-content-dir' } { typesGenerated: true } | { typesGenerated: false; reason: 'no-content-dir' }
@ -263,8 +263,9 @@ export async function createContentTypesGenerator({
fs, fs,
contentTypes, contentTypes,
contentPaths, contentPaths,
contentTypesBase, typeTemplateContent,
contentConfig: observable.status === 'loaded' ? observable.config : undefined, contentConfig: observable.status === 'loaded' ? observable.config : undefined,
contentEntryTypes: settings.contentEntryTypes,
}); });
if (observable.status === 'loaded' && ['info', 'warn'].includes(logLevel)) { if (observable.status === 'loaded' && ['info', 'warn'].includes(logLevel)) {
warnNonexistentCollections({ warnNonexistentCollections({
@ -322,13 +323,15 @@ async function writeContentFiles({
fs, fs,
contentPaths, contentPaths,
contentTypes, contentTypes,
contentTypesBase, typeTemplateContent,
contentEntryTypes,
contentConfig, contentConfig,
}: { }: {
fs: typeof fsMod; fs: typeof fsMod;
contentPaths: ContentPaths; contentPaths: ContentPaths;
contentTypes: ContentTypes; contentTypes: ContentTypes;
contentTypesBase: string; typeTemplateContent: string;
contentEntryTypes: ContentEntryType[];
contentConfig?: ContentConfig; contentConfig?: ContentConfig;
}) { }) {
let contentTypesStr = ''; let contentTypesStr = '';
@ -340,8 +343,11 @@ async function writeContentFiles({
for (const entryKey of entryKeys) { for (const entryKey of entryKeys) {
const entryMetadata = contentTypes[collectionKey][entryKey]; const entryMetadata = contentTypes[collectionKey][entryKey];
const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any'; const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any';
const renderType = `{ render(): Render[${JSON.stringify(
path.extname(JSON.parse(entryKey))
)}] }`;
const slugType = JSON.stringify(entryMetadata.slug); const slugType = JSON.stringify(entryMetadata.slug);
contentTypesStr += `${entryKey}: {\n id: ${entryKey},\n slug: ${slugType},\n body: string,\n collection: ${collectionKey},\n data: ${dataType}\n},\n`; contentTypesStr += `${entryKey}: {\n id: ${entryKey},\n slug: ${slugType},\n body: string,\n collection: ${collectionKey},\n data: ${dataType}\n} & ${renderType},\n`;
} }
contentTypesStr += `},\n`; contentTypesStr += `},\n`;
} }
@ -361,13 +367,21 @@ async function writeContentFiles({
configPathRelativeToCacheDir = configPathRelativeToCacheDir.replace(/\.ts$/, ''); configPathRelativeToCacheDir = configPathRelativeToCacheDir.replace(/\.ts$/, '');
} }
contentTypesBase = contentTypesBase.replace('// @@ENTRY_MAP@@', contentTypesStr); for (const contentEntryType of contentEntryTypes) {
contentTypesBase = contentTypesBase.replace( if (contentEntryType.contentModuleTypes) {
typeTemplateContent = contentEntryType.contentModuleTypes + '\n' + typeTemplateContent;
}
}
typeTemplateContent = typeTemplateContent.replace('// @@ENTRY_MAP@@', contentTypesStr);
typeTemplateContent = typeTemplateContent.replace(
"'@@CONTENT_CONFIG_TYPE@@'", "'@@CONTENT_CONFIG_TYPE@@'",
contentConfig ? `typeof import(${JSON.stringify(configPathRelativeToCacheDir)})` : 'never' contentConfig ? `typeof import(${JSON.stringify(configPathRelativeToCacheDir)})` : 'never'
); );
await fs.promises.writeFile(new URL(CONTENT_TYPES_FILE, contentPaths.cacheDir), contentTypesBase); await fs.promises.writeFile(
new URL(CONTENT_TYPES_FILE, contentPaths.cacheDir),
typeTemplateContent
);
} }
function warnNonexistentCollections({ function warnNonexistentCollections({

View file

@ -23,19 +23,6 @@ export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | '
remarkRehype: RemarkRehypeOptions; remarkRehype: RemarkRehypeOptions;
}; };
const contentEntryType = {
extensions: ['.mdx'],
async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
return {
data: parsed.data,
body: parsed.content,
slug: parsed.data.slug,
rawData: parsed.matter,
};
},
};
export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroIntegration { export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroIntegration {
return { return {
name: '@astrojs/mdx', name: '@astrojs/mdx',
@ -47,6 +34,23 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
addContentEntryType, addContentEntryType,
command, command,
}: any) => { }: any) => {
const contentEntryType = {
extensions: ['.mdx'],
async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
return {
data: parsed.data,
body: parsed.content,
slug: parsed.data.slug,
rawData: parsed.matter,
};
},
contentModuleTypes: await fs.readFile(
new URL('../template/content-module-types.d.ts', import.meta.url),
'utf-8'
),
};
addPageExtension('.mdx'); addPageExtension('.mdx');
addContentEntryType(contentEntryType); addContentEntryType(contentEntryType);

View file

@ -0,0 +1,9 @@
declare module 'astro:content' {
interface Render {
'.mdx': Promise<{
Content: import('astro').MarkdownInstance<{}>['Content'];
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}>;
}
}