# sveltejs/svelte — codebase unpack

> Canonical page: https://codevetter.com/unpack/svelte

This is a deterministic Repo Unpack scan of sveltejs/svelte — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: 636eaaaa6f064b55072e7d192bb76dc9d8c4516e (2026-09-18)
- Last upstream commit: Version Packages (#18750)
- Generated by codevetter 1.12.0 on 2026-09-22
- 4000 files scanned · 2.1 MB

## Languages

- Svelte: 4547 files
- JavaScript: 3438 files
- JSON: 461 files
- HTML: 254 files
- CSS: 189 files
- Markdown: 136 files
- TypeScript: 86 files
- YAML: 10 files

## Repository structure

- packages/ — 3813 files
- documentation/ — 106 files
- benchmarking/ — 33 files
- playgrounds/ — 16 files
- .github/ — 10 files
- .changeset/ — 2 files
- .well-known/ — 1 files
- .agents/ — 1 files

Probable source entrypoint: `packages/` (inferred from the tree, not upstream docs)

## Source layout — packages/

- packages/svelte/ — 134 files

## Package

- package.json: svelte-monorepo v0.0.1 — 19 declared dependencies

## Structural health signals

Average score 9/10 across 30 files; 1 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- 636eaaaa6f06 (2026-09-18) Version Packages (#18750)
- 8030ff2a2c8b (2026-09-18) fix: remove incorrect special handling of :export pseudo-selectors (#18839)
- a72dc8eadbf6 (2026-09-18) fix: make Object.hasOwn reactive for state proxies (#18838)
- 6eb720a1b7ca (2026-09-11) chore: scope.js stops importing from the analysis visitors (#18787)
- e6ae460caa72 (2026-09-11) chore: analysis and transform stop importing each other (#18786)

## Overview

This is the `sveltejs/svelte` monorepo: a single publishable package (`packages/svelte`, v5.57.1) containing both a compiler that turns `.svelte` components into plain JavaScript modules and a signals-based client/server runtime those modules import at runtime. The compiler runs a strict three-phase pipeline (parse → analyze → transform) with separate client and server code generators, while the runtime implements Svelte 5 runes ($state/$derived/$effect) on top of a push-pull reactive graph with bitflag-tagged signals, batched flushing, and an experimental async/fork mode. It is aimed at framework consumers (SvelteKit, vite-plugin-svelte, svelte-check, the REPL) rather than app authors directly, and is distinctive for shipping unbundled ESM source as the published artifact, generating its error/warning catalogue and its `.d.ts` types from source, and carrying ~3,400 fixture directories as its primary test surface.

## System map

The repo is a pnpm workspace (`pnpm-workspace.yaml` globs `packages/*` and `playgrounds/*`) with exactly one real package, `packages/svelte`, plus a Vite sandbox playground and a Node benchmarking harness at the root. The package has two runtime boundaries encoded in its `exports` map — `browser`/`worker` conditions split every entry into client and server implementations — and a third, the compiler, which is deliberately firewalled from runtime code by a custom ESLint rule. Storage is entirely file-based: there is no database; the only persistent artefacts are compiled test fixtures under `tests/*/samples/*/_output/`, generated docs under `documentation/docs/98-reference/.generated/`, and the checked-in `types/index.d.ts`. External integrations are all build-time libraries (acorn, zimmerframe, esrap, magic-string, devalue) rather than services. Deployment is npm publish via Changesets on push to `main`.
- The published artifact is unbundled ESM source: `packages/svelte/package.json` sets `"main"`/`"module"` to `./src/index-client.js` and its `files` array ships `src` directly, so consumers import the raw runtime sources rather than a bundle. — [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json)
- Client/server split is done entirely through export conditions — `.`, `./legacy`, `./reactivity`, and `./store` each map `worker` and `default` to `*-server.js` and `browser` to `*-client.js` (e.g. `./src/index-server.js` vs `./src/index-client.js`). — [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json) [packages/svelte/src/index-client.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/index-client.js) [packages/svelte/src/server/index.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/server/index.js)
- The compiler's public surface is five functions exported from `src/compiler/index.js`: `compile` (L23), `compileModule` (L69), `parse` (L117), `parseCss` (L131), plus re-exported `preprocess`, `print`, and `migrate` (L202). — [packages/svelte/src/compiler/index.js#L1-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L1-L60) [packages/svelte/src/compiler/index.js#L195-202](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L195-L202)
- Compilation is a fixed three-phase pipeline — `_parse` from `phases/1-parse/index.js`, then `analyze_component` (2-analyze/index.js:333), then `transform_component` (3-transform/index.js) — with `state.reset()` called first to clear module-global compiler state. — [packages/svelte/src/compiler/index.js#L23-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L23-L60) [packages/svelte/src/compiler/phases/2-analyze/index.js#L333](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/2-analyze/index.js#L333) [packages/svelte/src/compiler/phases/3-transform/index.js#L19-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/index.js#L19-L60)
- Codegen forks per target inside `transform_component`: `options.generate === 'server'` dispatches to `server_component` in `3-transform/server/transform-server.js`, otherwise `client_component` in `3-transform/client/transform-client.js`; both emit an ESTree program that `esrap`'s `print` serialises with a source map. — [packages/svelte/src/compiler/phases/3-transform/index.js#L19-62](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/index.js#L19-L62) [packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L135-200](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L135-L200)
- The runtime reactivity core lives in four files — `internal/client/runtime.js` (866 lines: `get`, `update_reaction`, `update_effect`, `is_dirty`), `reactivity/sources.js`, `reactivity/deriveds.js`, and `reactivity/batch.js` (1475 lines, the `Batch` class and `flushSync`). — [packages/svelte/src/internal/client/runtime.js#L548-600](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/runtime.js#L548-L600) [packages/svelte/src/internal/client/reactivity/batch.js#L97-210](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js#L97-L210) [packages/svelte/src/internal/client/reactivity/deriveds.js#L71-113](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/deriveds.js#L71-L113)
- Signal identity and lifecycle are encoded as bitflags in `internal/client/constants.js` — `DERIVED`, `EFFECT`, `RENDER_EFFECT`, `BLOCK_EFFECT`, `BRANCH_EFFECT`, `BOUNDARY_EFFECT`, `CLEAN`/`DIRTY`/`MAYBE_DIRTY`, `ASYNC`, `ERROR_VALUE` — rather than as distinct classes. — [packages/svelte/src/internal/client/constants.js#L1-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/constants.js#L1-L60)
- Server rendering is a string-accumulating `Renderer` class (`internal/server/renderer.js`) with a private `#out` array and a separate `head` sub-renderer; `render()` in `internal/server/index.js:71` is a thin wrapper over `Renderer.render`. — [packages/svelte/src/internal/server/index.js#L60-80](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/index.js#L60-L80) [packages/svelte/src/internal/server/renderer.js#L116-210](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/renderer.js#L116-L210)
- Build commands: `pnpm build` at root fans out to `packages/*`; inside `packages/svelte`, `build` = `rollup -c && pnpm generate && node scripts/check-treeshakeability.js`, where rollup produces only the UMD `compiler/index.js` for CommonJS/browser consumers. — [package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/package.json) [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json) [packages/svelte/rollup.config.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/rollup.config.js)
- `pnpm generate` is a three-step codegen chain: `scripts/process-messages` (markdown → `errors.js`/`warnings.js` + docs), `scripts/generate-types.js` (dts-buddy), and `scripts/generate-browser-support.ts` (web-features → docs page). — [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json) [packages/svelte/scripts/process-messages/index.js#L1-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/scripts/process-messages/index.js#L1-L50) [packages/svelte/scripts/generate-browser-support.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/scripts/generate-browser-support.ts)
- Tests run from the repo root via a single Vitest config that aliases every `svelte/*` import back to `packages/svelte` source using the package's own `exports` map, and picks the `server` export when the importer path contains `_output/server`. — [vitest.config.js#L1-35](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/vitest.config.js#L1-L35)
- Deployment shape is npm publish driven by Changesets: `.github/workflows/release.yml` runs on push to `main`, calls `changesets/action` with `pnpm changeset:version` / `pnpm changeset:publish`, and sets `NPM_CONFIG_PROVENANCE: true` with `id-token: write`. — [.github/workflows/release.yml](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/release.yml) [package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/package.json)
- The only non-library workspace member is `playgrounds/sandbox`, a Vite + `@sveltejs/vite-plugin-svelte` app that consumes `svelte` as `workspace:*` and excludes it from `optimizeDeps` so source edits are picked up without a dev-server restart. — [playgrounds/sandbox/vite.config.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/playgrounds/sandbox/vite.config.js) [playgrounds/sandbox/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/playgrounds/sandbox/package.json)
- Benchmarking is a separate root-level Node harness (`benchmarking/run.js`) that forks each benchmark into its own child process 'so that heap/GC/JIT state from one benchmark cannot contaminate the others', across compiler, reactivity, and SSR suites. — [benchmarking/run.js#L1-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/benchmarking/run.js#L1-L50) [package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/package.json)
- There is no persistent datastore anywhere in the repo; the closest thing to a storage layer is `compile_directory` in `tests/helpers.js`, which wipes and rewrites `<sample>/_output/{client,server}` on every test run. — [packages/svelte/tests/helpers.js#L63-90](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/helpers.js#L63-L90)

## Feature catalog

There are no routes or screens — the user-facing surface is a set of npm subpath exports and a compiler API. Broadly: the compiler API (`compile`, `compileModule`, `parse`, `parseCss`, `preprocess`, `print`, `migrate`), the component runtime lifecycle API (`mount`, `hydrate`, `unmount`, `flushSync`, `fork`, `tick`, `untrack`, `settled`), context and lifecycle helpers, and a set of reactive standard-library classes. Around those sit optional subpackages for transitions, animations, easing, motion springs, stores, attachments, and events. Compiler behaviour is configurable through a validated options table, and two experimental features — async mode and `hydratable` — gate themselves on a runtime flag.
- `compile(source, options)` returns `{ js, css, warnings, metadata: { runes }, ast }`, where `ast` is post-processed by `to_public_ast` into either a modern AST (metadata stripped via a zimmerframe walk) or a Svelte 4-shaped legacy AST via `convert` from `compiler/legacy.js`. — [packages/svelte/src/compiler/index.js#L23-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L23-L60) [packages/svelte/src/compiler/index.js#L145-175](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L145-L175) [packages/svelte/src/compiler/legacy.js#L1-20](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/legacy.js#L1-L20)
- `parseCss(source)` is a standalone CSS-only entry point that uses `Parser.forCss` — a static factory that `Object.create`s a Parser prototype and skips all Svelte component parsing setup — returning a `StyleSheetFile` node with collected `css_comments`. — [packages/svelte/src/compiler/index.js#L126-146](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L126-L146) [packages/svelte/src/compiler/phases/1-parse/index.js#L37-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/1-parse/index.js#L37-L50)
- `migrate(source, { filename, use_ts })` (2,003 lines in `compiler/migrate/index.js`) is a Svelte 4 → 5 source-to-source codemod exposed publicly through `svelte/compiler`. — [packages/svelte/src/compiler/migrate/index.js#L124](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/migrate/index.js#L124) [packages/svelte/src/compiler/index.js#L202](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L202)
- `preprocess` accepts a `PreprocessorGroup` or array and applies hooks in a fixed order per preprocessor — `markup`, then `script`, then `style` — matching `<script>`/`<style>` blocks with dedicated regexes and stitching sourcemaps back together. — [packages/svelte/src/compiler/preprocess/index.js#L253-268](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/preprocess/index.js#L253-L268) [packages/svelte/src/compiler/preprocess/index.js#L338-368](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/preprocess/index.js#L338-L368)
- Compiler options are a declarative validator table in `validate-options.js`: `css` ('injected'|'external'), `cssHash`, `dev`, `discloseVersion`, `hmr`, `modernAst`, `namespace` ('html'|'mathml'|'svg'), `preserveComments`, `preserveWhitespace`, `fragments` ('html'|'tree'), `warningFilter`, and `experimental.async`. — [packages/svelte/src/compiler/validate-options.js#L11-130](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/validate-options.js#L11-L130)
- `mount(component, options)` and `hydrate(component, options)` are the two client entry points; both funnel into `_mount`, which accepts `target`, `anchor`, `props`, `events`, `context`, `intro`, and `transformError`. — [packages/svelte/src/internal/client/render.js#L56-100](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/render.js#L56-L100) [packages/svelte/src/internal/client/render.js#L156-200](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/render.js#L156-L200)
- The top-level `svelte` export surfaces `onMount`, `onDestroy`, `beforeUpdate`, `afterUpdate`, `createEventDispatcher`, `getAbortSignal`, plus re-exports of `flushSync`/`fork`, the context family, `hydratable`, `mount`/`hydrate`/`unmount`, `tick`/`untrack`/`settled`, and `createRawSnippet`. — [packages/svelte/src/index-client.js#L230-250](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/index-client.js#L230-L250) [packages/svelte/src/index-client.js#L44-105](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/index-client.js#L44-L105)
- `getAbortSignal()` lazily creates an `AbortController` on the active reaction (`active_reaction.ac ??= new AbortController()`) and throws `get_abort_signal_outside_reaction` if called outside a derived/effect; the abort reason is the shared `STALE_REACTION` singleton error. — [packages/svelte/src/index-client.js#L66-74](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/index-client.js#L66-L74) [packages/svelte/src/internal/client/constants.js#L73-79](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/constants.js#L73-L79)
- `svelte/reactivity` ships seven reactive primitives: `SvelteDate`, `SvelteSet`, `SvelteMap`, `SvelteURL`, `SvelteURLSearchParams`, `MediaQuery`, and `createSubscriber`. — [packages/svelte/src/reactivity/index-client.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/reactivity/index-client.js)
- `svelte/attachments` provides `createAttachmentKey()` (a `Symbol(ATTACHMENT_KEY)` recognised when spread onto an element) and `fromAction()`, which adapts a legacy `use:` action into the newer `{@attach}` form. — [packages/svelte/src/attachments/index.js#L1-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/attachments/index.js#L1-L50)
- `render(component, options)` on the server accepts `props`, `context`, `idPrefix`, `csp`, and `transformError`, and hard-errors with `invalid_csp` if both `csp.hash` and `csp.nonce` are supplied. — [packages/svelte/src/internal/server/index.js#L64-77](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/index.js#L64-L77)
- `hydratable(key, fn)` is an experimental server→client data-transfer feature: the server side serialises values with `devalue` into a lookup keyed by string, and the client side reads them back out of `window.__svelte.h`; both sides throw `experimental_async_required` when `async_mode_flag` is false. — [packages/svelte/src/internal/server/hydratable.js#L1-40](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/hydratable.js#L1-L40) [packages/svelte/src/internal/client/hydratable.js#L1-33](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/hydratable.js#L1-L33)
- Legacy Svelte 4 compatibility is a first-class subpackage: `svelte/legacy` exports `createClassComponent`, `asClassComponent`, `run`, and `createBubbler` for imperative/class-component migration. — [packages/svelte/src/legacy/legacy-client.js#L32-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/legacy/legacy-client.js#L32-L50) [packages/svelte/src/legacy/legacy-client.js#L190-250](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/legacy/legacy-client.js#L190-L250)
- Dev-mode-only features live under `internal/client/dev/`: `hmr.js` (block-based component hot swap), `ownership.js` (mutation-of-non-bound-prop validation), `inspect.js`, `tracing.js`, `console-log.js`, and `debug.js`. — [packages/svelte/src/internal/client/dev/hmr.js#L1-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dev/hmr.js#L1-L60) [packages/svelte/src/internal/client/dev/ownership.js#L1-30](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dev/ownership.js#L1-L30)
- The sandbox playground offers `pnpm download <url|github-repo|test-name>` and `--create-test`, which drives a headless Chromium via Playwright to pull a reproduction into a local sample directory. — [playgrounds/sandbox/scripts/download.js#L1-55](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/playgrounds/sandbox/scripts/download.js#L1-L55) [playgrounds/sandbox/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/playgrounds/sandbox/package.json)

## Data flow

There are two distinct data pipelines. The compile-time pipeline moves a `.svelte` string through a hand-written state-machine parser into a Svelte AST, through a zimmerframe scope/analysis walk that attaches `metadata` and binding information, and out through target-specific ESTree builders into code + sourcemap + CSS. The runtime pipeline moves user mutations into `Source` signals, propagates dirtiness lazily through deriveds, collects affected effects into a `Batch`, and flushes them to write DOM. The two never share code — an ESLint rule enforces that runtime files cannot import from `../compiler/` or `#compiler`. State ownership is module-global on both sides: the compiler owns `compiler/state.js`, the runtime owns `active_reaction`/`active_effect`/`current_batch`.
- Input boundary for compilation is `remove_bom(source)` followed by `state.reset({ warning, filename })`, which clears `warnings`, `source`, `dev`, `runes`, and installs the user's `warningFilter` — every compile starts from a wiped module-global. — [packages/svelte/src/compiler/index.js#L23-30](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L23-L30) [packages/svelte/src/compiler/state.js#L130-150](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/state.js#L130-L150)
- Options arrive from two merged sources: the caller's `CompileOptions` (validated) and `<svelte:options>` attributes parsed out of the source into `parsed.options`, with the in-file values overriding the caller's for `css` and `runes`. — [packages/svelte/src/compiler/index.js#L27-42](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L27-L42) [packages/svelte/src/compiler/phases/1-parse/read/options.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/1-parse/read/options.js)
- TypeScript is detected before parsing via a `lang=` regex over `<script>` tags in the `Parser` constructor (setting `this.ts`), and TS nodes are then stripped from `fragment`/`instance`/`module` by `remove_typescript_nodes` before analysis ever sees them. — [packages/svelte/src/compiler/phases/1-parse/index.js#L13-90](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/1-parse/index.js#L13-L90) [packages/svelte/src/compiler/index.js#L42-55](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L42-L55)
- The parser is a mutable state machine: `Parser` holds `index`, `stack`, `fragments`, and loops `state = state(this) || fragment` over `template.length`, with states in `1-parse/state/{fragment,element,tag,text}.js`. — [packages/svelte/src/compiler/phases/1-parse/index.js#L100-120](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/1-parse/index.js#L100-L120)
- Analysis is a zimmerframe walk over a per-node-type visitor table; the fallthrough `_` visitor reconstructs `svelte-ignore` scope by scanning preceding `Comment` siblings and `leadingComments`, pushing onto `ignore_stack` in `compiler/state.js`. — [packages/svelte/src/compiler/phases/2-analyze/index.js#L88-125](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/2-analyze/index.js#L88-L125) [packages/svelte/src/compiler/state.js#L88-125](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/state.js#L88-L125)
- Scope and binding resolution is separated into `phases/scope.js` (`create_scopes`, `Scope`, `ScopeRoot`, `get_rune`, `set_scope`), and cyclic reactive statements are caught by `utils/check_graph_for_cycles.js`. — [packages/svelte/src/compiler/phases/2-analyze/index.js#L17-22](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/2-analyze/index.js#L17-L22) [packages/svelte/src/compiler/phases/scope.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/scope.js)
- CSS flows through its own sub-pipeline inside analysis — `analyze_css`, then `prune` (matching selectors against the template), then `warn_unused` — before `render_stylesheet` emits the final stylesheet in the transform phase. — [packages/svelte/src/compiler/phases/2-analyze/index.js#L22-26](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/2-analyze/index.js#L22-L26) [packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js) [packages/svelte/src/compiler/phases/3-transform/index.js#L46-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/index.js#L46-L50)
- The output boundary merges sourcemaps twice: `esrap`'s `print` emits a map with `sourceMapContent: source`, then `merge_with_preprocessor_map(js, options, js_source_name)` folds in any preprocessor map supplied via `options.sourcemap`. — [packages/svelte/src/compiler/phases/3-transform/index.js#L36-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/index.js#L36-L50) [packages/svelte/src/compiler/utils/mapped_code.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/utils/mapped_code.js)
- On the runtime side, `$state` values pass through `proxy()` in `internal/client/proxy.js`, which refuses non-objects, anything already carrying `STATE_SYMBOL`, anything with `COMPONENT_SYMBOL`, and any object whose prototype is neither `Object.prototype` nor `Array.prototype`. — [packages/svelte/src/internal/client/proxy.js#L34-55](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/proxy.js#L34-L55)
- Dependency tracking is a read-side side effect of `get(signal)`: when `active_reaction` is set and not `untracking`, the signal is appended to `new_deps` (or counted as `skipped_deps` when the read order matches the previous run, avoiding array allocation). — [packages/svelte/src/internal/client/runtime.js#L548-600](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/runtime.js#L548-L600)
- Writes go through `internal_set` in `reactivity/sources.js`, which bumps the global `write_version` (via `increment_write_version` in runtime.js) and schedules dependents; `old_values` is a module-level `Map` used to restore pre-batch values. — [packages/svelte/src/internal/client/reactivity/sources.js#L50-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/sources.js#L50-L60) [packages/svelte/src/internal/client/reactivity/sources.js#L189-280](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/sources.js#L189-L280) [packages/svelte/src/internal/client/runtime.js#L137-150](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/runtime.js#L137-L150)
- Effects are not run on write — they are collected into the singleton `current_batch` (`Batch.ensure()`), which owns `schedule`, `capture`, `apply`, `flush`, `discard`, and `settled`, and clears `collected_effects`/`legacy_updates`/`old_values` in its `finally` block. — [packages/svelte/src/internal/client/reactivity/batch.js#L45-100](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js#L45-L100) [packages/svelte/src/internal/client/reactivity/batch.js#L668-700](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js#L668-L700)
- Server output is accumulated as an array of strings and nested `Renderer` children in `#out`, with `head()` creating a second Renderer tagged `type = 'head'` so head content can be emitted out of document order. — [packages/svelte/src/internal/server/renderer.js#L116-210](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/renderer.js#L116-L210) [packages/svelte/src/internal/server/index.js#L84-99](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/index.js#L84-L99)
- Escaping is centralised in the standalone `src/escaping.js`: `escape_html(value, is_attr)` uses `/[&"<]/g` for attributes and `/[&<]/g` for content, escaping only `&`, `"`, and `<`. — [packages/svelte/src/escaping.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/escaping.js)
- Compile-time and runtime data are kept structurally disjoint by a bespoke ESLint rule `no_compiler_imports`, which string-searches every file under `src/` (excluding `src/compiler/`) for `../compiler/` or `#compiler` and reports a hard error. — [eslint.config.js#L4-40](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L4-L40) [eslint.config.js#L78-86](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L78-L86)

## Behavior traces

Three flows dominate. (1) Compile: `compile()` resets global state, validates options, parses, strips TS, analyzes, transforms, and re-attaches a public AST. (2) Client startup: `mount()` initialises DOM operation caches, opens a root effect and an error boundary, pushes a component context, invokes the compiled component function, and returns its exports. (3) Update: a `$state` write sets a source, which schedules its reactions into a `Batch`, which is flushed on the microtask or synchronously via `flushSync`. A fourth, the SSR→hydrate round trip, is what the test harness itself exercises on every fixture.
- Compile trace: `remove_bom` → `state.reset` → `validate_component_options` → `_parse(source)` → destructure `parsed.options` into `combined_options` → conditional `remove_typescript_nodes` → `analyze_component(parsed, source, combined_options)` → `transform_component(analysis, source, combined_options)` → `to_public_ast`. — [packages/svelte/src/compiler/index.js#L23-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js#L23-L60)
- Transform trace (client): `client_component` seeds state with `hoisted: [b.import_all('$', 'svelte/internal/client')]` and an empty `templates` Map, walks the template AST with the visitor table, appends `template.body` into `component_block.body`, pushes a `$$css` const when styles are injected, then re-orders `module.body` so imports stay on top followed by hoisted statements. — [packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L135-200](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L135-L200) [packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L377-500](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L377-L500)
- Mount trace: `mount()` → `_mount()` → `init_operations()` → `component_root(...)` → create an anchor text node via `target.appendChild(create_text())` → `boundary(anchor, { pending }, cb)` → `push({})` to open a component context → assign `props.$$events` if `events` given → set `should_intro = intro` → call `Component(anchor_node, props)`. — [packages/svelte/src/internal/client/render.js#L156-200](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/render.js#L156-L200)
- Hydrate trace: `hydrate()` forces `intro ?? false`, scans forward from `get_first_child(target)` for a comment node whose `data === HYDRATION_START`, throws `HYDRATION_ERROR` if absent, calls `set_hydrating(true)` + `set_hydrate_node(anchor)`, then delegates to the same `_mount` with the discovered anchor. — [packages/svelte/src/internal/client/render.js#L95-145](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/render.js#L95-L145)
- Hydration failure recovery: non-Svelte errors are `console.warn`ed, `options.recover === false` escalates to `e.hydration_failed()`, otherwise `init_operations()` + `clear_text_content(target)` runs and the component falls back to a fresh `mount(component, options)` — with `hydrating`/`hydrate_node` restored in `finally`. — [packages/svelte/src/internal/client/render.js#L118-150](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/render.js#L118-L150)
- Read trace: `get(signal)` adds the signal to `captured_signals`, registers the dependency on `active_reaction`, and takes one of two registration paths depending on whether `REACTION_IS_UPDATING` is set — the post-`await` path deduplicates with `includes.call` because `remove_reactions` would otherwise disconnect still-live deps. — [packages/svelte/src/internal/client/runtime.js#L548-605](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/runtime.js#L548-L605)
- Synchronous flush trace: `flushSync(fn)` sets `is_flushing_sync`, flushes any non-fork `current_batch` before running `fn`, then loops `flush_tasks()` + `current_batch.flush()` until `current_batch === null`, restoring the previous flag in `finally`. — [packages/svelte/src/internal/client/reactivity/batch.js#L1033-1058](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js#L1033-L1058)
- Batch flush trace: `Batch.flush()` clears DEV `source_stacks`, sets `is_processing = true` and `current_batch = this`, runs the private `#process()`, then in `finally` resets `flush_count`, `last_scheduled_effect`, `collected_effects`, `legacy_updates`, `current_batch`, `batch_values`, and `old_values`. — [packages/svelte/src/internal/client/reactivity/batch.js#L668-698](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js#L668-L698)
- `{@html}` trace: the `html()` block runs inside a `template_effect`, short-circuits on unchanged value, uses `parent_node.innerHTML = value` when it is the only child, otherwise builds a `<template>`/`<svg>`/`<math>` wrapper, sets `wrapper.innerHTML`, and inserts the resulting nodes via `anchor.before(...)`. — [packages/svelte/src/internal/client/dom/blocks/html.js#L50-160](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dom/blocks/html.js#L50-L160)
- HMR trace: `hmr(fn)` wraps the component in a `source(fn)` signal; a `block()` effect reads it, and on change deletes every key from the old `instance`, calls `destroy_effect(effect)`, and re-creates a `branch()` with `set_should_intro(false)` so the swap plays no transitions. — [packages/svelte/src/internal/client/dev/hmr.js#L14-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dev/hmr.js#L14-L60)
- Test trace: `runtime_suite` → `common_setup` builds a `CompileOptions` (forcing `accessors: true` by default and `experimental.async` from `runes && async_mode`) and calls `compile_directory(cwd, 'client')` and `compile_directory(cwd, 'server')` → `run_test_variant` imports from `_output/` and calls `render`, `mount`, or `hydrate` per variant. — [packages/svelte/tests/runtime-legacy/shared.ts#L241-300](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-legacy/shared.ts#L241-L300) [packages/svelte/tests/runtime-legacy/shared.ts#L417-520](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-legacy/shared.ts#L417-L520)
- `compile_directory` trace: wipe `_output/<generate>`, glob every non-`_`-prefixed file, route `.svelte.js` through `compileModule`, plain `.js` through a re-export shim so `_config.js` can share module state, and `.svelte` through optional `preprocess` then `compile`, honouring `.client.svelte`/`.server.svelte` suffixes. — [packages/svelte/tests/helpers.js#L63-140](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/helpers.js#L63-L140)
- Message codegen trace: `scripts/process-messages/index.js` deletes and recreates `documentation/docs/98-reference/.generated`, walks `messages/<category>/*.md`, matches `## CODE` headings, and throws on any duplicate code across categories. — [packages/svelte/scripts/process-messages/index.js#L14-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/scripts/process-messages/index.js#L14-L50)
- Benchmark trace: `benchmarking/run.js` in parent mode filters suites by `process.argv` substrings and forks one child per benchmark; in child mode (`BENCH_SINGLE` set) it calls `$.push({}, true)`, runs the benchmark under `with_cpu_profile`, `$.pop()`s, and `process.send`s the result with an exit callback. — [benchmarking/run.js#L1-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/benchmarking/run.js#L1-L50)

## Testing signals

Testing is Vitest-driven with a fixture-directory convention: 22 suites under `packages/svelte/tests/`, each a `test.ts` plus a `samples/` directory where every subdirectory is one test, optionally configured by a `_config.js`. Coverage is enormous by fixture count — 1,209 legacy runtime samples, 1,054 runes runtime samples, 335 validator samples, 184 CSS samples — and the runtime suites multiply each by four variants (dom, hydrate, ssr, async-ssr). Real-browser coverage is narrow: only `runtime-browser` (35 samples) and the custom-elements samples run under Playwright Chromium. CI runs the full suite on Node 20/22/24 across Windows, macOS, and Ubuntu, plus a no-async pass, a tsgo typecheck, a lint/types-drift gate, and a benchmark job.
- Vitest 4 is the single test runner, configured once at the repo root with `include` patterns `packages/svelte/**/*.test.ts`, `packages/svelte/tests/*/test.ts`, and `packages/svelte/tests/runtime-browser/test-ssr.ts`, and `**/samples/**` excluded so fixtures are never collected as tests. — [vitest.config.js#L28-45](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/vitest.config.js#L28-L45) [package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/package.json)
- The global `testTimeout` is raised to 30s with an in-file rationale: dev-mode tests that trip `effect_update_depth_exceeded` allocate ~1000 Error objects per flush for stack tracking, which overruns the 5s default under Vitest 4. — [vitest.config.js#L30-36](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/vitest.config.js#L30-L36)
- `tests/suite.ts` provides the two harness primitives: `suite()` (one `it` per sample dir) and `suite_with_variants()` (one `it` per dir × variant, with a `should_skip_variant` hook returning `true` to skip or `'no-test'` to omit entirely). — [packages/svelte/tests/suite.ts#L27-72](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/suite.ts#L27-L72)
- Test selection supports both `pnpm test <suite> -t <name>` and a `FILTER` env var that compiles to an anchored or `/regex/` RegExp in `suite.ts`, plus per-config `skip`/`solo`/`timeout` flags on `BaseTest`. — [packages/svelte/tests/suite.ts#L10-25](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/suite.ts#L10-L25) [CONTRIBUTING.md#L96-116](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L96-L116)
- Runtime suites run four variants per sample — `dom`, `hydrate`, `ssr`, `async-ssr` — with `async-ssr` gated on both runes mode and `async_mode` (i.e. `process.env.SVELTE_NO_ASYNC !== 'true'`). — [packages/svelte/tests/runtime-legacy/shared.ts#L176-236](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-legacy/shared.ts#L176-L236) [packages/svelte/tests/helpers.js#L196-198](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/helpers.js#L196-L198)
- Fixture counts skew heavily toward runtime behaviour: `runtime-legacy/samples` (1209), `runtime-runes/samples` (1054), `validator/samples` (335), `css/samples` (184), `compiler-errors/samples` (146), `server-side-rendering/samples` (132). — [packages/svelte/tests/runtime-legacy/test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-legacy/test.ts) [packages/svelte/tests/runtime-runes/test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-runes/test.ts) [packages/svelte/tests/validator/test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/validator/test.ts)
- Real-browser testing is limited to `tests/runtime-browser/test.ts`, which launches a single Chromium via `@playwright/test`, bundles each sample with `esbuild`, and runs `dom`/`hydrate` variants plus a separate `custom-elements-samples` suite — with `retry: 1` on CI because 'browser tests are brittle and slow on CI'. — [packages/svelte/tests/runtime-browser/test.ts#L1-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-browser/test.ts#L1-L60)
- XHTML/XML-document behaviour gets its own environment: `vitest-xhtml-environment.ts` wraps the jsdom builtin with `contentType: 'application/xhtml+xml'`, consumed by `tests/runtime-xhtml/test.ts` via a `@vitest-environment` pragma (13 samples). — [vitest-xhtml-environment.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/vitest-xhtml-environment.ts) [packages/svelte/tests/runtime-xhtml/test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/runtime-xhtml/test.ts)
- Unit tests colocated with source are rare — only `src/utils.test.ts`, `src/internal/client/proxy.test.ts`, `src/internal/client/error-handling.test.ts`, the four `src/reactivity/*.test.ts` files, and `src/internal/server/{crypto,hydratable,renderer}.test.ts`. — [packages/svelte/src/internal/client/proxy.test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/proxy.test.ts) [packages/svelte/src/internal/server/renderer.test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/renderer.test.ts) [packages/svelte/src/reactivity/map.test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/reactivity/map.test.ts)
- Snapshot-style suites (`snapshot`, `parser-modern`, `parser-legacy`, `print`, `sourcemaps`) assert generated output against checked-in expectations and are regenerated with `UPDATE_SNAPSHOTS=true pnpm test`. — [CONTRIBUTING.md#L118-122](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L118-L122) [packages/svelte/tests/snapshot/test.ts](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/snapshot/test.ts)
- CI's `Tests` job matrixes Node 20 across windows/macOS/ubuntu plus Node 22 and 24 on ubuntu, installs only Chromium (`PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'` then `pnpm playwright install chromium`), with a 15-minute timeout. — [.github/workflows/ci.yml#L10-45](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml#L10-L45)
- A dedicated `TestNoAsync` job re-runs only `runtime-runes` with `SVELTE_NO_ASYNC: true`, guarding the non-async code path that the default run never exercises. — [.github/workflows/ci.yml#L46-62](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml#L46-L62)
- The `Lint` job doubles as a drift gate: after `pnpm build` it fails if `git status --porcelain` is non-empty (generated types out of date) and separately checks `documentation/docs/07-misc/.generated/` for a stale browser-support page. — [.github/workflows/ci.yml#L78-100](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml#L78-L100)
- Coverage is configured (v8 provider, `lcov` + `html`, scoped to `packages/svelte/src/**`, `reportOnFailure: true`) but no CI job invokes it or enforces a threshold. — [vitest.config.js#L45-53](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/vitest.config.js#L45-L53) [.github/workflows/ci.yml](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml)
- `tests/manual/` exists as a suite directory with no `test.ts` and no samples, so nothing in it runs under `pnpm test`. (inferred) — [packages/svelte/tests/manual](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/manual) [vitest.config.js#L37-42](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/vitest.config.js#L37-L42)

## Risk map

The highest-severity surfaces are the two `innerHTML` sinks (`{@html}` and the template reconciler) and the server-side CSP/hash path, all of which are correct-by-design but unforgiving of misuse. Structurally, the biggest fragility is module-global mutable state on both sides of the codebase — `compiler/state.js` makes `compile()` non-reentrant, and `active_reaction`/`active_effect`/`current_batch`/`old_values` make the runtime a single-threaded global machine where any missed `finally` leaks state across components. Blast radius is concentrated in `runtime.js`, `reactivity/batch.js`, and `phases/2-analyze/index.js`, each of which sits under thousands of fixtures. Legacy code is not dead but is a permanent second code path: legacy mode, legacy AST conversion, and `beforeUpdate`/`afterUpdate` all have their own branches.
- `{@html}` is the primary XSS surface and is documented as such: `html()` assigns user values straight to `parent_node.innerHTML` or `wrapper.innerHTML` with no sanitisation, relying only on the fact that `innerHTML` does not execute `<script>`. — [packages/svelte/src/internal/client/dom/blocks/html.js#L78-145](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dom/blocks/html.js#L78-L145)
- A second `innerHTML` sink exists in the template reconciler, where compiled template strings are assigned via `create_trusted_html(html.replaceAll('<!>', '<!---->'))` — safe only because the input is compiler-generated rather than user-supplied. (inferred) — [packages/svelte/src/internal/client/dom/reconciler.js#L20-26](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dom/reconciler.js#L20-L26)
- `escape_html` escapes only `&`, `<`, and (in attribute mode) `"` — single quotes and `>` are deliberately not escaped, so any compiler codegen path that emits a single-quoted attribute without also escaping would be an injection hole. (inferred) — [packages/svelte/src/escaping.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/escaping.js)
- The server-side CSP path computes `sha256` hashes over inline script bodies and pushes them onto `global.csp.script_hashes`; `invalid_csp` is thrown only for the hash+nonce combination, and the nonce is interpolated into the attribute unescaped (` nonce="${this.global.csp.nonce}"`). — [packages/svelte/src/internal/server/renderer.js#L960-980](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/renderer.js#L960-L980) [packages/svelte/src/internal/server/index.js#L71-77](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/index.js#L71-L77)
- `internal/server/crypto.js` performs a deliberately obfuscated dynamic import — `import(/* @vite-ignore */ module_name)` on the string `'node:crypto'` — specifically so static analysers cannot see the Node dependency; this defeats bundler and supply-chain tooling that relies on static import graphs. — [packages/svelte/src/internal/server/crypto.js#L1-22](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/server/crypto.js#L1-L22)
- The compiler is not reentrant: `compiler/state.js` holds `warnings`, `source`, `filename`, `dev`, `runes`, `ignore_stack`, and `ignore_map` as module-level `let`s that `reset()` clobbers, so two concurrent `compile()` calls in one process would interleave diagnostics. (inferred) — [packages/svelte/src/compiler/state.js#L10-55](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/state.js#L10-L55) [packages/svelte/src/compiler/state.js#L130-150](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/state.js#L130-L150)
- The runtime has the same shape of risk at larger scale: `active_reaction`, `active_effect`, `untracking`, `new_deps`, `skipped_deps`, `untracked_writes`, `write_version`, and `update_version` are all module-level mutable globals in `runtime.js`. — [packages/svelte/src/internal/client/runtime.js#L60-150](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/runtime.js#L60-L150)
- `Batch.flush()` relies entirely on its `finally` block to reset seven pieces of cross-cutting state (`flush_count`, `last_scheduled_effect`, `collected_effects`, `legacy_updates`, `current_batch`, `batch_values`, `old_values`); an early return added above that block would leave the whole runtime wedged. (inferred) — [packages/svelte/src/internal/client/reactivity/batch.js#L668-698](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js#L668-L698)
- Signal semantics are encoded in 26 overlapping bitflags, and two of them collide by design — `MANAGED_EFFECT` and `DESTROYING` both use `1 << 24`, and `EFFECT_OFFSCREEN` and `DESTROYING` both use `1 << 25` — meaning correctness depends on flags never co-occurring on the same signal. — [packages/svelte/src/internal/client/constants.js#L1-56](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/constants.js#L1-L56)
- Hydration failure is silently recoverable by default: any non-Svelte error triggers `console.warn` + full client re-render, so a genuine SSR/CSR divergence only surfaces as a warning unless the consumer passes `recover: false`. — [packages/svelte/src/internal/client/render.js#L118-145](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/render.js#L118-L145)
- Legacy Svelte 4 support is a permanently parallel code path, not dead code: `legacy_mode_flag`, `compiler/legacy.js`'s AST `convert`, `legacy_pre_effect`/`legacy_pre_effect_reset`, `internal/client/dev/legacy.js`, and the `beforeUpdate`/`afterUpdate` callback queue all exist solely for it. — [packages/svelte/src/internal/flags/index.js#L1-10](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/flags/index.js#L1-L10) [packages/svelte/src/compiler/legacy.js#L1-20](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/legacy.js#L1-L20) [packages/svelte/src/index-client.js#L186-230](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/index-client.js#L186-L230) [packages/svelte/src/internal/client/reactivity/effects.js#L311-370](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/effects.js#L311-L370)
- Async mode is experimental and dual-pathed — `enable_async_mode_flag` is set only by importing `svelte/internal/flags/async`, and there is a `disable_async_mode_flag` explicitly annotated 'ONLY USE THIS DURING TESTING' — so the non-async path is only covered by the single `TestNoAsync` CI job. — [packages/svelte/src/internal/flags/index.js#L6-18](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/flags/index.js#L6-L18) [packages/svelte/src/internal/flags/async.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/flags/async.js) [.github/workflows/ci.yml#L46-62](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml#L46-L62)
- The `autofix.yml` workflow runs on `issue_comment` with `contents: write` and pushes a commit to the PR head branch; it mitigates the obvious fork risk by refusing non-same-repo PRs and gating on `OWNER`/`MEMBER`/`COLLABORATOR` author association. — [.github/workflows/autofix.yml#L1-70](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/autofix.yml#L1-L70)
- `ecosystem-ci-trigger.yml` mints a GitHub App token from `ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY` and includes an explicit TOCTOU guard: it fails with a 'Security warning' if `pr.head.repo.pushed_at` is newer than the triggering comment's `created_at`. — [.github/workflows/ecosystem-ci-trigger.yml#L60-105](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ecosystem-ci-trigger.yml#L60-L105)
- Four ESLint rules are disabled repo-wide with a `TODO: enable these rules` comment — `@stylistic/quotes`, `@typescript-eslint/no-unused-vars`, and `prefer-const` — so unused variables and accidental `let` are currently unenforced across all of `src/`. — [eslint.config.js#L57-66](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L57-L66)
- `playgrounds/sandbox/scripts/download.js` combines `execSync` from `node:child_process`, `new URL(url_arg)` on user input, and a Playwright Chromium launch against arbitrary URLs — the only shell-out + arbitrary-network path in the repo, and it is unlinted (`playgrounds/sandbox/**` is in the ESLint ignore list). — [playgrounds/sandbox/scripts/download.js#L1-55](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/playgrounds/sandbox/scripts/download.js#L1-L55) [eslint.config.js#L88-104](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L88-L104)

## Extension points

Almost every extension point is a plain object literal keyed by node type or property name, which makes adding language features mechanical: drop a file into the right `visitors/` directory and register it in the table. The three big ones are the analysis visitor table, the client and server transform visitor tables, and the `binding_properties` registry that defines what `bind:` can target. Below those sit the compiler options validator table, the markdown-driven message catalogue, the preprocessor hook interface, and the export map itself. On the runtime side, `createSubscriber` and `createAttachmentKey` are the sanctioned APIs for library authors who need to plug into reactivity and element lifecycle without touching internals.
- Adding a new analysable node type means creating `phases/2-analyze/visitors/<NodeType>.js` and adding it to the `visitors` object in `2-analyze/index.js` — the file currently imports ~60 such visitors explicitly by name. — [packages/svelte/src/compiler/phases/2-analyze/index.js#L27-88](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/2-analyze/index.js#L27-L88)
- Client codegen has a parallel registry: `const visitors = {...}` at `3-transform/client/transform-client.js:65`, backed by ~45 files in `3-transform/client/visitors/` plus a `shared/` directory for logic common to both targets. — [packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L65-135](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js#L65-L135) [packages/svelte/src/compiler/phases/3-transform/client/visitors](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/client/visitors)
- The server codegen mirrors it in `3-transform/server/transform-server.js` exporting `server_component` and `server_module`, so a new template feature generally requires a matching visitor in both client and server visitor directories. — [packages/svelte/src/compiler/phases/3-transform/server/transform-server.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js) [packages/svelte/src/compiler/phases/3-transform/index.js#L30-36](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/3-transform/index.js#L30-L36)
- `phases/bindings.js` exports `binding_properties`, a ~47-entry `Record<string, BindingProperty>` where each entry declares `event`, `bidirectional`, `omit_in_ssr`, `valid_elements`, and `invalid_elements` — this is the single table that defines which `bind:` directives exist. — [packages/svelte/src/compiler/phases/bindings.js#L1-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/bindings.js#L1-L60) [packages/svelte/src/compiler/phases/bindings.js#L200-215](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/bindings.js#L200-L215)
- New compiler options are registered declaratively in `validate-options.js` using the `string()`, `boolean()`, `list()`, `fun()`, and `warning()` combinators, split between `common_options` and `component_options`. — [packages/svelte/src/compiler/validate-options.js#L11-130](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/validate-options.js#L11-L130) [packages/svelte/src/compiler/validate-options.js#L260-345](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/validate-options.js#L260-L345)
- New errors/warnings are added as markdown `## CODE` sections under `packages/svelte/messages/<category>/*.md` across eight categories (client/server/shared/compile × errors/warnings); `scripts/process-messages` generates both the runtime throwers and the reference docs from them. — [packages/svelte/messages/compile-errors/template.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/messages/compile-errors/template.md) [packages/svelte/scripts/process-messages/index.js#L14-60](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/scripts/process-messages/index.js#L14-L60)
- The generated message modules are explicitly excluded from linting (`src/compiler/errors.js`, `src/internal/client/errors.js`, `src/internal/client/warnings.js`, `src/internal/shared/warnings.js`, `src/internal/server/warnings.js`), which is the signal that they must never be hand-edited. — [eslint.config.js#L88-100](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L88-L100)
- The public plugin interface for build tooling is `PreprocessorGroup` — an object with optional `markup`, `script`, and `style` async hooks, each receiving `{ content, attributes, markup, filename }` and returning `{ code, map }`. — [packages/svelte/src/compiler/preprocess/index.js#L260-310](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/preprocess/index.js#L260-L310) [packages/svelte/src/compiler/preprocess/index.js#L331-368](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/preprocess/index.js#L331-L368)
- New public subpaths are added by editing the `exports` map in `packages/svelte/package.json`; `scripts/check-treeshakeability.js` then reads that same map to resolve each entry and asserts via a rollup bundle that importing it produces no side effects. — [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json) [packages/svelte/scripts/check-treeshakeability.js#L1-50](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/scripts/check-treeshakeability.js#L1-L50)
- `createSubscriber` (in `src/reactivity/create-subscriber.js`, exported from `svelte/reactivity`) is the sanctioned hook for library authors wiring external event sources into Svelte's reactivity — it is how `MediaQuery` is built. — [packages/svelte/src/reactivity/index-client.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/reactivity/index-client.js) [packages/svelte/src/reactivity/create-subscriber.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/reactivity/create-subscriber.js) [packages/svelte/src/reactivity/media-query.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/reactivity/media-query.js)
- `createAttachmentKey()` is the programmatic extension point for element lifecycle: it returns a `Symbol(ATTACHMENT_KEY)` that the runtime recognises when spread onto an element, letting library authors attach behaviour without a `use:` directive. — [packages/svelte/src/attachments/index.js#L8-32](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/attachments/index.js#L8-L32)
- New block types plug into `internal/client/dom/blocks/` — the directory currently holds `async`, `await`, `boundary`, `branches`, `css-props`, `each`, `html`, `if`, `key`, `slot`, `snippet`, `svelte-component`, `svelte-element`, and `svelte-head`, each a function invoked by generated code. — [packages/svelte/src/internal/client/dom/blocks/if.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dom/blocks/if.js) [packages/svelte/src/internal/client/dom/blocks/each.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dom/blocks/each.js) [packages/svelte/src/internal/client/dom/blocks/boundary.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/dom/blocks/boundary.js)
- New effect kinds are created through the factory family in `reactivity/effects.js` — `user_effect`, `user_pre_effect`, `eager_effect`, `effect_root`, `component_root`, `render_effect`, `template_effect`, `block`, `managed`, `branch`, `async_effect` — all of which funnel into a shared internal `create_effect` with different flag combinations. — [packages/svelte/src/internal/client/reactivity/effects.js#L187-445](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/effects.js#L187-L445)
- Agent-facing workflows are themselves extensible via `.agents/skills/`, which currently holds one skill (`performance-investigation`) that `AGENTS.md` routes to by name. — [.agents/skills/performance-investigation/SKILL.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.agents/skills/performance-investigation/SKILL.md) [AGENTS.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/AGENTS.md)

## Contributor handoff

Read `AGENTS.md`, `CONTRIBUTING.md`, and `.github/PULL_REQUEST_TEMPLATE.md` first — the repo's own agent guide makes the PR template mandatory and forbids submitting without a full test run. The dominant convention is `snake_case` internally, `camelCase` publicly; the dominant workflow is add a fixture directory, not a new test file. The two hard edit boundaries are generated files (types, error/warning modules, generated docs) and the compiler/runtime import firewall. Expect long feedback loops: the full suite covers ~3,400 fixtures across 4 variants, so filter aggressively while iterating and only run the full suite before opening a PR.
- `AGENTS.md` is explicit that `CONTRIBUTING.md` must also be read, that `PULL_REQUEST_TEMPLATE.md` must be read and filled out correctly for any PR, and that PRs must not be submitted without running the full test suite. — [AGENTS.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/AGENTS.md) [.github/PULL_REQUEST_TEMPLATE.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/PULL_REQUEST_TEMPLATE.md)
- Naming convention is enforced, not advisory: `snake_case` for internal variables and methods, `camelCase` for public ones, backed by the `lube/svelte-naming-convention` ESLint rule set to `error` with `fixSameNames: true`. — [CONTRIBUTING.md#L136-140](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L136-L140) [eslint.config.js#L49-56](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L49-L56)
- Never hand-edit `packages/svelte/types/index.d.ts`, `src/compiler/errors.js`, or the four `warnings.js` modules — they are generated by `pnpm generate` and CI fails the `Lint` job if `git status` is dirty after `pnpm build`. — [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json) [.github/workflows/ci.yml#L88-96](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml#L88-L96) [CONTRIBUTING.md#L142-146](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L142-L146)
- Never import from `../compiler/` or `#compiler` inside `packages/svelte/src/**` outside `src/compiler/**` — the custom `no_compiler_imports` rule string-searches the file text and rejects even JSDoc type references. — [eslint.config.js#L4-40](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L4-L40) [eslint.config.js#L78-86](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/eslint.config.js#L78-L86)
- Adding a test means creating a directory under `packages/svelte/tests/<suite>/samples/`, optionally with a `_config.js` default-exporting the config object — `for_each_dir` in `suite.ts` discovers it automatically, so no test file is edited. — [packages/svelte/tests/suite.ts#L76-104](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/suite.ts#L76-L104) [CONTRIBUTING.md#L88-94](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L88-L94)
- Files prefixed with `_` inside a sample directory are excluded from compilation by `compile_directory`, which is how `_config.js` coexists with the component files it tests. — [packages/svelte/tests/helpers.js#L76-78](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/helpers.js#L76-L78)
- A sample directory containing only `_output` or `_actual.json` is treated as leftover from another branch and silently skipped — so a suite that appears to pass may simply not have run. — [packages/svelte/tests/suite.ts#L74-90](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/suite.ts#L74-L90)
- Tests require Chromium to be installed first (`pnpm playwright install chromium`) or the `runtime-browser` and custom-elements suites will fail to launch. — [CONTRIBUTING.md#L96-100](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L96-L100) [.github/workflows/ci.yml#L40-44](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/workflows/ci.yml#L40-L44)
- Iterate with `pnpm test <suite>`, `pnpm test <suite> -t <name>`, or `FILTER=<name> pnpm test <suite>`; snapshot suites are regenerated with `UPDATE_SNAPSHOTS=true pnpm test`. — [CONTRIBUTING.md#L100-122](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/CONTRIBUTING.md#L100-L122) [packages/svelte/tests/suite.ts#L10-25](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/suite.ts#L10-L25)
- Typechecking is a three-project run inside `packages/svelte`: `tsc --project tsconfig.runtime.json && tsc && cd ./tests/types && tsc` — the `tests/types` project exists purely to typecheck public API ergonomics (`actions.ts`, `snippet.ts`, `store.ts`, `bindings.svelte`, …). — [packages/svelte/package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/package.json) [packages/svelte/tests/types/tsconfig.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/types/tsconfig.json)
- Any change under `packages/svelte/src` requires a changeset (`npx changeset`) per the PR template; the root `changeset:version` script also runs `pnpm -r generate:version` so `src/version.js` stays in sync. — [.github/PULL_REQUEST_TEMPLATE.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.github/PULL_REQUEST_TEMPLATE.md) [package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/package.json) [packages/svelte/scripts/generate-version.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/scripts/generate-version.js)
- For performance work, use the `performance-investigation` skill: run `pnpm bench:compare main <branch>`, read `benchmarking/compare/.results/report.txt`, then diff the per-benchmark `.md` CPU-profile summaries, and re-run `pnpm test runtime-runes` afterward. — [.agents/skills/performance-investigation/SKILL.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/.agents/skills/performance-investigation/SKILL.md) [package.json](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/package.json)
- Files to read first for the compiler: `src/compiler/index.js`, `phases/1-parse/index.js`, `phases/2-analyze/index.js`, `phases/3-transform/index.js`, `phases/scope.js`, `phases/bindings.js`. — [packages/svelte/src/compiler/index.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/index.js) [packages/svelte/src/compiler/phases/scope.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/scope.js) [packages/svelte/src/compiler/phases/bindings.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/compiler/phases/bindings.js)
- Files to read first for the runtime: `internal/client/constants.js` (the flag vocabulary, which nothing else makes sense without), `internal/client/runtime.js`, `reactivity/{sources,deriveds,effects,batch}.js`, `internal/client/render.js`, `internal/client/proxy.js`. — [packages/svelte/src/internal/client/constants.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/constants.js) [packages/svelte/src/internal/client/runtime.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/runtime.js) [packages/svelte/src/internal/client/reactivity/batch.js](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/src/internal/client/reactivity/batch.js)
- Known trap: `tests/README.md` is a stale migration-era document listing 'TODOs' and 'tests not copied over yet' from the Svelte 4→5 rewrite; treat it as historical context rather than current guidance. (inferred) — [packages/svelte/tests/README.md](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/packages/svelte/tests/README.md)
- Known trap: `pnpm-workspace.yaml` sets `minimumReleaseAge: 2880` and `blockExoticSubdeps: true`, so adding a freshly-published third-party dependency will be rejected unless it is on the `minimumReleaseAgeExclude` list. — [pnpm-workspace.yaml](https://github.com/sveltejs/svelte/blob/636eaaaa6f064b55072e7d192bb76dc9d8c4516e/pnpm-workspace.yaml)

Source repository: https://github.com/sveltejs/svelte
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/svelte.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
