Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
bbde567
add prettier-plugin-sort-imports
jycouet Feb 8, 2026
d1c3221
format
jycouet Feb 8, 2026
42d8290
upate format
jycouet Feb 8, 2026
3db26ff
vanity
jycouet Feb 8, 2026
a68dcc1
also there!
jycouet Feb 8, 2026
9b55cdc
rmv importOrderSeparation
jycouet Feb 8, 2026
8b41a8d
git mv
jycouet Feb 8, 2026
375fc75
lock
jycouet Feb 8, 2026
a8d33d1
add changeset
jycouet Feb 8, 2026
ee8b509
Merge branch 'main' of github.com:sveltejs/cli into chore/move-things
jycouet Feb 8, 2026
e96b927
back
jycouet Feb 8, 2026
88aa2d8
one "@trivago/prettier-plugin-sort-imports":
jycouet Feb 8, 2026
9fd0d96
ok
jycouet Feb 8, 2026
ecd45e0
lib => src
jycouet Feb 8, 2026
9149c6e
core svelte
jycouet Feb 8, 2026
45d11bb
git mv core svelte
jycouet Feb 8, 2026
2643930
humm
jycouet Feb 8, 2026
1a112a0
test
jycouet Feb 8, 2026
7d7d829
prettier
jycouet Feb 8, 2026
fcf05c4
format
jycouet Feb 8, 2026
037b282
sniff
jycouet Feb 8, 2026
bf147ef
improve windows timing
jycouet Feb 8, 2026
6b93ab6
Merge branch 'main' of github.com:sveltejs/cli into chore/move-things
jycouet Feb 15, 2026
8954646
mv
jycouet Feb 15, 2026
7fd1730
mv
jycouet Feb 15, 2026
32e068f
5.51
jycouet Feb 15, 2026
b527bbe
update docs
jycouet Feb 15, 2026
a202253
silently push to sv
jycouet Feb 15, 2026
e92987b
TODO
jycouet Feb 15, 2026
fee0ddd
update
jycouet Feb 15, 2026
63bae15
yes we are already in sv-utils
jycouet Feb 17, 2026
9f8c1a7
Merge branch 'main' of github.com:sveltejs/cli into chore/move-things
jycouet Feb 17, 2026
47ff1a3
Merge branch 'main' of github.com:sveltejs/cli into chore/move-things
jycouet Feb 19, 2026
de00dff
git mv
jycouet Feb 19, 2026
00f194e
git mv 2
jycouet Feb 19, 2026
1d832c9
git mv 3
jycouet Feb 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/yellow-hoops-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/sv-utils': patch
---

feat: creation of `@sveltejs/sv-utils` to build addons _(experimental)_
4 changes: 2 additions & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
packages/sv/lib/cli/tests/snapshots/*
packages/sv/lib/**/tests/**/output.ts
packages/sv/src/cli/tests/snapshots/*
packages/sv-utils/src/tests/**/output.ts
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ pnpm dev

For each add-on we have integration tests setup. These install the deps, build the app, run the dev server and then run a few small snippets against the add-on to see if the changes introduced by the add-on are working as expected.

Tests are split into projects: `cli`, `core`, `addons`, `create`, `migrate`. **Always run tests by project** for faster feedback:
Tests are split into projects: `cli`, `core`, `sv-utils`, `addons`, `create`, `migrate`. **Always run tests by project** for faster feedback:

```sh
pnpm test --project migrate # Migrate tests
pnpm test --project core # Core utility tests
pnpm test --project core # Core tests
pnpm test --project create # Project creation tests
pnpm test --project addons # Add-on tests
pnpm test --project sv-utils # sv-utils tests

pnpm test --project addons eslint # Just eslint add-on tests
pnpm build && pnpm test --project cli # CLI tests
Expand Down Expand Up @@ -102,11 +103,11 @@ pnpm build
pnpm preview
```

