this repo has no description
TypeScript 92.5%
CSS 7.2%
Dockerfile 0.3%
20 3 0

Clone this repository

https://tangled.org/seth.computer/sitebase https://tangled.org/did:plc:imyu7ayhf22ffhscek4l5da3/sitebase
git@knot.tangled.wizardry.systems:seth.computer/sitebase git@knot.tangled.wizardry.systems:did:plc:imyu7ayhf22ffhscek4l5da3/sitebase

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

sitebase#

Export content from 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#

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#

# 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:

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:

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 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):

---
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:

{
  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:

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:

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.

cd packages/web
bun run dev

See packages/web/CLAUDE.md for web package details.

License#

MIT