diff --git a/.github/workflows/deploy-blog-cloudflare.yml b/.github/workflows/deploy-blog-cloudflare.yml new file mode 100644 index 0000000..5b9f07b --- /dev/null +++ b/.github/workflows/deploy-blog-cloudflare.yml @@ -0,0 +1,36 @@ +name: Deploy blog-cloudflare + +on: + push: + branches: [main] + paths: + - "blog-cloudflare/**" + + workflow_dispatch: + +jobs: + deploy: + name: Deploy to Cloudflare Workers + runs-on: ubuntu-latest + defaults: + run: + working-directory: blog-cloudflare + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install + run: pnpm install --no-frozen-lockfile + + - name: Deploy + run: pnpm deploy + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md index 1a056ef..8a2bb7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1,115 @@ -This repo is auto-synced from [emdash-cms/emdash](https://github.com/emdash-cms/emdash). Do not open PRs here — make changes in the `templates/` directory of the main repo instead. +> **This repo is auto-synced from [emdash-cms/emdash](https://github.com/emdash-cms/emdash).** Do not open PRs here — make changes in the `templates/` directory of the main repo instead. External PRs are closed automatically. + +# EmDash Templates + +A collection of starter templates for [EmDash](https://github.com/emdash-cms/emdash), a full-stack TypeScript CMS built on Astro. Each template is a self-contained Astro site with EmDash wired up, seed data for demo content, and its own `CLAUDE.md`. + +## Repository Layout + +``` +templates/ +├── blank/ # Minimal: one page, EmDash wired, nothing else +├── blog/ # Blog (Node.js variant: SQLite + local storage) +├── blog-cloudflare/ # Blog (Cloudflare variant: D1 + R2) +├── marketing/ # Landing page with content blocks (Node.js) +├── marketing-cloudflare/ # Landing page (Cloudflare) +├── portfolio/ # Portfolio/case studies (Node.js) +├── portfolio-cloudflare/ # Portfolio (Cloudflare) +├── starter/ # General-purpose base (Node.js) +├── starter-cloudflare/ # General-purpose base (Cloudflare) +└── screenshots.json # Page paths used by screenshot tooling +``` + +Each template directory is standalone — it has its own `package.json`, `astro.config.mjs`, `seed/seed.json`, and source tree. There is no monorepo workspace at the repo root; every template is installed and built independently. + +## Template Variants + +Each template (except `blank`) has two variants: + +| Variant | Adapter | Database | Storage | +|---|---|---|---| +| **Node.js** (`blog`, `marketing`, etc.) | `@astrojs/node` | SQLite via `better-sqlite3` | Local filesystem | +| **Cloudflare** (`blog-cloudflare`, etc.) | `@astrojs/cloudflare` | Cloudflare D1 | Cloudflare R2 | + +Cloudflare variants include a `wrangler.jsonc` and a `src/worker.ts` entry point. Node.js variants do not. + +## Working in a Template + +All commands run from inside the template directory, not the repo root. + +```bash +cd blog # or any other template + +pnpm install # install deps (pnpm is the package manager) +npx emdash dev # start dev server: runs migrations, seeds, generates types +npx emdash types # regenerate TypeScript types from schema (after schema changes) +pnpm typecheck # astro check (TypeScript) +pnpm build # production build +pnpm preview # preview production build (Node.js variants only) +``` + +- Site: `http://localhost:4321` +- CMS admin UI: `http://localhost:4321/_emdash/admin` + +## Key Files in Every Template + +| File | Purpose | +|---|---| +| `astro.config.mjs` | Astro config: `emdash()` integration, adapter, database, storage, plugins | +| `seed/seed.json` | Schema + demo content: collections, fields, taxonomies, menus, widgets | +| `emdash-env.d.ts` | Auto-generated TypeScript types for collections (regenerated on `emdash dev`) | +| `src/live.config.ts` | EmDash loader registration — boilerplate, do not modify | +| `src/layouts/Base.astro` | Base layout: menus, search, EmDash head/body hooks | +| `src/pages/` | All Astro pages (server-rendered) | +| `src/styles/theme.css` | CSS custom properties and global styles | + +## EmDash Conventions (apply across all templates) + +- **Always server-rendered.** All content pages use `output: "server"` in `astro.config.mjs`. Never use `getStaticPaths()` for CMS content — slugs are dynamic. +- **Image fields are objects.** A field typed `image` returns `{ src, alt, meta, ... }`, not a plain string. Always render with `` from `"emdash/ui"`. +- **Two IDs per entry.** `entry.id` is the URL slug; `entry.data.id` is the database ULID. API calls like `getEntryTerms` and `Comments` take `entry.data.id`. URLs use `entry.id`. +- **Cache hint.** Always call `Astro.cache.set(cacheHint)` on pages that query content. The `cacheHint` is returned by `getEmDashEntry` and `getEmDashCollection`. +- **Taxonomy names.** In queries, use the exact `"name"` field from the seed (e.g. `"tag"`, `"category"`), not the plural label. +- **Parallel queries.** Independent data fetches (e.g. tags + related posts) should run with `Promise.all` to avoid serial round-trips. +- **Slug decoding.** Use `decodeSlug(Astro.params.slug)` before passing to `getEmDashEntry` — handles encoded characters in URL params. + +## Template-Specific Notes + +### blog / blog-cloudflare +Collections: `posts`, `pages`. Features: reading time, tag/category taxonomy, full-text search, RSS, comments, sidebar widgets, TOC, bylines, SEO meta. Custom utility at `src/utils/reading-time.ts`. + +### marketing / marketing-cloudflare +Collections: `pages` with a Portable Text `content` field. Custom inline plugin at `src/plugins/marketing-blocks/` registers five block types (`marketing.hero`, `marketing.features`, `marketing.testimonials`, `marketing.pricing`, `marketing.faq`). Rendered by `src/components/MarketingBlocks.astro`. Also uses `@emdash-cms/plugin-forms` for the contact form. + +### portfolio / portfolio-cloudflare +Collections: `projects`. Features: tag filtering, RSS, contact form, case-study pages. + +### starter / starter-cloudflare +Minimal opinionated starting point. Collections: `posts`, `pages`. Tag and category taxonomy. No custom design — intended as a base to build from. + +### blank +Single `src/pages/index.astro`. No collections, no seed content, no styling. The absolute minimum to start from scratch. + +## CI + +GitHub Actions (`.github/workflows/ci.yml`) runs on every push and PR to `main`: + +1. **Typecheck** — `pnpm typecheck` in each template directory +2. **Build** — `pnpm build` in each template directory +3. **Smoke test** — starts the dev server for non-Cloudflare templates, hits `/_emdash/api/setup/dev-bypass` to create a dev session, then verifies `/_emdash/admin` renders without errors + +All nine templates are tested in parallel via a matrix strategy. Cloudflare variants skip the smoke test (no local D1/R2 in CI). + +## Skills for Working on Templates + +When working inside a specific template, load the relevant skill: + +- **building-emdash-site** — Content queries, Portable Text rendering, schema design, seed files, menus, widgets, SEO, comments, bylines +- **emdash-cli** — CLI commands for content management, seeding, type generation +- **creating-plugins** — Building EmDash plugins (hooks, storage, admin UI, API routes, Portable Text block types) + +Skills are also available as files in each template's `.agents/skills/` directory. + +## EmDash Documentation + +The live docs are available as an MCP server at `https://docs.emdashcms.com/mcp`. Each template ships with `.mcp.json`, `.cursor/mcp.json`, and `.vscode/mcp.json` so Claude Code, Cursor, and VS Code auto-discover it. When verifying an API, hook, config option, or field type, call `search_docs` against the live docs rather than relying on training-data recall. diff --git a/blog-cloudflare/wrangler.jsonc b/blog-cloudflare/wrangler.jsonc index 0e7d89d..304983c 100644 --- a/blog-cloudflare/wrangler.jsonc +++ b/blog-cloudflare/wrangler.jsonc @@ -1,6 +1,7 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "crosbynews", + "account_id": "bfadb24d6e09a43ae3187aa145992b68", "main": "./src/worker.ts", "compatibility_date": "2026-02-24", "compatibility_flags": ["nodejs_compat"],