# sitebase Export content from [standard.site](https://standard.site) publications to markdown files. Sitebase connects to ATProto Personal Data Servers (PDS) to fetch documents from `site.standard.publication` collections and exports them as markdown files with configurable templates and filtering. ## Installation ```bash bun install ``` ## Packages This is a monorepo with three packages: - **@sitebase/core** - Library for exporting publications and template utilities - **@sitebase/cli** - Command-line interface for running exports - **@sitebase/web** - Web UI for managing publications and documents ## CLI Usage ```bash # Auto-discover sitebase.config.ts in current directory sitebase export # Specify a config file sitebase export --config ./my-config.ts ``` The CLI looks for `sitebase.config.ts` or `sitebase.config.js` in the current directory. Use `-c` or `--config` to specify a different path. ## Configuration Create a `sitebase.config.ts` file in your project root: ```typescript import type { ExportConfig } from "@sitebase/core"; import { slugify } from "@sitebase/core"; const config: ExportConfig = { // AT URI of your publication publicationUri: "at://did:plc:xyz/site.standard.publication/rkey", // One or more export targets exports: [ { outputDir: "./content/posts", includeTags: ["post"], excludeTags: ["draft"], filename: (data) => { const date = data.publishedAt?.slice(0, 10) || "undated"; return `${date}_${slugify(data.title)}.md`; }, contentTemplate: "./templates/post.hbs", }, ], }; export default config; ``` ### Config Reference #### `ExportConfig` | Field | Type | Description | |-------|------|-------------| | `publicationUri` | `string` | AT URI of the publication (`at://did:plc:.../site.standard.publication/rkey`) | | `exports` | `ExportTarget[]` | Array of export targets | #### `ExportTarget` | Field | Type | Required | Description | |-------|------|----------|-------------| | `outputDir` | `string` | Yes | Directory to write output files | | `filename` | `(data: TemplateData) => string` | Yes | Function to generate filename | | `includeTags` | `string[]` | No | Only include documents with ANY of these tags | | `excludeTags` | `string[]` | No | Exclude documents with ANY of these tags | | `contentTemplate` | `string` | No | Path to Handlebars template file | | `content` | `(data: TemplateData) => string` | No | Function to generate content (overrides `contentTemplate`) | ### Template Data Both `filename` and `content` functions receive a `TemplateData` object: ```typescript interface TemplateData { title: string; path?: string; description?: string; content: string; // Markdown content tags: string[]; publishedAt?: string; // ISO 8601 date updatedAt?: string; // ISO 8601 date publication: { name: string; url: string; description?: string; }; } ``` ### Tag Filtering - **includeTags**: Only documents with at least one matching tag are included - **excludeTags**: Documents with any matching tag are excluded - When neither is specified, documents tagged "draft" are excluded by default ### Handlebars Templates Content templates use [Handlebars](https://handlebarsjs.com/) with these custom helpers: | Helper | Usage | Description | |--------|-------|-------------| | `slug` | `{{slug text}}` | Convert text to URL-safe slug | | `dateFormat` | `{{dateFormat date "YYYY-MM-DD"}}` | Format date (supports YYYY, MM, DD) | | `default` | `{{default value fallback}}` | Use fallback if value is empty | Example template (`templates/post.hbs`): ```handlebars --- title: "{{title}}" date: {{publishedAt}} slug: {{slug (default path title)}} {{#if tags.length}} tags: {{#each tags}} - {{this}} {{/each}} {{/if}} --- {{content}} ``` ### Using the Content Function For full control, use a `content` function instead of a template: ```typescript { outputDir: "./content", filename: (data) => `${slugify(data.title)}.md`, content: (data) => { return [ "---", `title: "${data.title}"`, `date: ${data.publishedAt}`, "---", "", data.content, ].join("\n"); }, } ``` ### Multiple Export Targets Export the same publication to different locations with different filters: ```typescript export default { publicationUri: "at://did:plc:xyz/site.standard.publication/rkey", exports: [ { outputDir: "./content/notes", includeTags: ["note"], filename: (data) => `${slugify(data.title)}.md`, }, { outputDir: "./content/posts", includeTags: ["post"], excludeTags: ["draft"], filename: (data) => `${data.publishedAt?.slice(0, 10)}_${slugify(data.title)}.md`, contentTemplate: "./templates/post.hbs", }, ], } as ExportConfig; ``` ## Core Library Use `@sitebase/core` directly in your own scripts: ```typescript import { exportPublication, slugify, createHandlebars } from "@sitebase/core"; const result = await exportPublication({ publicationUri: "at://did:plc:xyz/site.standard.publication/rkey", outputDir: "./output", filename: (data) => `${slugify(data.title)}.md`, }); console.log(`Wrote ${result.filesWritten.length} files`); ``` ### Exports - `exportPublication(options)` - Export a publication to markdown files - `exportFromConfig(config, baseDir)` - Export using a config object - `findConfigFile(dir)` - Find `sitebase.config.{ts,js}` in directory - `loadExportConfig(path)` - Load and validate a config file - `slugify(text)` - Convert text to URL-safe slug - `createHandlebars()` - Create Handlebars instance with helpers registered - `generateContent(hbs, template, data)` - Render a Handlebars template ## Web UI The web package provides a management interface for publications and documents with ATProto OAuth authentication. ```bash cd packages/web bun run dev ``` See `packages/web/CLAUDE.md` for web package details. ## License MIT