|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What This Is |
| 6 | + |
| 7 | +Core Data Places is a config-driven, multi-tenant Astro website for searching and exploring cultural heritage data. Each deployment is customized via `public/config.json` and a separate GitHub content repo (for TinaCMS-managed pages, posts, paths, branding, and i18n). The same codebase powers multiple projects (USS, ArchNet, etc.) with different configs and content. |
| 8 | + |
| 9 | +## Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +npm start # Full dev server (build script + TinaCMS + Astro on port 4321) |
| 13 | +npm run build # Production build (validates config, builds TinaCMS admin, builds Astro) |
| 14 | +npm run vitest # Run unit tests (vitest) |
| 15 | +npm run test-config # Validate config.json schema only |
| 16 | +npm run playwright # Run Playwright e2e + a11y tests (requires A11Y_HOST env var) |
| 17 | +npx vitest run test/config.test.ts # Single vitest file |
| 18 | +npx playwright test --grep "Home page" # Single Playwright test by name |
| 19 | +``` |
| 20 | + |
| 21 | +`npm start` runs `scripts/build.mjs` first, which: fetches remote config, fetches Core Data field descriptors, clones the GitHub content repo, builds search.json, and copies custom components. This runs on every start. |
| 22 | + |
| 23 | +## Environment Setup |
| 24 | + |
| 25 | +Copy `.env.example` to `.env`. Key variables: |
| 26 | +- `CONFIG_URL` or `CONFIG_FILE` — where to get config.json |
| 27 | +- `GITHUB_OWNER`/`GITHUB_REPO`/`GITHUB_BRANCH`/`GITHUB_PERSONAL_ACCESS_TOKEN` — content repo |
| 28 | +- `TINA_PUBLIC_IS_LOCAL=true` — use local TinaCMS datalayer (recommended for dev) |
| 29 | +- `A11Y_HOST` — base URL for Playwright tests (e.g., `http://localhost:4321`) |
| 30 | +- `USE_CONTENT_CACHE=true` — skip content loaders on restart (faster dev iteration) |
| 31 | + |
| 32 | +TinaCMS uses port 9000 for its datalayer. Kill stale processes on 9000/4321 if `npm start` fails. |
| 33 | + |
| 34 | +### Multi-Tenant Env Var Gotchas |
| 35 | + |
| 36 | +This repo deploys many client sites (USS, ArchNet, etc.) to Netlify. When `netlify dev` runs, it injects the linked site's env vars into the shell, and `dotenv` won't override them. If your `.env` targets one project but the linked Netlify site targets another, the Netlify values win silently. |
| 37 | + |
| 38 | +**Proper fix:** Use deploy context-scoped env vars. Set `dev` context values on the Netlify site via `netlify env:set VAR --context dev` or the Netlify UI. See the `netlify` skill for details. |
| 39 | + |
| 40 | +**Quick workaround:** Run `npm start` directly instead of `netlify dev` to avoid Netlify env var injection. |
| 41 | + |
| 42 | +### TinaCMS Content Path |
| 43 | + |
| 44 | +Do **not** set `TINA_LOCAL_CONTENT_PATH` when the content repo has no `tina/` directory — TinaCMS will fail with "Unable to find Tina folder". The build script (`build.content.mjs`) clones the content repo automatically; `TINA_LOCAL_CONTENT_PATH` is only needed when developing TinaCMS schemas in a local content repo checkout that includes its own `tina/` directory. |
| 45 | + |
| 46 | +### Switching Projects |
| 47 | + |
| 48 | +When switching between projects (e.g., USS to ArchNet), always clean generated content first: |
| 49 | +```bash |
| 50 | +rm -rf content/ .tina/ |
| 51 | +``` |
| 52 | +`build.content.mjs` uses `fs.cpSync` which merges rather than replaces, so stale files from the previous project persist otherwise. |
| 53 | + |
| 54 | +## Architecture |
| 55 | + |
| 56 | +### Config-Driven Features |
| 57 | + |
| 58 | +`public/config.json` (aliased as `@config`) controls everything: |
| 59 | +- `core_data.url` + `core_data.project_ids` — Core Data API endpoint |
| 60 | +- `search[]` — each entry creates a `/[lang]/search/[name]` route with its own Typesense index |
| 61 | +- `detail_pages.models` — which Core Data models get detail pages (places, people, events, works, items, instances, organizations) |
| 62 | +- `content.collections` — which TinaCMS collections are enabled (`paths`, `posts`) |
| 63 | +- `layers[]` — map layers (raster, vector, geojson, georeference) |
| 64 | +- `i18n.locales` — drives Astro i18n routing |
| 65 | + |
| 66 | +Validated on every build by `test/config.test.ts`. |
| 67 | + |
| 68 | +### Routing |
| 69 | + |
| 70 | +All user-facing routes are under `[lang]/` (Astro i18n, `prefixDefaultLocale: true`): |
| 71 | +- `/[lang]/` — home page |
| 72 | +- `/[lang]/search/[name]/` — search pages (one per `config.search[]` entry) |
| 73 | +- `/[lang]/{places,people,events,works,items,instances,organizations}/[uuid]/` — Core Data detail pages |
| 74 | +- `/[lang]/pages/[slug]`, `/[lang]/posts/[slug]`, `/[lang]/paths/[slug]` — TinaCMS content |
| 75 | +- `/api/[model]/[uuid]/` — Core Data proxy API |
| 76 | + |
| 77 | +### Key Modules |
| 78 | + |
| 79 | +- **`src/apps/search/map/`** — Map-based search (Peripleo + MapLibre + Typesense). `MapSearchContext.tsx` manages state. |
| 80 | +- **`src/apps/search/list/`** — List-based search with pagination. |
| 81 | +- **`src/apps/detailPages/`** — Astro components per Core Data model. `RecordDetail.astro` is the shared wrapper. |
| 82 | +- **`src/apps/pages/`** — TinaCMS page builder components (Banner, MultiColumn, FreeText, etc.) |
| 83 | +- **`src/backend/tina/`** — TinaCMS GraphQL query wrappers (`fetchBranding`, `fetchPages`, `fetchPosts`, etc.) |
| 84 | +- **`src/services/coreData/`** — Per-model service classes for Core Data API. `factory.ts` has `getService(name)`. |
| 85 | +- **`src/loaders/`** — Astro content collection loaders (only used in static builds). |
| 86 | +- **`src/store/`** — Nanostores atoms (`pages.ts`, `notifications.ts`). |
| 87 | + |
| 88 | +### Build Pipeline (`scripts/`) |
| 89 | + |
| 90 | +`build.mjs` orchestrates pre-build steps: |
| 91 | +1. `build.config.mjs` — resolves config.json from `CONFIG_URL`, `CONFIG_FILE`, or `config.dev.json` |
| 92 | +2. `build.fields.mjs` — fetches Core Data descriptors → writes `src/i18n/userDefinedFields.json` |
| 93 | +3. `build.content.mjs` — clones `GITHUB_REPO` → copies `content/` directory (uses `fs.cpSync`, merges not replaces) |
| 94 | +4. `build.search.mjs` — writes `src/i18n/search.json` from config |
| 95 | +5. `build.components.mjs` — copies `content/components/` → `src/components/custom/project/` |
| 96 | + |
| 97 | +### Content Repo Pattern |
| 98 | + |
| 99 | +TinaCMS content lives in a **separate GitHub repo** (e.g., `uss-content`). The build script clones it and copies the `content/` directory into the project. In local dev, `TINA_LOCAL_CONTENT_PATH` points to a local checkout. The `content/` directory in this project is gitignored. |
| 100 | + |
| 101 | +Custom hit components can be provided via `content/components/` in the content repo, overriding defaults in `src/components/custom/default/`. |
| 102 | + |
| 103 | +### Generated/Gitignored Files |
| 104 | + |
| 105 | +These are built by `scripts/build.mjs` and should not be committed: |
| 106 | +- `content/` — cloned from content repo |
| 107 | +- `src/i18n/userDefinedFields.json`, `src/i18n/search.json` |
| 108 | +- `src/components/custom/project/` |
| 109 | +- `public/config.dev.json` |
| 110 | + |
| 111 | +## TypeScript Path Aliases |
| 112 | + |
| 113 | +`@config` → `public/config.json`, `@apps/*` → `src/apps/*`, `@backend/*` → `src/backend/*`, `@components/*` → `src/components/*`, `@services/*` → `src/services/*`, `@store/*` → `src/store/*`, `@utils/*` → `src/utils/*`, `@types` → `src/types.ts`, `@tina/*` → `tina/__generated__/*`, `@loaders/*` → `src/loaders/*`, `@i18n/*` → `src/i18n/*`, `@layouts/*` → `src/layouts/*`, `@visualizations/*` → `src/visualizations/*` |
| 114 | + |
| 115 | +## Deployment |
| 116 | + |
| 117 | +Netlify with SSR (server mode by default). Functions in `netlify/functions/tina.ts` handle TinaCMS backend and S3 media uploads. Static build available with `STATIC_BUILD=true`. |
| 118 | + |
| 119 | +## Style |
| 120 | + |
| 121 | +- 2-space indent for `.tsx` and `.astro` files |
| 122 | +- Astro components for server-rendered content, React (`.tsx`) with `client:only='react'` or `client:load` for interactive UI |
| 123 | +- Detail pages use `server:defer` for deferred SSR |
| 124 | +- Tailwind CSS v4 for styling |
| 125 | + |
| 126 | +## Related Skills |
| 127 | + |
| 128 | +Invoke these skills for domain-specific guidance: |
| 129 | + |
| 130 | +- **performant-studio** — Performant Studio product suite (FairData, FairImage, FairCopy), pricing tiers, and product context |
| 131 | +- **astro** — Astro framework patterns, configuration, content collections, and conventions |
| 132 | +- **netlify** — Netlify site management, deployments, environment variables, and functions |
| 133 | +- **tinacms** — TinaCMS configuration, schemas, content collections, and self-hosted setup |
| 134 | +- **playwright-a11y** — Playwright E2E testing with embedded WCAG 2.2 AA accessibility checks via axe-core |
0 commit comments