sv 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { $ as pipe, A as filePaths, B as updateAgent, C as templates, D as buildAndLogArgs, E as PackageJSONSchema, F as isNodeError, G as version, H as updateReadme, I as normalizePosix, J as literal, K as array, L as parseAddonOptions, M as getHelpSections, N as getPadding, O as cliOptions, P as helpConfig, Q as picklist, R as runAndValidateVerifications, S as create$1, T as getSharedFiles, U as UnsupportedError, V as updateLibraryBuild, W as name, X as optional, Y as nonEmpty, Z as parse$1, _t as we, a as setupAddons, at as union, b as exec, c as AGENT_NAMES, ct as Option, d as installDependencies, dt as R, et as record, f as installOption, ft as Re, g as createLoadedAddon, gt as vt, h as from, ht as pe, i as prepareSvApi, it as transform, j as forwardExitCode, k as errorAndExit, l as addAllowBuildsIfPnpm, lt as Ce, m as up$1, mt as le, n as applyAddons, nt as strictObject, o as createWorkspace, ot as program, p as packageManagerPrompt, pt as ge, q as boolean, r as orderAddons, rt as string, s as findWorkspaceRoot, st as Command, tt as safeParse, u as detectPackageManager, ut as Ee, vt as R$1, w as dist, x as execSync, y as getErrorHint, z as runCommand } from "./engine-LhRAh1ZA.mjs";
3
- import { n as officialAddons$1, t as getAddonDetails } from "./addons-BvT-iII-.mjs";
2
+ import { $ as parse$1, A as cliOptions, B as runAndValidateVerifications, C as execSync, D as getSharedFiles, E as dist, F as getPadding, G as UnsupportedError, H as updateAgent, I as helpConfig, J as array, K as name, L as isNodeError, M as filePaths, N as forwardExitCode, O as PackageJSONSchema, P as getHelpSections, Q as optional, R as normalizePosix, S as exec, T as templates, U as updateLibraryBuild, V as runCommand, W as updateReadme, X as literal, Y as boolean, Z as nonEmpty, _ as createLoadedAddon, _t as pe, a as setupAddons, at as string, b as getErrorHint, bt as R$1, c as AGENT_NAMES, ct as program, d as installDependencies, dt as Ce, et as picklist, f as installOption, ft as Ee, g as from, gt as le, h as up$2, ht as ge, i as prepareSvApi, it as strictObject, j as errorAndExit, k as buildAndLogArgs, l as addAllowBuildsIfPnpm, lt as Command, m as up$1, mt as Re, n as applyAddons, nt as record, o as createWorkspace, ot as transform, p as packageManagerPrompt, pt as R, q as version, r as orderAddons, rt as safeParse, s as findWorkspaceRoot, st as union, tt as pipe, u as detectPackageManager, ut as Option, vt, w as create$1, x as NonZeroExitError, yt as we, z as parseAddonOptions } from "./engine-DB9amJxd.mjs";
3
+ import { n as officialAddons$1, t as getAddonDetails } from "./addons-OcP_HeQ_.mjs";
4
4
  import * as fs$1 from "node:fs";
5
5
  import fs from "node:fs";
6
6
  import * as path$1 from "node:path";
@@ -1218,22 +1218,16 @@ function isPrettierInstalled(cwd) {
1218
1218
  /** Nearest dir from `cwd` up to the workspace root with a `format` or `fmt` package.json script. */
1219
1219
  function findFormatScript(cwd) {
1220
1220
  const workspaceRoot = findWorkspaceRoot(cwd);
1221
- let directory = path.resolve(cwd);
1222
- const { root } = path.parse(directory);
1223
- while (directory && directory.length >= workspaceRoot.length) {
1224
- if (fs.existsSync(path.join(directory, "package.json"))) {
1225
- const { data } = loadPackageJson(directory);
1226
- if (data.scripts?.format) return {
1227
- dir: directory,
1228
- name: "format"
1229
- };
1230
- if (data.scripts?.fmt) return {
1231
- dir: directory,
1232
- name: "fmt"
1233
- };
1234
- }
1235
- if (directory === root) break;
1236
- directory = path.dirname(directory);
1221
+ for (const directory of up$2(path.resolve(cwd), { last: workspaceRoot })) if (fs.existsSync(path.join(directory, "package.json"))) {
1222
+ const { data } = loadPackageJson(directory);
1223
+ if (data.scripts?.format) return {
1224
+ dir: directory,
1225
+ name: "format"
1226
+ };
1227
+ if (data.scripts?.fmt) return {
1228
+ dir: directory,
1229
+ name: "fmt"
1230
+ };
1237
1231
  }
1238
1232
  }
1239
1233
  async function withSpinner(startMsg, task) {
@@ -1258,12 +1252,16 @@ async function run(command, args, cwd) {
1258
1252
  });
1259
1253
  return {};
1260
1254
  } catch (e) {
1261
- if (e?.code === "ENOENT") return {
1255
+ if (e instanceof NonZeroExitError) {
1256
+ const { stdout, stderr } = e.output ?? {};
1257
+ return { error: [stderr, stdout].filter(Boolean).join("\n").trim() };
1258
+ }
1259
+ if (!isNodeError(e)) return { error: "unknown error" };
1260
+ if (e.code === "ENOENT") return {
1262
1261
  notFound: true,
1263
1262
  error: `${command} not found`
1264
1263
  };
1265
- const output = e?.output;
1266
- return { error: [output?.stderr, output?.stdout].filter(Boolean).join("\n").trim() || (e instanceof Error ? e.message : "unknown error") };
1264
+ return { error: e.message };
1267
1265
  }
1268
1266
  }
1269
1267
  //#endregion
@@ -1519,6 +1517,32 @@ async function resolveNonOfficialAddons(refs, downloadCheck) {
1519
1517
  }
1520
1518
  return selectedAddons;
1521
1519
  }
