Skip to main content

sv

sv exposes a programmatic API for creating projects and running add-ons.

defineAddon

Creates an add-on definition. See create your own for a full guide.

import { 
const transforms: {
    script(cb: (file: {
        ast: Program;
        comments: Comments;
        content: string;
        js: typeof index_d_exports$3;
    }) => void | false, options?: TransformOptions): (content: string) => string;
    svelte(cb: (file: {
        ast: AST.Root;
        content: string;
        svelte: typeof index_d_exports$4;
        js: typeof index_d_exports$3;
    }) => void | false, options?: TransformOptions): (content: string) => string;
    ... 6 more ...;
    text(cb: (file: {
        content: string;
        text: typeof text_d_exports;
    }) => string | false): TransformFn;
}

File transform primitives that know their format.

sv-utils = what to do to content, sv = where and when to do it.

Each transform wraps: parse -> callback({ast/data, utils}) -> generateCode(). 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 are curried: call with the callback to get a (content: string) => string function that plugs directly into sv.file().

@example
import { transforms } from '@sveltejs/sv-utils';

// use with sv.file() - curried form plugs in directly
sv.file(files.viteConfig, transforms.script(({ ast, js }) => {
  js.vite.addPlugin(ast, { code: 'kitRoutes()' });
}));

// standalone usage / testing
const result = transforms.script(({ ast, js }) => {
  js.imports.addDefault(ast, { as: 'foo', from: 'foo' });
})(fileContent);
transforms
} from '@sveltejs/sv-utils';
import { function defineAddon<const Id extends string, Args extends OptionDefinition>(config: Addon<Args, Id>): Addon<Args, Id> (+1 overload)

The entry point for your addon, It will hold every thing! (options, setup, run, nextSteps, ...)

For dynamic options added via addOption in setup, use the generic to get strong typing:

const addon = defineAddon<{ extra: boolean }>()({ ... });
addon.options.extra.default // boolean
defineAddon
, function defineAddonOptions(): OptionBuilder<{}>

Options for an addon.

Will be prompted to the user if there are not answered by args when calling the cli.

const options = defineAddonOptions()
  .add('demo', {
	question: `demo? ${color.optional('(a cool one!)')}`
	type: string | boolean | number | select | multiselect,
	default: true,
  })
  .build();

To define by args, you can do

npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
defineAddonOptions
} from 'sv';
export default defineAddon<"my-addon", {}>(config: Addon<{}, "my-addon">): Addon<{}, "my-addon"> (+1 overload)

The entry point for your addon, It will hold every thing! (options, setup, run, nextSteps, ...)

For dynamic options added via addOption in setup, use the generic to get strong typing:

