Skip to content

Commit

Permalink
feat: allow Render type injection
Browse files Browse the repository at this point in the history
  • Loading branch information
bholmesdev committed Feb 16, 2023
1 parent cdb7e9e commit 8bcd23c
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 31 deletions.
1 change: 1 addition & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,7 @@ export interface ContentEntryType {
body: string;
slug: string;
}>;
contentModuleTypes?: string;
}

export interface AstroSettings {
Expand Down
20 changes: 11 additions & 9 deletions packages/astro/src/content/template/types.d.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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@@
};
Expand Down
32 changes: 23 additions & 9 deletions packages/astro/src/content/types-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -58,7 +58,7 @@ export async function createContentTypesGenerator({
let events: Promise<{ shouldGenerateTypes: boolean; error?: Error }>[] = [];
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' }
Expand Down Expand Up @@ -245,8 +245,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({
Expand Down Expand Up @@ -304,13 +305,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 = '';
Expand All @@ -322,8 +325,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`;
}
Expand All @@ -343,13 +349,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({
Expand Down
30 changes: 17 additions & 13 deletions packages/integrations/mdx/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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);

Expand Down
9 changes: 9 additions & 0 deletions packages/integrations/mdx/template/content-module-types.d.ts
Original file line number Diff line number Diff line change
@@ -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>;
}>;
}
}

0 comments on commit 8bcd23c

Please sign in to comment.