1520
+ /**
1521
+ * Walks the `dependsOn` graph and returns the first cycle found as a path
1522
+ * (e.g. `['a', 'b', 'a']`), or `undefined` when the graph is acyclic.
1523
+ */
1524
+ function findDependencyCycle(addonIds, setupResults) {
1525
+ const known = new Set(addonIds);
1526
+ const settled = /* @__PURE__ */ new Set();
1527
+ const path = [];
1528
+ const visit = (id) => {
1529
+ if (settled.has(id)) return;
1530
+ const cycleStart = path.indexOf(id);
1531
+ if (cycleStart !== -1) return [...path.slice(cycleStart), id];
1532
+ path.push(id);
1533
+ for (const depId of setupResults[id]?.dependsOn ?? []) {
1534
+ if (!known.has(depId)) continue;
1535
+ const cycle = visit(depId);
1536
+ if (cycle) return cycle;
1537
+ }
1538
+ path.pop();
1539
+ settled.add(id);
1540
+ };
1541
+ for (const id of addonIds) {
1542
+ const cycle = visit(id);
1543
+ if (cycle) return cycle;
1544
+ }
1545
+ }
1522
1546
  async function promptAddonQuestions({ options, loadedAddons, workspace }) {
1523
1547
  const addons = [...loadedAddons];
1524
1548
  const emptyAnswersReducer = (acc, id) => {
@@ -1612,7 +1636,6 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
1612
1636
  const additionalSetupResults = await setupAddons(missingSetupResults, workspace);
1613
1637
  Object.assign(setupResults, additionalSetupResults);
1614
1638
  }
1615
- const dependencyChains = /* @__PURE__ */ new Map();
1616
1639
  let hasNewDependencies = true;
1617
1640
  while (hasNewDependencies) {
1618
1641
  hasNewDependencies = false;
@@ -1623,30 +1646,9 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
1623
1646
  if (!setupResult) errorAndExit(`Setup result missing for addon: ${addonId}`);
1624
1647
  const missingDependencies = setupResult.dependsOn.filter((depId) => !addons.some((a) => a.addon.id === depId));
1625
1648
  for (const depId of missingDependencies) {
1626
- const addonChain = dependencyChains.get(addonId) ?? /* @__PURE__ */ new Set();
1627
- if (addonChain.has(depId)) {
1628
- const cyclePath = [
1629
- ...addonChain,
1630
- addonId,
1631
- depId
1632
- ].join(" → ");
1633
- errorAndExit(`Circular dependency detected: ${cyclePath}\nAdd-ons cannot have circular dependencies.`);
1634
- }
1635
- const depChain = new Set(addonChain);
1636
- depChain.add(addonId);
1637
- dependencyChains.set(depId, depChain);
1649
+ if (!officialAddons.some((a) => a.id === depId)) errorAndExit(`'${color.addon(addonId)}' depends on '${color.addon(depId)}', which is not an official add-on.\nAdd it to the command explicitly.`);
1638
1650
  hasNewDependencies = true;
1639
- if (!addons.find((a) => a.addon.id === depId)) {
1640
- if (!officialAddons.find((a) => a.id === depId)) throw new Error(`'${addonId}' depends on an invalid add-on: '${depId}'`);
1641
- const officialAddonDetails = getAddonDetails(depId);
1642
- addons.push(createLoadedAddon(officialAddonDetails));
1643
- answers[depId] = {};
1644
- continue;
1645
- }
1646
- if (await le({ message: `The ${color.addon(addonId)} add-on requires ${color.addon(depId)} to also be setup. ${color.success("Include it?")}` }) !== true) {
1647
- ge("Operation cancelled.");
1648
- V.exit(1);
1649
- }
1651
+ addons.push(createLoadedAddon(getAddonDetails(depId)));
1650
1652
  answers[depId] = {};
1651
1653
  }
1652
1654
  }
@@ -1656,6 +1658,8 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
1656
1658
  Object.assign(setupResults, newSetupResults);
1657
1659
  }
1658
1660
  }
1661
+ const cycle = findDependencyCycle(addons.map((a) => a.addon.id), setupResults);
1662
+ if (cycle) errorAndExit(`Circular dependency detected: ${cycle.map((id) => color.addon(id)).join(" → ")}\nAdd-ons cannot have circular dependencies.`);
1659
1663
  const addonDefinitions = addons.map((a) => a.addon);
1660
1664
  const verifications = [...verifyCleanWorkingDirectory(options.cwd, options.gitCheck), ...verifyUnsupportedAddons(addonDefinitions, setupResults)];
1661
1665
  await runAndValidateVerifications(verifications);
@@ -3003,21 +3007,9 @@ const migrationTasks = [
3003
3007
  },
