diff --git a/documentation/docs/20-commands/20-sv-add.md b/documentation/docs/20-commands/20-sv-add.md
index d4993651c..d8fc782e8 100644
--- a/documentation/docs/20-commands/20-sv-add.md
+++ b/documentation/docs/20-commands/20-sv-add.md
@@ -71,49 +71,20 @@ Prevents installing dependencies
> [!NOTE]
> Svelte maintainers have not reviewed community add-ons for malicious code!
-You can find [community add-ons on npm](https://www.npmx.dev/search?q=keyword:sv-add) by searching for keyword: `sv-add`.
-
-### How to install a community add-on
-
-```sh
-npx sv add [PROTOCOL][COMMUNITY_ADDON]
-```
-
-You can:
-
-- mix and match official and community add-ons
-- use the interactive prompt or give args to the cli
-- use the `--add` option in the `create` command
-
-```sh
-npx sv add eslint "@supacool"
-```
+Community add-ons are npm packages published by the community. Look out for add-ons from your favorite libraries and tools. _(soon)_ many are building `sv` add-ons to make integration a one-liner. You can find them [on npm](https://www.npmx.dev/search?q=keyword:sv-add) by searching for keyword: `sv-add`.
```sh
-npx sv create --add eslint "@supacool"
-```
-
-### Package Protocols
-
-```sh
-# Scoped package: @org (preferred), we will look for @org/sv
-npx sv add "@supacool"
-
-# Regular npm package (with or without scope)
-npx sv add my-cool-addon
+# Install a community add-on by org
+npx sv add @supacool
-# Local add-on
+# Use a local add-on (for development or custom/private add-ons)
npx sv add file:../path/to/my-addon
-```
-
-### How to create a community add-on
-To start on a good track, create your add-on with the `addon` template.
+# Mix and match official and community add-ons
+npx sv add eslint @supacool
-```sh
-npx sv create --template addon [path]
+# Also works when creating a new project directly
+npx sv create --add eslint @supacool
```
-In your new add-on directory, check out the `README.md` and `CONTRIBUTING.md` to get started.
-
-Then you can continue with the [API docs](/docs/cli/add-on) to start building your add-on. You can also have a look at the [official addons source code](https://github.com/sveltejs/cli/tree/main/packages/sv/src/addons) to get some inspiration on what can be done.
+Want to create your own? Check the [Add-on Docs](community).
diff --git a/documentation/docs/30-add-ons/16-better-auth.md b/documentation/docs/30-add-ons/02-better-auth.md
similarity index 100%
rename from documentation/docs/30-add-ons/16-better-auth.md
rename to documentation/docs/30-add-ons/02-better-auth.md
diff --git a/documentation/docs/30-add-ons/99-community.md b/documentation/docs/30-add-ons/99-community.md
new file mode 100644
index 000000000..438d25fca
--- /dev/null
+++ b/documentation/docs/30-add-ons/99-community.md
@@ -0,0 +1,222 @@
+---
+title: [create your own]
+---
+
+> [!NOTE]
+> Community add-ons are currently **experimental**. The API may change. Don't use them in production yet!
+
+This guide covers how to create, test, and publish community add-ons for `sv`.
+
+## Quick start
+
+The easiest way to create an add-on is using the `addon` template:
+
+```sh
+npx sv create --template addon [path]
+```
+
+The project has a `README.md` and `CONTRIBUTING.md` to guide you along.
+
+## Project structure
+
+Typically, an add-on looks like this:
+
+```js
+// @noErrors
+import { transforms } from '@sveltejs/sv-utils';
+import { defineAddon, defineAddonOptions } from 'sv';
+
+// your add-on definition, the entry point
+export default defineAddon({
+ id: 'your-addon-name',
+
+ // optional: one-liner shown in prompts
+ shortDescription: 'does X',
+
+ // optional: link to docs/repo
+ homepage: 'https://...',
+
+ // Define options for user prompts (or passed as arguments)
+ options: defineAddonOptions()
+ .add('who', {
+ question: 'To whom should the addon say hello?',
+ type: 'string' // boolean | number | select | multiselect
+ })
+ .build(),
+
+ // preparing step, check requirements and dependencies
+ setup: ({ dependsOn }) => {
+ dependsOn('tailwindcss');
+ },
+
+ // actual execution of the addon
+ run: ({ isKit, cancel, sv, options, directory }) => {
+ if (!isKit) return cancel('SvelteKit is required');
+
+ // Add "Hello [who]!" to the root page
+ sv.file(
+ directory.kitRoutes + '/+page.svelte',
+ transforms.svelte(({ ast, svelte }) => {
+ svelte.addFragment(ast, `
Hello ${options.who}!
`);
+ })
+ );
+ }
+});
+```
+
+> `sv` is responsible for the file system - `sv.file()` accepts a `path` to the file and a callback function to modify it.
+> `@sveltejs/sv-utils` is responsible for the content - `transforms.svelte()` provides you with the proper AST and utils to modify the file. See [sv-utils](/docs/cli/sv-utils) for the full API.
+
+## Development
+
+You can run your add-on locally using the `file:` protocol:
+
+```sh
+cd /path/to/test-project
+npx sv add file:../path/to/my-addon
+```
+
+This allows you to iterate quickly without publishing to npm.
+
+The `file:` protocol also works for custom or private add-ons that you don't intend to publish - for example, to standardize project setup across your team or organization.
+
+> [!NOTE]
+> It is not necessary to build your add-on during development.
+
+## Testing
+
+The `sv/testing` module provides utilities for testing your add-on:
+
+```js
+import { setupTest } from 'sv/testing';
+import { test, expect } from 'vitest';
+import addon from './index.js';
+
+test('adds hello message', async () => {
+ const { content } = await setupTest({
+ addon,
+ options: { who: 'World' },
+ files: {
+ 'src/routes/+page.svelte': 'Welcome
'
+ }
+ });
+
+ expect(content('src/routes/+page.svelte')).toContain('Hello World!');
+});
+```
+
+## Publishing
+
+### Bundling
+
+Community add-ons are bundled with [tsdown](https://tsdown.dev/) into a single file. Everything is bundled except `sv`. (It is a peer dependency provided at runtime.)
+
+### `package.json`
+
+Your add-on must have `sv` as a peer dependency and **no** `dependencies` in `package.json`:
+
+```jsonc
+{
+ "name": "@your-org/sv",
+ "version": "1.0.0",
+ "type": "module",
+ // entrypoint during development
+ "exports": {
+ ".": "./src/index.js"
+ },
+ "publishConfig": {
+ "access": "public",
+ // entrypoint on build
+ "exports": {
+ ".": { "default": "./dist/index.js" }
+ }
+ },
+ // cannot have dependencies
+ "dependencies": {},
+ "peerDependencies": {
+ // minimum version required to run by this addon
+ "sv": "^0.13.0"
+ },
+ // Add this keyword so users can discover your add-on
+ "keywords": ["sv-add"]
+}
+```
+
+### Naming convention
+
+Name your package `@your-org/sv`. Users install it by typing just the org:
+
+```sh
+# npm package: @your-org/sv
+npx sv add @your-org
+```
+
+> [!NOTE]
+> Unscoped packages are not supported yet
+
+### Export options
+
+`sv` first tries to import `your-package/sv`, then falls back to the default export. This means you have two options:
+
+1. **Default export** (recommended for dedicated add-on packages):
+
+ ```json
+ {
+ "exports": {
+ ".": "./src/index.js"
+ }
+ }
+ ```
+
+2. **`./sv` export** (for packages that also export other functionality):
+ ```json
+ {
+ "exports": {
+ ".": "./src/main.js",
+ "./sv": "./src/addon.js"
+ }
+ }
+ ```
+
+### Publish to npm
+
+```sh
+npm login
+npm publish
+```
+
+> `prepublishOnly` automatically runs the build before publishing.
+
+## Next steps
+
+You can optionally display guidance in the console after your add-on runs:
+
+```js
+// @noErrors
+import { color } from '@sveltejs/sv-utils';
+
+export default defineAddon({
+ // ...
+ nextSteps: ({ options }) => [
+ `Run ${color.command('npm run dev')} to start developing`,
+ `Check out the docs at https://...`
+ ]
+});
+```
+
+## Version compatibility
+
+Your add-on should specify a minimum `sv` version in `peerDependencies`. Your user will get a compatibility warning if their `sv` version has a different major version than what was specified.
+
+## Examples
+
+See the [official add-on source code](https://github.com/sveltejs/cli/tree/main/packages/sv/src/addons) for some real world examples.
+
+## Architecture
+
+The Svelte CLI is split into two packages with a clear boundary:
+
+- **`sv`** = **where and when** to do it. It owns paths, workspace detection, dependency tracking, and file I/O. The engine orchestrates add-on execution.
+- **`@sveltejs/sv-utils`** = **what** to do to content. It provides parsers, language tooling, and typed transforms. Everything here is pure - no file system, no workspace awareness.
+
+This separation means transforms are testable without a workspace and composable across add-ons.
diff --git a/documentation/docs/40-api/10-add-on.md b/documentation/docs/40-api/10-add-on.md
deleted file mode 100644
index db4bc8ebc..000000000
--- a/documentation/docs/40-api/10-add-on.md
+++ /dev/null
@@ -1,192 +0,0 @@
----
-title: add-on
----
-
-> [!NOTE]
-> Community add-ons are currently **experimental**. The API may change. Don't use them in production yet!
-
-This guide covers how to create, test, and publish community add-ons for `sv`.
-
-## Quick start
-
-The easiest way to create an add-on is using the addon template:
-
-```sh
-npx sv create --template addon my-addon
-```
-
-## Add-on structure
-
-Typically, an add-on looks like this:
-
-```js
-// @noErrors
-import { transforms } from '@sveltejs/sv-utils';
-import { defineAddon, defineAddonOptions } from 'sv';
-
-// Define options that will be prompted to the user (or passed as arguments)
-const options = defineAddonOptions()
- .add('who', {
- question: 'To whom should the addon say hello?',
- type: 'string' // boolean | number | select | multiselect
- })
- .build();
-
-// your add-on definition, the entry point
-export default defineAddon({
- id: 'your-addon-name',
- // shortDescription: 'does X', // optional: one-liner shown in prompts
- // homepage: 'https://...', // optional: link to docs/repo
-
- options,
-
- // preparing step, check requirements and dependencies
- setup: ({ dependsOn }) => {
- dependsOn('tailwindcss');
- },
-
- // actual execution of the addon
- run: ({ isKit, cancel, sv, options, directory }) => {
- if (!isKit) return cancel('SvelteKit is required');
-
- // Add "Hello [who]!" to the root page
- sv.file(
- directory.routes + '/+page.svelte',
- transforms.svelte(({ ast, svelte }) => {
- svelte.addFragment(ast, `Hello ${options.who}!
`);
- })
- );
- }
-});
-```
-
-> `sv` owns the file system - `sv.file()` resolves the path, reads the file, applies the edit function, and writes the result.
-> `@sveltejs/sv-utils` owns the content - `transforms.svelte()` returns a curried function that handles parsing, gives you the AST and utils, and serializes back. See [sv-utils](/docs/cli/sv-utils) for the full API.
-
-## Development with `file:` protocol
-
-While developing your add-on, you can test it locally using the `file:` protocol:
-
-```sh
-# In your test project
-npx sv add file:../path/to/my-addon
-```
-
-This allows you to iterate quickly without publishing to npm.
-
-## Testing with `sv/testing`
-
-The `sv/testing` module provides utilities for testing your add-on:
-
-```js
-import { setupTest } from 'sv/testing';
-import { test, expect } from 'vitest';
-import addon from './index.js';
-
-test('adds hello message', async () => {
- const { content } = await setupTest({
- addon,
- options: { who: 'World' },
- files: {
- 'src/routes/+page.svelte': 'Welcome
'
- }
- });
-
- expect(content('src/routes/+page.svelte')).toContain('Hello World!');
-});
-```
-
-## Building and publishing
-
-### Bundling
-
-Community add-ons are bundled with [tsdown](https://tsdown.dev/) into a single file. Everything is bundled except `sv` (peer dependency, provided at runtime).
-
-```sh
-npm run build
-```
-
-### Package structure
-
-Your add-on must have `sv` as a peer dependency and **no** `dependencies` in `package.json`:
-
-```json
-{
- "name": "@your-org/sv",
- "version": "1.0.0",
- "type": "module",
- "exports": {
- ".": "./src/index.js"
- },
- "publishConfig": {
- "access": "public",
- "exports": {
- ".": { "default": "./dist/index.js" }
- }
- },
- "peerDependencies": {
- "sv": "^0.13.0"
- },
- "keywords": ["sv-add"]
-}
-```
-
-- `exports` points to `./src/index.js` for local development with the `file:` protocol.
-- `publishConfig.exports` overrides exports when publishing, pointing to the bundled `./dist/index.js`.
-
-> [!NOTE]
-> Add the `sv-add` keyword so users can discover your add-on on npm.
-
-### Export options
-
-Your package can export the add-on in two ways:
-
-1. **Default export** (recommended for dedicated add-on packages):
-
- ```json
- {
- "exports": {
- ".": "./src/index.js"
- }
- }
- ```
-
-2. **`/sv` export** (for packages that have other functionality):
- ```json
- {
- "exports": {
- ".": "./src/main.js",
- "./sv": "./src/addon.js"
- }
- }
- ```
-
-### Publishing
-
-Community add-ons must be scoped packages (e.g. `@your-org/sv`). Users install with `npx sv add @your-org`.
-
-```sh
-npm login
-npm publish
-```
-
-> `prepublishOnly` automatically runs the build before publishing.
-
-## Next steps
-
-You can optionally display guidance after your add-on runs:
-
-```js
-// @noErrors
-export default defineAddon({
- // ...
- nextSteps: ({ options }) => [
- `Run ${color.command('npm run dev')} to start developing`,
- `Check out the docs at https://...`
- ]
-});
-```
-
-## Version compatibility
-
-Your add-on should specify the minimum `sv` version it requires in `peerDependencies`. If a user's `sv` version has a different major version than what your add-on was built for, they will see a compatibility warning.
diff --git a/documentation/docs/40-api/20-sv-utils.md b/documentation/docs/50-api/20-sv-utils.md
similarity index 77%
rename from documentation/docs/40-api/20-sv-utils.md
rename to documentation/docs/50-api/20-sv-utils.md
index daa0b82ac..4a714618d 100644
--- a/documentation/docs/40-api/20-sv-utils.md
+++ b/documentation/docs/50-api/20-sv-utils.md
@@ -5,29 +5,24 @@ title: sv-utils
> [!NOTE]
> `@sveltejs/sv-utils` is currently **experimental**. The API may change. Full documentation is not yet available.
-`@sveltejs/sv-utils` provides utilities for parsing, transforming, and generating code in add-ons.
+`@sveltejs/sv-utils` is an add-on utilty for parsing, transforming, and generating code..
```sh
npm install -D @sveltejs/sv-utils
```
-## Architecture
+## transforms
-The Svelte CLI is split into two packages with a clear boundary:
-
-- **`sv`** = **where and when** to do it. It owns paths, workspace detection, dependency tracking, and file I/O. The engine orchestrates add-on execution.
-- **`@sveltejs/sv-utils`** = **what** to do to content. It provides parsers, language tooling, and typed transforms. Everything here is pure - no file system, no workspace awareness.
-
-This separation means transforms are testable without a workspace and composable across add-ons.
-
-## Transforms
-
-Transforms are curried, parser-aware functions that turn `string -> string`. Call a transform with your callback to get a function that plugs directly into `sv.file()`. The parser choice is baked into the transform type - you can't accidentally parse a vite config as Svelte because you never call a parser yourself.
+`transforms` is a collection of parser-aware functions that lets you modify the files via abstract syntax tree (AST). It accepts a callback function. The return value is designed to be be passed directly into `sv.file()`. The parser choice is baked into the transform type - you can't accidentally parse a vite config as Svelte because you never call a parser yourself.
Each transform injects relevant utilities into the callback, so you only need one import:
```js
import { transforms } from '@sveltejs/sv-utils';
+
+transforms.script(/* ... */);
+transforms.svelte(/* ... */);
+// ...
```
### `transforms.script`
@@ -73,7 +68,7 @@ import { transforms } from '@sveltejs/sv-utils';
sv.file(
layoutPath,
- transforms.svelteScript({ language }, ({ ast, svelte, js }) => {
+ transforms.svelteScript({ language: 'ts' }, ({ ast, svelte, js }) => {
js.imports.addDefault(ast.instance.content, { as: 'Foo', from: './Foo.svelte' });
svelte.addFragment(ast, '');
})
@@ -161,22 +156,26 @@ Transforms are curried functions - call them with the callback, then apply to co
```js
import { transforms } from '@sveltejs/sv-utils';
-const result = transforms.script(({ ast, js }) => {
+const transform = transforms.script(({ ast, js }) => {
js.imports.addDefault(ast, { as: 'foo', from: 'foo' });
-})('export default {}');
+});
+const result = transform('export default {}');
```
### Composability
-For cases where you need to mix transforms and raw edits, use `sv.file` with a content callback and invoke the curried transform manually:
+For cases where you need to mix and match transforms and raw edits, use `sv.file` with a content callback and invoke the curried transform manually:
```js
// @noErrors
sv.file(path, (content) => {
// curried
- content = transforms.script(({ ast, js }) => {
+ const transform = transforms.script(({ ast, js }) => {
js.imports.addDefault(ast, { as: 'foo', from: 'bar' });
- })(content);
+ });
+
+ // parser manipulation
+ content = transform(content);
// raw string manipulation
content = content.replace('foo', 'baz');
@@ -198,9 +197,14 @@ export const addFooImport = transforms.svelte(({ ast, svelte, js }) => {
});
```
+```js
+sv.file('+page.svelte', addFooImport);
+sv.file('index.svelte', addFooImport);
+```
+
## Parsers (low-level)
-For cases where transforms don't fit (e.g., conditional parsing, error handling around the parser), the `parse` namespace is still available:
+`transforms` will fit most users needs (e.g., conditional parsing, error handling around the parser). If not, `parse` is a low-level API available to you:
```js
// @noErrors
diff --git a/documentation/docs/40-api/index.md b/documentation/docs/50-api/index.md
similarity index 100%
rename from documentation/docs/40-api/index.md
rename to documentation/docs/50-api/index.md