From 5eecbab40244fa50ca36c58b3e482b5f9d79dfb4 Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 2 Sep 2025 18:56:33 +0200 Subject: [PATCH 01/11] Builder pattern for addon options --- packages/addons/drizzle/index.ts | 32 ++++++++++++++++---------------- packages/core/addon/config.ts | 28 ++++++++++++++++++++++++++++ packages/core/addon/options.ts | 9 ++++----- packages/core/index.ts | 2 +- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts index f5fb27f18..4d30deb59 100644 --- a/packages/addons/drizzle/index.ts +++ b/packages/addons/drizzle/index.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { common, exports, functions, imports, object, variables } from '@sveltejs/cli-core/js'; -import { defineAddon, defineAddonOptions, dedent, type OptionValues } from '@sveltejs/cli-core'; +import { defineAddon, prepareAddonOptions, dedent, type OptionValues } from '@sveltejs/cli-core'; import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; import { resolveCommand } from 'package-manager-detector/commands'; import { getNodeTypesVersion } from '../common.ts'; @@ -12,8 +12,8 @@ const PORTS = { sqlite: '' } as const; -const options = defineAddonOptions({ - database: { +const options = prepareAddonOptions() + .add('database', { question: 'Which database would you like to use?', type: 'select', default: 'sqlite', @@ -22,8 +22,8 @@ const options = defineAddonOptions({ { value: 'mysql', label: 'MySQL' }, { value: 'sqlite', label: 'SQLite' } ] - }, - postgresql: { + }) + .add('postgresql', { question: 'Which PostgreSQL client would you like to use?', type: 'select', group: 'client', @@ -32,9 +32,9 @@ const options = defineAddonOptions({ { value: 'postgres.js', label: 'Postgres.JS', hint: 'recommended for most users' }, { value: 'neon', label: 'Neon', hint: 'popular hosted platform' } ], - condition: ({ database }) => database === 'postgresql' - }, - mysql: { + condition: ({ database }) => database === 'postgresql2' + }) + .add('mysql', { question: 'Which MySQL client would you like to use?', type: 'select', group: 'client', @@ -44,8 +44,8 @@ const options = defineAddonOptions({ { value: 'planetscale', label: 'PlanetScale', hint: 'popular hosted platform' } ], condition: ({ database }) => database === 'mysql' - }, - sqlite: { + }) + .add('sqlite', { question: 'Which SQLite client would you like to use?', type: 'select', group: 'client', @@ -56,16 +56,16 @@ const options = defineAddonOptions({ { value: 'turso', label: 'Turso', hint: 'popular hosted platform' } ], condition: ({ database }) => database === 'sqlite' - }, - docker: { + }) + .add('docker', { question: 'Do you want to run the database locally with docker-compose?', default: false, type: 'boolean', condition: ({ database, mysql, postgresql }) => (database === 'mysql' && mysql === 'mysql2') || (database === 'postgresql' && postgresql === 'postgres.js') - } -}); + }) + .build(); export default defineAddon({ id: 'drizzle', @@ -128,7 +128,7 @@ export default defineAddon({ if (content.length > 0) return content; const imageName = options.database === 'mysql' ? 'mysql' : 'postgres'; - const port = PORTS[options.database]; + const port = PORTS[options.database as keyof typeof PORTS]; const USER = 'root'; const PASSWORD = 'mysecretpassword'; @@ -443,7 +443,7 @@ function generateEnvFileContent(content: string, opts: OptionValues MaybePromise<{ success: boolean; message: string | undefined }>; }; + +// Builder pattern for addon options +export type OptionBuilder> = { + add>>( + key: K, + question: Q + ): OptionBuilder>; + build(): T; +}; + +export function prepareAddonOptions(): OptionBuilder> { + return createOptionBuilder({} as Record); +} + +function createOptionBuilder(options: T = {} as T): OptionBuilder { + return { + add>>( + key: K, + question: Q + ): OptionBuilder> { + const newOptions = { ...options, [key]: question } as T & Record; + return createOptionBuilder(newOptions); + }, + build(): T { + return options; + } + }; +} diff --git a/packages/core/addon/options.ts b/packages/core/addon/options.ts index 0510804a6..77e47faf1 100644 --- a/packages/core/addon/options.ts +++ b/packages/core/addon/options.ts @@ -30,21 +30,20 @@ export type MultiSelectQuestion = { required: boolean; }; -export type BaseQuestion = { +export type BaseQuestion = { question: string; group?: string; /** * When this condition explicitly returns `false`, the question's value will * always be `undefined` and will not fallback to the specified `default` value. */ - condition?: (options: any) => boolean; - // TODO: we want to type `options` similar to OptionValues so that its option values can be inferred + condition?: (options: OptionValues) => boolean; }; -export type Question = BaseQuestion & +export type Question = BaseQuestion & (BooleanQuestion | StringQuestion | NumberQuestion | SelectQuestion | MultiSelectQuestion); -export type OptionDefinition = Record; +export type OptionDefinition = Record>; export type OptionValues = { [K in keyof Args]: Args[K] extends StringQuestion ? string diff --git a/packages/core/index.ts b/packages/core/index.ts index 92fed5cf4..73981d78d 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,4 +1,4 @@ -export { defineAddon, defineAddonOptions } from './addon/config.ts'; +export { defineAddon, defineAddonOptions, prepareAddonOptions } from './addon/config.ts'; export { log } from '@clack/prompts'; export { default as colors } from 'picocolors'; export { default as dedent } from 'dedent'; From 297cdb589834ddefbc7ac2288a431c85570d3bfc Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 2 Sep 2025 19:03:12 +0200 Subject: [PATCH 02/11] Piouf even more amazing --- packages/addons/drizzle/index.ts | 14 +++++++------- packages/core/addon/options.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts index 4d30deb59..443af54ae 100644 --- a/packages/addons/drizzle/index.ts +++ b/packages/addons/drizzle/index.ts @@ -16,33 +16,33 @@ const options = prepareAddonOptions() .add('database', { question: 'Which database would you like to use?', type: 'select', - default: 'sqlite', + default: 'sqlite' as const, options: [ { value: 'postgresql', label: 'PostgreSQL' }, { value: 'mysql', label: 'MySQL' }, { value: 'sqlite', label: 'SQLite' } - ] + ] as const }) .add('postgresql', { question: 'Which PostgreSQL client would you like to use?', type: 'select', group: 'client', - default: 'postgres.js', + default: 'postgres.js' as const, options: [ { value: 'postgres.js', label: 'Postgres.JS', hint: 'recommended for most users' }, { value: 'neon', label: 'Neon', hint: 'popular hosted platform' } - ], - condition: ({ database }) => database === 'postgresql2' + ] as const, + condition: ({ database }) => database === 'postgresql' }) .add('mysql', { question: 'Which MySQL client would you like to use?', type: 'select', group: 'client', - default: 'mysql2', + default: 'mysql2' as const, options: [ { value: 'mysql2', hint: 'recommended for most users' }, { value: 'planetscale', label: 'PlanetScale', hint: 'popular hosted platform' } - ], + ] as const, condition: ({ database }) => database === 'mysql' }) .add('sqlite', { diff --git a/packages/core/addon/options.ts b/packages/core/addon/options.ts index 77e47faf1..5b2243a6d 100644 --- a/packages/core/addon/options.ts +++ b/packages/core/addon/options.ts @@ -17,6 +17,19 @@ export type NumberQuestion = { placeholder?: string; }; +// Helper type to extract option values from a select question +export type ExtractSelectValues = T extends { + type: 'select'; + options: Array<{ value: infer V }>; +} + ? V + : never; + +// Helper type to extract all option values as a union +export type ExtractAllOptionValues = T extends { options: Array<{ value: infer V }> } + ? V + : never; + export type SelectQuestion = { type: 'select'; default: Value; From a1193aca15cfb75d29e914b2351c9a83b39a8ac0 Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 2 Sep 2025 19:05:44 +0200 Subject: [PATCH 03/11] internal should be ok --- packages/cli/commands/add/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/commands/add/index.ts b/packages/cli/commands/add/index.ts index e92e6d666..f7449f588 100644 --- a/packages/cli/commands/add/index.ts +++ b/packages/cli/commands/add/index.ts @@ -637,7 +637,7 @@ function getOptionChoices(details: AddonWithoutExplicitArgs) { const options: Record = {}; for (const [id, question] of Object.entries(details.options)) { let values: string[] = []; - const applyDefault = question.condition?.(options) !== false; + const applyDefault = question.condition?.(options as any) !== false; if (question.type === 'boolean') { values = ['yes', `no`]; if (applyDefault) { From 18429dfd629eaa5333884d539dd2b1acc58f4188 Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 2 Sep 2025 19:13:27 +0200 Subject: [PATCH 04/11] rmv defineAddonOptions and break things --- community-addon-template/src/index.js | 10 +++++----- packages/addons/lucia/index.ts | 10 +++++----- packages/addons/paraglide/index.ts | 14 +++++++------- packages/addons/sveltekit-adapter/index.ts | 10 +++++----- packages/addons/tailwindcss/index.ts | 10 +++++----- packages/addons/vitest-addon/index.ts | 10 +++++----- packages/core/addon/config.ts | 4 ---- packages/core/index.ts | 2 +- 8 files changed, 33 insertions(+), 37 deletions(-) diff --git a/community-addon-template/src/index.js b/community-addon-template/src/index.js index 1190d7e39..d7a227422 100644 --- a/community-addon-template/src/index.js +++ b/community-addon-template/src/index.js @@ -1,14 +1,14 @@ -import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; +import { defineAddon, prepareAddonOptions } from '@sveltejs/cli-core'; import { imports } from '@sveltejs/cli-core/js'; import { parseSvelte } from '@sveltejs/cli-core/parsers'; -export const options = defineAddonOptions({ - demo: { +export const options = prepareAddonOptions() + .add('demo', { question: 'Do you want to use a demo?', type: 'boolean', default: false - } -}); + }) + .build(); export default defineAddon({ id: 'community-addon', diff --git a/packages/addons/lucia/index.ts b/packages/addons/lucia/index.ts index 739564ecb..7ebf5c150 100644 --- a/packages/addons/lucia/index.ts +++ b/packages/addons/lucia/index.ts @@ -3,7 +3,7 @@ import { colors, dedent, defineAddon, - defineAddonOptions, + prepareAddonOptions, log, utils, Walker @@ -26,13 +26,13 @@ type Dialect = 'mysql' | 'postgresql' | 'sqlite' | 'turso'; let drizzleDialect: Dialect; let schemaPath: string; -const options = defineAddonOptions({ - demo: { +const options = prepareAddonOptions() + .add('demo', { type: 'boolean', default: true, question: `Do you want to include a demo? ${colors.dim('(includes a login/register page)')}` - } -}); + }) + .build(); export default defineAddon({ id: 'lucia', diff --git a/packages/addons/paraglide/index.ts b/packages/addons/paraglide/index.ts index 0809a0a36..5e1ad6628 100644 --- a/packages/addons/paraglide/index.ts +++ b/packages/addons/paraglide/index.ts @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { colors, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core'; +import { colors, defineAddon, prepareAddonOptions, log } from '@sveltejs/cli-core'; import { common, imports, variables, exports, kit as kitJs, vite } from '@sveltejs/cli-core/js'; import * as html from '@sveltejs/cli-core/html'; import { parseHtml, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; @@ -16,8 +16,8 @@ const DEFAULT_INLANG_PROJECT = { } }; -const options = defineAddonOptions({ - languageTags: { +const options = prepareAddonOptions() + .add('languageTags', { question: `Which languages would you like to support? ${colors.gray('(e.g. en,de-ch)')}`, type: 'string', default: 'en, es', @@ -39,13 +39,13 @@ const options = defineAddonOptions({ return undefined; } - }, - demo: { + }) + .add('demo', { type: 'boolean', default: true, question: 'Do you want to include a demo?' - } -}); + }) + .build(); export default defineAddon({ id: 'paraglide', diff --git a/packages/addons/sveltekit-adapter/index.ts b/packages/addons/sveltekit-adapter/index.ts index dab76511b..61f2f3d40 100644 --- a/packages/addons/sveltekit-adapter/index.ts +++ b/packages/addons/sveltekit-adapter/index.ts @@ -1,4 +1,4 @@ -import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; +import { defineAddon, prepareAddonOptions } from '@sveltejs/cli-core'; import { exports, functions, imports, object, type AstTypes } from '@sveltejs/cli-core/js'; import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; @@ -17,14 +17,14 @@ const adapters: Adapter[] = [ { id: 'netlify', package: '@sveltejs/adapter-netlify', version: '^5.0.0' } ]; -const options = defineAddonOptions({ - adapter: { +const options = prepareAddonOptions() + .add('adapter', { type: 'select', question: 'Which SvelteKit adapter would you like to use?', options: adapters.map((p) => ({ value: p.id, label: p.id, hint: p.package })), default: 'auto' - } -}); + }) + .build(); export default defineAddon({ id: 'sveltekit-adapter', diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index 614a15b65..d5b224016 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -1,4 +1,4 @@ -import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; +import { defineAddon, prepareAddonOptions } from '@sveltejs/cli-core'; import { imports, vite } from '@sveltejs/cli-core/js'; import { parseCss, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import { addSlot } from '@sveltejs/cli-core/html'; @@ -25,15 +25,15 @@ const plugins: Plugin[] = [ } ]; -const options = defineAddonOptions({ - plugins: { +const options = prepareAddonOptions() + .add('plugins', { type: 'multiselect', question: 'Which plugins would you like to add?', options: plugins.map((p) => ({ value: p.id, label: p.id, hint: p.package })), default: [], required: false - } -}); + }) + .build(); export default defineAddon({ id: 'tailwindcss', diff --git a/packages/addons/vitest-addon/index.ts b/packages/addons/vitest-addon/index.ts index 895d4e051..b266bca7f 100644 --- a/packages/addons/vitest-addon/index.ts +++ b/packages/addons/vitest-addon/index.ts @@ -1,9 +1,9 @@ -import { dedent, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core'; +import { dedent, defineAddon, prepareAddonOptions, log } from '@sveltejs/cli-core'; import { array, exports, functions, object } from '@sveltejs/cli-core/js'; import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; -const options = defineAddonOptions({ - usages: { +const options = prepareAddonOptions() + .add('usages', { question: 'What do you want to use vitest for?', type: 'multiselect', default: ['unit', 'component'], @@ -12,8 +12,8 @@ const options = defineAddonOptions({ { value: 'component', label: 'component testing' } ], required: true - } -}); + }) + .build(); export default defineAddon({ id: 'vitest', diff --git a/packages/core/addon/config.ts b/packages/core/addon/config.ts index 6a8d8d64c..df55aa7cf 100644 --- a/packages/core/addon/config.ts +++ b/packages/core/addon/config.ts @@ -78,10 +78,6 @@ export type TestDefinition = { condition?: (options: OptionValues) => boolean; }; -export function defineAddonOptions(options: Args): Args { - return options; -} - type MaybePromise = Promise | T; export type Verification = { diff --git a/packages/core/index.ts b/packages/core/index.ts index 73981d78d..1d6624281 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,4 +1,4 @@ -export { defineAddon, defineAddonOptions, prepareAddonOptions } from './addon/config.ts'; +export { defineAddon, prepareAddonOptions } from './addon/config.ts'; export { log } from '@clack/prompts'; export { default as colors } from 'picocolors'; export { default as dedent } from 'dedent'; From 2153082ee228c6c4a21d05ad26cc3a216b8f4393 Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 2 Sep 2025 20:19:27 +0200 Subject: [PATCH 05/11] typiiiiiing --- packages/addons/drizzle/index.ts | 12 ++++++------ packages/addons/tailwindcss/index.ts | 2 +- packages/core/addon/config.ts | 6 +++--- packages/core/addon/options.ts | 21 ++++----------------- 4 files changed, 14 insertions(+), 27 deletions(-) diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts index 443af54ae..d92aed48f 100644 --- a/packages/addons/drizzle/index.ts +++ b/packages/addons/drizzle/index.ts @@ -16,33 +16,33 @@ const options = prepareAddonOptions() .add('database', { question: 'Which database would you like to use?', type: 'select', - default: 'sqlite' as const, + default: 'sqlite' as 'postgresql' | 'mysql' | 'sqlite', options: [ { value: 'postgresql', label: 'PostgreSQL' }, { value: 'mysql', label: 'MySQL' }, { value: 'sqlite', label: 'SQLite' } - ] as const + ] }) .add('postgresql', { question: 'Which PostgreSQL client would you like to use?', type: 'select', group: 'client', - default: 'postgres.js' as const, + default: 'postgres.js' as 'postgres.js' | 'neon', options: [ { value: 'postgres.js', label: 'Postgres.JS', hint: 'recommended for most users' }, { value: 'neon', label: 'Neon', hint: 'popular hosted platform' } - ] as const, + ], condition: ({ database }) => database === 'postgresql' }) .add('mysql', { question: 'Which MySQL client would you like to use?', type: 'select', group: 'client', - default: 'mysql2' as const, + default: 'mysql2' as 'mysql2' | 'planetscale', options: [ { value: 'mysql2', hint: 'recommended for most users' }, { value: 'planetscale', label: 'PlanetScale', hint: 'popular hosted platform' } - ] as const, + ], condition: ({ database }) => database === 'mysql' }) .add('sqlite', { diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index d5b224016..c5b5b2caa 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -30,7 +30,7 @@ const options = prepareAddonOptions() type: 'multiselect', question: 'Which plugins would you like to add?', options: plugins.map((p) => ({ value: p.id, label: p.id, hint: p.package })), - default: [], + default: [] as string[], // if not it will be of type never[] required: false }) .build(); diff --git a/packages/core/addon/config.ts b/packages/core/addon/config.ts index df55aa7cf..e1e0a1be1 100644 --- a/packages/core/addon/config.ts +++ b/packages/core/addon/config.ts @@ -86,7 +86,7 @@ export type Verification = { }; // Builder pattern for addon options -export type OptionBuilder> = { +export type OptionBuilder> = { add>>( key: K, question: Q @@ -94,8 +94,8 @@ export type OptionBuilder> = build(): T; }; -export function prepareAddonOptions(): OptionBuilder> { - return createOptionBuilder({} as Record); +export function prepareAddonOptions(): OptionBuilder> { + return createOptionBuilder({} as Record); } function createOptionBuilder(options: T = {} as T): OptionBuilder { diff --git a/packages/core/addon/options.ts b/packages/core/addon/options.ts index 5b2243a6d..954051c4d 100644 --- a/packages/core/addon/options.ts +++ b/packages/core/addon/options.ts @@ -17,29 +17,16 @@ export type NumberQuestion = { placeholder?: string; }; -// Helper type to extract option values from a select question -export type ExtractSelectValues = T extends { - type: 'select'; - options: Array<{ value: infer V }>; -} - ? V - : never; - -// Helper type to extract all option values as a union -export type ExtractAllOptionValues = T extends { options: Array<{ value: infer V }> } - ? V - : never; - export type SelectQuestion = { type: 'select'; default: Value; - options: Array<{ value: Value; label?: string; hint?: string }>; + options: Array<{ value: string; label?: string; hint?: string }>; }; export type MultiSelectQuestion = { type: 'multiselect'; - default: Value[]; - options: Array<{ value: Value; label?: string; hint?: string }>; + default: Value; + options: Array<{ value: string; label?: string; hint?: string }>; required: boolean; }; @@ -67,6 +54,6 @@ export type OptionValues = { : Args[K] extends SelectQuestion ? Value : Args[K] extends MultiSelectQuestion - ? Value[] + ? Value // as the type of the Value should already be an array! : never; }; From 3a7700232870a1006986f51709349e10bb64bfdf Mon Sep 17 00:00:00 2001 From: jycouet Date: Wed, 3 Sep 2025 16:19:00 +0200 Subject: [PATCH 06/11] update changelog --- .changeset/early-trees-go.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/early-trees-go.md diff --git a/.changeset/early-trees-go.md b/.changeset/early-trees-go.md new file mode 100644 index 000000000..c6d18c6f4 --- /dev/null +++ b/.changeset/early-trees-go.md @@ -0,0 +1,5 @@ +--- +'sv': patch +--- + +internal(core): remove `defineAddonOptions({ /*config */ })` in favor of `prepareAddonOptions().add('key', { /*config */ }).build()`. This change brings better type safety. From bf65fbc421d7f18e4ada583a2951d5e5c3d61fd1 Mon Sep 17 00:00:00 2001 From: jycouet Date: Wed, 3 Sep 2025 19:12:33 +0200 Subject: [PATCH 07/11] naming update --- .changeset/early-trees-go.md | 2 +- community-addon-template/src/index.js | 4 ++-- packages/addons/drizzle/index.ts | 4 ++-- packages/addons/lucia/index.ts | 4 ++-- packages/addons/paraglide/index.ts | 4 ++-- packages/addons/sveltekit-adapter/index.ts | 4 ++-- packages/addons/tailwindcss/index.ts | 4 ++-- packages/addons/vitest-addon/index.ts | 4 ++-- packages/core/addon/config.ts | 2 +- packages/core/index.ts | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.changeset/early-trees-go.md b/.changeset/early-trees-go.md index c6d18c6f4..be44e2227 100644 --- a/.changeset/early-trees-go.md +++ b/.changeset/early-trees-go.md @@ -2,4 +2,4 @@ 'sv': patch --- -internal(core): remove `defineAddonOptions({ /*config */ })` in favor of `prepareAddonOptions().add('key', { /*config */ }).build()`. This change brings better type safety. +internal(core): change `defineAddonOptions({ /*config */ })` to `defineAddonOptions().add('key', { /*config */ }).build()` in order to provide better type safety. diff --git a/community-addon-template/src/index.js b/community-addon-template/src/index.js index d7a227422..61dc312ea 100644 --- a/community-addon-template/src/index.js +++ b/community-addon-template/src/index.js @@ -1,8 +1,8 @@ -import { defineAddon, prepareAddonOptions } from '@sveltejs/cli-core'; +import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; import { imports } from '@sveltejs/cli-core/js'; import { parseSvelte } from '@sveltejs/cli-core/parsers'; -export const options = prepareAddonOptions() +export const options = defineAddonOptions() .add('demo', { question: 'Do you want to use a demo?', type: 'boolean', diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts index d92aed48f..36571de67 100644 --- a/packages/addons/drizzle/index.ts +++ b/packages/addons/drizzle/index.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { common, exports, functions, imports, object, variables } from '@sveltejs/cli-core/js'; -import { defineAddon, prepareAddonOptions, dedent, type OptionValues } from '@sveltejs/cli-core'; +import { defineAddon, defineAddonOptions, dedent, type OptionValues } from '@sveltejs/cli-core'; import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; import { resolveCommand } from 'package-manager-detector/commands'; import { getNodeTypesVersion } from '../common.ts'; @@ -12,7 +12,7 @@ const PORTS = { sqlite: '' } as const; -const options = prepareAddonOptions() +const options = defineAddonOptions() .add('database', { question: 'Which database would you like to use?', type: 'select', diff --git a/packages/addons/lucia/index.ts b/packages/addons/lucia/index.ts index 7ebf5c150..491226dbc 100644 --- a/packages/addons/lucia/index.ts +++ b/packages/addons/lucia/index.ts @@ -3,7 +3,7 @@ import { colors, dedent, defineAddon, - prepareAddonOptions, + defineAddonOptions, log, utils, Walker @@ -26,7 +26,7 @@ type Dialect = 'mysql' | 'postgresql' | 'sqlite' | 'turso'; let drizzleDialect: Dialect; let schemaPath: string; -const options = prepareAddonOptions() +const options = defineAddonOptions() .add('demo', { type: 'boolean', default: true, diff --git a/packages/addons/paraglide/index.ts b/packages/addons/paraglide/index.ts index 5e1ad6628..0679d0d4e 100644 --- a/packages/addons/paraglide/index.ts +++ b/packages/addons/paraglide/index.ts @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { colors, defineAddon, prepareAddonOptions, log } from '@sveltejs/cli-core'; +import { colors, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core'; import { common, imports, variables, exports, kit as kitJs, vite } from '@sveltejs/cli-core/js'; import * as html from '@sveltejs/cli-core/html'; import { parseHtml, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; @@ -16,7 +16,7 @@ const DEFAULT_INLANG_PROJECT = { } }; -const options = prepareAddonOptions() +const options = defineAddonOptions() .add('languageTags', { question: `Which languages would you like to support? ${colors.gray('(e.g. en,de-ch)')}`, type: 'string', diff --git a/packages/addons/sveltekit-adapter/index.ts b/packages/addons/sveltekit-adapter/index.ts index 61f2f3d40..66f40d053 100644 --- a/packages/addons/sveltekit-adapter/index.ts +++ b/packages/addons/sveltekit-adapter/index.ts @@ -1,4 +1,4 @@ -import { defineAddon, prepareAddonOptions } from '@sveltejs/cli-core'; +import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; import { exports, functions, imports, object, type AstTypes } from '@sveltejs/cli-core/js'; import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; @@ -17,7 +17,7 @@ const adapters: Adapter[] = [ { id: 'netlify', package: '@sveltejs/adapter-netlify', version: '^5.0.0' } ]; -const options = prepareAddonOptions() +const options = defineAddonOptions() .add('adapter', { type: 'select', question: 'Which SvelteKit adapter would you like to use?', diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index c5b5b2caa..0fdf45723 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -1,4 +1,4 @@ -import { defineAddon, prepareAddonOptions } from '@sveltejs/cli-core'; +import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; import { imports, vite } from '@sveltejs/cli-core/js'; import { parseCss, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import { addSlot } from '@sveltejs/cli-core/html'; @@ -25,7 +25,7 @@ const plugins: Plugin[] = [ } ]; -const options = prepareAddonOptions() +const options = defineAddonOptions() .add('plugins', { type: 'multiselect', question: 'Which plugins would you like to add?', diff --git a/packages/addons/vitest-addon/index.ts b/packages/addons/vitest-addon/index.ts index b266bca7f..07180a677 100644 --- a/packages/addons/vitest-addon/index.ts +++ b/packages/addons/vitest-addon/index.ts @@ -1,8 +1,8 @@ -import { dedent, defineAddon, prepareAddonOptions, log } from '@sveltejs/cli-core'; +import { dedent, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core'; import { array, exports, functions, object } from '@sveltejs/cli-core/js'; import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; -const options = prepareAddonOptions() +const options = defineAddonOptions() .add('usages', { question: 'What do you want to use vitest for?', type: 'multiselect', diff --git a/packages/core/addon/config.ts b/packages/core/addon/config.ts index e1e0a1be1..58a55ed20 100644 --- a/packages/core/addon/config.ts +++ b/packages/core/addon/config.ts @@ -94,7 +94,7 @@ export type OptionBuilder> = { build(): T; }; -export function prepareAddonOptions(): OptionBuilder> { +export function defineAddonOptions(): OptionBuilder> { return createOptionBuilder({} as Record); } diff --git a/packages/core/index.ts b/packages/core/index.ts index 1d6624281..92fed5cf4 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,4 +1,4 @@ -export { defineAddon, prepareAddonOptions } from './addon/config.ts'; +export { defineAddon, defineAddonOptions } from './addon/config.ts'; export { log } from '@clack/prompts'; export { default as colors } from 'picocolors'; export { default as dedent } from 'dedent'; From 9c10f24c2613a39f129ab5dd111027a256a9e05e Mon Sep 17 00:00:00 2001 From: jycouet Date: Wed, 3 Sep 2025 19:22:14 +0200 Subject: [PATCH 08/11] TS --- packages/addons/drizzle/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts index 36571de67..0f3b8344a 100644 --- a/packages/addons/drizzle/index.ts +++ b/packages/addons/drizzle/index.ts @@ -6,17 +6,18 @@ import { parseJson, parseScript } from '@sveltejs/cli-core/parsers'; import { resolveCommand } from 'package-manager-detector/commands'; import { getNodeTypesVersion } from '../common.ts'; -const PORTS = { +type Database = 'mysql' | 'postgresql' | 'sqlite'; +const PORTS: Record = { mysql: '3306', postgresql: '5432', sqlite: '' -} as const; +}; const options = defineAddonOptions() .add('database', { question: 'Which database would you like to use?', type: 'select', - default: 'sqlite' as 'postgresql' | 'mysql' | 'sqlite', + default: 'sqlite' as Database, options: [ { value: 'postgresql', label: 'PostgreSQL' }, { value: 'mysql', label: 'MySQL' }, @@ -128,7 +129,7 @@ export default defineAddon({ if (content.length > 0) return content; const imageName = options.database === 'mysql' ? 'mysql' : 'postgres'; - const port = PORTS[options.database as keyof typeof PORTS]; + const port = PORTS[options.database]; const USER = 'root'; const PASSWORD = 'mysecretpassword'; @@ -443,7 +444,7 @@ function generateEnvFileContent(content: string, opts: OptionValues Date: Fri, 5 Sep 2025 14:07:38 +0200 Subject: [PATCH 09/11] playing with types --- packages/addons/tailwindcss/index.ts | 39 ++++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index 0fdf45723..b0d17eeb1 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -3,34 +3,27 @@ import { imports, vite } from '@sveltejs/cli-core/js'; import { parseCss, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import { addSlot } from '@sveltejs/cli-core/html'; -type Plugin = { - id: string; - package: string; - version: string; - identifier: string; -}; - -const plugins: Plugin[] = [ - { - id: 'typography', +function typedEntries>(obj: T): Array<[keyof T, T[keyof T]]> { + return Object.entries(obj) as Array<[keyof T, T[keyof T]]>; +} + +const plugins = { + typography: { package: '@tailwindcss/typography', - version: '^0.5.15', - identifier: 'typography' + version: '^0.5.15' }, - { - id: 'forms', + forms: { package: '@tailwindcss/forms', - version: '^0.5.9', - identifier: 'forms' + version: '^0.5.9' } -]; +} as const; const options = defineAddonOptions() .add('plugins', { type: 'multiselect', question: 'Which plugins would you like to add?', - options: plugins.map((p) => ({ value: p.id, label: p.id, hint: p.package })), - default: [] as string[], // if not it will be of type never[] + options: typedEntries(plugins).map(([id, p]) => ({ value: id, label: id, hint: p.package })), + default: [] as Array, required: false }) .build(); @@ -49,8 +42,8 @@ export default defineAddon({ if (prettierInstalled) sv.devDependency('prettier-plugin-tailwindcss', '^0.6.11'); - for (const plugin of plugins) { - if (!options.plugins.includes(plugin.id)) continue; + for (const [id, plugin] of typedEntries(plugins)) { + if (!options.plugins.includes(id)) continue; sv.devDependency(plugin.package, plugin.version); } @@ -88,8 +81,8 @@ export default defineAddon({ const lastAtRule = atRules.findLast((rule) => ['plugin', 'import'].includes(rule.name)); const pluginPos = lastAtRule!.source!.end!.offset; - for (const plugin of plugins) { - if (!options.plugins.includes(plugin.id)) continue; + for (const [id, plugin] of typedEntries(plugins)) { + if (!options.plugins.includes(id)) continue; const pluginRule = findAtRule('plugin', plugin.package); if (!pluginRule) { From fa2a545e06dc1a8ef9007afb0c3979e26b41c356 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Sep 2025 15:12:14 +0200 Subject: [PATCH 10/11] add comment --- packages/core/addon/options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/addon/options.ts b/packages/core/addon/options.ts index 954051c4d..cb1c4134b 100644 --- a/packages/core/addon/options.ts +++ b/packages/core/addon/options.ts @@ -23,7 +23,7 @@ export type SelectQuestion = { options: Array<{ value: string; label?: string; hint?: string }>; }; -export type MultiSelectQuestion = { +export type MultiSelectQuestion = { type: 'multiselect'; default: Value; options: Array<{ value: string; label?: string; hint?: string }>; @@ -54,6 +54,6 @@ export type OptionValues = { : Args[K] extends SelectQuestion ? Value : Args[K] extends MultiSelectQuestion - ? Value // as the type of the Value should already be an array! + ? Value // as the type of Value should already be an array (default: [] or default: ['foo', 'bar']) : never; }; From bb624c421cf1114ca14000e6012c182ba40ad977 Mon Sep 17 00:00:00 2001 From: Manuel <30698007+manuel3108@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:15:28 +0200 Subject: [PATCH 11/11] Update .changeset/early-trees-go.md --- .changeset/early-trees-go.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/early-trees-go.md b/.changeset/early-trees-go.md index be44e2227..9001a55d1 100644 --- a/.changeset/early-trees-go.md +++ b/.changeset/early-trees-go.md @@ -2,4 +2,4 @@ 'sv': patch --- -internal(core): change `defineAddonOptions({ /*config */ })` to `defineAddonOptions().add('key', { /*config */ }).build()` in order to provide better type safety. +chore(core): change `defineAddonOptions({ /*config */ })` to `defineAddonOptions().add('key', { /*config */ }).build()` in order to provide better type safety.