This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Pyra.js is a full-stack web framework built as a TypeScript monorepo using pnpm workspaces. It provides file-based routing with SSR (server-side rendering), an HMR dev server, esbuild-based production builds, request tracing, and a CLI with project scaffolding. The architecture is framework-agnostic — the PyraAdapter interface decouples the UI framework from the core runtime. The React adapter (@pyra-js/adapter-react) is the reference implementation.
Five packages with strict build order: shared → core + adapter-react → cli + create-pyra
- packages/shared (
@pyra-js/shared) — Types (types.ts), config loader (config-loader.ts), env loader (env-loader.ts), logger (logger.ts), network utilities (net-utils.ts) - packages/core (
@pyra-js/core) — Dev server with HMR, production server, esbuild bundler with caching, file-based route scanner, trie-based router, middleware runner, request context, request tracer, metrics collection, build orchestrator, CORS, CSS extraction, image optimization plugin - packages/adapter-react (
@pyra-js/adapter-react) — React SSR adapter implementingPyraAdapterinterface. ExportscreateReactAdapter(),<Link>,<Image>. UsesrenderToString()for server rendering and a persistentPyraAppshell withhydrateRoot()for client-side navigation. - packages/cli (
@pyra-js/cli) — CLI commands (bin.tsentry point), interactive scaffolding (commands/init.ts), package manager detection (pm.ts), dependency graph visualization (graph/), project diagnostics (commands/doctor.ts), dev/prod banners, keyboard shortcuts, templates. Re-exports user-facing types from@pyra-js/shared. - packages/create-pyra (
create-pyra) — Standalone scaffolder fornpm create pyra. Interactive prompts, template copying, post-copy patching (patches.ts), Tailwind integration. - packages/compat-pyrajs-* — Compat shims re-exporting from
@pyra-js/*for backward compatibility with oldpyrajs-*package names. Published at same version as main packages.
# Install dependencies
pnpm install
# Build all packages (respects build order)
pnpm build
# Link CLI globally for testing
pnpm dev:link # runs pnpm build && cd packages/cli && npm link
# Remove global CLI link
pnpm dev:unlink
# Type checking across all packages
pnpm typecheck
# Run tests
pnpm test
pnpm test:watch
pnpm test:coverage
# Clean build artifacts
pnpm clean
# Run CLI in development without building
cd packages/cli && pnpm dev:run
# Publish all packages (syncs compat versions, builds, publishes)
./scripts/publish-all.sh
./scripts/publish-all.sh --dry-runIndividual packages are built with tsup. The CLI build also runs scripts/copy-templates.mjs to copy template files to dist/templates/.
The CLI entry point is packages/cli/src/bin.ts. Main commands:
pyra dev— Start dev server with HMR (HTTP + WebSocket). Options:--port,--open,--config,--mode,--verbosepyra build— Production build via esbuild. Options:--out-dir,--minify,--sourcemap,--config,--mode,--silentpyra start— Start production server (requirespyra buildfirst). Options:--port,--config,--dist,--silentpyra init [name]— Interactive scaffolding with template/language/Tailwind/React Compiler selection. Options:--template,--language,--pm,--tailwind,--ui,--skip-install,--force,--silentpyra graph [path]— Dependency graph visualization. Formats: html/svg/png/mermaid/dot/json. Options:--format,--outfile,--open,--internal-only,--external-only,--filter,--hide-dev,--hide-peer,--cycles,--stats,--pm,--json,--silentpyra doctor— Diagnose project setup (detects SSR vs SPA mode, validates config, scans routes). Options:--config,--silent
All three server commands (dev, build, start) require config.adapter to be set — they error with a helpful message if it is missing. The adapter is never hardcoded in the CLI; it must come from the project's pyra.config.ts.
Config loader is in packages/shared/src/config-loader.ts. Auto-discovers files in order: pyra.config.ts → .js → .mjs → .cjs → .pyrarc.ts → .pyrarc.js → .pyrarc.mjs. Supports static objects, mode-aware functions (defineConfigFn), and async configs. Priority: defaults < config file < CLI flags.
Key config fields: root, entry, routesDir (default src/routes), adapter (required — a PyraAdapter instance), server (DevServerConfig), build (BuildConfig), resolve, env, plugins, trace ({ production: 'off' | 'header' | 'on', bufferSize }), buildReport ({ warnSize }).
- Scanner (
core/src/scanner.ts): Recursively walkssrc/routes/discoveringpage.tsx(pages),route.ts(APIs),layout.tsx(layouts),middleware.ts(middleware),error.tsx(error boundaries),404.tsx(not-found page). Supports route groups(name), dynamic segments[slug], catch-all[...path]. Validates no route collisions and resolves layout/middleware/error ancestry. - Router (
core/src/router.ts): Trie-based URL matching with priority: static > dynamic > catch-all. Built fromScanResultviacreateRouter(). Matches returnRouteMatchwith route, params, and layout chain. - Middleware (
core/src/middleware.ts):runMiddleware()executes a chain ofMiddlewarefunctions withnext()pattern. Short-circuits if middleware returns a Response without callingnext(). - Request Context (
core/src/request-context.ts): BuildsRequestContextfrom Node'sIncomingMessage. Includes Web standardRequest,URL, params,CookieJar, env vars (filtered byPYRA_prefix), and response helpers (json(),html(),redirect(),text()). Also exportscreateBuildTimeRequestContext()for SSG prerendering.
- DevServer (
core/src/dev/dev-server.ts): HTTP server with WebSocket HMR. Modular handlers split acrossdev-ssr.ts,dev-api.ts,dev-static.ts,dev-errors.ts,dev-proxy.ts,dev-dashboard.ts. Pipeline: route match → compile → load → render → inject assets. Special endpoints:/__pyra/modules/*(JS bundles),/__pyra/styles/*(extracted CSS astext/css),/_pyra(dashboard UI),/_pyra/navigate(client-side navigation JSON),/_pyra/image(on-demand image optimization),/_pyra/api/traces(trace API). HMR client at/__pyra_hmr_client. - ProdServer (
core/src/prod/prod-server.ts): Serves prebuilt assets fromdist/. Manifest-driven asset injection. Graceful shutdown withinflightCounttracking (503 during shutdown, 10s drain timeout). Conditional request tracing viashouldTrace(). Endpoint/_pyra/navigatefor client-side navigation. Endpoint/_pyra/imageserves pre-built image variants.
- Bundler (
core/src/bundler.ts): Wraps esbuild with an in-memory cache (5-second TTL). Maintains a separatecssOutputCache— CSS extracted from browser-platform builds stored here and exposed viagetCSSOutput(filePath). Both caches cleared byclearBundleCache()andinvalidateDependentCache(). - Build Orchestrator (
core/src/build/build-orchestrator.ts): Production build producingdist/client/+dist/server/+dist/manifest.json. Per-route hydration entries, esbuild code splitting with content hashing, export detection via metafile. Prerender support (static and dynamic withpaths()). Build report with sizes, modes, shared chunks, gzip estimates, size warnings.
/_pyra/navigateendpoint: Available in both dev and prod. Accepts?path=query param. Runs middleware chain +load(), returns JSON{ data, clientEntry, layoutClientEntries, routeId }. Redirects returned as{ redirect: location }.PyraAppshell:getHydrationScript()generates a persistent React component usinguseState/useEffectinstead of a one-shothydrateRoot(). Swaps page component and data on navigation. Compares layout chains — falls back to full reload if layouts differ.<Link>component (adapter-react/src/Link.tsx): Intercepts same-origin clicks, passes through modifier-key clicks (Cmd/Ctrl/Shift/Alt), callswindow.__pyra.navigate(). Falls back tolocation.hrefif navigate not registered.
- RequestTracer (
core/src/tracer.ts): Per-request timing viaperformance.now().start()/end()pairs for pipeline stages. ProducesServer-Timingheaders (W3C format), tree-style terminal logs with bottleneck highlighting (yellow >50%, red >80%), andRequestTraceobjects. - MetricsStore (
core/src/metrics.ts): Singleton collecting build metrics (last 50), HMR events (last 100), dependency graph data, and request traces (ring buffer, default 200).routeStats()computes avg/p50/p95/p99 response times per route.
- Adapter (
adapter-react/src/adapter.ts): ImplementsPyraAdapterinterface.name: "react",fileExtensions: [".tsx", ".jsx"].renderToHTML()usescreateElement+renderToString()with layout wrapping (outermost first).getHydrationScript()generates thePyraApppersistent shell with layout imports anduseState/useEffect.getDocumentShell()returns HTML template with<!--pyra-head-->and<!--pyra-outlet-->markers. <Image>(adapter-react/src/Image.tsx): Generates responsive<picture>with<source>tags + fallback<img>. Uses/_pyra/imageendpoint.<Link>(adapter-react/src/Link.tsx): Client-side navigation. See above.
- Package Manager Detection (
cli/src/pm.ts): Detects npm/pnpm/yarn/bun via lockfile presence →npm_config_user_agent→ PATH availability → user prompt. - Graph System (
cli/src/graph/):buildGraph.tsanalyzes package.json files and lockfiles. Serializers output dot, html, json, and mermaid formats. Supports workspace detection, cycle detection, filtering, directed arrows, workspace color palette, in-degree badges. - Templates (
cli/templates/):react-ts-fullstack,react-js-fullstack,react-ts-spa,react-js-spa,vanilla-ts,vanilla-js. Copied todist/templates/at build time. Full-stack templates includepyra.config.tswithcreateReactAdapter(),style.cssinsrc/routes/, imported inlayout.tsx.{{PROJECT_NAME}}and{{PYRA_VERSION}}placeholders replaced at scaffold time. - Reporter (
cli/src/utils/reporter.ts):withBanner()wraps command execution with timing and banner display. Respects--silentflag andPYRA_SILENTenv var. - Keyboard Shortcuts (
cli/src/utils/keyboard.ts): TTY keyboard shortcuts for dev/prod servers (restart, quit, open browser, clear). - Dev Banner (
cli/src/utils/dev-banner.ts): Styled startup banners for dev and production servers with capability detection (Unicode, color, CI). - Doctor (
cli/src/commands/doctor.ts): Project diagnostics — detects project mode (Static SPA vs Full-Stack SSR vs Misconfigured), validates config, scans routes, checks adapter. Usesconfig.adapter === falseto detect intentionally adapter-less (SPA) mode.
packages/create-pyra/src/index.ts: Main scaffolding fornpm create pyra. Prompts: project name, framework (vanilla/react/preact), app mode (SSR/SPA), language (TS/JS), Tailwind, React Compiler. Copies template fromtemplate-{framework}-{ts|js}/directory.packages/create-pyra/src/patches.ts: Post-copy patching system — applies targeted file modifications after template copy (e.g., React Router or TanStack Router setup, React Compiler config).- Tailwind scaffolding: For full-stack (SSR) mode, prepends
@tailwinddirectives to existingstyle.css(preserving template styles). For SPA/vanilla, creates a newindex.cssand injects the import. - Templates:
template-react-ts,template-react-js,template-preact-ts,template-preact-js,template-vanilla-ts,template-vanilla-jsplus SPA variants. All React full-stack templates include@pyra-js/adapter-reactin dependencies andcreateReactAdapter()inpyra.config.
Core types are in packages/shared/src/types.ts. Key types:
PyraConfig— Full config object.adapterfield isstring | PyraAdapter | false— usefalsefor intentional SPA (no SSR),PyraAdapterinstance for full-stack. String form is reserved but not currently used.PyraAdapter— Framework adapter interface:name,fileExtensions,esbuildPlugins(),renderToHTML(component, data, context),getHydrationScript(clientEntryPath, containerId, layoutClientPaths?),getDocumentShell()RouteNode— Route definition (id, pattern, filePath, type, params, catchAll, layoutId, middlewarePaths, errorBoundaryId, children)RouteGraph— Router interface (nodes, match, get, pageRoutes, apiRoutes, toJSON)RequestContext— Per-request context (request, url, params, headers, cookies, env, mode, routeId, json/html/redirect/text helpers)Middleware—(context, next) => Response | Promise<Response>RenderContext— Passed to adapter: component, data, params, layouts array, error (for error boundaries)RequestTrace/TraceStage— Request tracing data structuresRouteManifest/ManifestRouteEntry— Build manifest types (includes clientEntry, layoutClientEntries, hasLoad, middleware, prerendered, cache, errorBoundaryEntry)ImageFormat,ImageConfig,ImageVariant,ImageManifestEntry— Image optimization typesdefineConfig()/defineConfigFn()— Config helper functions (re-exported from@pyra-js/cli)
User-facing types (RequestContext, Middleware, ErrorPageProps, CacheConfig, PrerenderConfig, defineConfig, defineConfigFn) are re-exported from @pyra-js/cli — application developers never need to import from @pyra-js/shared directly.
All docs live in docs/:
adapter-react.md— React adapter: setup,<Link>,<Image>, SSR internals, TypeScript configadapters.md— Framework adapter architecture andPyraAdapterinterfaceapi-routes.md— Building API routes with HTTP method handlersci.md/ci-cd.mdx— CI/CD integration guidescli-reference.md— Complete CLI command referenceconfiguration.md— Configuration system (all fields, discovery order, mode-aware configs)cookies.md— Cookie handling in routes and middlewarecors.md— CORS configuration viacors.tsdashboard.md— Dev server dashboard UI (/_pyra)Dev-server.md— Development server internalsenv.md— Environment variables (PYRA_prefix,.envfiles)image-optimization.mdx—pyraImages()plugin +<Image>component guidelayouts.md— Layout nesting, route groups, what layouts can't domiddleware.md— Middleware creation, stacking, and auth patternspages.md— Page files: static/dynamic/catch-all routes,load(), prerendering, cache controlplugins.mdx— Plugin API documentationrequest-context.md—RequestContextinterface and all helpersrequest-tracing.md— Request tracing, Server-Timing headers, dashboard metricsrouting.md— File-based routing overview, route types, priority, navigationssr-and-data-loading.md— SSR pipeline,load()function, hydrationtesting.mdx— Testing guidetutorial-todo-app.md— Beginner-friendly full-stack todo app tutorial
- TypeScript (ES2020, strict mode, bundler module resolution)
- tsup for package builds
- esbuild ^0.25.0 for bundling
- Commander.js for CLI argument parsing
- @inquirer/prompts for interactive
pyra initprompts - @clack/prompts for interactive
create-pyraprompts - chokidar ^4.0.3 for file watching (HMR)
- ws ^8.18.0 for WebSocket HMR
- picocolors / chalk for terminal output
- sharp >=0.33.0 (optional peer dep of
@pyra-js/corefor image optimization) - React 18/19 (peer dependency of
@pyra-js/adapter-react) - Node.js >=18.0.0, ESM output format
- pnpm 10.x as package manager
- vitest for testing