Using dev mode with browser DevTools is often the fastest way to debug UI issues - you can inspect network requests, console errors, and the DOM directly. Once you identify the issue, fix it in the addon source (`packages/sv/lib/addons/[addon].ts`) and re-run the test.
Using dev mode with browser DevTools is often the fastest way to debug UI issues - you can inspect network requests, console errors, and the DOM directly. Once you identify the issue, fix it in the addon source (`packages/sv/src/addons/[addon].ts`) and re-run the test.

### Update snapshots

Some snapshots are testing the output of `sv` directly from the generated binary. They are located in `packages/sv/lib/cli/tests/snapshots`. Make sure to generate a new binary before updating these snapshots.
Some snapshots are testing the output of `sv` directly from the generated binary. They are located in `packages/sv/src/cli/tests/snapshots`. Make sure to generate a new binary before updating these snapshots.

In one command:

Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/20-commands/20-sv-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ npx sv create --template addon [path]

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/lib/addons) to get some inspiration on what can be done.
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.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Typically, an add-on looks like this:
_hover keywords in the code to have some more context_

```js
import { defineAddon, defineAddonOptions, parse, svelte } from 'sv/core';
import { parse, svelte } 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()
Expand All @@ -48,7 +49,7 @@ export default defineAddon({
run: ({ kit, cancel, sv, options }) => {
if (!kit) return cancel('SvelteKit is required');

// Add "Hello [who]!"" to the root page
// Add "Hello [who]!" to the root page
sv.file(kit.routesDirectory + '/+page.svelte', (content) => {
const { ast, generateCode } = parse.svelte(content);

Expand Down
12 changes: 12 additions & 0 deletions documentation/docs/40-api/20-sv-utils.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
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.

```sh
npm install @sveltejs/sv-utils
```
30 changes: 7 additions & 23 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,20 @@ export default [
eqeqeq: 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/require-await': 'error',
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['**/core/**/*'],
message: 'Import from "lib/core.ts" instead of directly from "../core/..."'
}
]
}
]
}
},
{
files: ['packages/sv/lib/core.ts'],
rules: {
'no-restricted-imports': 'off'
'@typescript-eslint/require-await': 'error'
}
},
{
ignores: [
'**/temp/*',
'**/.test-output/*',
'**/dist/*',
'packages/sv/lib/create/shared/**/*',
'packages/sv/lib/create/scripts/**/*',
'packages/sv/lib/create/templates/**/*',
'packages/sv/lib/cli/tests/snapshots/*',
'packages/sv/lib/**/tests/**/{output,input}.ts'
'packages/sv/src/create/shared/**/*',
'packages/sv/src/create/scripts/**/*',
'packages/sv/src/create/templates/**/*',
'packages/sv/src/cli/tests/snapshots/*',
'packages/sv/src/**/tests/**/{output,input}.ts',
'packages/sv-utils/src/**/tests/**/{output,input}.ts'
]
}
];
1 change: 1 addition & 0 deletions packages/sv-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @sveltejs/sv-utils
19 changes: 19 additions & 0 deletions packages/sv-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @sveltejs/sv-utils

Utility functions for building [sv](https://github.com/sveltejs/cli) add-ons. Parse, transform, and generate code across multiple languages.

```sh
npm install @sveltejs/sv-utils
```

## Documentation

[Documentation](https://svelte.dev/docs/cli/sv-utils)

## Changelog

[Changelog](./CHANGELOG.md)

## License

[MIT](../../LICENSE)
45 changes: 45 additions & 0 deletions packages/sv-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@sveltejs/sv-utils",
"version": "0.0.1",
"type": "module",
"description": "Utility functions for sv",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/cli.git",
"directory": "packages/sv-utils"
},
"homepage": "https://svelte.dev",
"scripts": {
"check": "tsgo",
"format": "pnpm lint --write",
"lint": "prettier --check . --config ../../prettier.config.js --ignore-path ../../.gitignore --ignore-path ../../.prettierignore"
},
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
}
},
"devDependencies": {
"@types/estree": "^1.0.8",
"decircular": "^1.0.0",
"dedent": "^1.7.0",
"esrap": "^2.2.2",
"package-manager-detector": "^1.6.0",
"silver-fleece": "^1.2.1",
"smol-toml": "^1.5.2",
"svelte": "^5.51.0",
"yaml": "^2.8.2",
"zimmerframe": "^1.1.4"
},
"keywords": [
"sv",
"sv-add",
"svelte",
"sveltekit"
]
}
18 changes: 18 additions & 0 deletions packages/sv-utils/src/color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { styleText } from 'node:util';

export const color = {
// Semantic colors
addon: (str: string): string => styleText('greenBright', str),
command: (str: string): string => styleText(['bold', 'cyanBright'], str),
env: (str: string): string => styleText('yellow', str),
path: (str: string): string => styleText('blueBright', str),
route: (str: string): string => styleText(['bold', 'underline'], str),
website: (str: string): string => styleText('cyan', str),
optional: (str: string): string => styleText('gray', str),
dim: (str: string): string => styleText(['gray', 'dim'], str), // needed for terminal that don't support `dim` well

// Status colors
success: (str: string): string => styleText('green', str),
warning: (str: string): string => styleText('yellow', str),
error: (str: string): string => styleText('red', str)
};
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const inMemoryCache = new Map<string, any>();

export const downloadJson = async (url: string) => {
export const downloadJson = async (url: string): Promise<any> => {
if (inMemoryCache.has(url)) {
return inMemoryCache.get(url);
}
Expand Down
53 changes: 28 additions & 25 deletions packages/sv/lib/core.ts → packages/sv-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,35 @@ import {
parseSvelte,
parseToml,
parseYaml
} from './core/tooling/parsers.ts';
} from './tooling/parsers.ts';

// from externals
export { log } from '@clack/prompts';
// External re-exports
export { default as dedent } from 'dedent';
export * as Walker from 'zimmerframe';
export {
AGENTS,
type AgentName,
COMMANDS,
constructCommand,
detect,
resolveCommand
} from 'package-manager-detector';

// from internals
export { defineAddon, defineAddonOptions } from './core/addon/config.ts';
export { color } from './cli/add/utils.ts';
export { isVersionUnsupportedBelow } from './core/common.ts';
export { fileExists } from './cli/add/utils.ts';
export { resolveCommand } from 'package-manager-detector/commands';
export { getNodeTypesVersion, addToDemoPage } from './addons/_engine/common.ts';
export { createPrinter } from './core/utils.ts';

// parsing & languages
export * as css from './core/tooling/css/index.ts';
export * as js from './core/tooling/js/index.ts';
export * as html from './core/tooling/html/index.ts';
export * as text from './core/tooling/text.ts';
export * as json from './core/tooling/json.ts';
export * as svelte from './core/tooling/svelte/index.ts';
// Parsing & language namespaces
export * as css from './tooling/css/index.ts';
export * as js from './tooling/js/index.ts';
export * as html from './tooling/html/index.ts';
export * as text from './tooling/text.ts';
export * as json from './tooling/json.ts';
export * as svelte from './tooling/svelte/index.ts';

/**
* Will help you `parse` code into an `ast` from all supported languages.
* Then manipulate the `ast` as you want,
* and finally `generateCode()` to write it back to the file.
*
* ```ts
* import { parse } from 'sv/core';
* import { parse } from '@sveltejs/sv-utils';
*
* const { ast, generateCode } = parse.css('body { color: red; }');
* const { ast, generateCode } = parse.html('<div>Hello, world!</div>');
Expand All @@ -57,9 +55,14 @@ export const parse = {
yaml: parseYaml as typeof parseYaml
};