const addon = defineAddon<{ extra: boolean }>()({ ... });
addon.options.extra.default // boolean
defineAddon
({
id: "my-addon"id: 'my-addon', options: {}options: function defineAddonOptions(): OptionBuilder<{}>

Options for an addon.

Will be prompted to the user if there are not answered by args when calling the cli.

const options = defineAddonOptions()
  .add('demo', {
	question: `demo? ${color.optional('(a cool one!)')}`
	type: string | boolean | number | select | multiselect,
	default: true,
  })
  .build();

To define by args, you can do

npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
defineAddonOptions
().function build(): {}build(),
// called before run - declare dependencies, environment requirements, and dynamic options
setup?: ((workspace: Workspace & {
    dependsOn: (name: keyof OfficialAddons) => void;
    unsupported: (reason: string) => void;
    runsAfter: (name: keyof OfficialAddons) => void;
    addOption: (key: string, question: Question) => void;
}) => MaybePromise<...>) | undefined
setup
: ({ dependsOn: (name: keyof OfficialAddons) => void

On what official addons does this addon depend on?

dependsOn
, unsupported: (reason: string) => void

Why is this addon not supported?

unsupported
, addOption: (key: string, question: Question) => voidaddOption, isKit: booleanisKit }) => {
if (!isKit: booleanisKit) unsupported: (reason: string) => void

Why is this addon not supported?

unsupported
('Requires SvelteKit');
dependsOn: (name: keyof OfficialAddons) => void

On what official addons does this addon depend on?

dependsOn
('eslint');
// dynamically add options based on workspace state or fetched data addOption: (key: string, question: Question) => voidaddOption('theme', { question: stringquestion: 'Which theme?', type: "select"type: 'select', default: anydefault: 'dark',
options: {
    value: any;
    label?: string;
    hint?: string;
}[]
options
: [{ value: anyvalue: 'dark' }, { value: anyvalue: 'light' }]
}); }, // the actual work — add files, edit files, declare dependencies
run: (workspace: Workspace & {
    options: OptionValues<{}> & Record<string, unknown>;
    sv: SvApi;
    cancel: (reason: string) => void;
}) => MaybePromise<void>
run
: ({ sv: SvApisv, options: OptionValues<{}> & Record<string, unknown>

Add-on options (includes dynamically added options from setup)

options
, cancel: (reason: string) => void

Cancel the addon at any time!

cancel
}) => {
// add a dependency sv: SvApisv.devDependency: (pkg: string, version: string) => voiddevDependency('my-lib', '^1.0.0'); // create or edit files using transforms from @sveltejs/sv-utils sv: SvApisv.file: (path: string, edit: (content: string) => string | false) => void

Edit a file in the workspace. (will create it if it doesn't exist)

Return false from the callback to abort - the original content is returned unchanged.

file
('src/lib/foo.ts', (content: stringcontent) => {
return 'export const foo = true;'; }); sv: SvApisv.file: (path: string, edit: (content: string) => string | false) => void

Edit a file in the workspace. (will create it if it doesn't exist)

Return false from the callback to abort - the original content is returned unchanged.

file
(
'src/routes/+page.svelte',
const transforms: {
    script(cb: (file: {
        ast: Program;
        comments: Comments;
        content: string;
        js: typeof index_d_exports$3;
    }) => void | false, options?: TransformOptions): (content: string) => string;
    svelte(cb: (file: {
        ast: AST.Root;
        content: string;
        svelte: typeof index_d_exports$4;
        js: typeof index_d_exports$3;
    }) => void | false, options?: TransformOptions): (content: string) => string;
    ... 6 more ...;
    text(cb: (file: {
        content: string;
        text: typeof text_d_exports;
    }) => string | false): TransformFn;
}

File transform primitives that know their format.

sv-utils = what to do to content, sv = where and when to do it.

Each transform wraps: parse -> callback({ast/data, utils}) -> generateCode(). 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 are curried: call with the callback to get a (content: string) => string function that plugs directly into sv.file().

@example
import { transforms } from '@sveltejs/sv-utils';

// use with sv.file() - curried form plugs in directly
sv.file(files.viteConfig, transforms.script(({ ast, js }) => {
  js.vite.addPlugin(ast, { code: 'kitRoutes()' });
}));

// standalone usage / testing
const result = transforms.script(({ ast, js }) => {
  js.imports.addDefault(ast, { as: 'foo', from: 'foo' });
})(fileContent);
transforms
.
function svelte(cb: (file: {
    ast: AST.Root;
    content: string;
    svelte: typeof index_d_exports$4;
    js: typeof index_d_exports$3;
}) => void | false, options?: TransformOptions): (content: string) => string

Transform a Svelte component file.

Return false from the callback to abort - the original content is returned unchanged.

svelte
(({ ast: AST.Rootast, svelte: typeof index_d_exports$4svelte }) => {
svelte: typeof index_d_exports$4svelte.
index_d_exports$4.addFragment(ast: AST.Root, content: string, options?: {
    mode?: "append" | "prepend";
}): void
export index_d_exports$4.addFragment
addFragment
(ast: AST.Rootast, '<p>Hello!</p>');
}) ); // cancel at any point if something is wrong // cancel('reason'); }, // displayed after the add-on runs
nextSteps?: ((workspace: Workspace & {
    options: OptionValues<{}> & Record<string, unknown>;
}) => string[]) | undefined
nextSteps
: ({ options: OptionValues<{}> & Record<string, unknown>options }) => ['Run `npm run dev` to get started']
});

The sv object in run provides file, dependency, devDependency, and execute. For file transforms (AST-based editing of scripts, Svelte components, CSS, JSON, etc.) and package manager helpers, see @sveltejs/sv-utils.

Typed dynamic options

If your add-on adds options dynamically in setup (e.g. from a fetch), you can pass a type parameter to defineAddon to get strong typing for those options:

const 
const addon: Addon<{} & SetupOptions<{
    theme: string;
}>, "my-addon">
addon
=
defineAddon<{
    theme: string;
}>(): <Id, Args>(config: Omit<Addon<Args & SetupOptions<{
    theme: string;
}>, Id>, "options"> & {
    options: Args;
}) => Addon<Args & SetupOptions<{
    theme: string;
}>, Id> (+1 overload)

The entry point for your addon, It will hold every thing! (options, setup, run, nextSteps, ...)

For dynamic options added via addOption in setup, use the generic to get strong typing:

const addon = defineAddon<{ extra: boolean }>()({ ... });
addon.options.extra.default // boolean
defineAddon
<{ theme: stringtheme: string }>()({
id: "my-addon"id: 'my-addon', options: {}options: function defineAddonOptions(): OptionBuilder<{}>

Options for an addon.

Will be prompted to the user if there are not answered by args when calling the cli.

const options = defineAddonOptions()
  .add('demo', {
	question: `demo? ${color.optional('(a cool one!)')}`
	type: string | boolean | number | select | multiselect,
	default: true,
  })
  .build();

To define by args, you can do

npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
defineAddonOptions
().function build(): {}build(),
setup?: ((workspace: Workspace & {
    dependsOn: (name: keyof OfficialAddons) => void;
    unsupported: (reason: string) => void;
    runsAfter: (name: keyof OfficialAddons) => void;
    addOption: (key: string, question: Question) => void;
}) => MaybePromise<...>) | undefined
setup
: ({ addOption: (key: string, question: Question) => voidaddOption }) => {
addOption: (key: string, question: Question) => voidaddOption('theme', { question: stringquestion: 'Which theme?', type: "string"type: 'string', default: stringdefault: 'dark' }); },
run: (workspace: Workspace & {
    options: OptionValues<{} & SetupOptions<{
        theme: string;
    }>> & Record<string, unknown>;
    sv: SvApi;
    cancel: (reason: string) => void;
}) => MaybePromise<void>
run
: ({
options: OptionValues<{} & SetupOptions<{
    theme: string;
}>> & Record<string, unknown>

Add-on options (includes dynamically added options from setup)

options
}) => {
options: OptionValues<{} & SetupOptions<{
    theme: string;
}>> & Record<string, unknown>

Add-on options (includes dynamically added options from setup)

options
.theme: stringtheme; // string
} });

The type parameter maps value types (boolean, string, number) to question definitions. Without it, defineAddon stays strict and only allows statically defined options.

defineAddonOptions

Builder for add-on options. Chained with .add() and finalized with .build().

import { function defineAddonOptions(): OptionBuilder<{}>

Options for an addon.

Will be prompted to the user if there are not answered by args when calling the cli.

const options = defineAddonOptions()
  .add('demo', {
	question: `demo? ${color.optional('(a cool one!)')}`
	type: string | boolean | number | select | multiselect,
	default: true,
  })
  .build();

To define by args, you can do

npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
defineAddonOptions
} from 'sv';
const
const options: {
    database: {
        readonly question: "Which database?";
        readonly type: "select";
        readonly default: "postgresql";
        readonly options: [{
            readonly value: "postgresql";
        }, {
            readonly value: "mysql";
        }, {
            readonly value: "sqlite";
        }];
    };
    docker: {
        readonly question: "Add a docker-compose file?";
        readonly type: "boolean";
        readonly default: false;
        readonly condition: (opts: OptionValues<Record<"database", {
            readonly question: "Which database?";
            readonly type: "select";
            readonly default: "postgresql";
            readonly options: [{
                readonly value: "postgresql";
            }, {
                readonly value: "mysql";
            }, {
                readonly value: "sqlite";
            }];
        }> & Record<...>>) => boolean;
    };
}
options
= function defineAddonOptions(): OptionBuilder<{}>

Options for an addon.

Will be prompted to the user if there are not answered by args when calling the cli.

const options = defineAddonOptions()
  .add('demo', {
	question: `demo? ${color.optional('(a cool one!)')}`
	type: string | boolean | number | select | multiselect,
	default: true,
  })
  .build();

To define by args, you can do

npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
defineAddonOptions
()
.
add<"database", {
    readonly question: "Which database?";
    readonly type: "select";
    readonly default: "postgresql";
    readonly options: [{
        readonly value: "postgresql";
    }, {
        readonly value: "mysql";
    }, {
        readonly value: "sqlite";
    }];
}>(key: "database", question: {
    readonly question: "Which database?";
    readonly type: "select";
    readonly default: "postgresql";
    readonly options: [{
        readonly value: "postgresql";
    }, {
        readonly value: "mysql";
    }, {
        readonly value: "sqlite";
    }];
}): OptionBuilder<Record<"database", {
    readonly question: "Which database?";
    readonly type: "select";
    readonly default: "postgresql";
    readonly options: [{
        readonly value: "postgresql";
    }, {
        readonly value: "mysql";
    }, {
        readonly value: "sqlite";
    }];
}>>

This type is a bit complex, but in usage, it's quite simple!

The idea is to add() options one by one, with the key and the question.

  .add('demo', {
	question: 'Do you want to add a demo?',
	type: 'boolean',  // string, number, select, multiselect
	default: true,
	// condition: (o) => o.previousOption === 'ok',
  })
add
('database', {
question: "Which database?"question: 'Which database?', type: "select"type: 'select', default: "postgresql"default: 'postgresql',
options: [{
    readonly value: "postgresql";
}, {
    readonly value: "mysql";
}, {
    readonly value: "sqlite";
}]
options
: [
{ value: "postgresql"value: 'postgresql' }, { value: "mysql"value: 'mysql' }, { value: "sqlite"value: 'sqlite' } ] }) .
add<"docker", {
    readonly question: "Add a docker-compose file?";
    readonly type: "boolean";
    readonly default: false;
    readonly condition: (opts: OptionValues<Record<"database", {
        readonly question: "Which database?";
        readonly type: "select";
        readonly default: "postgresql";
        readonly options: [{
            readonly value: "postgresql";
        }, {
            readonly value: "mysql";
        }, {
            readonly value: "sqlite";
        }];
    }> & Record<"docker", any>>) => boolean;
}>(key: "docker", question: {
    readonly question: "Add a docker-compose file?";
    readonly type: "boolean";
    readonly default: false;
    readonly condition: (opts: OptionValues<Record<"database", {
        readonly question: "Which database?";
        readonly type: "select";
        readonly default: "postgresql";
        readonly options: [{
            readonly value: "postgresql";
        }, {
            readonly value: "mysql";
        }, {
            readonly value: "sqlite";
        }];
    }> & Record<"docker", any>>) => boolean;
}): OptionBuilder<...>

This type is a bit complex, but in usage, it's quite simple!

The idea is to add() options one by one, with the key and the question.

  .add('demo', {
	question: 'Do you want to add a demo?',
	type: 'boolean',  // string, number, select, multiselect
	default: true,
	// condition: (o) => o.previousOption === 'ok',
  })
add
('docker', {
question: "Add a docker-compose file?"question: 'Add a docker-compose file?', type: "boolean"type: 'boolean', default: falsedefault: false, // only ask when database is not sqlite
condition: (opts: OptionValues<Record<"database", {
    readonly question: "Which database?";
    readonly type: "select";
    readonly default: "postgresql";
    readonly options: [{
        readonly value: "postgresql";
    }, {
        readonly value: "mysql";
    }, {
        readonly value: "sqlite";
    }];
}> & Record<"docker", any>>) => boolean
condition
: (
opts: OptionValues<Record<"database", {
    readonly question: "Which database?";
    readonly type: "select";
    readonly default: "postgresql";
    readonly options: [{
        readonly value: "postgresql";
    }, {
        readonly value: "mysql";
    }, {
        readonly value: "sqlite";
    }];
}> & Record<"docker", any>>
opts
) =>
opts: OptionValues<Record<"database", {
    readonly question: "Which database?";
    readonly type: "select";
    readonly default: "postgresql";
    readonly options: [{
        readonly value: "postgresql";
    }, {
        readonly value: "mysql";
    }, {
        readonly value: "sqlite";
    }];
}> & Record<"docker", any>>
opts
.database: "postgresql" | "mysql" | "sqlite"database !== 'sqlite'
}) .
function build(): {
    database: {
        readonly question: "Which database?";
        readonly type: "select";
        readonly default: "postgresql";
        readonly options: [{
            readonly value: "postgresql";
        }, {
            readonly value: "mysql";
        }, {
            readonly value: "sqlite";
        }];
    };
    docker: {
        readonly question: "Add a docker-compose file?";
        readonly type: "boolean";
        readonly default: false;
        readonly condition: (opts: OptionValues<Record<"database", {
            readonly question: "Which database?";
            readonly type: "select";
            readonly default: "postgresql";
            readonly options: [{
                readonly value: "postgresql";
            }, {
                readonly value: "mysql";
            }, {
                readonly value: "sqlite";
            }];
        }> & Record<...>>) => boolean;
    };
}
build
();

Options are asked in order. The condition callback receives the answers collected so far — return false to skip the question (its value will be undefined).

create

Programmatically create a new Svelte project.

import { function create(cwd: string, options: Options): voidcreate } from 'sv';

function create(cwd: string, options: Options): voidcreate('./my-app', {
	name: stringname: 'my-app',
	template: "minimal" | "demo" | "library" | "addon" | "svelte"template: 'minimal',
	types: "typescript" | "checkjs" | "none"types: 'typescript'
});

add

Programmatically run add-ons against an existing project.

import { 
function add<Addons extends AddonMap>({ addons, cwd, options, packageManager }: InstallOptions<Addons>): Promise<ReturnType<({ loadedAddons, workspace, setupResults, options }: ApplyAddonOptions) => Promise<{
    filesToFormat: string[];
    pnpmBuildDependencies: string[];
    status: Record<string, string[] | "success">;
}>>>
add
, const officialAddons: OfficialAddonsofficialAddons } from 'sv';
await
add<{
    prettier: Addon<any, string>;
}>({ addons, cwd, options, packageManager }: InstallOptions<{
    prettier: Addon<any, string>;
}>): Promise<ReturnType<({ loadedAddons, workspace, setupResults, options }: ApplyAddonOptions) => Promise<{
    filesToFormat: string[];
    pnpmBuildDependencies: string[];
    status: Record<string, string[] | "success">;
}>>>
add
({
cwd: stringcwd: './my-app',
addons: {
    prettier: Addon<any, string>;
}
addons
: { prettier: Addon<any, string>prettier: const officialAddons: OfficialAddonsofficialAddons.prettier: Addon<any, string>prettier },
options: OptionMap<{
    prettier: Addon<any, string>;
}>
options
: { prettier: {}prettier: {} },
packageManager?: AgentName | undefinedpackageManager: 'npm' });

Edit this page on GitHub llms.txt