withastro/astro
github.com/withastro/astro ↗ · 4000 files · 5.1 MB · scan capped
Astro is a website build tool for the modern web — powerful developer experience meets lightweight output.
Overview
Astro is a website framework for developers building static sites and applications with on-demand server rendering. This monorepo contains the Astro CLI and runtime, framework integrations, deployment adapters, language tooling, examples, and development infrastructure. At commit 4c1470a7f907fe678ef5e7dceaa972ca83d297da, distinctive mechanisms include selectively hydrated browser islands, sequential Vite environment builds, schema-validated content loaders with persistent stores, and composable request handlers driven by an SSRManifest.
System map
The repository is a pnpm workspace whose main product is the published astro package, with companion integrations, adapters, scaffolding, and language tools. The executable CLI loads built modules, while development and production builds orchestrate Vite environments. Server requests use a manifest-driven functional core and browser islands handle selective hydration. Content persistence belongs to build/dev infrastructure, while runtime content access goes through a portable read interface. This inventory is bounded and does not enumerate every package or exported API.
- The root is a private ESM workspace pinned to pnpm@11.27.0 with Node >=22.12.0; its build scripts select Astro, create-astro, @astrojs packages, and benchmark packages through Turbo.
- pnpm-workspace.yaml includes packages, examples, scripts, benchmarks, and temporary test workspaces, and prefers linked workspace packages over registry Astro versions.
- The published astro package is version 7.3.5; its executable is bin/astro.mjs and its public exports include config, app, fetch, Hono, content loaders, assets, middleware, and container APIs.
- bin/astro.mjs checks Node compatibility, normalizes Windows drive-letter casing, then imports dist/cli/index.js and passes process.argv to cli().
- Server request architecture is keyed by SSRManifest with per-request FetchState; App and NodeApp remain compatibility shells and owning modules hold per-manifest memoized state.
- Development creates a Vite server and route list, runs config hooks and syncInternal(), and exposes request handling through Vite middlewares.
- Build output can be static or server-oriented; AstroBuilder rejects server output without an adapter and passes collected page data to viteBuild().
- Vite build orchestration runs prerender, optional SSR, and client builds in that order; client inputs are discovered during earlier builds and sorted before the client build.
- Content storage supports deterministic single-file devalue serialization and content-addressed chunk files committed through a manifest; runtime storage defaults to InMemorySource.
- The repository includes React, Vue, Svelte, Node, Vercel, Cloudflare, Netlify, MDX, RSS, and language-tooling packages; Cloudflare declares static/server support and a Workers-compatible image-service constraint.
Feature catalog
Astro exposes developer commands alongside site-facing routing, rendering, actions, content, and image capabilities. Interactive components are hydrated according to client directives rather than requiring one application-wide client runtime. Integrations supply framework renderers and deployment-specific behavior. The inspected feature set also includes sessions, caching, internationalization, browser navigation, and font providers, but is not an exhaustive catalog of all workspace products.
- The CLI recognizes add, sync, telemetry, preferences, dev, build, preview, check, create-key, docs, and info, plus help/version behavior.
- astro dev provides stop, status, and logs subcommands, explicit background startup, agent-inferred background startup, and --ignore-lock behavior.
- astro build accepts output-directory, mode, development-output, and force flags; force is documented as clearing content caches for a full rebuild.
- File-based routes include .astro and supported Markdown extensions, with bracket parameters and spread parameters parsed into RoutePart records.
- Matched endpoints execute renderEndpoint(); page routes obtain props and a rendering result before calling renderPage(), including streaming and slot inputs.
- Astro Actions support JSON and FormData handlers with optional Zod input schemas; RPC calls return serialized responses while form calls leave results in locals for page rendering.
- The file content loader reads JSON, YAML, or TOML, accepts custom parsers, validates entries through parseData(), and reloads the collection on watched file changes.
- getCollection() reads stored content and resolves image references; live collections instead call loader.loadCollection() and apply their schema at runtime.
- Browser islands recognize load, idle, visible, media, and only directives, dynamically import component and renderer modules, and dispatch astro:hydrate after hydration.
- The image endpoint parses transformation URLs, loads allowed remote or local sources, invokes the configured image service, and returns transformed bytes with cache and ETag headers.
- astro/fetch exposes composable sessions, redirects, actions, i18n, and cache handlers; navigate() performs browser transitions and warns once when called during server rendering.
- The fontProviders registry exposes Adobe, Bunny, Fontshare, Fontsource, Google, Google Icons, local, and npm provider factories.
Data flow
Configuration enters through CLI flags and executable project config, then becomes validated settings and generated manifests. Build environments discover routes and client assets before emitting deployment output. Requests use a manifest plus isolated FetchState to move through middleware, action or page dispatch, and provider finalization. Content loaders transform source records into scoped mutable stores; serialized snapshots become generated runtime modules and read-only query sources.
- resolveConfig() resolves the root, loads project config, merges inline configuration with higher priority, and validates the merged result before returning userConfig and astroConfig.
- Config discovery checks astro.config.mjs, .js, .ts, and .mts; loadConfig() delegates executable configuration loading to loadConfigWithVite().
- Integration setup mutates renderer, script, injected-route, watch-file, toolbar, client-directive, and middleware settings through named hook callbacks.
- DefaultFetchHandler.fetch() combines a Request, render options, and explicit or ambient manifest into FetchState, then delegates to handleRequest().
- FetchState initializes locals, URL, cookies, client address, and timing; forwarded headers are applied only with configured allowedDomains and for non-prerendered routes.
- The request handler registers session/cache providers, runs middleware around actions and pages, applies i18n processing, finalizes providers, and prepares the outgoing response.
- Action request input is parsed according to Content-Type and manifest.actionBodySizeLimit, then bound to an ActionAPIContext before invoking the resolved handler.
- ContentLayer supplies each loader a scoped store, metadata store, schema parser, Markdown renderer, digest generator, watcher, and entry-type map.
- MutableDataStore.set() updates collection maps and schedules saving; writeToDisk() serializes in-process writes, invokes the writer, and notifies listeners only when persisted data changed.
- Runtime content loading imports astro:data-layer-content, handles Map, flattened-array, or chunk-manifest representations, wraps the immutable store in InMemorySource, and exposes queries through globalDataStore.
Behavior traces
The inspected flows cover development startup, production building, action execution, content persistence and retrieval, and browser hydration. Each trace follows named functions across source boundaries rather than relying on package descriptions. Several flows have important branches: foreground versus background dev, RPC versus form actions, and single-file versus chunked content storage. These are source-derived traces, not observed executions.
- Development entry trace: bin main() → cli(process.argv) → resolveCommand() → runCommand('dev') → cli/dev.dev(); foreground startup then calls flagsToAstroInlineConfig() → devServer().
- Development configuration trace: core dev() → createContainerWithAutomaticRestart() → resolveConfig() → createSettings() → createContainer(); createContainer runs config hooks, scans routes, creates Vite, and runs syncInternal().
- Development readiness trace: restore MutableDataStore → attachDataStoreInvalidation() → attachContentServerListeners() → initialize ContentLayer → watchContentConfig() → sync() → startContainer() → viteServer.listen() → server-start hook.
- Build trace: CLI build() → flagsToAstroInlineConfig() → core build() → resolveConfig() → createSettings() → AstroBuilder.run() → setup() → build() → viteBuild().
- Build emission trace: viteBuild() tracks pages and clears configured output → buildEnvironments() → runHookBuildSetup() → vite.createBuilder() → builder.buildApp(); the callback builds prerender, optional SSR, and sorted client inputs.
- Action trace: handleRequest() → handleMiddleware() → actionsAndPages() → handleAction() → getActionContext() → getAction() → parseRequestBody() → bound defineAction handler → serializeActionResult(); RPC returns a response and form actions continue to handlePages().
- Content ingestion trace: ContentLayer.sync() queues #doSync() → constructs loader context and parseData() → loader.load(context); file-loader load() resolves the configured file → syncData() reads/parses it → parseData() → scoped store.set().
- Content persistence trace: store.set() schedules saving → waitUntilSaveComplete() → writeToDisk() → writer.write(); FileWriter uses deterministic serialization and atomic rename, while ChunkedWriter writes hashed parts before the manifest and prunes stale files.
- Content query trace: getCollection() → globalDataStore.get() → ImmutableDataStore.fromModule() → InMemorySource → store.values() → resolveEntryData() → optional filter → returned collection entries.
- Hydration trace: AstroIsland.connectedCallback() → childrenConnectedCallback() → start() → Astro[directive] callback → importWithRetry(component/renderer) → hydrate(); hydration waits for a parent island, revives props, calls the renderer, removes ssr, and emits astro:hydrate.
Testing signals
Tests and CI were inspected, not executed; a test's existence is not a passing result. Core tests use node:test with built dist imports, while browser tests use Playwright and benchmark tooling uses Vitest. Inspected cases cover persistence, action behavior, origin-check composition, provider finalization, and hydration recovery. CI defines lint, type, cross-platform test, browser, smoke, and benchmark jobs. Inspection was bounded, so coverage completeness and untested repository-wide behavior remain unestablished.
- Core unit-test guidance requires node:test and node:assert/strict, placement under package test/units, and imports from built dist output.
- Astro's test script runs unit, integration, and type suites; unit tests use strip-types and teardown, integration tests use parallel execution, and browser suites run Chrome and Firefox configurations.
- astro-scripts test uses node:test.run(), supports match/only/parallel/setup/teardown options, and selects process isolation for parallel tests versus no isolation for sequential tests.
- Store-persistence tests assert restored data after updates and include chunked entry records crossing part boundaries.
- Action integration tests create a fixture with a unique outDir, start dev, build a test-adapter app, and assert invalid direct invocation and form-action page results.
- Action origin tests mount Hono actions() before middleware() and assert that cross-origin requests return 403 without executing the action, while same-origin requests execute it.
- Endpoint origin tests mount pages() without middleware() and assert cross-origin rejection before endpoint execution; provider-finalization tests assert a 500 when a finalizer rejects.
- Playwright hydration tests abort the first component import and assert recovery without reload; persistent failure tests assert one hydration-error event and no page errors.
- CI's Astro/integration matrix covers Ubuntu with Node 22 and 24, and macOS/Windows with Node 24 after explicit exclusions; separate jobs execute E2E and smoke scripts.
- CI includes Biome, lint:ci, publint, and typecheck; Linux ts-plugin test failures are converted to warnings, so that step is not a strict passing gate.
- Example diagnostics run test:check-examples; continuous benchmarks build benchmark projects and invoke Vitest benches through CodSpeed in four shards, which establishes configured measurement infrastructure but no observed performance result.
Risk map
These risks are source observations and explicitly labeled hypotheses, not confirmed vulnerabilities. Sensitive boundaries include executable configuration, package-manager subprocesses, request-body parsing, forwarded headers, remote images, encrypted server data, and output-directory cleanup. Runtime composition and content persistence are coupling hotspots with regression tests present in the inspected sample. Documentation drift can misdirect changes, and CI has at least one intentionally nonblocking test step. No repository-wide security or coverage conclusion follows from this bounded inspection.
- Configuration loading is an execution boundary: loadConfig() calls loadConfigWithVite(), rather than parsing a passive JSON document.
- Package discovery can dynamically import resolved packages and trigger installation outside CI; installation uses a detected package manager and user confirmation unless skipAsk is set.
- Origin protection is enforced at action and composable page dispatch sinks as well as through middleware; its predicate exempts safe methods/prerendering and allows requests lacking both Fetch Metadata and Origin.
- Action namespace traversal blocks forbidden path keys, requires own properties, and treats functions as leaves to prevent traversal into handler properties.
- Hypothesis requiring adapter-specific verification: action parsing trusts a finite Content-Length for direct json()/formData() parsing, whereas missing-length requests use streamed byte accounting; correctness depends on how the host constructs Request bodies and headers. (inferred)
- Unexpected action exceptions become INTERNAL_SERVER_ERROR results containing the original Error.message, and serialization includes that message in the response body.
- Remote image requests validate initial sources and each redirect destination, cap redirect depth, and buffer the successful response; this is a network and memory-allocation boundary.
- Builds empty the configured outDir unless emptyOutDir is false; AstroBuilder explicitly rejects equality with root, making output-path configuration a destructive file-I/O boundary to inspect before execution.
- Content file loaders resolve names against config.root and read through fs; chunk writers prune their target directory after committing the manifest, so configured paths control the filesystem scope.
- Atomic store writes use a fixed .tmp sibling and require callers to serialize concurrent writes; MutableDataStore supplies in-instance serialization, but cross-process coordination was not established by these files.
- Cryptographic code reads the ASTRO_KEY variable by name, uses AES-GCM with 256-bit generated keys and random 12-byte IVs, and supports authenticated additionalData; no key values were inspected.
- The agent architecture guide describes Pipeline classes while core/README.md explicitly describes functional handlers and compatibility facades; CONTRIBUTING.md also lists pnpm 10.28.0 while package.json pins 11.27.0.
Extension points
Astro extends through named integration hooks, framework renderer registration, route injection, content loader protocols, and replaceable runtime providers. Vite virtual modules bridge generated configuration and bundled runtime code. Composable fetch APIs support alternative host-framework request chains. Several internal hooks are deliberately outside semver guarantees, so agents should distinguish supported public contracts from implementation conveniences.
- Integration objects expose hooks by name; runHookInternal() supplies an integration logger, awaits execution, and reports long-running hooks.
- astro:config:setup supplies addRenderer(), requiring a renderer name and serverEntrypoint; React registers its renderer and Vite configuration through this hook.
- Integrations can inject routes and scripts, merge configuration, add watch files, and register dev-toolbar applications.
- addClientDirective() rejects duplicate names and builds directive entrypoints; addMiddleware() registers entrypoints before or after application middleware.
- Internal addPageExtension(), addContentEntryType(), and addDataEntryType() hooks modify registries but are explicitly documented as outside semver guarantees.
- Adapters are integrations placed first in the setup list; Cloudflare uses setAdapter() to declare output features, preview entrypoint, and supported capabilities.
- Content collections can supply function loaders or object loaders with load(), schema, and createSchema(); the loader context scopes data and metadata access to the collection.
- DataStoreWriter separates persistence from mutable storage, while DataStoreSource separates async-capable runtime queries from the default in-memory implementation.
- astro/fetch exposes independently composable middleware, pages, actions, sessions, i18n, and cache functions; logic belongs in handler modules rather than the public wrapper file.
- FetchState context providers support lazy creation and finalization; the session provider registers AstroSession and persists it through PERSIST_SYMBOL.
- Vite SSR plugins can expose api.buildPostHook for extracted chunk mutation after environment builds; React's options plugin demonstrates resolveId/load generation of a virtual module.
Contributor handoff
Begin with the pinned manifests, current core architecture README, and the exact owning modules for the requested change. Tests and CI were inspected, not executed, and this brief supplies no passing baseline. Preserve runtime portability, built-output test imports, request finalization, and deterministic content persistence. Documentation contains drift, so confirm names and behavior against source before planning edits. The inspection is bounded; widen searches only where the task requires more evidence.
- Read core/README.md before request-runtime work: SSRManifest, owning-module memoization, RenderEnvironment, and FetchState are the documented current architecture.
- Use package.json as the package-manager authority: it pins pnpm@11.27.0, while CONTRIBUTING.md contains older pnpm guidance.
- AGENTS.md calls for explicit assumptions, simple solutions, surgical changes, and verifiable goals; unrelated cleanup should remain outside the change.
- Runtime files and generated runtime virtual modules must avoid Node APIs to support environments such as Cloudflare Workers; Node APIs are permitted in Vite plugin implementation code.
- For future authorized verification, rebuild before running tests that import dist, then select a specific astro-scripts test file before broad package suites.
- Reuse the documented createTestApp(), createPage(), createEndpoint(), createMockFetchState(), and domain-specific helpers instead of duplicating setup.
- Request-chain changes should preserve dispatch-level origin checks and provider finalization; dedicated tests exercise actions-before-middleware, pages-without-middleware, and rejecting finalizers.
- Content changes should preserve deterministic serialization, atomic rename, chunk manifest-last commits, and explicit onFileWritten notifications used for dev invalidation.
- AstroIsland source is prebuilt; the package prebuild script also serializes client directive implementations, so browser-runtime edits must account for generated artifacts.
- Formatting uses tabs, single quotes, and semicolons for JavaScript/TypeScript; AGENTS.md identifies pnpm format and pnpm lint:ai, whose execution remains outside this read-only investigation.
- Treat CI definitions as intended checks, not observed results; the Linux ts-plugin command explicitly tolerates failure, and Playwright has CI retries and one worker.
Languages
| Language | Files | Share of source |
|---|---|---|
| TypeScript | 2304 | 70% |
| JSON | 907 | docs/config |
| JavaScript | 784 | 24% |
| Markdown | 476 | docs/config |
| CSS | 108 | docs/config |
| Svelte | 74 | 2% |
| Vue | 70 | 2% |
| YAML | 57 | docs/config |
Start reading here
Probable source entrypoint: packages/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
packages/ (3410 files)
Repository structure
| Top-level path | Files |
|---|---|
packages/ | 3410 |
examples/ | 337 |
benchmark/ | 74 |
.github/ | 50 |
.agents/ | 32 |
.devcontainer/ | 29 |
.changeset/ | 23 |
scripts/ | 12 |
Docs found: AGENTS.mdCONTRIBUTING.mdREADME.md
Package
package.json — root v0.0.0, 22 declared dependencies.
@astrojs/check@biomejs/biome@changesets/changelog-github@changesets/cli@earendil-works/pi-ai@flue/runtime@types/nodeastro-benchmarkbgproceslinteslint-plugin-regexpknip
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructions.github/workflows/check-merge.yml— GitHub Actions workflow.github/workflows/check.yml— GitHub Actions workflow.github/workflows/ci.yml— GitHub Actions workflow.github/workflows/cleanup-cache.yml— GitHub Actions workflow.github/workflows/congrats.yml— GitHub Actions workflow.github/workflows/continuous_benchmark.yml— GitHub Actions workflow.github/workflows/diff-dependencies.yml— GitHub Actions workflow.github/workflows/examples-deploy.yml— GitHub Actions workflow
Structural health signals
Average score 9/10 across 96 analyzed source files; 0 hotspots surfaced.
packages/astro/test/fixtures/content-layer/src/content.config.ts— 409 lines, score 6.9 (Deep nesting, High churn)packages/integrations/cloudflare/test/fixtures/vite-plugin/src/content.config.ts— 275 lines, score 6.9 (Deep nesting, High churn)packages/astro/test/test-utils.ts— 412 lines, score 7.1 (Long function/block, High churn)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
4c1470a7f9072026-10-02 — Update examples (#18204)322cd139fadb2026-10-02 — Update astro (#18203)64e40396dc7e2026-10-01 — Port Go's csrf protection (#15595)956930f0a4252026-10-01 — [ci] format4f85749fc0982026-10-01 — fix(cloudflare): resolve image format from the source when `f` is absent (#17677)