sv 0.16.5 → 0.16.6

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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as name, a as detectPackageManager, d as Command, g as helpConfig, h as forwardExitCode, l as from, n as add, r as create, u as program, v as version } from "./engine-CCAozEpo.mjs";
2
+ import { _ as name, a as detectPackageManager, d as Command, g as helpConfig, h as forwardExitCode, l as from, n as add, r as create, u as program, v as version } from "./engine-DbEL5s1q.mjs";
3
3
  import { color, resolveCommandArray } from "@sveltejs/sv-utils";
4
4
  import V from "node:process";
5
5
  import { execSync } from "node:child_process";
@@ -65,7 +65,7 @@ type BaseQuestion<Args extends OptionDefinition> = {
65
65
  };
66
66
  type Question<Args extends OptionDefinition = OptionDefinition> = BaseQuestion<Args> & (BooleanQuestion | StringQuestion | NumberQuestion | SelectQuestion<any> | MultiSelectQuestion<any>);
67
67
  type OptionDefinition = Record<string, Question<any>>;
68
- 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[] : "ERROR: The value for this type is invalid. Ensure that the `default` value exists in `options`." };
68
+ 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
69
  //#endregion
70
70
  //#region src/core/workspace.d.ts
71
71
  type WorkspaceOptions<Args extends OptionDefinition> = OptionValues<Args>;
@@ -134,7 +134,7 @@ type SvApi = {
134
134
  */
135
135
  file: (path: string, edit: (content: string) => string | false) => void;
136
136
  };
