UNPKG

astro-cc2ks

Version:

Astro plugin to generate Keystatic config from content collections

155 lines (136 loc) 5.53 kB
import type { AstroIntegration } from 'astro'; import fs from 'fs/promises'; import path from 'path'; import { URL } from 'url'; import { parseContentConfig } from './src/astro-cc2ks/cc2ks-Parser'; import { transformToKeystaticConfig } from './src/astro-cc2ks/cc2ks-Transformer'; export function astroCC2KS(): AstroIntegration { return { name: 'astro-cc2ks', hooks: { 'astro:config:setup': async ({ updateConfig, config, logger }) => { logger.info('Astro Content Collection To Keystatic plugin initialized'); const contentConfigPath = new URL('./src/content/config.ts', config.root).pathname; const keystaticConfigPath = new URL('./keystatic.config.ts', config.root).pathname; const keystaticGeneratedPath = new URL('./keystatic.generated.ts', config.root).pathname; async function generateKeystaticConfig() { try { logger.info(`Checking for content config at: ${contentConfigPath}`); logger.info(`Current working directory: ${process.cwd()}`); if (!await fileExists(contentConfigPath)) { logger.warn('No content config found. Creating a default one.'); await ensureDirectoryExists(contentConfigPath); await createDefaultContentConfig(contentConfigPath); logger.info('Default content config created successfully.'); } else { logger.info('Content config found. Proceeding with Keystatic config generation.'); } logger.info('Parsing content config...'); let contentConfig; try { contentConfig = parseContentConfig(contentConfigPath); logger.info('Content config parsed successfully.'); } catch (parseError) { logger.error(`Error parsing content config: ${parseError instanceof Error ? parseError.message : String(parseError)}`); if (parseError instanceof Error && parseError.stack) { logger.error(`Stack trace: ${parseError.stack}`); } return; // Exit the function if parsing fails } logger.info('Transforming to Keystatic config...'); const keystaticConfig = transformToKeystaticConfig(contentConfig); logger.info('Keystatic config transformation complete.'); logger.info(`Writing Keystatic config to: ${keystaticGeneratedPath}`); await writeKeystaticConfig(keystaticGeneratedPath, keystaticConfig); logger.info('Keystatic config written successfully.'); if (!await fileExists(keystaticConfigPath)) { logger.info(`Creating default Keystatic config at: ${keystaticConfigPath}`); await createDefaultKeystaticConfig(keystaticConfigPath); logger.info('Default Keystatic config created successfully.'); } else { logger.info('Keystatic config already exists. Skipping default config creation.'); } } catch (error) { if (error instanceof Error) { logger.error(`Error generating Keystatic config: ${error.message}`); if (error.stack) { logger.error(`Stack trace: ${error.stack}`); } } else { logger.error(`Error generating Keystatic config: ${String(error)}`); } } } await generateKeystaticConfig(); // Watch for changes in content config updateConfig({ vite: { plugins: [{ name: 'keystatic-config-regenerator', async handleHotUpdate({ file }) { if (file === contentConfigPath) { logger.info(`Content config changed. Regenerating Keystatic config...`); await generateKeystaticConfig(); } }, }], }, }); }, }, }; } async function writeKeystaticConfig(configPath: string, config: string): Promise<void> { await fs.writeFile(configPath, config, 'utf-8'); } async function createDefaultKeystaticConfig(configPath: string): Promise<void> { const defaultConfig = ` import { config } from '@keystatic/core'; import { generatedCollections } from './keystatic.generated'; export default config({ storage: { kind: 'local', }, collections: { ...generatedCollections, // Add any additional collections or overrides here }, // Add any other Keystatic-specific configurations here }); `; await fs.writeFile(configPath, defaultConfig, 'utf-8'); } async function createDefaultContentConfig(configPath: string): Promise<void> { const defaultConfig = ` import { defineCollection, z } from 'astro:content'; const blogCollection = defineCollection({ type: 'content', schema: z.object({ title: z.string(), pubDate: z.date(), description: z.string(), author: z.string(), image: z.object({ url: z.string(), alt: z.string() }).optional(), tags: z.array(z.string()).default(['uncategorized']) }) }); export const collections = { 'blog': blogCollection, }; `; await fs.writeFile(configPath, defaultConfig, 'utf-8'); } async function fileExists(filePath: string): Promise<boolean> { try { await fs.access(filePath); return true; } catch { return false; } } async function ensureDirectoryExists(filePath: string): Promise<void> { await fs.mkdir(path.dirname(filePath), { recursive: true }); }