3004
3008
  {
3005
3009
  title: "Move adapter-node `ORIGIN` to `paths.origin`",
3006
- checks: [{
3007
- include: [
3008
- "Dockerfile*",
3009
- "**/Dockerfile*",
3010
- "**/*.{js,ts,mjs,mts,json,jsonc,yml,yaml,toml}"
3011
- ],
3012
- patterns: [
3013
- /\bprocess\.env\.ORIGIN\b/,
3014
- /\benv\s*\[\s*['"]ORIGIN['"]\s*\]/,
3015
- /^\s*(?:ENV|ARG)\s+ORIGIN\b/m,
3016
- /^\s*ORIGIN\s*[:=]/m
3017
- ]
3018
- }],
3010
+ dependency: "@sveltejs/adapter-node",
3019
3011
  summary: "The adapter-node `ORIGIN` environment variable is removed.",
3020
- instructions: "If this value configures adapter-node, move the public-facing origin to `paths.origin` in `sveltekit(...)`. Remove the environment variable only after confirming it has no unrelated use.",
3012
+ instructions: "Find where `ORIGIN` is set (`.env` files, scripts, hosting dashboard, ...) and move the public-facing origin to `paths.origin` in `sveltekit(...)`. Do not delete `ORIGIN`: it is ignored by adapter-node now and may have other uses.",
3021
3013
  links: guideLink("Adapters-adapter-node")
3022
3014
  },
3023
3015
  {
@@ -3098,9 +3090,10 @@ const migrationTasks = [
3098
3090
  var collect_migration_instructions_default = defineMigrationTask({
3099
3091
  id: "collect-migration-instructions",
3100
3092
  description: `Collect instructions for non-automated migration tasks; creates ${REPORT_PATH} whose instructions you should follow to complete the migration`,
3101
- run: ({ sv }) => {
3093
+ run: ({ sv, dependencyVersion }) => {
3102
3094
  const findings = [];
3103
3095
  for (const task of migrationTasks) {
3096
+ if (task.dependency && !dependencyVersion(task.dependency)) continue;
3104
3097
  const files = /* @__PURE__ */ new Set();
3105
3098
  if (!task.checks) findings.push({
3106
3099
  task,
@@ -3879,7 +3872,7 @@ var lib_alias_default = defineMigrationTask({
3879
3872
  const KIT3_MIN_VERSIONS = {
3880
3873
  vite: "^8.0.12",
3881
3874
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
3882
- svelte: "^5.56.4",
3875
+ svelte: "^5.57.1",
3883
3876
  "@sveltejs/package": "^2.5.8",
3884
3877
  "svelte-check": "^4.7.5",
3885
3878
  typescript: "^6.0.0"
@@ -3962,6 +3955,7 @@ function migrateMatcher(cwd, file, target, context) {
3962
3955
  const importDeclarations = [];
3963
3956
  const body = [];
3964
3957
  const paramMatcherTypes = /* @__PURE__ */ new Set(["ParamMatcher"]);
3958
+ const typescript = target.endsWith(".ts");
3965
3959
  let declaresMatch = false;
3966
3960
  let exportsMatch = false;
3967
3961
  for (const statement of parsed.ast.body) {
@@ -3984,7 +3978,7 @@ function migrateMatcher(cwd, file, target, context) {
3984
3978
  if (statement.type === "ExportAllDeclaration") throw new Error(`Cannot migrate '${file}' because it re-exports another module`);
3985
3979
  if (statement.type === "ExportNamedDeclaration") {
3986
3980
  if (statement.declaration) {
3987
- stripParamMatcherType(statement.declaration, paramMatcherTypes);
3981
+ stripParamMatcherType(statement.declaration, paramMatcherTypes, typescript);
3988
3982
  if (declares(statement.declaration, "match")) {
3989
3983
  declaresMatch = true;
3990
3984
  exportsMatch = true;
@@ -3993,7 +3987,7 @@ function migrateMatcher(cwd, file, target, context) {
3993
3987
  } else exportsMatch ||= statement.specifiers.some((specifier) => specifier.exported.type === "Identifier" && specifier.exported.name === "match");
3994
3988
  continue;
3995
3989
  }
3996
- stripParamMatcherType(statement, paramMatcherTypes);
3990
+ stripParamMatcherType(statement, paramMatcherTypes, typescript);
3997
3991
  declaresMatch ||= declares(statement, "match");
3998
3992
  body.push(statement);
3999
3993
  }
@@ -4092,13 +4086,27 @@ function generateImportCode(context) {
4092
4086
  for (const source of context.sideEffectImports) lines.push(`import ${quoteString(source)};`);
4093
4087
  return lines;
4094
4088
  }
4095
- function stripParamMatcherType(node, paramMatcherTypes) {
4089
+ function stripParamMatcherType(node, paramMatcherTypes, typescript) {
4090
+ if (node.type === "FunctionDeclaration") {
4091
+ if (typescript && node.id?.name === "match") annotateParam(node);
4092
+ return;
4093
+ }
4096
4094
  if (node.type !== "VariableDeclaration") return;
4097
4095
  const declaration = node.declarations.find((declaration) => declaration.id.type === "Identifier" && declaration.id.name === "match");
4098
4096
  if (!declaration || declaration.id.type !== "Identifier") return;
4099
4097
  const annotation = declaration.id.typeAnnotation?.typeAnnotation;
4100
4098
  if (annotation?.type === "TSTypeReference" && annotation.typeName.type === "Identifier" && paramMatcherTypes.has(annotation.typeName.name)) delete declaration.id.typeAnnotation;
4101
4099
  if ((declaration.init?.type === "TSSatisfiesExpression" || declaration.init?.type === "TSAsExpression") && declaration.init.typeAnnotation.type === "TSTypeReference" && declaration.init.typeAnnotation.typeName.type === "Identifier" && paramMatcherTypes.has(declaration.init.typeAnnotation.typeName.name)) declaration.init = declaration.init.expression;
4100
+ if (typescript) annotateParam(declaration.init);
4101
+ }
4102
+ function annotateParam(init) {
4103
+ if (init?.type !== "ArrowFunctionExpression" && init?.type !== "FunctionExpression" && init?.type !== "FunctionDeclaration") return;
4104
+ const [param] = init.params;
4105
+ if (param?.type !== "Identifier" || param.typeAnnotation) return;
4106
+ param.typeAnnotation = {
4107
+ type: "TSTypeAnnotation",
4108
+ typeAnnotation: { type: "TSStringKeyword" }
4109
+ };
4102
4110
  }
4103
4111
  function stripParamMatcherComments(content) {
4104
4112
  return content.replace(/\/\*\*[\s\S]*?\*\//g, (comment) => {
@@ -4800,6 +4808,20 @@ const INHERITED_OPTIONS = {
4800
4808
  target: "esnext",
4801
4809
  verbatimModuleSyntax: true
4802
4810
  };
4811
+ /**
4812
+ * Root entries type-checked by the Kit 2 generated config (vite config, `test`, `tests`), plus the
4813
+ * configs `sv create` adds to `include`. Only the ones present in the project are kept.
4814
+ */
4815
+ const ROOT_INCLUDES = [
4816
+ "vite.config.ts",
4817
+ "vite.config.js",
4818
+ "drizzle.config.ts",
4819
+ "drizzle.config.js",
4820
+ "playwright.config.ts",
4821
+ "playwright.config.js",
4822
+ "test",
4823
+ "tests"
4824
+ ];
4803
4825
  var tsconfig_default = defineMigrationTask({
4804
4826
  id: "tsconfig",
4805
4827
  description: "Extend the generated $app/tsconfig instead of .svelte-kit/tsconfig.json",
@@ -4814,11 +4836,11 @@ var tsconfig_default = defineMigrationTask({
4814
4836
  extended[index] = PARENT_CONFIG;
4815
4837
  data.extends = Array.isArray(data.extends) ? extended : PARENT_CONFIG;
4816
4838
  }
4817
- const include = data.include ??= ["src"];
4839
+ const include = data.include ??= ["src", ...ROOT_INCLUDES.filter((entry) => fs.existsSync(path.join(cwd, entry)))];
4818
4840
  include.push(...moved.filter((entry) => !include.includes(entry)));
4819
4841
  if (data.compilerOptions?.types) {
4820
4842
  const types = data.compilerOptions.types;
4821
- if (!types.includes("$app/types")) types.push("$app/types");
4843
+ if (!types.includes("$app/types")) types.unshift("$app/types");
4822
4844
  }
4823
4845
  for (const [key, value] of Object.entries(data.compilerOptions ?? {})) if (INHERITED_OPTIONS[key] === value) delete data.compilerOptions[key];
4824
4846
  }));
@@ -1,22 +1,26 @@
1
1
  import { AgentName } from "@sveltejs/sv-utils";
2
- //#region src/addons/index.d.ts
3
- type OfficialAddons = {
4
- prettier: Addon<any>;
5
- eslint: Addon<any>;
6
- vitest: Addon<any>;
7
- playwright: Addon<any>;
8
- tailwindcss: Addon<any>;
9
- enhancedImg: Addon<any>;
10
- sveltekitAdapter: Addon<any>;
11
- drizzle: Addon<any>;
12
- betterAuth: Addon<any>;
13
- mdsvex: Addon<any>;
14
- paraglide: Addon<any>;
15
- storybook: Addon<any>;
16
- aiTools: Addon<any>;
17
- experimental: Addon<any>;
2
+ //#region src/addons/ids.d.ts
3
+ /**
4
+ * Official add-on ids, declared without importing the add-ons themselves so that
5
+ * add-ons can reference each other in `dependsOn`/`runsAfter` without import cycles.
6
+ */
7
+ declare const ADDON_IDS: {
8
+ readonly prettier: "prettier";
9
+ readonly eslint: "eslint";
10
+ readonly vitest: "vitest";
11
+ readonly playwright: "playwright";
12
+ readonly tailwindcss: "tailwindcss";
13
+ readonly enhancedImg: "enhanced-img";
14
+ readonly sveltekitAdapter: "sveltekit-adapter";
15
+ readonly drizzle: "drizzle";
16
+ readonly betterAuth: "better-auth";
17
+ readonly mdsvex: "mdsvex";
18
+ readonly paraglide: "paraglide";
19
+ readonly storybook: "storybook";
20
+ readonly aiTools: "ai-tools";
21
+ readonly experimental: "experimental";
18
22
  };
19
- declare const officialAddons: OfficialAddons;
23
+ type OfficialAddonId = (typeof ADDON_IDS)[keyof typeof ADDON_IDS];
20
24
  //#endregion
21
25
  //#region src/core/options.d.ts
22
26
  type BooleanQuestion = {
@@ -65,6 +69,12 @@ type BaseQuestion<Args extends OptionDefinition> = {
65
69
  };
66
70
  type Question<Args extends OptionDefinition = OptionDefinition> = BaseQuestion<Args> & (BooleanQuestion | StringQuestion | NumberQuestion | SelectQuestion<any> | MultiSelectQuestion<any>);
67
71
  type OptionDefinition = Record<string, Question<any>>;
72
+ type QuestionFor<Value> = BaseQuestion<any> & ([Value] extends [boolean] ? BooleanQuestion : [Value] extends [number] ? NumberQuestion : [Value] extends [Array<infer Item>] ? MultiSelectQuestion<Item> : [string] extends [Value] ? StringQuestion : SelectQuestion<Value>);
73
+ /** The questions of an add-on, described by the values they produce. See `defineAddonOptions<Values>()`. */
74
+ type AddonOptions<Values extends Record<string, unknown>> = { [K in keyof Values]: QuestionFor<Values[K]>; } extends (infer Questions extends OptionDefinition) ? Questions : never;
75
+ type SameType<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;
76
+ /** Keys whose question is missing, extra, or produces a different value than declared. */
77
+ type MismatchedOptions<Args extends OptionDefinition, Values> = { [K in keyof Args | keyof Values]: K extends keyof Args ? K extends keyof Values ? SameType<OptionValues<Args>[K], Values[K]> extends true ? never : K : K : K; }[keyof Args | keyof Values];
68
78
  type OptionValues<Args extends OptionDefinition> = { [K in keyof Args]: Args[K] extends StringQuestion ? string : Args[K] extends BooleanQuestion ? boolean : Args[K] extends NumberQuestion ? number : Args[K] extends SelectQuestion<infer Value> ? Value : Args[K] extends MultiSelectQuestion<infer Value> ? Value[] : Args[K] extends Question<any> ? unknown : "ERROR: The value for this type is invalid. Ensure that the `default` value exists in `options`."; };
69
79
  //#endregion
70
80
  //#region src/core/workspace.d.ts
@@ -151,6 +161,7 @@ type SvApi = {
151
161
  where?: (content: string) => boolean;
152
162
  }, edit: FileEditMultiple) => void;
153
163
  };
164
+ type AddonId = OfficialAddonId | (string & {});
154
165
  type Addon<Args extends OptionDefinition, Id extends string = string, Setup extends Record<string, unknown> = Record<string, unknown>> = {
155
166
  id: Id;
156
167
  alias?: string;
@@ -163,16 +174,16 @@ type Addon<Args extends OptionDefinition, Id extends string = string, Setup exte
163
174
  options: Args;
164
175
  /** Setup the addon. Will be called before the addon is run. */
165
176
  setup?: (workspace: Workspace & {
166
- /** On what official addons does this addon depend on? */
167
- dependsOn: (name: keyof typeof officialAddons) => void;
177
+ /** Required add-ons. Implies `runsAfter`. Official ids are installed automatically, others must be part of the same run. */
178
+ dependsOn: (id: AddonId) => void;
168
179
  /**
169
180
  * Why is this addon not supported?
170
181
  * @example
171
182
  * if (!isKit) unsupported('Requires SvelteKit');
172
183
  */
173
184
  unsupported: (reason: string) => void;
174
- /** On what official addons does this addon run after? */
175
- runsAfter: (name: keyof typeof officialAddons) => void;
185
+ /** Ordering only: run after these add-ons when they are part of the same run. */
186
+ runsAfter: (id: AddonId) => void;
176
187
  /** Dynamically add an option to be prompted to the user */
177
188
  addOption: <K extends Extract<keyof Setup, string>>(key: K, question: SetupOptions<Setup>[K]) => void;
178
189
  }) => MaybePromise<void>;
@@ -281,7 +292,7 @@ type SetupResult = {
281
292
  type AddonDefinition<Id extends string = string> = Addon<Record<string, Question<any>>, Id>;
282
293
  type MaybePromise<T> = Promise<T> | T;
283
294
  type Prettify<T> = { [K in keyof T]: T[K]; } & unknown;
284
- type OptionBuilder<T extends OptionDefinition> = {
295
+ type OptionBuilder<T extends OptionDefinition, Values extends Record<string, unknown> = never> = {
285
296
  /**
286
297
  * This type is a bit complex, but in usage, it's quite simple!
287
298
  *
@@ -296,9 +307,9 @@ type OptionBuilder<T extends OptionDefinition> = {
296
307
  * })
297
308
  * ```
298
309
  */
299
- add<K extends string, const Q extends Question<T & Record<K, Q>>>(key: K, question: Q): OptionBuilder<T & Record<K, Q>>;
310
+ add<K extends string, const Q extends Question<T & Record<K, Q>>>(key: K, question: Q): OptionBuilder<T & Record<K, Q>, Values>;
300
311
  /** Finalize all options of your `add-on`. */
301
- build(): Prettify<T>;
312
+ build(...check: [Values] extends [never] ? [] : [MismatchedOptions<T, Values>] extends [never] ? [] : [mismatchedOptions: MismatchedOptions<T, Values>]): [Values] extends [never] ? Prettify<T> : AddonOptions<Values>;
302
313
  };
303
314
  /**
304
315
  * Options for an addon.
@@ -319,8 +330,15 @@ type OptionBuilder<T extends OptionDefinition> = {
319
330
  * ```sh
320
331
  * npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
321
332
  * ```
333
+ *
334
+ * Pass the values your options produce to get a small, stable type to export, checked both ways
335
+ * against the questions:
336
+ * ```ts
337
+ * export type MyAddonOptions = { demo: boolean };
338
+ * const options = defineAddonOptions<MyAddonOptions>().add('demo', { ... }).build();
339
+ * ```
322
340
  */
323
- declare function defineAddonOptions(): OptionBuilder<{}>;
341
+ declare function defineAddonOptions<Values extends Record<string, unknown> = never>(): OptionBuilder<{}, Values>;
324
342
  //#endregion
325
343
  //#region src/core/engine.d.ts
326
344
  type InstallOptions<Addons extends AddonMap> = {
@@ -347,4 +365,4 @@ declare function applyAddons({ loadedAddons, workspace, setupResults, options }:
347
365
  installNeeded: boolean;
348
366
  }>;
349
367
  //#endregion
350
- export { StringQuestion as A, BooleanQuestion as C, OptionValues as D, OptionDefinition as E, Question as O, BaseQuestion as S, NumberQuestion as T, SvApi as _, Addon as a, Workspace as b, AddonReference as c, ConditionDefinition as d, ConfiguredAddon as f, SetupResult as g, PreparedAddon as h, add as i, officialAddons as j, SelectQuestion as k, AddonResult as l, OptionBuilder as m, InstallOptions as n, AddonDefinition as o, LoadedAddon as p, OptionMap as r, AddonInput as s, AddonMap as t, AddonSource as u, defineAddon as v, MultiSelectQuestion as w, WorkspaceOptions as x, defineAddonOptions as y };
368
+ export { SelectQuestion as A, BaseQuestion as C, OptionDefinition as D, NumberQuestion as E, ADDON_IDS as M, OptionValues as O, AddonOptions as S, MultiSelectQuestion as T, SvApi as _, Addon as a, Workspace as b, AddonReference as c, ConditionDefinition as d, ConfiguredAddon as f, SetupResult as g, PreparedAddon as h, add as i, StringQuestion as j, Question as k, AddonResult as l, OptionBuilder as m, InstallOptions as n, AddonDefinition as o, LoadedAddon as p, OptionMap as r, AddonInput as s, AddonMap as t, AddonSource as u, defineAddon as v, BooleanQuestion as w, WorkspaceOptions as x, defineAddonOptions as y };
@@ -5020,7 +5020,7 @@ function safeParse(schema, input, config$1) {
5020
5020
  //#endregion
5021
5021
  //#region package.json
5022
5022
  var name = "sv";
5023
- var version = "1.0.1";
5023
+ var version = "1.1.0";
5024
5024
  //#endregion
5025
5025
  //#region src/core/errors.ts
5026
5026
  var UnsupportedError = class extends Error {
@@ -5890,6 +5890,13 @@ function createLoadedAddon(addon) {
5890
5890
  * ```sh
5891
5891
  * npx sv add <addon>=<option1>:<value1>+<option2>:<value2>
5892
5892
  * ```
5893
+ *
5894
+ * Pass the values your options produce to get a small, stable type to export, checked both ways
5895
+ * against the questions:
5896
+ * ```ts
5897
+ * export type MyAddonOptions = { demo: boolean };
5898
+ * const options = defineAddonOptions<MyAddonOptions>().add('demo', { ... }).build();
5899
+ * ```
5893
5900
  */
5894
5901
  function defineAddonOptions() {
5895
5902
  return createOptionBuilder({});
@@ -6089,20 +6096,14 @@ async function createWorkspace({ cwd, packageManager, override }) {
6089
6096
  let dependencies = {};
6090
6097
  if (override?.dependencies) dependencies = override.dependencies;
6091
6098
  else {
6092
- let directory = resolvedCwd;
6093
- const workspaceRoot = findWorkspaceRoot(directory);
6094
- const { root } = path.parse(directory);
6095
- while (directory && directory.length >= workspaceRoot.length) {
6096
- if (fs.existsSync(path.join(directory, filePaths.packageJson))) {
6097
- const { data: packageJson } = loadPackageJson(directory);
6098
- dependencies = {
6099
- ...packageJson.devDependencies,
6100
- ...packageJson.dependencies,
6101
- ...dependencies
6102
- };
6103
- }
6104
- if (root === directory) break;
6105
- directory = path.dirname(directory);
6099
+ const workspaceRoot = findWorkspaceRoot(resolvedCwd);
6100
+ for (const directory of up$1(resolvedCwd, { last: workspaceRoot })) if (fs.existsSync(path.join(directory, filePaths.packageJson))) {
6101
+ const { data: packageJson } = loadPackageJson(directory);
6102
+ dependencies = {
6103
+ ...packageJson.devDependencies,
6104
+ ...packageJson.dependencies,
6105
+ ...dependencies
6106
+ };
6106
6107
  }
6107
6108
  }
6108
6109
  for (const [key, value] of Object.entries(dependencies)) try {
@@ -6147,9 +6148,7 @@ async function createWorkspace({ cwd, packageManager, override }) {
6147
6148
  };
6148
6149
  }
6149
6150
  function findWorkspaceRoot(cwd) {
6150
- const { root } = path.parse(cwd);
6151
- let directory = cwd;
6152
- while (directory && directory !== root) {
6151
+ for (const directory of up$1(cwd)) {
6153
6152
  if (fs.existsSync(path.join(directory, filePaths.packageJson))) {
6154
6153
  if (fs.existsSync(path.join(directory, "pnpm-workspace.yaml"))) return directory;
6155
6154
  const { data } = loadPackageJson(directory);
@@ -6157,7 +6156,6 @@ function findWorkspaceRoot(cwd) {
6157
6156
  }
6158
6157
  const parent = path.dirname(directory);
6159
6158
  if (directory.includes(".test-output") && !parent.includes(".test-output")) break;
6160
- directory = parent;
6161
6159
  }
6162
6160
  return cwd;
6163
6161
  }
@@ -6475,4 +6473,4 @@ function orderAddons(addons, setupResults) {
6475
6473
  return ordered;
6476
6474
  }
6477
6475
  //#endregion
6478
- export { pipe as $, filePaths as A, updateAgent as B, templates as C, buildAndLogArgs as D, PackageJSONSchema as E, isNodeError as F, version as G, updateReadme as H, normalizePosix as I, literal as J, array as K, parseAddonOptions as L, getHelpSections as M, getPadding as N, cliOptions as O, helpConfig as P, picklist as Q, runAndValidateVerifications as R, create as S, getSharedFiles as T, UnsupportedError as U, updateLibraryBuild as V, name as W, optional as X, nonEmpty as Y, parse$1 as Z, defineAddon as _, we as _t, setupAddons as a, union as at, exec as b, __require as bt, AGENT_NAMES as c, Option as ct, installDependencies as d, R as dt, record as et, installOption as f, Re as ft, createLoadedAddon as g, vt as gt, from as h, pe as ht, prepareSvApi as i, transform as it, forwardExitCode as j, errorAndExit as k, addAllowBuildsIfPnpm as l, Ce as lt, up as m, le as mt, applyAddons as n, strictObject as nt, createWorkspace as o, program as ot, packageManagerPrompt as p, ge as pt, boolean as q, orderAddons as r, string as rt, findWorkspaceRoot as s, Command as st, add as t, safeParse as tt, detectPackageManager as u, Ee as ut, defineAddonOptions as v, R$1 as vt, dist as w, execSync as x, __toESM as xt, getErrorHint as y, __commonJSMin as yt, runCommand as z };
6476
+ export { parse$1 as $, cliOptions as A, runAndValidateVerifications as B, execSync as C, __toESM as Ct, getSharedFiles as D, dist as E, getPadding as F, UnsupportedError as G, updateAgent as H, helpConfig as I, array as J, name as K, isNodeError as L, filePaths as M, forwardExitCode as N, PackageJSONSchema as O, getHelpSections as P, optional as Q, normalizePosix as R, exec as S, __require as St, templates as T, updateLibraryBuild as U, runCommand as V, updateReadme as W, literal as X, boolean as Y, nonEmpty as Z, createLoadedAddon as _, pe as _t, setupAddons as a, string as at, getErrorHint as b, R$1 as bt, AGENT_NAMES as c, program as ct, installDependencies as d, Ce as dt, picklist as et, installOption as f, Ee as ft, from as g, le as gt, up$1 as h, ge as ht, prepareSvApi as i, strictObject as it, errorAndExit as j, buildAndLogArgs as k, addAllowBuildsIfPnpm as l, Command as lt, up as m, Re as mt, applyAddons as n, record as nt, createWorkspace as o, transform as ot, packageManagerPrompt as p, R as pt, version as q, orderAddons as r, safeParse as rt, findWorkspaceRoot as s, union as st, add as t, pipe as tt, detectPackageManager as u, Option as ut, defineAddon as v, vt, create as w, NonZeroExitError as x, __commonJSMin as xt, defineAddonOptions as y, we as yt, parseAddonOptions as z };
@@ -1,4 +1,4 @@
1
- import { A as StringQuestion, C as BooleanQuestion, D as OptionValues, E as OptionDefinition, O as Question, S as BaseQuestion, T as NumberQuestion, _ as SvApi, a as Addon, b as Workspace, c as AddonReference, d as ConditionDefinition, f as ConfiguredAddon, g as SetupResult, h as PreparedAddon, i as add, j as officialAddons, k as SelectQuestion, l as AddonResult, m as OptionBuilder, n as InstallOptions, o as AddonDefinition, p as LoadedAddon, r as OptionMap, s as AddonInput, t as AddonMap, u as AddonSource, v as defineAddon, w as MultiSelectQuestion, x as WorkspaceOptions, y as defineAddonOptions } from "../engine-D2x5kvrf.mjs";
1
+ import { A as SelectQuestion, C as BaseQuestion, D as OptionDefinition, E as NumberQuestion, M as ADDON_IDS, O as OptionValues, S as AddonOptions, T as MultiSelectQuestion, _ as SvApi, a as Addon, b as Workspace, c as AddonReference, d as ConditionDefinition, f as ConfiguredAddon, g as SetupResult, h as PreparedAddon, i as add, j as StringQuestion, k as Question, l as AddonResult, m as OptionBuilder, n as InstallOptions, o as AddonDefinition, p as LoadedAddon, r as OptionMap, s as AddonInput, t as AddonMap, u as AddonSource, v as defineAddon, w as BooleanQuestion, x as WorkspaceOptions, y as defineAddonOptions } from "../engine-BLZdMrao.mjs";
2
2
  //#region src/create/index.d.ts
3
3
  type TemplateType = (typeof templateTypes)[number];
4
4
  type LanguageType = (typeof languageTypes)[number];
@@ -12,6 +12,83 @@ type Options = {
12
12
  };
13
13
  export declare function create({ cwd, ...options }: Options): void;
14
14
  //#endregion
15
+ //#region src/addons/ai-tools.d.ts
16
+ type AiToolsOptions = {
17
+ ide: string[];
18
+ delivery: "plugin" | "tools";
19
+ tools: string[];
20
+ mcpSetup: "local" | "remote";
21
+ };
22
+ //#endregion
23
+ //#region src/addons/better-auth.d.ts
24
+ type BetterAuthOptions = {
25
+ demo: Array<"password" | "github">;
26
+ };
27
+ //#endregion
28
+ //#region src/addons/drizzle.d.ts
29
+ type Database = "mysql" | "postgresql" | "sqlite" | "d1";
30
+ type DrizzleOptions = {
31
+ database: Database;
32
+ postgresql: "postgres.js" | "neon";
33
+ mysql: "mysql2" | "planetscale";
34
+ sqlite: "node-sqlite" | "better-sqlite3" | "libsql" | "turso";
35
+ docker: boolean;
36
+ };
37
+ //#endregion
38
+ //#region src/addons/experimental.d.ts
39
+ type ExperimentalOptions = {
40
+ features: string[];
41
+ };
42
+ //#endregion
43
+ //#region src/addons/paraglide.d.ts
44
+ type ParaglideOptions = {
45
+ languageTags: string;
46
+ demo: boolean;
47
+ };
48
+ //#endregion
49
+ //#region src/addons/playwright.d.ts
50
+ type PlaywrightOptions = {
51
+ demo: boolean;
52
+ };
53
+ //#endregion
54
+ //#region src/addons/sveltekit-adapter.d.ts
55
+ type SveltekitAdapterOptions = {
56
+ adapter: "auto" | "node" | "static" | "vercel" | "cloudflare" | "netlify";
57
+ cfTarget: "workers" | "pages";
58
+ };
59
+ //#endregion
60
+ //#region src/addons/tailwindcss.d.ts
61
+ type TailwindcssOptions = {
62
+ plugins: Array<"typography" | "forms">;
63
+ };
64
+ //#endregion
65
+ //#region src/addons/vitest-addon.d.ts
66
+ type VitestOptions = {
67
+ usages: Array<"unit" | "component">;
68
+ };
69
+ //#endregion
70
+ //#region src/addons/index.d.ts
71
+ type NoOptions = {};
72
+ type OfficialAddons = {
73
+ [ADDON_IDS.prettier]: Addon<NoOptions, typeof ADDON_IDS.prettier>;
74
+ [ADDON_IDS.eslint]: Addon<NoOptions, typeof ADDON_IDS.eslint>;
75
+ [ADDON_IDS.vitest]: Addon<AddonOptions<VitestOptions>, typeof ADDON_IDS.vitest>;
76
+ [ADDON_IDS.playwright]: Addon<AddonOptions<PlaywrightOptions>, typeof ADDON_IDS.playwright>;
77
+ [ADDON_IDS.tailwindcss]: Addon<AddonOptions<TailwindcssOptions>, typeof ADDON_IDS.tailwindcss>;
78
+ [ADDON_IDS.enhancedImg]: Addon<NoOptions, typeof ADDON_IDS.enhancedImg>;
79
+ [ADDON_IDS.sveltekitAdapter]: Addon<AddonOptions<SveltekitAdapterOptions>, typeof ADDON_IDS.sveltekitAdapter>;
80
+ [ADDON_IDS.drizzle]: Addon<AddonOptions<DrizzleOptions>, typeof ADDON_IDS.drizzle>;
81
+ [ADDON_IDS.betterAuth]: Addon<AddonOptions<BetterAuthOptions>, typeof ADDON_IDS.betterAuth>;
82
+ [ADDON_IDS.mdsvex]: Addon<NoOptions, typeof ADDON_IDS.mdsvex>;
83
+ [ADDON_IDS.paraglide]: Addon<AddonOptions<ParaglideOptions>, typeof ADDON_IDS.paraglide>;
84
+ [ADDON_IDS.storybook]: Addon<NoOptions, typeof ADDON_IDS.storybook>;
85
+ [ADDON_IDS.aiTools]: Addon<AddonOptions<AiToolsOptions>, typeof ADDON_IDS.aiTools>;
86
+ [ADDON_IDS.experimental]: Addon<AddonOptions<ExperimentalOptions>, typeof ADDON_IDS.experimental>;
87
+ };
88
+ /** What `add()` accepts as `options` for official add-ons, keyed by add-on id. */
89
+ type OfficialAddonOptions = OptionMap<OfficialAddons>;
90
+ export declare const officialAddons: OfficialAddons;
91
+ //#endregion
15
92
  //#region src/core/processors.d.ts
16
93
  type FileEditor = Workspace & {
17
94
  content: string;
@@ -22,4 +99,4 @@ type FileType = {
22
99
  content: (editor: FileEditor) => string;
23
100
  };
24
101
  //#endregion
25
- export { type Addon, type AddonDefinition, type AddonInput, type AddonMap, type AddonReference, type AddonResult, type AddonSource, type BaseQuestion, type BooleanQuestion, type ConfiguredAddon, type FileEditor, type FileType, type InstallOptions, type LanguageType, type LoadedAddon, type MultiSelectQuestion, type NumberQuestion, type OptionBuilder, type OptionDefinition, type OptionMap, type OptionValues, type PreparedAddon, type Question, type SelectQuestion, type SetupResult, type StringQuestion, type SvApi, type TemplateType, type Workspace, type WorkspaceOptions, add, defineAddon, defineAddonOptions, officialAddons };
102
+ export { type Addon, type AddonDefinition, type AddonInput, type AddonMap, type AddonOptions, type AddonReference, type AddonResult, type AddonSource, type BaseQuestion, type BooleanQuestion, type ConfiguredAddon, type FileEditor, type FileType, type InstallOptions, type LanguageType, type LoadedAddon, type MultiSelectQuestion, type NumberQuestion, type OfficialAddonOptions, type OfficialAddons, type OptionBuilder, type OptionDefinition, type OptionMap, type OptionValues, type PreparedAddon, type Question, type SelectQuestion, type SetupResult, type StringQuestion, type SvApi, type TemplateType, type Workspace, type WorkspaceOptions, add, defineAddon, defineAddonOptions };
@@ -1,3 +1,3 @@
1
- import { S as create, _ as defineAddon, t as add, v as defineAddonOptions } from "../engine-LhRAh1ZA.mjs";
2
- import { n as officialAddons } from "../addons-BvT-iII-.mjs";
1
+ import { t as add, v as defineAddon, w as create, y as defineAddonOptions } from "../engine-DB9amJxd.mjs";
2
+ import { n as officialAddons } from "../addons-OcP_HeQ_.mjs";
3
3
  export { add, create, defineAddon, defineAddonOptions, officialAddons };
@@ -1,4 +1,4 @@
1
- import { r as OptionMap, t as AddonMap } from "../engine-D2x5kvrf.mjs";
1
+ import { r as OptionMap, t as AddonMap } from "../engine-BLZdMrao.mjs";
2
2
  import { Page } from "@playwright/test";
3
3
  import { TestProject } from "vitest/node";
4
4
  //#region src/testing.d.ts
@@ -1,4 +1,4 @@
1
- import { S as create, b as exec, bt as __require, l as addAllowBuildsIfPnpm, t as add, x as execSync, xt as __toESM, yt as __commonJSMin } from "../engine-LhRAh1ZA.mjs";
1
+ import { C as execSync, Ct as __toESM, S as exec, St as __require, l as addAllowBuildsIfPnpm, t as add, w as create, xt as __commonJSMin } from "../engine-DB9amJxd.mjs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import V from "node:process";
@@ -22,11 +22,11 @@
22
22
  "access": "public"
23
23
  },
24
24
  "peerDependencies": {
25
- "sv": "^1.0.1"
25
+ "sv": "^1.1.0"
26
26
  },
27
27
  "devDependencies": {
28
- "sv": "^1.0.1",
29
- "@sveltejs/sv-utils": "^1.0.0",
28
+ "sv": "^1.1.0",
29
+ "@sveltejs/sv-utils": "^1.0.1",
30
30
  "@playwright/test": "^1.60.0",
31
31
  "@types/node": "^25.9.1",
32
32
  "tsdown": "^0.22.1",