this repo has no description
at export-config 204 lines 6.1 kB view raw
1#!/usr/bin/env bun 2import { access, mkdir, writeFile } from "node:fs/promises"; 3import { dirname, join, resolve } from "node:path"; 4import { Command } from "commander"; 5import { 6 exportFromConfig, 7 findConfigFile, 8 loadExportConfig, 9} from "@sitebase/core"; 10 11const program = new Command(); 12 13program 14 .name("sitebase") 15 .description("CLI tools for standard.site publications") 16 .version("0.0.1"); 17 18program 19 .command("export") 20 .description("Export a publication to markdown files") 21 .option( 22 "-c, --config <file>", 23 "Path to config file (auto-discovers sitebase.config.{ts,js} if not specified)", 24 ) 25 .action(async (options: { config?: string }) => { 26 try { 27 // Find config file 28 let configPath = options.config ? resolve(options.config) : null; 29 30 if (!configPath) { 31 // Auto-discover 32 configPath = await findConfigFile(process.cwd()); 33 if (!configPath) { 34 console.error( 35 "No config file found. Create sitebase.config.ts or specify with --config", 36 ); 37 process.exit(1); 38 } 39 } 40 41 console.log(`Using config: ${configPath}`); 42 const config = await loadExportConfig(configPath); 43 44 console.log(`Exporting publication: ${config.publicationUri}`); 45 console.log(`Export targets: ${config.exports.length}`); 46 47 const configDir = dirname(configPath); 48 const exportResults = await exportFromConfig(config, configDir); 49 50 // Print results for each target 51 for (const [i, result] of exportResults.entries()) { 52 const target = config.exports[i]; 53 console.log(`\nTarget ${i + 1}: ${target?.outputDir}`); 54 console.log(` Documents processed: ${result.documentsProcessed}`); 55 console.log(` Documents skipped: ${result.documentsSkipped}`); 56 console.log(` Files written: ${result.filesWritten.length}`); 57 58 if (result.warnings.length > 0) { 59 console.log(` Warnings:`); 60 for (const warning of result.warnings) { 61 console.log(` - ${warning}`); 62 } 63 } 64 65 if (result.filesWritten.length > 0) { 66 console.log(` Files:`); 67 for (const file of result.filesWritten) { 68 console.log(` - ${file}`); 69 } 70 } 71 } 72 } catch (error) { 73 console.error( 74 `Error: ${error instanceof Error ? error.message : String(error)}`, 75 ); 76 process.exit(1); 77 } 78 }); 79 80program 81 .command("init") 82 .description("Create a sitebase.config.ts file with starter configuration") 83 .action(async () => { 84 const cwd = process.cwd(); 85 const configPath = join(cwd, "sitebase.config.ts"); 86 const templatesDir = join(cwd, "templates"); 87 const templatePath = join(templatesDir, "post.hbs"); 88 89 // Check if config already exists 90 try { 91 await access(configPath); 92 console.error("Error: sitebase.config.ts already exists"); 93 process.exit(1); 94 } catch { 95 // File doesn't exist, we can proceed 96 } 97 98 // Config file template with comments 99 const configContent = `// sitebase.config.ts 100import type { ExportConfig } from "@sitebase/core"; 101import { slugify } from "@sitebase/core"; 102 103/** 104 * Sitebase Export Configuration 105 * 106 * This file configures how your AT Protocol publication is exported to markdown files. 107 * For more information, see: https://github.com/sethetter/sitebase 108 */ 109const config: ExportConfig = { 110 // The AT URI of your publication (required) 111 // Format: at://did:plc:xxx/site.standard.publication/rkey 112 // Find this in your PDS or use the standard.site dashboard 113 publicationUri: "at://YOUR_DID/site.standard.publication/YOUR_RKEY", 114 115 // Export targets - each entry exports documents to a different location/format 116 // You can have multiple targets to export the same publication different ways 117 exports: [ 118 { 119 // Directory where markdown files will be written (relative to this config file) 120 outputDir: "./content", 121 122 // Optional: Only include documents with at least one of these tags 123 // If not specified, all documents are included (except those matching excludeTags) 124 // includeTags: ["post", "article"], 125 126 // Optional: Exclude documents with any of these tags 127 // Defaults to ["draft"] if includeTags is not specified 128 // excludeTags: ["draft", "private"], 129 130 // Function to generate the filename for each document (required) 131 // Receives an object with: title, path, publishedAt, updatedAt, tags, content, etc. 132 filename: (data) => { 133 // Example: "2024-01-15_my-post-title.md" 134 const date = data.publishedAt?.slice(0, 10) || "undated"; 135 return \`\${date}_\${slugify(data.title)}.md\`; 136 }, 137 138 // Optional: Path to a Handlebars template for content generation 139 // The template receives the same data object as the filename function 140 contentTemplate: "./templates/post.hbs", 141 142 // Optional: Function to generate content (overrides contentTemplate if both specified) 143 // content: (data) => \`--- 144 // title: "\${data.title}" 145 // date: \${data.publishedAt} 146 // --- 147 // 148 // \${data.content} 149 // \`, 150 }, 151 ], 152}; 153 154export default config; 155`; 156 157 // Handlebars template for posts 158 const templateContent = `--- 159title: "{{title}}" 160{{#if description}} 161description: "{{description}}" 162{{/if}} 163{{#if publishedAt}} 164date: {{publishedAt}} 165{{/if}} 166{{#if updatedAt}} 167updated: {{updatedAt}} 168{{/if}} 169{{#if tags.length}} 170tags: 171{{#each tags}} 172 - {{this}} 173{{/each}} 174{{/if}} 175--- 176 177{{{content}}} 178`; 179 180 try { 181 // Write config file 182 await writeFile(configPath, configContent, "utf-8"); 183 console.log("Created sitebase.config.ts"); 184 185 // Create templates directory and template file 186 await mkdir(templatesDir, { recursive: true }); 187 await writeFile(templatePath, templateContent, "utf-8"); 188 console.log("Created templates/post.hbs"); 189 190 console.log("\nNext steps:"); 191 console.log( 192 " 1. Update publicationUri in sitebase.config.ts with your publication's AT URI", 193 ); 194 console.log(" 2. Customize the export targets as needed"); 195 console.log(" 3. Run: sitebase export"); 196 } catch (error) { 197 console.error( 198 `Error: ${error instanceof Error ? error.message : String(error)}`, 199 ); 200 process.exit(1); 201 } 202 }); 203 204program.parse();