// Utilities
export { splitVersion, isVersionUnsupportedBelow } from './common.ts';
export { createPrinter } from './utils.ts';
export { sanitizeName } from './sanitize.ts';
export { downloadJson } from './downloadJson.ts';

// Terminal styling
export { color } from './color.ts';

// Types
export type * from './core/addon/processors.ts';
export type * from './core/addon/options.ts';
export type * from './core/addon/config.ts';
export type * from './core/addon/workspace.ts';
export type { Comments, AstTypes, SvelteAst } from './core/tooling/index.ts';
export type { Comments, AstTypes, SvelteAst } from './tooling/index.ts';
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const categoryDirectories = getDirectoryNames(baseDir);
const prettierConfig = await prettier.resolveConfig(import.meta.url);
if (!prettierConfig) throw new Error('Failed to resolve prettier config');
prettierConfig.filepath = 'output.css';
// warm up prettier's lazy plugin loading to avoid timeout on first test (slow on Windows)
await prettier.format('', prettierConfig);

for (const categoryDirectory of categoryDirectories) {
describe(categoryDirectory, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const categoryDirectories = getDirectoryNames(baseDir);
const prettierConfig = await prettier.resolveConfig(import.meta.url);
if (!prettierConfig) throw new Error('Failed to resolve prettier config');
prettierConfig.filepath = 'output.html';
// warm up prettier's lazy plugin loading to avoid timeout on first test (slow on Windows)
await prettier.format('', prettierConfig);

for (const categoryDirectory of categoryDirectories) {
describe(categoryDirectory, () => {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { type SvelteAst, svelte } from '../../../../../index.ts';

export function run(ast: SvelteAst.Root): void {
svelte.ensureScript(ast, { language: 'ts' });
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type SvelteAst, svelte } from '../../../../../core.ts';
import { type SvelteAst, svelte } from '../../../../../index.ts';

export function run(ast: SvelteAst.Root): void {
svelte.ensureScript(ast);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type SvelteAst, svelte } from '../../../../../core.ts';
import { type SvelteAst, svelte } from '../../../../../index.ts';

export function run(ast: SvelteAst.Root): void {
svelte.ensureScript(ast);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type SvelteAst, svelte } from '../../../../../core.ts';
import { type SvelteAst, svelte } from '../../../../../index.ts';

export function run(ast: SvelteAst.Root): void {
svelte.addSlot(ast, { svelteVersion: '4.0.0' });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type SvelteAst, svelte } from '../../../../../core.ts';
import { type SvelteAst, svelte } from '../../../../../index.ts';

export function run(ast: SvelteAst.Root): void {
svelte.addSlot(ast, { svelteVersion: '5.0.0' });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type SvelteAst, svelte } from '../../../../../core.ts';
import { type SvelteAst, svelte } from '../../../../../index.ts';

export function run(ast: SvelteAst.Root): void {
svelte.addFragment(ast, '<span>Appended Fragment</span>');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, test } from 'vitest';
import { parseSvelte, serializeSvelte } from '../../tooling/index.ts';
import { parseSvelte, serializeSvelte } from '../../../../src/tooling/index.ts';

const baseDir = resolve(fileURLToPath(import.meta.url), '..');
const categoryDirectories = getDirectoryNames(baseDir);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
parseCss as svelteParseCss
} from 'svelte/compiler';
import * as yaml from 'yaml';
import { Walker } from '../../core.ts';
import * as Walker from 'zimmerframe';
import type { TsEstree } from './js/ts-estree.ts';
import { ensureScript } from './svelte/index.ts';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import decircular from 'decircular';
import dedent from 'dedent';
import { Walker } from '../../../core.ts';
import * as Walker from 'zimmerframe';
import { type AstTypes, type Comments, parseScript, serializeScript, stripAst } from '../index.ts';

export function addJsDocTypeComment(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type AstTypes, Walker } from '../../../core.ts';
import * as Walker from 'zimmerframe';
import type { AstTypes } from '../index.ts';
import { areNodesEqual } from './common.ts';

export function addEmpty(node: AstTypes.Program, options: { from: string }): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type AstTypes, type Comments, Walker } from '../../../core.ts';
import * as Walker from 'zimmerframe';
import type { AstTypes, Comments } from '../index.ts';
import * as common from './common.ts';
import * as exports from './exports.ts';
import * as functions from './function.ts';
Expand Down
File renamed without changes.
Loading
Loading