feat: allow Render type injection
This commit is contained in:
parent
7cc28032c2
commit
3a2a829236
5 changed files with 61 additions and 31 deletions
|
@ -991,6 +991,7 @@ export interface ContentEntryType {
|
|||
body: string;
|
||||
slug: string;
|
||||
}>;
|
||||
contentModuleTypes?: string;
|
||||
}
|
||||
|
||||
export interface AstroSettings {
|
||||
|
|
20
packages/astro/src/content/template/types.d.ts
vendored
20
packages/astro/src/content/template/types.d.ts
vendored
|
@ -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' {
|
||||
export { z } from 'astro/zod';
|
||||
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 =
|
||||
| import('astro/zod').AnyZodObject
|
||||
|
@ -57,14 +67,6 @@ declare module 'astro:content' {
|
|||
Required<ContentConfig['collections'][C]>['schema']
|
||||
>;
|
||||
|
||||
type Render = {
|
||||
render(): Promise<{
|
||||
Content: import('astro').MarkdownInstance<{}>['Content'];
|
||||
headings: import('astro').MarkdownHeading[];
|
||||
remarkPluginFrontmatter: Record<string, any>;
|
||||
}>;
|
||||
};
|
||||
|
||||
const entryMap: {
|
||||
// @@ENTRY_MAP@@
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import type fsMod from 'node:fs';
|
|||
import * as path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
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 { info, LogOptions, warn } from '../core/logger/core.js';
|
||||
import { isRelativePath } from '../core/path.js';
|
||||
|
@ -63,7 +63,7 @@ export async function createContentTypesGenerator({
|
|||
let events: EventWithOptions[] = [];
|
||||
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<
|
||||
{ typesGenerated: true } | { typesGenerated: false; reason: 'no-content-dir' }
|
||||
|
@ -263,8 +263,9 @@ export async function createContentTypesGenerator({
|
|||
fs,
|
||||
contentTypes,
|
||||
contentPaths,
|
||||
contentTypesBase,
|
||||
typeTemplateContent,
|
||||
contentConfig: observable.status === 'loaded' ? observable.config : undefined,
|
||||
contentEntryTypes: settings.contentEntryTypes,
|
||||
});
|
||||
if (observable.status === 'loaded' && ['info', 'warn'].includes(logLevel)) {
|
||||
warnNonexistentCollections({
|
||||
|
@ -322,13 +323,15 @@ async function writeContentFiles({
|
|||
fs,
|
||||
contentPaths,
|
||||
contentTypes,
|
||||
contentTypesBase,
|
||||
typeTemplateContent,
|
||||
contentEntryTypes,
|
||||
contentConfig,
|
||||
}: {
|
||||
fs: typeof fsMod;
|
||||
contentPaths: ContentPaths;
|
||||
contentTypes: ContentTypes;
|
||||
contentTypesBase: string;
|
||||
typeTemplateContent: string;
|
||||
contentEntryTypes: ContentEntryType[];
|
||||
contentConfig?: ContentConfig;
|
||||
}) {
|
||||
let contentTypesStr = '';
|
||||
|
@ -340,8 +343,11 @@ async function writeContentFiles({
|
|||
for (const entryKey of entryKeys) {
|
||||
const entryMetadata = contentTypes[collectionKey][entryKey];
|
||||
const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : 'any';
|
||||
const renderType = `{ render(): Render[${JSON.stringify(
|
||||
path.extname(JSON.parse(entryKey))
|
||||
)}] }`;
|
||||
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`;
|
||||
}
|
||||
|
@ -361,13 +367,21 @@ async function writeContentFiles({
|
|||
configPathRelativeToCacheDir = configPathRelativeToCacheDir.replace(/\.ts$/, '');
|
||||
}
|
||||
|
||||
contentTypesBase = contentTypesBase.replace('// @@ENTRY_MAP@@', contentTypesStr);
|
||||
contentTypesBase = contentTypesBase.replace(
|
||||
for (const contentEntryType of contentEntryTypes) {
|
||||
if (contentEntryType.contentModuleTypes) {
|
||||
typeTemplateContent = contentEntryType.contentModuleTypes + '\n' + typeTemplateContent;
|
||||
}
|
||||
}
|
||||
typeTemplateContent = typeTemplateContent.replace('// @@ENTRY_MAP@@', contentTypesStr);
|
||||
typeTemplateContent = typeTemplateContent.replace(
|
||||
"'@@CONTENT_CONFIG_TYPE@@'",
|
||||
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({
|
||||
|
|
|
@ -23,19 +23,6 @@ export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | '
|
|||
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 {
|
||||
return {
|
||||
name: '@astrojs/mdx',
|
||||
|
@ -47,6 +34,23 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
|
|||
addContentEntryType,
|
||||
command,
|
||||
}: 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');
|
||||
addContentEntryType(contentEntryType);
|
||||
|
||||
|
|
9
packages/integrations/mdx/template/content-module-types.d.ts
vendored
Normal file
9
packages/integrations/mdx/template/content-module-types.d.ts
vendored
Normal 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>;
|
||||
}>;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue