feat: allow Render type injection
This commit is contained in:
parent
28520666fa
commit
576b481adb
5 changed files with 61 additions and 31 deletions
|
@ -991,6 +991,7 @@ export interface ContentEntryType {
|
||||||
body: string;
|
body: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}>;
|
}>;
|
||||||
|
contentModuleTypes?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AstroSettings {
|
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' {
|
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@@
|
||||||
};
|
};
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
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