sv 0.13.1 → 0.14.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 { a as detectPackageManager, c as name, h as program, l as version, m as Command, n as add, o as forwardExitCode, p as from, r as create, s as helpConfig } from "./engine-lCnxwqd0.mjs";
3
- import { color, resolveCommand } from "@sveltejs/sv-utils";
2
+ import { a as detectPackageManager, c as name, h as program, l as version, m as Command, n as add, o as forwardExitCode, p as from, r as create, s as helpConfig } from "./engine-BaqBUQsx.mjs";
3
+ import { color, resolveCommandArray } from "@sveltejs/sv-utils";
4
4
  import process from "node:process";
5
5
  import { execSync } from "node:child_process";
6
6
  //#region src/cli/check.ts
@@ -12,8 +12,7 @@ const check = new Command("check").description("a CLI for checking your Svelte c
12
12
  async function runCheck(cwd, args) {
13
13
  const pm = await detectPackageManager(cwd);
14
14
  if (!from(cwd, "svelte-check", true)) {
15
- const cmd = resolveCommand(pm, "add", ["-D", "svelte-check"]);
16
- console.error(`'svelte-check' is not installed locally. Install it with: ${color.command(`${cmd.command} ${cmd.args.join(" ")}`)}`);
15
+ console.error(`'svelte-check' is not installed locally. Install it with: ${color.command(resolveCommandArray(pm, "add", ["-D", "svelte-check"]))}`);
17
16
  process.exit(1);
18
17
  }
19
18
  if (args.includes("--help")) {
@@ -21,8 +20,7 @@ async function runCheck(cwd, args) {
21
20
  console.log("Find here all options for both tools");
22
21
  }
23
22
  try {
24
- const cmd = resolveCommand(pm, "execute-local", ["svelte-check", ...args]);
25
- execSync(`${cmd.command} ${cmd.args.join(" ")}`, {
23
+ execSync(resolveCommandArray(pm, "execute-local", ["svelte-check", ...args]).join(" "), {
26
24
  stdio: "inherit",
27
25
  cwd
28
26
  });
@@ -42,8 +40,7 @@ async function runMigrate(cwd, args) {
42
40
  try {
43
41
  const cmdArgs = ["svelte-migrate@latest", ...args];
44
42
  if (pm === "npm") cmdArgs.unshift("--yes");
45
- const cmd = resolveCommand(pm, "execute", cmdArgs);
46
- execSync(`${cmd.command} ${cmd.args.join(" ")}`, {
43
+ execSync(resolveCommandArray(pm, "execute", cmdArgs).join(" "), {
47
44
  stdio: "inherit",
48
45
  cwd
49
46
  });
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { AGENTS, COMMANDS, Walker, color, commonFilePaths, constructCommand, createPrinter, dedent, detect, downloadJson, fileExists, getPackageJson, installPackages, isVersionUnsupportedBelow, js, parse, readFile, resolveCommand, sanitizeName, splitVersion, svelte, text, transforms, writeFile } from "@sveltejs/sv-utils";
2
+ import { AGENTS, COMMANDS, Walker, color, commonFilePaths, constructCommand, createPrinter, dedent, detect, downloadJson, fileExists, getPackageJson, installPackages, isVersionUnsupportedBelow, js, parse, readFile, resolveCommand, resolveCommandArray, sanitizeName, splitVersion, svelte, text, transforms, writeFile } from "@sveltejs/sv-utils";
3
3
  import fs, { existsSync } from "node:fs";
4
4
  import path, { delimiter, dirname, isAbsolute, join, normalize, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -5882,14 +5882,21 @@ const addEslintConfigPrettier = transforms.script(({ ast, js }) => {
5882
5882
  });
5883
5883
  const fallbackConfig = js.common.parseExpression("[]");
5884
5884
  const eslintConfig = js.exports.createDefault(ast, { fallback: fallbackConfig }).value;
5885
- if (eslintConfig.type !== "ArrayExpression" && eslintConfig.type !== "CallExpression") return false;
5885
+ let elements = [];
5886
+ if (eslintConfig.type === "ArrayExpression") elements = eslintConfig.elements;
5887
+ else if (eslintConfig.type === "CallExpression") if (eslintConfig.arguments.length === 1 && eslintConfig.arguments[0].type === "ArrayExpression") elements = eslintConfig.arguments[0].elements;
5888
+ else elements = eslintConfig.arguments;
5889
+ else return false;
5886
5890
  const prettier = js.common.parseExpression("prettier");
5887
5891
  const sveltePrettierConfig = js.common.parseExpression(`${svelteImportName}.configs.prettier`);
5888
5892
  const nodesToInsert = [];
5889
5893
  if (!js.common.contains(eslintConfig, prettier)) nodesToInsert.push(prettier);
5890
5894
  if (!js.common.contains(eslintConfig, sveltePrettierConfig)) nodesToInsert.push(sveltePrettierConfig);
5891
- const elements = eslintConfig.type === "ArrayExpression" ? eslintConfig.elements : eslintConfig.arguments;
5892
- const idx = elements.findIndex((el) => el?.type === "MemberExpression" && el.object.type === "MemberExpression" && el.object.property.type === "Identifier" && el.object.property.name === "configs" && el.object.object.type === "Identifier" && el.object.object.name === svelteImportName);
5895
+ const isSvelteConfig = (maybeSpread) => {
5896
+ const el = maybeSpread?.type === "SpreadElement" ? maybeSpread?.argument : maybeSpread;
5897
+ return el?.type === "MemberExpression" && el.object.type === "MemberExpression" && el.object.object.type === "Identifier" && el.object.object.name === svelteImportName && el.object.property.type === "Identifier" && el.object.property.name === "configs";
5898
+ };
5899
+ const idx = elements.findIndex(isSvelteConfig);
5893
5900
  if (idx !== -1) elements.splice(idx + 1, 0, ...nodesToInsert);
5894
5901
  else elements.push(...nodesToInsert);
5895
5902
  });
@@ -6209,7 +6216,7 @@ var better_auth_default = defineAddon({
6209
6216
  ${!d1 ? "import { auth } from '$lib/server/auth';" : ""}
6210
6217
  ${needsAPIError ? "import { APIError } from 'better-auth/api';" : ""}
6211
6218
 
6212
- export const load${ts(": PageServerLoad")} = async (event) => {
6219
+ export const load${ts(": PageServerLoad")} = (event) => {
6213
6220
  if (event.locals.user) {
6214
6221
  return redirect(302, '/demo/better-auth');
6215
6222
  }
@@ -6277,7 +6284,7 @@ var better_auth_default = defineAddon({
6277
6284
  ${ts("import type { PageServerLoad } from './$types';")}
6278
6285
  ${!d1 ? "import { auth } from '$lib/server/auth';" : ""}
6279
6286
 
6280
- export const load${ts(": PageServerLoad")} = async (event) => {
6287
+ export const load${ts(": PageServerLoad")} = (event) => {
6281
6288
  if (!event.locals.user) {
6282
6289
  return redirect(302, '/demo/better-auth/login');
6283
6290
  }
@@ -6312,11 +6319,9 @@ var better_auth_default = defineAddon({
6312
6319
  }
6313
6320
  },
6314
6321
  nextSteps: ({ options, packageManager }) => {
6315
- const { command: authCmd, args: authArgs } = resolveCommand(packageManager, "run", ["auth:schema"]);
6316
- const { command: dbCmd, args: dbArgs } = resolveCommand(packageManager, "run", ["db:push"]);
6317
6322
  const steps = [
6318
- `Run ${color.command(`${authCmd} ${authArgs.join(" ")}`)} to generate the auth schema`,
6319
- `Run ${color.command(`${dbCmd} ${dbArgs.join(" ")}`)} to update your database`,
6323
+ `Run ${color.command(resolveCommandArray(packageManager, "run", ["auth:schema"]))} to generate the auth schema`,
6324
+ `Run ${color.command(resolveCommandArray(packageManager, "run", ["db:push"]))} to update your database`,
6320
6325
  `Check ${color.env("ORIGIN")} & ${color.env("BETTER_AUTH_SECRET")} in ${color.path(".env")} and adjust it to your needs`
6321
6326
  ];
6322
6327
  if (options.demo.includes("github")) steps.push(`Set your ${color.env("GITHUB_CLIENT_ID")} and ${color.env("GITHUB_CLIENT_SECRET")} in ${color.path(".env")}`);
@@ -6650,7 +6655,7 @@ var drizzle_default = defineAddon({
6650
6655
  from: "drizzle-orm/d1",
6651
6656
  imports: ["drizzle"]
6652
6657
  });
6653
- const getDbFn = js.common.parseStatement(`export const getDb = (d1${ts(": D1Database")} => drizzle(d1, { schema });`);
6658
+ const getDbFn = js.common.parseStatement(`export const getDb = (d1${ts(": D1Database")}) => drizzle(d1, { schema });`);
6654
6659
  ast.body.push(getDbFn);
6655
6660
  return;
6656
6661
  }
@@ -6758,21 +6763,17 @@ var drizzle_default = defineAddon({
6758
6763
  const steps = [];
6759
6764
  if (options.database === "d1") {
6760
6765
  const ext = fileExists(cwd, "wrangler.toml") ? "toml" : "jsonc";
6761
- const { command, args } = resolveCommand(packageManager, "run", [
6766
+ steps.push(`Add your ${color.env("CLOUDFLARE_ACCOUNT_ID")}, ${color.env("CLOUDFLARE_DATABASE_ID")}, and ${color.env("CLOUDFLARE_D1_TOKEN")} to ${color.path(".env")}`);
6767
+ steps.push(`Run ${color.command(resolveCommandArray(packageManager, "run", [
6762
6768
  "wrangler",
6763
6769
  "d1",
6764
6770
  "create",
6765
- `<DATABASE_NAME>`
6766
- ]);
6767
- steps.push(`Add your ${color.env("CLOUDFLARE_ACCOUNT_ID")}, ${color.env("CLOUDFLARE_DATABASE_ID")}, and ${color.env("CLOUDFLARE_D1_TOKEN")} to ${color.path(".env")}`);
6768
- steps.push(`Run ${color.command(`${command} ${args.join(" ")}`)} to generate a D1 database ID for your ${color.path(`wrangler.${ext}`)}`);
6769
- }
6770
- if (options.docker) {
6771
- const { command, args } = resolveCommand(packageManager, "run", ["db:start"]);
6772
- steps.push(`Run ${color.command(`${command} ${args.join(" ")}`)} to start the docker container`);
6773
- } else if (options.database !== "d1") steps.push(`Check ${color.env("DATABASE_URL")} in ${color.path(".env")} and adjust it to your needs`);
6774
- const { command, args } = resolveCommand(packageManager, "run", ["db:push"]);
6775
- steps.push(`Run ${color.command(`${command} ${args.join(" ")}`)} to update your database schema`);
6771
+ "<DATABASE_NAME>"
6772
+ ]))} to generate a D1 database ID for your ${color.path(`wrangler.${ext}`)}`);
6773
+ }
6774
+ if (options.docker) steps.push(`Run ${color.command(resolveCommandArray(packageManager, "run", ["db:start"]))} to start the docker container`);
6775
+ else if (options.database !== "d1") steps.push(`Check ${color.env("DATABASE_URL")} in ${color.path(".env")} and adjust it to your needs`);
6776
+ steps.push(`Run ${color.command(resolveCommandArray(packageManager, "run", ["db:push"]))} to update your database schema`);
6776
6777
  return steps;
6777
6778
  }
6778
6779
  });
@@ -6900,6 +6901,17 @@ var eslint_default = defineAddon({
6900
6901
  });
6901
6902
  eslintConfigs.push(svelteTSParserConfig);
6902
6903
  }
6904
+ const rulesOverride = js.object.create({ rules: {} });
6905
+ eslintConfigs.push(rulesOverride);
6906
+ if (rulesOverride.properties[0].type !== "Property") throw new Error("rulesOverride.properties[0].type !== \"Property\"");
6907
+ comments.add(rulesOverride.properties[0].key, {
6908
+ type: "Line",
6909
+ value: " Override or add rule settings here, such as:"
6910
+ });
6911
+ comments.add(rulesOverride.properties[0].key, {
6912
+ type: "Line",
6913
+ value: " 'svelte/button-has-type': 'error'"
6914
+ });
6903
6915
  const exportExpression = js.functions.createCall({
6904
6916
  name: "defineConfig",
6905
6917
  args: []
@@ -7610,10 +7622,7 @@ var sveltekit_adapter_default = defineAddon({
7610
7622
  },
7611
7623
  nextSteps({ options, packageManager }) {
7612
7624
  const steps = [];
7613
- if (options.adapter === "cloudflare") {
7614
- const { command, args } = resolveCommand(packageManager, "run", ["gen"]);
7615
- steps.push(`Run ${color.command(`${command} ${args.join(" ")}`)} to update ${color.addon("cloudflare")} types`);
7616
- }
7625
+ if (options.adapter === "cloudflare") steps.push(`Run ${color.command(resolveCommandArray(packageManager, "run", ["gen"]))} to update ${color.addon("cloudflare")} types`);
7617
7626
  return steps;
7618
7627
  }
7619
7628
  });
@@ -7899,7 +7908,7 @@ function getAddonDetails(id) {
7899
7908
  //#endregion
7900
7909
  //#region package.json
7901
7910
  var name = "sv";
7902
- var version = "0.13.1";
7911
+ var version = "0.14.0";
7903
7912
  //#endregion
7904
7913
  //#region src/core/errors.ts
7905
7914
  var UnsupportedError = class extends Error {
@@ -8046,8 +8055,7 @@ function buildAndLogArgs(agent, command, args, lastArgs = []) {
8046
8055
  ];
8047
8056
  if (agent === null || agent === void 0) allArgs.push("--no-install");
8048
8057
  else allArgs.push("--install", agent);
8049
- const res = resolveCommand(agent ?? "npm", "execute", [...allArgs, ...lastArgs]);
8050
- const message = [res.command, ...res.args].join(" ");
8058
+ const message = resolveCommandArray(agent ?? "npm", "execute", [...allArgs, ...lastArgs]).join(" ");
8051
8059
  R$1.message(color.optional(color.dim(`To skip prompts next time, run:`)));
8052
8060
  R$1.info(color.optional(message), { spacing: -1 });
8053
8061
  return message;
@@ -10810,7 +10818,7 @@ async function downloadPackage(options) {
10810
10818
  if (platform() === "win32" && (error.code === "EPERM" || error.code === "EACCES")) copyDirectorySync(options.path, dest);
10811
10819
  else throw error;
10812
10820
  }
10813
- return await importAddonCode(pkg.name);
10821
+ return await importAddonCode(pkg.name, pkg.version);
10814
10822
  }
10815
10823
  const tarballUrl = pkg.dist.tarball;
10816
10824
  const data = await fetch(tarballUrl);
@@ -10819,14 +10827,22 @@ async function downloadPackage(options) {
10819
10827
  header.name = header.name.replace("package", pkg.name);
10820
10828
  return header;
10821
10829
  } }));
10822
- return await importAddonCode(pkg.name);
10830
+ return await importAddonCode(pkg.name, pkg.version);
10823
10831
  }
10824
- async function importAddonCode(pkgName) {
10832
+ async function importAddonCode(pkgName, pkgVersion) {
10833
+ const issues = [];
10834
+ let details;
10825
10835
  try {
10826
- const { default: details } = await import(`${pkgName}/sv`);
10827
- return details;
10828
- } catch {}
10829
- const { default: details } = await import(pkgName);
10836
+ ({default: details} = await import(`${pkgName}/sv`));
10837
+ } catch {
10838
+ issues.push(`'/sv' export not found`);
10839
+ }
10840
+ if (!details) try {
10841
+ ({default: details} = await import(pkgName));
10842
+ } catch {
10843
+ issues.push(`default export not found`);
10844
+ }
10845
+ if (!details && issues.length > 0) throw new Error(`Failed to load add-on '${pkgName}@${pkgVersion}':\n- ${issues.join("\n- ")}\n\nPlease report this to the add-on author.`);
10830
10846
  return details;
10831
10847
  }
10832
10848
  async function getPackageJSON(ref) {
@@ -11391,15 +11407,10 @@ const create = new Command("create").description("Scaffold a new project (--add
11391
11407
  const pathHasSpaces = relative.includes(" ");
11392
11408
  initialSteps.push(` ${i++}: ${color.command(`cd ${pathHasSpaces ? `"${relative}"` : relative}`)}`);
11393
11409
  }
11394
- if (!packageManager) {
11395
- const { args, command } = resolveCommand(pm, "install", []);
11396
- initialSteps.push(` ${i++}: ${color.command(`${command} ${args.join(" ")}`)}`);
11397
- }
11398
- const { args, command } = resolveCommand(pm, "run", ["dev", "--open"]);
11399
- const pmRunCmd = `${command} ${args.join(" ")}`;
11410
+ if (!packageManager) initialSteps.push(` ${i++}: ${color.command(resolveCommandArray(pm, "install", []))}`);
11400
11411
  const steps = [
11401
11412
  ...initialSteps,
11402
- ` ${i++}: ${color.command(pmRunCmd)}`,
11413
+ ` ${i++}: ${color.command(resolveCommandArray(pm, "run", ["dev", "--open"]))}`,
11403
11414
  "",
11404
11415
  `To close the dev server, hit ${color.command("Ctrl-C")}`
11405
11416
  ];
@@ -11483,8 +11494,23 @@ async function createProject(cwd, options) {
11483
11494
  const projectPath = path.resolve(directory);
11484
11495
  const basename = path.basename(projectPath);
11485
11496
  const parentDirName = path.basename(path.dirname(projectPath));
11486
- const projectName = parentDirName.startsWith("@") ? `${parentDirName}/${basename}` : basename;
11487
- if (template === "addon" && !projectName.startsWith("@")) errorAndExit(`Community add-ons must be published under an npm org (e.g. ${color.command("@my-org/sv")}). Unscoped package names are not supported at this stage.`);
11497
+ let projectName = parentDirName.startsWith("@") ? `${parentDirName}/${basename}` : basename;
11498
+ if (template === "addon" && !projectName.startsWith("@")) {
11499
+ const org = await Qt({
11500
+ message: `Community add-ons must be published under an npm org. Enter the name of your npm org:`,
11501
+ placeholder: " @my-org",
11502
+ validate: (value) => {
11503
+ if (!value) return "Organization name is required";
11504
+ if (!value.startsWith("@")) return "Must start with @";
11505
+ if (value.includes("/")) return "Just the org, not the full package name";
11506
+ }
11507
+ });
11508
+ if (Ct$1(org)) {
11509
+ Pt("Operation cancelled.");
11510
+ process$1.exit(0);
11511
+ }
11512
+ projectName = `${org}/${basename}`;
11513
+ }
11488
11514
  if (template === "addon" && options.add.length > 0) errorAndExit(`The ${color.command("--add")} flag cannot be used with the ${color.command("addon")} template.`);
11489
11515
  let loadedAddons = [];
11490
11516
  let answers = {};
@@ -11522,8 +11548,9 @@ async function createProject(cwd, options) {
11522
11548
  let argsFormattedAddons = [];
11523
11549
  let addOnFilesToFormat = [];
11524
11550
  let addOnSuccessfulAddons = [];
11551
+ let addonSetupResults = {};
11525
11552
  if (template !== "addon" && (options.addOns || options.add.length > 0)) {
11526
- const { argsFormattedAddons: argsFormatted, filesToFormat, successfulAddons } = await runAddonsApply({
11553
+ const { argsFormattedAddons: argsFormatted, filesToFormat, successfulAddons, setupResults } = await runAddonsApply({
11527
11554
  answers,
11528
11555
  options: {
11529
11556
  cwd: projectPath,
@@ -11540,6 +11567,7 @@ async function createProject(cwd, options) {
11540
11567
  argsFormattedAddons = argsFormatted;
11541
11568
  addOnFilesToFormat = filesToFormat;
11542
11569
  addOnSuccessfulAddons = successfulAddons;
11570
+ addonSetupResults = setupResults;
11543
11571
  }
11544
11572
  const packageManager = options.install === false ? null : options.install === true ? await packageManagerPrompt(projectPath) : options.install;
11545
11573
  const argsFormatted = [];
@@ -11551,7 +11579,7 @@ async function createProject(cwd, options) {
11551
11579
  updateReadme(directory, buildAndLogArgs(packageManager, "create", argsFormatted, [directory]));
11552
11580
  updateAgent(directory, language, packageManager ?? "npm", loadedAddons);
11553
11581
  if (packageManager) workspace.packageManager = packageManager;
11554
- const addOnNextSteps = getNextSteps(addOnSuccessfulAddons, workspace, answers);
11582
+ const addOnNextSteps = getNextSteps(addOnSuccessfulAddons, workspace, answers, addonSetupResults);
11555
11583
  await addPnpmBuildDependencies(projectPath, packageManager, ["esbuild"]);
11556
11584
  if (packageManager) {
11557
11585
  await installDependencies(packageManager, projectPath);
@@ -11731,6 +11759,8 @@ const add$1 = new Command("add").description("applies specified add-ons into a p
11731
11759
  " sv add prettier eslint",
11732
11760
  " sv add vitest=\"usages:unit\" tailwindcss=\"plugins:none\"",
11733
11761
  " sv add drizzle=\"database:postgresql+client:postgres.js+docker:yes\"",
11762
+ " sv add prettier @supacool",
11763
+ " sv add @supacool/sv@0.1.2",
11734
11764
  ""
11735
11765
  ].join("\n");
11736
11766
  }
@@ -11997,7 +12027,8 @@ async function runAddonsApply({ answers, options, loadedAddons, setupResults, wo
11997
12027
  nextSteps: [],
11998
12028
  argsFormattedAddons: [],
11999
12029
  filesToFormat: [],
12000
- successfulAddons: []
12030
+ successfulAddons: [],
12031
+ setupResults: {}
12001
12032
  };
12002
12033
  const { filesToFormat, pnpmBuildDependencies, status } = await applyAddons({
12003
12034
  loadedAddons,
@@ -12058,15 +12089,16 @@ async function runAddonsApply({ answers, options, loadedAddons, setupResults, wo
12058
12089
  });
12059
12090
  }
12060
12091
  return {
12061
- nextSteps: getNextSteps(successfulAddons, workspace, answers),
12092
+ nextSteps: getNextSteps(successfulAddons, workspace, answers, setupResults),
12062
12093
  argsFormattedAddons,
12063
12094
  filesToFormat,
12064
- successfulAddons
12095
+ successfulAddons,
12096
+ setupResults
12065
12097
  };
12066
12098
  }
12067
- function getNextSteps(loadedAddons, workspace, answers) {
12068
- return loadedAddons.map((loaded) => {
12069
- const addon = loaded.addon;
12099
+ function getNextSteps(loadedAddons, workspace, answers, setupResults) {
12100
+ return orderAddons(loadedAddons.map((l) => l.addon), setupResults).map((loaded) => {
12101
+ const { addon } = loadedAddons.find((l) => l.addon.id === loaded.id);
12070
12102
  if (!addon.nextSteps) return;
12071
12103
  const addonOptions = answers[addon.id];
12072
12104
  const addonNextSteps = addon.nextSteps({
@@ -12134,7 +12166,8 @@ function formatAddonHelpSection(opts) {
12134
12166
  if (!option) return formatItem(id, "(no options)");
12135
12167
  return formatItem(id, option.choices);
12136
12168
  });
12137
- if (addonList.length > 0) output.push(styleTitle("Add-Ons:"), ...addonList, "");
12169
+ if (addonList.length > 0) output.push(styleTitle("Official Add-Ons:"), ...addonList, "");
12170
+ output.push(styleTitle("Community Add-Ons:"), " Find on: https://www.npmjs.com/search?q=keywords:sv-add", "");
12138
12171
  output.push(styleTitle("Add-On Syntax:"), " <addon> add with defaults (may still prompt)", " <addon>=<opt>:<val> set a single option", " <addon>=<opt1>:<val1>+<opt2>:<val2> set multiple options", " <addon>=<opt>:none explicitly set no value (for multiselect)", " To skip prompts, explicitly set ALL options (use defaults shown above).", "");
12139
12172
  return output;
12140
12173
  }
@@ -12337,7 +12370,7 @@ async function runAddon({ addon, loaded, multiple, workspace, workspaceOptions }
12337
12370
  execute: async (commandArgs, stdio) => {
12338
12371
  const { command, args } = resolveCommand(workspace.packageManager, "execute", commandArgs);
12339
12372
  const addonPrefix = multiple ? `${addon.id}: ` : "";
12340
- const executedCommand = `${command} ${args.join(" ")}`;
12373
+ const executedCommand = [command, ...args].join(" ");
12341
12374
  if (!TESTING) R$1.step(`${addonPrefix}Running external command ${color.optional(`(${executedCommand})`)}`);
12342
12375
  if (workspace.packageManager === "npm") args.unshift("--yes");
12343
12376
  try {
@@ -139,7 +139,7 @@ type Scripts = {
139
139
  condition?: ConditionDefinition;
140
140
  };
141
141
  type SvApi = {
142
- /** Add a package to the pnpm build dependencies. */pnpmBuildDependency: (pkg: string) => void; /** Add a package to the dependencies. */
142
+ /** Add a package to the pnpm onlyBuiltDependencies. */pnpmBuildDependency: (pkg: string) => void; /** Add a package to the dependencies. */
143
143
  dependency: (pkg: string, version: string) => void; /** Add a package to the dev dependencies. */
144
144
  devDependency: (pkg: string, version: string) => void; /** Execute a command in the workspace. */
145
145
  execute: (args: string[], stdio: "inherit" | "pipe") => Promise<void>;
@@ -152,15 +152,15 @@ type SvApi = {
152
152
  };
153
153
  type Addon<Args extends OptionDefinition, Id extends string = string> = {
154
154
  id: Id;
155
- alias?: string;
156
- shortDescription?: string;
155
+ alias?: string; /** one-liner shown in prompts */
156
+ shortDescription?: string; /** link to docs/repo */
157
157
  homepage?: string; /** If true, this addon won't appear in the interactive prompt but can still be used via CLI */
158
158
  hidden?: boolean;
159
159
  options: Args; /** Setup the addon. Will be called before the addon is run. */
160
160
  setup?: (workspace: Workspace & {
161
161
  /** On what official addons does this addon depend on? */dependsOn: (name: keyof typeof officialAddons) => void;
162
- /** Why is this addon not supported?
163
- *
162
+ /**
163
+ * Why is this addon not supported?
164
164
  * @example
165
165
  * if (!isKit) unsupported('Requires SvelteKit');
166
166
  */
@@ -176,7 +176,7 @@ type Addon<Args extends OptionDefinition, Id extends string = string> = {
176
176
  */
177
177
  cancel: (reason: string) => void;
178
178
  }) => MaybePromise<void>; /** Next steps to display after the addon is run. */
179
- nextSteps?: (data: Workspace & {
179
+ nextSteps?: (workspace: Workspace & {
180
180
  options: WorkspaceOptions<Args>;
181
181
  }) => string[];
182
182
  };
@@ -1,4 +1,4 @@
1
- import { A as BooleanQuestion, C as defineAddon, D as WorkspaceOptions, E as Workspace, F as Question, I as SelectQuestion, L as StringQuestion, M as NumberQuestion, N as OptionDefinition, O as createWorkspace, P as OptionValues, R as officialAddons, S as Verification, T as getErrorHint, _ as Scripts, a as Addon, b as TestDefinition, c as AddonReference, d as ConditionDefinition, f as ConfiguredAddon, g as PreparedAddon, h as PackageDefinition, i as add, j as MultiSelectQuestion, k as BaseQuestion, 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 SetupResult, w as defineAddonOptions, x as Tests, y as SvApi } from "../engine-fG9K-byo.mjs";
1
+ import { A as BooleanQuestion, C as defineAddon, D as WorkspaceOptions, E as Workspace, F as Question, I as SelectQuestion, L as StringQuestion, M as NumberQuestion, N as OptionDefinition, O as createWorkspace, P as OptionValues, R as officialAddons, S as Verification, T as getErrorHint, _ as Scripts, a as Addon, b as TestDefinition, c as AddonReference, d as ConditionDefinition, f as ConfiguredAddon, g as PreparedAddon, h as PackageDefinition, i as add, j as MultiSelectQuestion, k as BaseQuestion, 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 SetupResult, w as defineAddonOptions, x as Tests, y as SvApi } from "../engine-BdGJvT03.mjs";
2
2
 
3
3
  //#region src/create/index.d.ts
4
4
  type TemplateType = (typeof templateTypes)[number];
@@ -1,2 +1,2 @@
1
- import { d as defineAddon, f as defineAddonOptions, t as add, u as officialAddons, v as create } from "../engine-lCnxwqd0.mjs";
1
+ import { d as defineAddon, f as defineAddonOptions, t as add, u as officialAddons, v as create } from "../engine-BaqBUQsx.mjs";
2
2
  export { add, create, defineAddon, defineAddonOptions, officialAddons };
@@ -1,5 +1,6 @@
1
- import { r as OptionMap, t as AddonMap } from "../engine-fG9K-byo.mjs";
1
+ import { r as OptionMap, t as AddonMap } from "../engine-BdGJvT03.mjs";
2
2
  import { AgentName } from "@sveltejs/sv-utils";
3
+ import * as _playwright_test0 from "@playwright/test";
3
4
  import { Page } from "@playwright/test";
4
5
  import * as vitest from "vitest";
5
6
  import { TestProject } from "vitest/node";
@@ -102,11 +103,12 @@ declare function prepareServer({
102
103
  buildCommand,
103
104
  previewCommand
104
105
  }: PrepareServerOptions): Promise<PrepareServerReturn>;
106
+ type PlaywrightContext = Pick<typeof _playwright_test0, "chromium">;
105
107
  type VitestContext = Pick<typeof vitest, "inject" | "test" | "beforeAll" | "beforeEach">;
106
- declare function createSetupTest(vitest: VitestContext): <Addons extends AddonMap>(addons: Addons, options?: SetupTestOptions<Addons>) => {
108
+ declare function createSetupTest(vitest: VitestContext, playwright?: PlaywrightContext): <Addons extends AddonMap>(addons: Addons, options?: SetupTestOptions<Addons>) => {
107
109
  test: vitest.TestAPI<Fixtures>;
108
110
  testCases: Array<AddonTestCase<AddonMap>>;
109
111
  prepareServer: typeof prepareServer;
110
112
  };
111
113
  //#endregion
112
- export { AddonTestCase, CreateProject, Fixtures, PrepareServerOptions, PrepareServerReturn, ProjectVariant, SetupTestOptions, VitestContext, addPnpmBuildDependencies, createProject, createSetupTest, prepareServer, setup, setupGlobal, startPreview, variants };
114
+ export { AddonTestCase, CreateProject, Fixtures, PlaywrightContext, PrepareServerOptions, PrepareServerReturn, ProjectVariant, SetupTestOptions, VitestContext, addPnpmBuildDependencies, createProject, createSetupTest, prepareServer, setup, setupGlobal, startPreview, variants };
@@ -1,4 +1,4 @@
1
- import { _ as z, b as __require, g as R, i as addPnpmBuildDependencies, t as add, v as create, x as __toESM, y as __commonJSMin } from "../engine-lCnxwqd0.mjs";
1
+ import { _ as z, b as __require, g as R, i as addPnpmBuildDependencies, t as add, v as create, x as __toESM, y as __commonJSMin } from "../engine-BaqBUQsx.mjs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import process$1 from "node:process";
@@ -753,7 +753,7 @@ async function prepareServer({ cwd, page, buildCommand = "pnpm build", previewCo
753
753
  close
754
754
  };
755
755
  }
756
- function createSetupTest(vitest) {
756
+ function createSetupTest(vitest, playwright) {
757
757
  return function setupTest(addons, options) {
758
758
  const { inject, test: vitestTest, beforeAll, beforeEach } = vitest;
759
759
  const test = vitestTest.extend({});
@@ -765,7 +765,8 @@ function createSetupTest(vitest) {
765
765
  let browser;
766
766
  if (withBrowser) beforeAll(async () => {
767
767
  let chromium;
768
- try {
768
+ if (playwright) chromium = playwright.chromium;
769
+ else try {
769
770
  ({chromium} = await import("@playwright/test"));
770
771
  } catch {
771
772
  throw new Error("Browser testing requires @playwright/test. Install it with: pnpm add -D @playwright/test");
@@ -10,7 +10,7 @@ const browser = false;
10
10
  const { test, prepareServer, testCases } = setupTest(
11
11
  { addon },
12
12
  {
13
- kinds: [{ type: 'default', options: { addon: { who: 'you' } } }],
13
+ kinds: [{ type: 'default', options: { [addon.id]: { who: 'you' } } }],
14
14
  filter: (testCase) => testCase.variant.includes('kit'),
15
15
  browser
16
16
  }
@@ -21,15 +21,18 @@ test.concurrent.for(testCases)(
21
21
  async (testCase, { page, ...ctx }) => {
22
22
  const cwd = ctx.cwd(testCase);
23
23
 
24
- const msg =
25
- "This is a text file made by the Community Addon Template demo for the add-on: '~SV-NAME-TODO~'!";
24
+ const msg = "Community Addon Template demo for the add-on: '~SV-NAME-TODO~'!";
26
25
 
27
26
  const contentPath = path.resolve(cwd, `src/lib/~SV-NAME-TODO~/content.txt`);
28
27
  const contentContent = fs.readFileSync(contentPath, 'utf8');
29
-
30
28
  // Check if we have the imports
31
29
  expect(contentContent).toContain(msg);
32
30
 
31
+ const helloPath = path.resolve(cwd, `src/lib/~SV-NAME-TODO~/HelloComponent.svelte`);
32
+ const helloContent = fs.readFileSync(helloPath, 'utf8');
33
+ // Check if we have the imports
34
+ expect(helloContent).toContain('you');
35
+
33
36
  // For browser testing
34
37
  if (browser) {
35
38
  const { close } = await prepareServer({ cwd, page });
@@ -6,8 +6,8 @@
6
6
  "license": "MIT",
7
7
  "scripts": {
8
8
  "demo-create": "sv create demo --types ts --template minimal --no-add-ons --no-install",
9
- "demo-add": "sv add file:../ --cwd demo --no-git-check --no-install",
10
- "demo-add:ci": "sv add file:../=who:you --cwd demo --no-git-check --no-download-check --no-install",
9
+ "demo-add": "pnpm build && sv add file:../ --cwd demo --no-git-check --no-install",
10
+ "demo-add:ci": "pnpm build && sv add file:../=who:you --cwd demo --no-git-check --no-download-check --no-install",
11
11
  "build": "tsdown",
12
12
  "prepublishOnly": "npm run build",
13
13
  "test": "vitest run"
@@ -15,16 +15,11 @@
15
15
  "files": ["dist"],
16
16
  "exports": {
17
17
  ".": {
18
- "default": "./src/index.js"
18
+ "default": "./dist/index.mjs"
19
19
  }
20
20
  },
21
21
  "publishConfig": {
22
- "access": "public",
23
- "exports": {
24
- ".": {
25
- "default": "./dist/index.js"
26
- }
27
- }
22
+ "access": "public"
28
23
  },
29
24
  "peerDependencies": {
30
25
  "sv": "latest"
@@ -37,5 +32,5 @@
37
32
  "tsdown": "^0.21.4",
38
33
  "vitest": "^4.1.0"
39
34
  },
40
- "keywords": ["sv-add"]
35
+ "keywords": ["sv-add", "svelte", "sveltekit"]
41
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sv",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
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.0.4"
28
+ "@sveltejs/sv-utils": "0.1.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0",