137
- type Addon<Args extends OptionDefinition, Id extends string = string> = {
137
+ type Addon<Args extends OptionDefinition, Id extends string = string, Setup extends Record<string, unknown> = Record<string, unknown>> = {
138
138
  id: Id;
139
139
  alias?: string; /** one-liner shown in prompts */
140
140
  shortDescription?: string; /** link to docs/repo */
@@ -149,10 +149,11 @@ type Addon<Args extends OptionDefinition, Id extends string = string> = {
149
149
  * if (!isKit) unsupported('Requires SvelteKit');
150
150
  */
151
151
  unsupported: (reason: string) => void; /** On what official addons does this addon run after? */
152
- runsAfter: (name: keyof typeof officialAddons) => void;
152
+ runsAfter: (name: keyof typeof officialAddons) => void; /** Dynamically add an option to be prompted to the user */
153
+ addOption: <K extends Extract<keyof Setup, string>>(key: K, question: SetupOptions<Setup>[K]) => void;
153
154
  }) => MaybePromise<void>; /** Run the addon. The actual execution of the addon... Add files, edit files, etc. */
154
155
  run: (workspace: Workspace & {
155
- /** Add-on options */options: WorkspaceOptions<Args>; /** Api to interact with the workspace. */
156
+ /** Add-on options (includes dynamically added options from setup) */options: WorkspaceOptions<Args> & Record<string, unknown>; /** Api to interact with the workspace. */
156
157
  sv: SvApi;
157
158
  /** Cancel the addon at any time!
158
159
  * @example
@@ -161,13 +162,24 @@ type Addon<Args extends OptionDefinition, Id extends string = string> = {
161
162
  cancel: (reason: string) => void;
162
163
  }) => MaybePromise<void>; /** Next steps to display after the addon is run. */
163
164
  nextSteps?: (workspace: Workspace & {
164
- options: WorkspaceOptions<Args>;
165
+ options: WorkspaceOptions<Args> & Record<string, unknown>;
165
166
  }) => string[];
166
167
  };
168
+ /** Maps value types to question definitions for dynamic setup options */
169
+ type SetupOptions<T extends Record<string, unknown>> = { [K in keyof T]: BaseQuestion<any> & (T[K] extends boolean ? BooleanQuestion : T[K] extends string ? StringQuestion : T[K] extends number ? NumberQuestion : T[K] extends Array<infer V> ? MultiSelectQuestion<V> : Question<any>) };
167
170
  /**
168
171
  * The entry point for your addon, It will hold every thing! (options, setup, run, nextSteps, ...)
172
+ *
173
+ * For dynamic options added via `addOption` in setup, use the generic to get strong typing:
174
+ * ```ts
175
+ * const addon = defineAddon<{ extra: boolean }>()({ ... });
176
+ * addon.options.extra.default // boolean
177
+ * ```
169
178
  */
170
179
  declare function defineAddon<const Id extends string, Args extends OptionDefinition>(config: Addon<Args, Id>): Addon<Args, Id>;
180
+ declare function defineAddon<SetupValues extends Record<string, unknown>>(): <const Id extends string, Args extends OptionDefinition>(config: Omit<Addon<Args & SetupOptions<SetupValues>, Id, SetupValues>, "options"> & {
181
+ options: Args;
182
+ }) => Addon<Args & SetupOptions<SetupValues>, Id, SetupValues>;
171
183
  /**
172
184
  * Stage 1: Raw CLI input - what the user typed
173
185
  */
@@ -229,6 +241,7 @@ type SetupResult = {
229
241
  dependsOn: string[];
230
242
  unsupported: string[];
231
243
  runsAfter: string[];
244
+ additionalOptions: Record<string, Question>;
232
245
  };
233
246
  type AddonDefinition<Id extends string = string> = Addon<Record<string, Question<any>>, Id>;
234
247
  type MaybePromise<T> = Promise<T> | T;
@@ -8,7 +8,7 @@ import * as b$1 from "node:readline";
8
8
  import G from "node:readline";
9
9
  import { ReadStream } from "node:tty";
10
10
  import { fileURLToPath } from "node:url";
11
- import childProcess, { exec, spawn } from "node:child_process";
11
+ import childProcess, { exec, spawn, spawnSync } from "node:child_process";
12
12
  import { pipeline } from "node:stream/promises";
13
13
  import { PassThrough } from "node:stream";
14
14
  import { EventEmitter } from "node:events";
@@ -493,9 +493,9 @@ function W({ input: r = stdin, output: t = stdout, overwrite: s = !0, hideCursor
493
493
  r.off("keypress", n), e && t.write(import_src.cursor.show), r instanceof ReadStream && r.isTTY && !q$1 && r.setRawMode(!1), i.terminal = !1, i.close();
494
494
  };
495
495
  }
496
- const A = (r) => "columns" in r && typeof r.columns == "number" ? r.columns : 80, L = (r) => "rows" in r && typeof r.rows == "number" ? r.rows : 20;
497
- function B(r, t, s, e = s, i = s, n) {
498
- return wrapAnsi(t, A(r ?? stdout) - s.length, {
496
+ const A$1 = (r) => "columns" in r && typeof r.columns == "number" ? r.columns : 80, L$1 = (r) => "rows" in r && typeof r.rows == "number" ? r.rows : 20;
497
+ function B$1(r, t, s, e = s, i = s, n) {
498
+ return wrapAnsi(t, A$1(r ?? stdout) - s.length, {
499
499
  hard: !0,
500
500
  trim: !1
501
501
  }).split(`
@@ -629,7 +629,7 @@ var m = class {
629
629
  if (t !== this._prevFrame) {
630
630
  if (this.state === "initial") this.output.write(import_src.cursor.hide);
631
631
  else {
632
- const s = Y$1(this._prevFrame, t), e = L(this.output);
632
+ const s = Y$1(this._prevFrame, t), e = L$1(this.output);
633
633
  if (this.restoreCursor(), s) {
634
634
  const i = Math.max(0, s.numLinesAfter - e), n = Math.max(0, s.numLinesBefore - e);
635
635
  let o = s.lines.find((u) => u >= i);
@@ -805,7 +805,7 @@ const tt = se(), at = () => process.env.CI === "true", Tt = (t) => t.isTTY === !
805
805
  removals: c
806
806
  };
807
807
  }, F$1 = ({ cursor: t, options: i, style: s, output: r = process.stdout, maxItems: u = Number.POSITIVE_INFINITY, columnPadding: n = 0, rowPadding: a = 4 }) => {
808
- const c = A(r) - n, o = L(r), l = styleText("dim", "..."), d = Math.max(o - a, 0), g = Math.max(Math.min(u, d), 5);
808
+ const c = A$1(r) - n, o = L$1(r), l = styleText("dim", "..."), d = Math.max(o - a, 0), g = Math.max(Math.min(u, d), 5);
809
809
  let p = 0;
810
810
  t >= g - 3 && (p = Math.max(Math.min(t - g + 3, i.length - g), 0));
811
811
  let f = g < i.length && p > 0, h = g < i.length && p + g < i.length;
@@ -843,7 +843,7 @@ const le = (t) => {
843
843
  output: t.output,
844
844
  initialValue: t.initialValue ?? !0,
845
845
  render() {
846
- const r = t.withGuide ?? h$1.withGuide, u = `${P$1(this.state)} `, n = r ? `${styleText("gray", $)} ` : "", a = B(t.output, t.message, n, u), c = `${r ? `${styleText("gray", $)}
846
+ const r = t.withGuide ?? h$1.withGuide, u = `${P$1(this.state)} `, n = r ? `${styleText("gray", $)} ` : "", a = B$1(t.output, t.message, n, u), c = `${r ? `${styleText("gray", $)}
847
847
  ` : ""}${a}
848
848
  `, o = this.value ? i : s;
849
849
  switch (this.state) {
@@ -961,7 +961,7 @@ ${styleText("reset", styleText("dim", `Press ${styleText([
961
961
  ], " space ")} to select, ${styleText("gray", styleText("bgWhite", styleText("inverse", " enter ")))} to submit`))}`;
962
962
  },
963
963
  render() {
964
- const r = t.withGuide ?? h$1.withGuide, u = B(t.output, t.message, r ? `${ft(this.state)} ` : "", `${P$1(this.state)} `), n = `${r ? `${styleText("gray", $)}
964
+ const r = t.withGuide ?? h$1.withGuide, u = B$1(t.output, t.message, r ? `${ft(this.state)} ` : "", `${P$1(this.state)} `), n = `${r ? `${styleText("gray", $)}
965
965
  ` : ""}${u}
966
966
  `, a = this.value ?? [], c = (o, l) => {
967
967
  if (o.disabled) return i(o, "disabled");
@@ -971,12 +971,12 @@ ${styleText("reset", styleText("dim", `Press ${styleText([
971
971
  switch (this.state) {
972
972
  case "submit": {
973
973
  const o = this.options.filter(({ value: d }) => a.includes(d)).map((d) => i(d, "submitted")).join(styleText("dim", ", ")) || styleText("dim", "none");
974
- return `${n}${B(t.output, o, r ? `${styleText("gray", $)} ` : "")}`;
974
+ return `${n}${B$1(t.output, o, r ? `${styleText("gray", $)} ` : "")}`;
975
975
  }
976
976
  case "cancel": {
977
977
  const o = this.options.filter(({ value: d }) => a.includes(d)).map((d) => i(d, "cancelled")).join(styleText("dim", ", "));
978
978
  if (o.trim() === "") return `${n}${styleText("gray", $)}`;
979
- return `${n}${B(t.output, o, r ? `${styleText("gray", $)} ` : "")}${r ? `
979
+ return `${n}${B$1(t.output, o, r ? `${styleText("gray", $)} ` : "")}${r ? `
980
980
  ${styleText("gray", $)}` : ""}`;
981
981
  }
982
982
  case "error": {
@@ -1027,7 +1027,7 @@ ${r ? styleText("cyan", x$1) : ""}
1027
1027
  }, Ce = (t = "", i = "", s) => {
1028
1028
  const r = s?.output ?? V.stdout, u = s?.withGuide ?? h$1.withGuide, n = s?.format ?? be, a = [
1029
1029
  "",
1030
- ...Se(t, A(r) - 6, n).split(`
1030
+ ...Se(t, A$1(r) - 6, n).split(`
1031
1031
  `).map(n),
1032
1032
  ""
1033
1033
  ], c = fastStringWidth(i), o = Math.max(a.reduce((p, f) => {
@@ -1053,7 +1053,7 @@ ${styleText("gray", g + st.repeat(o + 2) + dt)}
1053
1053
  ], delay: a = tt ? 80 : 120, signal: c, ...o } = {}) => {
1054
1054
  const l = at();
1055
1055
  let d, g, p = !1, f = !1, h = "", I, m = performance.now();
1056
- const y = A(s), v = o?.styleFrame ?? _e, C = (_) => {
1056
+ const y = A$1(s), v = o?.styleFrame ?? _e, C = (_) => {
1057
1057
  const A = _ > 1 ? u ?? h$1.messages.error : r ?? h$1.messages.cancel;
1058
1058
  f = _ === 1, p && (W$1(A, _), f && typeof i == "function" && i());
1059
1059
  }, S = () => C(2), b = () => C(1), G = () => {
@@ -1136,17 +1136,17 @@ ${styleText("gray", g + st.repeat(o + 2) + dt)}
1136
1136
  output: t.output,
1137
1137
  initialValue: t.initialValue,
1138
1138
  render() {
1139
- const s = t.withGuide ?? h$1.withGuide, r = `${P$1(this.state)} `, u = `${ft(this.state)} `, n = B(t.output, t.message, u, r), a = `${s ? `${styleText("gray", $)}
1139
+ const s = t.withGuide ?? h$1.withGuide, r = `${P$1(this.state)} `, u = `${ft(this.state)} `, n = B$1(t.output, t.message, u, r), a = `${s ? `${styleText("gray", $)}
1140
1140
  ` : ""}${n}
1141
1141
  `;
1142
1142
  switch (this.state) {
1143
1143
  case "submit": {
1144
1144
  const c = s ? `${styleText("gray", $)} ` : "";
1145
- return `${a}${B(t.output, i(this.options[this.cursor], "selected"), c)}`;
1145
+ return `${a}${B$1(t.output, i(this.options[this.cursor], "selected"), c)}`;
1146
1146
  }
1147
1147
  case "cancel": {
1148
1148
  const c = s ? `${styleText("gray", $)} ` : "";
1149
- return `${a}${B(t.output, i(this.options[this.cursor], "cancelled"), c)}${s ? `
1149
+ return `${a}${B$1(t.output, i(this.options[this.cursor], "cancelled"), c)}${s ? `
1150
1150
  ${styleText("gray", $)}` : ""}`;
1151
1151
  }
1152
1152
  default: {
@@ -1192,7 +1192,7 @@ ${Bt}${r.trimStart()}`), s = 3 + stripVTControlCharacters(r.trimStart()).length)
1192
1192
  warning: (t) => q.warn(t),
1193
1193
  error: (t) => q.message(t, { symbol: styleText("red", yt) })
1194
1194
  }, Oe = (t) => t.replace(/\x1b\[(?:\d+;)*\d*[ABCDEFGHfJKSTsu]|\x1b\[(s|u)/g, ""), Pe = (t) => {
1195
- const i = t.output ?? process.stdout, s = A(i), r = styleText("gray", $), u = t.spacing ?? 1, n = 3, a = t.retainLog === !0, c = !at() && Tt(i);
1195
+ const i = t.output ?? process.stdout, s = A$1(i), r = styleText("gray", $), u = t.spacing ?? 1, n = 3, a = t.retainLog === !0, c = !at() && Tt(i);
1196
1196
  i.write(`${r}
1197
1197
  `), i.write(`${styleText("green", H)} ${t.title}
1198
1198
  `);
@@ -1348,7 +1348,7 @@ ${i ? styleText("cyan", x$1) : ""}
1348
1348
  //#endregion
1349
1349
  //#region package.json
1350
1350
  var name = "sv";
1351
- var version = "0.16.5";
1351
+ var version = "0.16.6";
1352
1352
  //#endregion
1353
1353
  //#region src/core/errors.ts
1354
1354
  var UnsupportedError = class extends Error {
@@ -1885,10 +1885,12 @@ var k = class extends Error {
1885
1885
  this.output = t;
1886
1886
  }
1887
1887
  };
1888
+ const A = /\r?\n/;
1888
1889
  const j = {
1889
1890
  timeout: void 0,
1890
1891
  persist: false
1891
1892
  };
1893
+ const M = { timeout: void 0 };
1892
1894
  const N = { windowsHide: true };
1893
1895
  function P(e) {
1894
1896
  const t = new AbortController();
@@ -2036,12 +2038,56 @@ var I = class {
2036
2038
  if (this._resolveClose) this._resolveClose();
2037
2039
  };
2038
2040
  };
2041
+ function L(e, r, i) {
2042
+ const a = {
2043
+ ...M,
2044
+ ...i
2045
+ };
2046
+ const o = cwd();
2047
+ const s = {
2048
+ windowsHide: true,
2049
+ ...a.nodeOptions
2050
+ };
2051
+ if (a.timeout !== void 0) s.timeout = a.timeout;
2052
+ s.env = y(o, s.env, a.nodePath);
2053
+ const c = D(e, r ?? [], s);
2054
+ const l = spawnSync(c.command, c.args, c.options);
2055
+ if (l.error) throw l.error;
2056
+ const u = l.stdout?.toString() ?? "";
2057
+ const d = l.stderr?.toString() ?? "";
2058
+ const f = l.status ?? void 0;
2059
+ const p = l.signal != null;
2060
+ const m = {
2061
+ stdout: u,
2062
+ stderr: d,
2063
+ get exitCode() {
2064
+ return f;
2065
+ },
2066
+ get pid() {
2067
+ return l.pid;
2068
+ },
2069
+ get killed() {
2070
+ return p;
2071
+ },
2072
+ *[Symbol.iterator]() {
2073
+ for (const e of [u, d]) {
2074
+ if (!e) continue;
2075
+ const t = e.split(A);
2076
+ if (t[t.length - 1] === "") t.pop();
2077
+ yield* t;
2078
+ }
2079
+ }
2080
+ };
2081
+ if (a.throwOnError && f !== 0 && f !== void 0) throw new k(m, m);
2082
+ return m;
2083
+ }
2039
2084
  const R = (e, t, n) => {
2040
2085
  const r = new I(e, t, n);
2041
2086
  r.spawn();
2042
2087
  return r;
2043
2088
  };
2044
2089
  const z = R;
2090
+ const B = L;
2045
2091
  //#endregion
2046
2092
  //#region ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/error.js
2047
2093
  /**
@@ -5637,11 +5683,9 @@ function pipe(...pipe$1) {
5637
5683
  }
5638
5684
  //#endregion
5639
5685
  //#region src/core/config.ts
5640
- /**
5641
- * The entry point for your addon, It will hold every thing! (options, setup, run, nextSteps, ...)
5642
- */
5643
- function defineAddon(config) {
5644
- return config;
5686
+ function defineAddon(...args) {
5687
+ if (args.length === 0) return (config) => config;
5688
+ return args[0];
5645
5689
  }
5646
5690
  /**
5647
5691
  * Generates an inline error hint based on the addon source
@@ -10722,21 +10766,23 @@ async function formatFiles(options) {
10722
10766
  //#endregion
10723
10767
  //#region src/core/package-manager.ts
10724
10768
  const AGENT_NAMES = AGENTS.filter((agent) => !agent.includes("@"));
10725
- const agentOptions = AGENT_NAMES.map((pm) => ({
10726
- value: pm,
10727
- label: pm
10728
- }));
10729
- agentOptions.unshift({
10730
- label: "None",
10731
- value: void 0
10732
- });
10733
10769
  const installOption = new Option("--install <package-manager>", "installs dependencies with a specified package manager").choices(AGENT_NAMES);
10734
10770
  async function packageManagerPrompt(cwd) {
10735
10771
  const agent = (await detect({ cwd }))?.name ?? getUserAgent();
10736
10772
  if (!V.stdout.isTTY) return agent;
10737
10773
  const pm = await Ee({
10738
10774
  message: "Which package manager do you want to install dependencies with?",
10739
- options: agentOptions,
10775
+ options: [{
10776
+ label: "None",
10777
+ value: void 0
10778
+ }, ...AGENT_NAMES.map((agent) => {
10779
+ const installed = isInstalled(agent);
10780
+ return {
10781
+ value: agent,
10782
+ label: installed ? agent : color.dim(`${agent} (not installed)`),
10783
+ installed
10784
+ };
10785
+ }).sort((a, b) => Number(b.installed) - Number(a.installed))],
10740
10786
  initialValue: agent
10741
10787
  });
10742
10788
  if (R$2(pm)) {
@@ -10745,7 +10791,12 @@ async function packageManagerPrompt(cwd) {
10745
10791
  }
10746
10792
  return pm;
10747
10793
  }
10794
+ /** Returns `false` when the package manager isn't installed and the install was skipped. */
10748
10795
  async function installDependencies(agent, cwd) {
10796
+ if (!isInstalled(agent)) {
10797
+ R$1.warn(`${color.command(agent)} is not installed, skipping dependency installation.`);
10798
+ return false;
10799
+ }
10749
10800
  const task = Pe({
10750
10801
  title: `Installing dependencies with ${color.command(agent)}...`,
10751
10802
  limit: Math.ceil(V.stdout.rows / 2),
@@ -10769,12 +10820,12 @@ async function installDependencies(agent, cwd) {
10769
10820
  } catch {}
10770
10821
  if ((proc.exitCode ?? 0) === 0) {
10771
10822
  task.success(`Successfully installed dependencies with ${color.command(agent)}`);
10772
- return;
10823
+ return true;
10773
10824
  }
10774
10825
  if (agent === "pnpm" && output.join("\n").includes("ERR_PNPM_IGNORED_BUILDS")) {
10775
10826
  task.success(`Installed dependencies with ${color.command(agent)}`);
10776
10827
  R$1.warn(`Some build scripts were skipped. Run ${color.command(`${agent} approve-builds`)} to approve them.`);
10777
- return;
10828
+ return true;
10778
10829
  }
10779
10830
  task.error("Failed to install dependencies");
10780
10831
  ge("Operation failed.");
@@ -10791,6 +10842,20 @@ function getUserAgent() {
10791
10842
  const name = pmSpec.substring(0, separatorPos);
10792
10843
  return AGENTS.includes(name) ? name : void 0;
10793
10844
  }
10845
+ const installedCache = /* @__PURE__ */ new Map();
10846
+ function isInstalled(agent) {
10847
+ let installed = installedCache.get(agent);
10848
+ if (installed === void 0) {
10849
+ try {
10850
+ B(agent, ["--version"], { nodeOptions: { stdio: "ignore" } });
10851
+ installed = true;
10852
+ } catch {
10853
+ installed = false;
10854
+ }
10855
+ installedCache.set(agent, installed);
10856
+ }
10857
+ return installed;
10858
+ }
10794
10859
  function addPnpmAllowBuilds(cwd, packageManager, ...packages) {
10795
10860
  if (packageManager !== "pnpm" || packages.length === 0) return;
10796
10861
  const found = up$1("pnpm-workspace.yaml", { cwd });
@@ -11225,7 +11290,7 @@ const create = new Command("create").description("Scaffold a new project (--add
11225
11290
  V.exit(1);
11226
11291
  }
11227
11292
  runCommand(async () => {
11228
- const { directory, addOnNextSteps, packageManager } = await createProject(cwd, options);
11293
+ const { directory, addOnNextSteps, packageManager, depsInstalled } = await createProject(cwd, options);
11229
11294
  let i = 1;
11230
11295
  const initialSteps = ["📁 Project steps", ""];
11231
11296
  const relative = path.relative(V.cwd(), directory);
@@ -11234,7 +11299,8 @@ const create = new Command("create").description("Scaffold a new project (--add
11234
11299
  const pathHasSpaces = relative.includes(" ");
11235
11300
  initialSteps.push(` ${i++}: ${color.command(`cd ${pathHasSpaces ? `"${relative}"` : relative}`)}`);
11236
11301
  }
11237
- if (!packageManager) initialSteps.push(` ${i++}: ${color.command(resolveCommandArray(pm, "install", []))}`);
11302
+ if (packageManager && !depsInstalled) initialSteps.push(` ${i++}: Install ${color.command(pm)}`);
11303
+ if (!packageManager || !depsInstalled) initialSteps.push(` ${i++}: ${color.command(resolveCommandArray(pm, "install", []))}`);
11238
11304
  const steps = [
11239
11305
  ...initialSteps,
11240
11306
  ` ${i++}: ${color.command(resolveCommandArray(pm, "run", ["dev", "--open"]))}`,
@@ -11410,18 +11476,20 @@ async function createProject(cwd, options) {
11410
11476
  updateAgent(directory, language, packageManager ?? "npm", loadedAddons);
11411
11477
  const addOnNextSteps = getNextSteps(addOnSuccessfulAddons, workspace, answers, addonSetupResults);
11412
11478
  addPnpmAllowBuilds(projectPath, packageManager, "esbuild");
11479
+ let depsInstalled = false;
11413
11480
  if (packageManager) {
11414
- await installDependencies(packageManager, projectPath);
11415
- await formatFiles({
11481
+ depsInstalled = await installDependencies(packageManager, projectPath);
11482
+ if (depsInstalled) await formatFiles({
11416
11483
  packageManager,
11417
11484
  cwd: projectPath,
11418
- filesToFormat: addOnFilesToFormat
11485
+ filesToFormat: addOnSuccessfulAddons.some((addon) => addon.addon.id === "prettier") ? ["."] : addOnFilesToFormat
11419
11486
  });
11420
11487
  }
11421
11488
  return {
11422
11489
  directory: projectPath,
11423
11490
  addOnNextSteps,
11424
- packageManager
11491
+ packageManager,
11492
+ depsInstalled
11425
11493
  };
11426
11494
  }
11427
11495
  async function createProjectFromPlayground(url, cwd) {
@@ -11713,9 +11781,9 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
11713
11781
  }
11714
11782
  for (const loaded of addons) answers[loaded.addon.id] ??= {};
11715
11783
  let setupResults = {};
11716
- if (addons.length > 0) setupResults = setupAddons(addons, workspace);
11784
+ if (addons.length > 0) setupResults = await setupAddons(addons, workspace);
11717
11785
  if (addons.length === 0) {
11718
- const results = setupAddons(officialAddons.map((a) => createLoadedAddon(a)), workspace);
11786
+ const results = await setupAddons(officialAddons.map((a) => createLoadedAddon(a)), workspace);
11719
11787
  const addonOptions = officialAddons.filter(({ id, hidden }) => results[id].unsupported.length === 0 && !hidden).map(({ id, homepage, shortDescription }) => ({
11720
11788
  label: id,
11721
11789
  value: id,
@@ -11735,11 +11803,11 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
11735
11803
  addons.push(createLoadedAddon(addon));
11736
11804
  answers[id] = {};
11737
11805
  }
11738
- setupResults = setupAddons(addons, workspace);
11806
+ setupResults = await setupAddons(addons, workspace);
11739
11807
  }
11740
11808
  const missingSetupResults = addons.filter((a) => !setupResults[a.addon.id]);
11741
11809
  if (missingSetupResults.length > 0) {
11742
- const additionalSetupResults = setupAddons(missingSetupResults, workspace);
11810
+ const additionalSetupResults = await setupAddons(missingSetupResults, workspace);
11743
11811
  Object.assign(setupResults, additionalSetupResults);
11744
11812
  }
11745
11813
  const dependencyChains = /* @__PURE__ */ new Map();
@@ -11779,7 +11847,7 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
11779
11847
  }
11780
11848
  const newlyAddedAddons = addons.filter((a) => !setupResults[a.addon.id]);
11781
11849
  if (newlyAddedAddons.length > 0) {
11782
- const newSetupResults = setupAddons(newlyAddedAddons, workspace);
11850
+ const newSetupResults = await setupAddons(newlyAddedAddons, workspace);
11783
11851
  Object.assign(setupResults, newSetupResults);
11784
11852
  }
11785
11853
  }
@@ -11851,7 +11919,7 @@ async function promptAddonQuestions({ options, loadedAddons, workspace }) {
11851
11919
  };
11852
11920
  }
11853
11921
  async function runAddonsApply({ answers, options, loadedAddons, setupResults, workspace, fromCommand }) {
11854
- if (!setupResults) setupResults = setupAddons(loadedAddons.length ? loadedAddons : officialAddons.map((a) => createLoadedAddon(a)), workspace);
11922
+ if (!setupResults) setupResults = await setupAddons(loadedAddons.length ? loadedAddons : officialAddons.map((a) => createLoadedAddon(a)), workspace);
11855
11923
  if (loadedAddons.length === 0) return {
11856
11924
  nextSteps: [],
11857
11925
  argsFormattedAddons: [],
@@ -11910,17 +11978,20 @@ async function runAddonsApply({ answers, options, loadedAddons, setupResults, wo
11910
11978
  if (!options.gitCheck) argsFormattedAddons.push("--no-git-check");
11911
11979
  buildAndLogArgs(packageManager, "add", argsFormattedAddons);
11912
11980
  }
11981
+ let depsInstalled = true;
11913
11982
  if (packageManager) {
11914
11983
  workspace.packageManager = packageManager;
11915
- await installDependencies(packageManager, options.cwd);
11916
- await formatFiles({
11984
+ depsInstalled = await installDependencies(packageManager, options.cwd);
11985
+ if (depsInstalled) await formatFiles({
11917
11986
  packageManager,
11918
11987
  cwd: options.cwd,
11919
11988
  filesToFormat
11920
11989
  });
11921
11990
  }
11991
+ const nextSteps = getNextSteps(successfulAddons, workspace, answers, setupResults);
11992
+ if (packageManager && !depsInstalled) nextSteps.unshift(`Install ${color.command(packageManager)}, then run ${color.command(resolveCommandArray(packageManager, "install", []))}`);
11922
11993
  return {
11923
- nextSteps: getNextSteps(successfulAddons, workspace, answers, setupResults),
11994
+ nextSteps,
11924
11995
  argsFormattedAddons,
11925
11996
  filesToFormat,
11926
11997
  successfulAddons,
@@ -12133,7 +12204,7 @@ async function add({ addons, cwd, options, packageManager = "npm" }) {
12133
12204
  loadedAddons,
12134
12205
  workspace,
12135
12206
  options,
12136
- setupResults: setupAddons(loadedAddons, workspace)
12207
+ setupResults: await setupAddons(loadedAddons, workspace)
12137
12208
  });
12138
12209
  }
12139
12210
  async function applyAddons({ loadedAddons, workspace, setupResults, options }) {
@@ -12176,29 +12247,35 @@ async function applyAddons({ loadedAddons, workspace, setupResults, options }) {
12176
12247
  };
12177
12248
  }
12178
12249
  /** Setup addons - takes LoadedAddon[] and returns setup results */
12179
- function setupAddons(loadedAddons, workspace) {
12250
+ async function setupAddons(loadedAddons, workspace) {
12180
12251
  const setupResults = {};
12181
12252
  for (const loaded of loadedAddons) {
12182
12253
  const addon = loaded.addon;
12254
+ const additionalOptions = {};
12183
12255
  const setupResult = {
12184
12256
  unsupported: [],
12185
12257
  dependsOn: [],
12186
- runsAfter: []
12258
+ runsAfter: [],
12259
+ additionalOptions
12187
12260
  };
12188
12261
  try {
12189
- addon.setup?.({
12262
+ await addon.setup?.({
12190
12263
  ...workspace,
12191
12264
  dependsOn: (name) => {
12192
12265
  setupResult.dependsOn.push(name);
12193
12266
  setupResult.runsAfter.push(name);
12194
12267
  },
12195
12268
  unsupported: (reason) => setupResult.unsupported.push(reason),
12196
- runsAfter: (name) => setupResult.runsAfter.push(name)
12269
+ runsAfter: (name) => setupResult.runsAfter.push(name),
12270
+ addOption: (key, question) => {
12271
+ additionalOptions[key] = question;
12272
+ }
12197
12273
  });
12198
12274
  } catch (err) {
12199
12275
  const msg = err instanceof Error ? err.message : String(err);
12200
12276
  throw new Error(`Add-on '${addon.id}' failed during setup: ${msg}\n\n${getErrorHint(loaded.reference.source)}`, { cause: err });
12201
12277
  }
12278
+ if (Object.keys(additionalOptions).length > 0) Object.assign(addon.options, additionalOptions);
12202
12279
  setupResults[addon.id] = setupResult;
12203
12280
  }
12204
12281
  return setupResults;
@@ -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-CtSoxta7.mjs";
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-BhR6Vrxy.mjs";
2
2
 
3
3
  //#region src/create/index.d.ts
4
4
  type TemplateType = (typeof templateTypes)[number];
@@ -1,4 +1,4 @@
1
- import { c as defineAddonOptions, m as create$1, o as officialAddons, s as defineAddon, t as add, y as svDeprecated } from "../engine-CCAozEpo.mjs";
1
+ import { c as defineAddonOptions, m as create$1, o as officialAddons, s as defineAddon, t as add, y as svDeprecated } from "../engine-DbEL5s1q.mjs";
2
2
  //#region src/index.ts
3
3
  function create(cwdOrOptions, legacyOptions) {
4
4
  if (typeof cwdOrOptions === "string") {
@@ -1,4 +1,4 @@
1
- import { r as OptionMap, t as AddonMap } from "../engine-CtSoxta7.mjs";
1
+ import { r as OptionMap, t as AddonMap } from "../engine-BhR6Vrxy.mjs";
2
2
  import { Page } from "@playwright/test";
3
3
  import { TestProject } from "vitest/node";
4
4
 
@@ -1,4 +1,4 @@
1
- import { S as __toESM, b as __commonJSMin, f as R, i as addPnpmAllowBuilds, m as create, p as z, t as add, x as __require } from "../engine-CCAozEpo.mjs";
1
+ import { S as __toESM, b as __commonJSMin, f as R, i as addPnpmAllowBuilds, m as create, p as z, t as add, x as __require } from "../engine-DbEL5s1q.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": "^0.16.5"
25
+ "sv": "^0.16.6"
26
26
  },
27
27
  "devDependencies": {
28
- "sv": "^0.16.5",
29
- "@sveltejs/sv-utils": "^0.3.1",
28
+ "sv": "^0.16.6",
29
+ "@sveltejs/sv-utils": "^0.3.2",
30
30
  "@playwright/test": "^1.60.0",
31
31
  "@types/node": "^25.9.1",
32
32
  "tsdown": "^0.22.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sv",
3
- "version": "0.16.5",
3
+ "version": "0.16.6",
4
4
  "type": "module",
5
5
  "description": "A command line interface (CLI) for creating and maintaining Svelte applications",
6
6
  "license": "MIT",
@@ -25,7 +25,7 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@sveltejs/sv-utils": "0.3.1"
28
+ "@sveltejs/sv-utils": "0.3.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.5.0",