Skip to content

Latest commit

 

History

History
208 lines (145 loc) · 7.13 KB

File metadata and controls

208 lines (145 loc) · 7.13 KB

Contributing to EmDash

Beta. EmDash is published to npm. During development you work inside the monorepo -- packages use workspace:* links, so everything "just works" without publishing.

Prerequisites

  • Node.js 22+
  • pnpm 10+ (corepack enable if you don't have it)
  • Git

Quick Setup

git clone <repo-url> && cd emdash
pnpm install
pnpm build          # build all packages (required before first run)

Run the Demo

The demos/simple/ app is the primary development target. It is kept in sync with templates/blog/ and uses Node.js + SQLite — no Cloudflare account needed.

pnpm --filter emdash-demo seed   # seed sample content
pnpm --filter emdash-demo dev    # http://localhost:4321

Open the admin at http://localhost:4321/_emdash/admin.

In dev mode, passkey auth is bypassed automatically. If you hit the login screen, visit:

http://localhost:4321/_emdash/api/setup/dev-bypass?redirect=/_emdash/admin

Run with Cloudflare (optional)

demos/cloudflare/ runs on the real workerd runtime with D1. See its README for setup.

Developing Templates

Templates in templates/ are workspace members and can be run directly:

# First time: set up database and seed content
pnpm --filter @emdash-cms/template-portfolio bootstrap

# Run the dev server
pnpm --filter @emdash-cms/template-portfolio dev

Available templates:

Template Filter Name
Blog @emdash-cms/template-blog
Portfolio @emdash-cms/template-portfolio
Marketing @emdash-cms/template-marketing

Edit files in templates/{name}/src/ and changes hot reload.

Cloudflare variants (*-cloudflare) share source with their base templates via scripts/sync-cloudflare-templates.sh. Run that script after editing base template shared files.

Demo/template sync is handled by scripts/sync-blog-demos.sh:

  • Full sync: templates/blog -> demos/simple
  • Frontend sync (keep runtime-specific config/files):
    • templates/blog-cloudflare -> demos/cloudflare
    • templates/blog-cloudflare -> demos/preview
    • templates/blog -> demos/postgres

To start fresh, delete the database and re-bootstrap:

rm templates/portfolio/data.db
pnpm --filter @emdash-cms/template-portfolio bootstrap

Development Workflow

Watch Mode

For iterating on core packages alongside the demo, run two terminals:

# Terminal 1 — rebuild packages/core on change
pnpm --filter emdash dev

# Terminal 2 — run the demo
pnpm --filter emdash-demo dev

Changes to packages/core/src/ will be picked up by the demo's dev server automatically.

Checks

Run these before committing:

pnpm typecheck       # TypeScript (packages)
pnpm typecheck:demos # TypeScript (Astro demos)
pnpm --silent lint:quick   # fast lint (< 1s) — run often
pnpm --silent lint:json    # full type-aware lint (~10s) — run before commits
pnpm format          # auto-format with oxfmt

Type checking must pass. Lint must pass. Don't commit with known failures.

Tests

pnpm test                              # all packages
pnpm --filter emdash test            # core only
pnpm --filter emdash test --watch    # watch mode
pnpm test:e2e                          # Playwright (requires demo running)

Tests use real in-memory SQLite — no mocking. Each test gets a fresh database.

Repository Layout

emdash/
├── packages/
│   ├── core/              # emdash — the main package (Astro integration + APIs + admin)
│   ├── auth/              # @emdash-cms/auth — passkeys, OAuth, magic links
│   ├── admin/             # @emdash-cms/admin — React admin SPA
│   ├── cloudflare/        # @emdash-cms/cloudflare — CF adapter + plugin sandbox
│   ├── create-emdash/   # create-emdash — project scaffolder
│   ├── gutenberg-to-portable-text/  # WP block → Portable Text converter
│   └── plugins/           # first-party plugins (each dir = package)
├── demos/
│   ├── simple/            # emdash-demo — primary dev/test app (Node.js + SQLite)
│   ├── cloudflare/        # Cloudflare Workers demo (D1)
│   ├── plugins-demo/      # plugin development testbed
│   └── ...
├── templates/             # starter templates (blog, portfolio, marketing + cloudflare variants)
├── docs/                  # public documentation site (Starlight)
└── e2e/                   # Playwright test fixtures

The main package is packages/core. Most of your work will happen there.

Building Your Own Site (Inside the Monorepo)

The easiest way to build a real site during development is to add it as a workspace member.

  1. Copy templates/blog/ (or templates/blank/) into demos/:

    cp -r templates/blog demos/my-site
  2. Edit demos/my-site/package.json — set a unique name field.

  3. Run pnpm install from the root to link workspace dependencies.

  4. Start developing:

    pnpm --filter my-site dev

Your site will use workspace:* links to the local packages, so any changes you make to core will be reflected immediately (with watch mode).

Key Architectural Concepts

  • Schema lives in the database, not in code. _emdash_collections and _emdash_fields are the source of truth.
  • Real SQL tables per collection (ec_posts, ec_products), not EAV.
  • Kysely for all queries. Never interpolate into SQL -- see AGENTS.md for the full rules.
  • Handler layer (api/handlers/*.ts) holds business logic. Route files are thin wrappers.
  • Middleware chain: runtime init -> setup check -> auth -> request context.

Adding a Migration

  1. Create packages/core/src/database/migrations/NNN_description.ts (zero-padded sequence number).
  2. Export up(db) and down(db) functions.
  3. Register it in packages/core/src/database/migrations/runner.ts — migrations are statically imported, not auto-discovered (Workers bundler compatibility).

Adding an API Route

  1. Create the file in packages/core/src/astro/routes/api/.
  2. Start with export const prerender = false;.
  3. Use apiError(), handleError(), parseBody() from #api/.
  4. Check authorization with requirePerm() on all state-changing routes.
  5. Register the route in packages/core/src/astro/integration/routes.ts.

Commits and PRs

  • Branch from main.
  • Commit messages: describe why, not just what.
  • Ensure pnpm typecheck and pnpm --silent lint:json pass before pushing.
  • Run relevant tests.

What's Intentionally Missing (For Now)

These are known gaps -- don't try to fix them unless specifically asked:

  • Rate limiting -- no brute-force protection on auth endpoints
  • Password auth -- passkeys + magic links + OAuth only, by design
  • Plugin marketplace -- architecture exists, runtime installation is post-beta
  • Real-time collaboration -- planned for v1

Getting Help

  • Read AGENTS.md for architecture and code patterns
  • Check the documentation site for guides and API reference
  • Open an issue or ask in the chat