Files
DuckQ1u 93d1b7c3d3
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
first commit
2026-04-22 19:51:20 +07:00

284 lines
11 KiB
Markdown

# AGENTS.md
This file provides guidance to AI Agents when working with code in this repository.
## Package Manager
**Always use `pnpm` for all commands.** This repository uses pnpm workspaces, not npm.
## Monorepo Structure
Ghost is a pnpm + Nx monorepo with three workspace groups:
### ghost/* - Core Ghost packages
- **ghost/core** - Main Ghost application (Node.js/Express backend)
- Core server: `ghost/core/core/server/`
- Frontend rendering: `ghost/core/core/frontend/`
- **ghost/admin** - Ember.js admin client (legacy, being migrated to React)
- **ghost/i18n** - Centralized internationalization for all apps
### apps/* - React-based UI applications
Two categories of apps:
**Admin Apps** (embedded in Ghost Admin):
- `admin-x-settings`, `admin-x-activitypub` - Settings and integrations
- `posts`, `stats` - Post analytics and site-wide analytics
- Built with Vite + React + `@tanstack/react-query`
**Public Apps** (served to site visitors):
- `portal`, `comments-ui`, `signup-form`, `sodo-search`, `announcement-bar`
- Built as UMD bundles, loaded via CDN in site themes
**Foundation Libraries**:
- `admin-x-framework` - Shared API hooks, routing, utilities
- `admin-x-design-system` - Legacy design system (being phased out)
- `shade` - New design system (shadcn/ui + Radix UI + react-hook-form + zod)
### e2e/ - End-to-end tests
- Playwright-based E2E tests with Docker container isolation
- See `e2e/CLAUDE.md` for detailed testing guidance
## Common Commands
### Development
```bash
corepack enable pnpm # Enable corepack to use the correct pnpm version
pnpm run setup # First-time setup (installs deps + submodules)
pnpm dev # Start development (Docker backend + host frontend dev servers)
```
### Building
```bash
pnpm build # Build all packages (Nx handles dependencies)
pnpm build:clean # Clean build artifacts and rebuild
```
### Testing
```bash
# Unit tests (from root)
pnpm test:unit # Run all unit tests in all packages
# Ghost core tests (from ghost/core/)
cd ghost/core
pnpm test:unit # Unit tests only
pnpm test:integration # Integration tests
pnpm test:e2e # E2E API tests (not browser)
pnpm test:all # All test types
# E2E browser tests (from root)
pnpm test:e2e # Run e2e/ Playwright tests
# Running a single test
cd ghost/core
pnpm test:single test/unit/path/to/test.test.js
```
### Linting
```bash
pnpm lint # Lint all packages
cd ghost/core && pnpm lint # Lint Ghost core (server, shared, frontend, tests)
cd ghost/admin && pnpm lint # Lint Ember admin
```
### Database
```bash
pnpm knex-migrator migrate # Run database migrations
pnpm reset:data # Reset database with test data (1000 members, 100 posts) (requires pnpm dev running)
pnpm reset:data:empty # Reset database with no data (requires pnpm dev running)
```
### Docker
```bash
pnpm docker:build # Build Docker images
pnpm docker:clean # Stop containers, remove volumes and local images
pnpm docker:down # Stop containers
```
### How `pnpm dev` works
The `pnpm dev` command uses a **hybrid Docker + host development** setup:
**What runs in Docker:**
- Ghost Core backend (with hot-reload via mounted source)
- MySQL, Redis, Mailpit
- Caddy gateway/reverse proxy
**What runs on host:**
- Frontend dev servers (Admin, Portal, Comments UI, etc.) in watch mode with HMR
- Foundation libraries (shade, admin-x-framework, etc.)
**Setup:**
```bash
# Start everything (Docker + frontend dev servers)
pnpm dev
# With optional services (uses Docker Compose file composition)
pnpm dev:analytics # Include Tinybird analytics
pnpm dev:storage # Include MinIO S3-compatible object storage
pnpm dev:all # Include all optional services
```
**Accessing Services:**
- Ghost: `http://localhost:2368` (database: `ghost_dev`)
- Mailpit UI: `http://localhost:8025` (email testing)
- MySQL: `localhost:3306`
- Redis: `localhost:6379`
- Tinybird: `http://localhost:7181` (when analytics enabled)
- MinIO Console: `http://localhost:9001` (when storage enabled)
- MinIO S3 API: `http://localhost:9000` (when storage enabled)
## Architecture Patterns
### Admin Apps Integration (Micro-Frontend)
**Build Process:**
1. Admin-x React apps build to `apps/*/dist` using Vite
2. `ghost/admin/lib/asset-delivery` copies them to `ghost/core/core/built/admin/assets/*`
3. Ghost admin serves from `/ghost/assets/{app-name}/{app-name}.js`
**Runtime Loading:**
- Ember admin uses `AdminXComponent` to dynamically import React apps
- React components wrapped in Suspense with error boundaries
- Apps receive config via `additionalProps()` method
### Public Apps Integration
- Built as UMD bundles to `apps/*/umd/*.min.js`
- Loaded via `<script>` tags in theme templates (injected by `{{ghost_head}}`)
- Configuration passed via data attributes
### i18n Architecture
**Centralized Translations:**
- Single source: `ghost/i18n/locales/{locale}/{namespace}.json`
- Namespaces: `ghost`, `portal`, `signup-form`, `comments`, `search`
- 60+ supported locales
- Context descriptions: `ghost/i18n/locales/context.json` — every key must have a non-empty description
**Translation Workflow:**
```bash
pnpm --filter @tryghost/i18n translate # Extract keys from source, update all locale files + context.json
pnpm --filter @tryghost/i18n lint:translations # Validate interpolation variables across locales
```
`translate` is run as part of `pnpm --filter @tryghost/i18n test`. In CI, it fails if translation keys or `context.json` are out of date (`failOnUpdate: process.env.CI`). Always run `pnpm --filter @tryghost/i18n translate` after adding or changing `t()` calls.
**Rules for Translation Keys:**
1. **Never split sentences across multiple `t()` calls.** Translators cannot reorder words across separate keys. Instead, use `@doist/react-interpolate` to embed React elements (links, bold, etc.) within a single translatable string.
2. **Always provide context descriptions.** When adding a new key, add a description in `context.json` explaining where the string appears and what it does. CI will reject empty descriptions.
3. **Use interpolation for dynamic values.** Ghost uses `{variable}` syntax: `t('Welcome back, {name}!', {name: firstname})`
4. **Use `<tag>` syntax for inline elements.** Combined with `@doist/react-interpolate`: `t('Click <a>here</a> to retry')` with `mapping={{ a: <a href="..." /> }}`
**Correct pattern (using Interpolate):**
```jsx
import Interpolate from '@doist/react-interpolate';
<Interpolate
mapping={{ a: <a href={link} /> }}
string={t('Could not sign in. <a>Click here to retry</a>')}
/>
```
**Incorrect pattern (split sentences):**
```jsx
// BAD: translators cannot reorder "Click here to retry" relative to the first sentence
{t('Could not sign in.')} <a href={link}>{t('Click here to retry')}</a>
```
See `apps/portal/src/components/pages/email-receiving-faq.js` for a canonical example of correct `Interpolate` usage.
### Build Dependencies (Nx)
Critical build order (Nx handles automatically):
1. `shade` + `admin-x-design-system` build
2. `admin-x-framework` builds (depends on #1)
3. Admin apps build (depend on #2)
4. `ghost/admin` builds (depends on #3, copies via asset-delivery)
5. `ghost/core` serves admin build
## CSS Architecture
### TailwindCSS v4 Setup
Ghost Admin uses **TailwindCSS v4** via the `@tailwindcss/vite` plugin. CSS processing is centralized — only `apps/admin/vite.config.ts` loads the `@tailwindcss/vite` plugin. All embedded React apps (posts, stats, activitypub, admin-x-settings, admin-x-design-system) are scanned from this single entry point.
### Entry Point
`apps/admin/src/index.css` is the main CSS entry point. It contains:
- `@source` directives that scan class usage in shade, posts, stats, activitypub, admin-x-settings, admin-x-design-system, and kg-unsplash-selector
- `@import "@tryghost/shade/styles.css"` which loads the Shade design system styles
### Shade Styles
`apps/shade/styles.css` uses **unlayered** Tailwind imports:
```css
@import "tailwindcss/theme.css";
@import "./preflight.css";
@import "tailwindcss/utilities.css";
@import "tw-animate-css";
@import "./tailwind.theme.css";
```
**Why unlayered:** Ember's legacy CSS (`.flex`, `.hidden`, etc.) is unlayered. If Tailwind utilities were in a `@layer`, they would lose to Ember's unlayered CSS in the cascade. Keeping both unlayered means source order determines specificity.
Theme tokens/variants/animations are defined in CSS (`apps/shade/tailwind.theme.css` + runtime vars in `styles.css`), so there is no JS `@config` bridge in the Admin runtime lane. `tw-animate-css` is the v4 replacement for `tailwindcss-animate`.
### Critical Rule: Embedded Apps Must NOT Import Shade Independently
Apps consumed via `@source` (posts, stats, activitypub) must **NOT** import `@tryghost/shade/styles.css` in their own CSS. Doing so causes duplicate Tailwind utilities and cascade conflicts. All Tailwind CSS is generated once via the admin entry point.
### Public Apps
Public-facing apps (`comments-ui`, `signup-form`, `sodo-search`, `portal`, `announcement-bar`) remain on **TailwindCSS v3**. They are built as UMD bundles for CDN distribution and are independent of the admin CSS pipeline.
### Legacy Apps
`admin-x-design-system` and `admin-x-settings` are consumed via `@source` in admin's centralized v4 pipeline for production, and both packages build with CSS-first Tailwind v4 setup.
## Code Guidelines
### Commit Messages
When the user asks you to create a commit or draft a commit message, load and follow the `commit` skill from `.agents/skills/commit`.
### When Working on Admin UI
- **New features:** Build in React (`apps/admin-x-*` or `apps/posts`)
- **Use:** `admin-x-framework` for API hooks (`useBrowse`, `useEdit`, etc.)
- **Use:** `shade` design system for new components (not admin-x-design-system)
- **Translations:** Add to `ghost/i18n/locales/en/ghost.json`
### When Working on Public UI
- **Edit:** `apps/portal`, `apps/comments-ui`, etc.
- **Translations:** Separate namespaces (`portal.json`, `comments.json`)
- **Build:** UMD bundles for CDN distribution
### When Working on Backend
- **Core logic:** `ghost/core/core/server/`
- **Database Schema:** `ghost/core/core/server/data/schema/`
- **API routes:** `ghost/core/core/server/api/`
- **Services:** `ghost/core/core/server/services/`
- **Models:** `ghost/core/core/server/models/`
- **Frontend & theme rendering:** `ghost/core/core/frontend/`
### Design System Usage
- **New components:** Use `shade` (shadcn/ui-inspired)
- **Legacy:** `admin-x-design-system` (being phased out, avoid for new work)
### Analytics (Tinybird)
- **Local development:** `pnpm dev:analytics` (starts Tinybird + MySQL)
- **Config:** Add Tinybird config to `ghost/core/config.development.json`
- **Scripts:** `ghost/core/core/server/data/tinybird/scripts/`
- **Datafiles:** `ghost/core/core/server/data/tinybird/`
## Troubleshooting
### Build Issues
```bash
pnpm fix # Clean cache + node_modules + reinstall
pnpm build:clean # Clean build artifacts
pnpm nx reset # Reset Nx cache
```
### Test Issues
- **E2E failures:** Check `e2e/CLAUDE.md` for debugging tips
- **Docker issues:** `pnpm docker:clean && pnpm docker:build`