vela 0.10.0 → 0.10.1

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.js CHANGED
@@ -7,7 +7,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "vela",
10
- version: "0.10.0",
10
+ version: "0.10.1",
11
11
  type: "module",
12
12
  description: "A CLI for creating and updating SvelteKit projects",
13
13
  license: "MIT",
@@ -279,6 +279,161 @@ function stubCommand(name, description, fullName) {
279
279
  return cmd;
280
280
  }
281
281
 
282
+ // src/lib/workspace.ts
283
+ import fs3 from "node:fs";
284
+ import path2 from "node:path";
285
+ import process3 from "node:process";
286
+
287
+ // src/lib/constants.ts
288
+ var DATA_DIR = "data";
289
+ var MIGRATIONS_DIR = "migrations";
290
+ var PUBLIC_DIR = "(public)";
291
+ var APP_DIR = "(app)";
292
+ var LEGAL_DIR = "(legal)";
293
+ var API_URL = "https://velastack.dev";
294
+ var FIXTURE_PREFIX = "vela";
295
+
296
+ // src/lib/package-json.ts
297
+ import fs2 from "node:fs";
298
+ var DEP_KINDS = ["dependencies", "devDependencies"];
299
+ function mergePackageJson(user, template) {
300
+ const merged = structuredClone(user);
301
+ const added = [];
302
+ const conflicts = [];
303
+ const replaced = [];
304
+ for (const kind of DEP_KINDS) {
305
+ const templateDeps = template[kind];
306
+ if (!templateDeps) continue;
307
+ const userDeps = merged[kind] ??= {};
308
+ const otherKind = kind === "dependencies" ? "devDependencies" : "dependencies";
309
+ const userOther = user[otherKind] ?? {};
310
+ for (const [name, templateValue] of Object.entries(templateDeps)) {
311
+ if (name in userDeps) {
312
+ if (userDeps[name] !== templateValue) {
313
+ conflicts.push({ kind, name, templateValue, userValue: userDeps[name] });
314
+ }
315
+ continue;
316
+ }
317
+ if (name in userOther) {
318
+ continue;
319
+ }
320
+ userDeps[name] = templateValue;
321
+ added.push({ kind, name, templateValue });
322
+ }
323
+ }
324
+ const templateScripts = template.scripts;
325
+ if (templateScripts) {
326
+ const userScripts = merged.scripts ??= {};
327
+ for (const [name, templateValue] of Object.entries(templateScripts)) {
328
+ const existing = userScripts[name];
329
+ if (existing === void 0) {
330
+ userScripts[name] = templateValue;
331
+ added.push({ kind: "scripts", name, templateValue });
332
+ continue;
333
+ }
334
+ if (existing === templateValue) continue;
335
+ userScripts[name] = templateValue;
336
+ replaced.push({ kind: "scripts", name, templateValue, userValue: existing });
337
+ }
338
+ }
339
+ for (const kind of DEP_KINDS) {
340
+ const deps = merged[kind];
341
+ if (deps) merged[kind] = sortKeys(deps);
342
+ }
343
+ return { merged, added, conflicts, replaced };
344
+ }
345
+ function readPackageJson(path36) {
346
+ return JSON.parse(fs2.readFileSync(path36, "utf8"));
347
+ }
348
+ var PACKAGE_NAME_PLACEHOLDER = /~TODO~/g;
349
+ var APP_NAME_PLACEHOLDER = /~APP_NAME~/g;
350
+ var CLI_VERSION_PLACEHOLDER = /~VELA_VERSION~/g;
351
+ function fillTemplatePlaceholders(raw, values) {
352
+ const packageName = toValidPackageName(values.appName);
353
+ const appName = escapeSingleQuoted(values.appName);
354
+ return raw.replace(PACKAGE_NAME_PLACEHOLDER, () => packageName).replace(APP_NAME_PLACEHOLDER, () => appName).replace(CLI_VERSION_PLACEHOLDER, () => values.cliVersion);
355
+ }
356
+ function escapeSingleQuoted(value) {
357
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
358
+ }
359
+ function readTemplatePackageJson(path36, values) {
360
+ const raw = fillTemplatePlaceholders(fs2.readFileSync(path36, "utf8"), values);
361
+ return JSON.parse(raw);
362
+ }
363
+ function writePackageJson(path36, pkg) {
364
+ fs2.writeFileSync(path36, JSON.stringify(pkg, null, " ") + "\n");
365
+ }
366
+ function toValidPackageName(name) {
367
+ return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9~.-]+/g, "-");
368
+ }
369
+ function sortKeys(obj) {
370
+ const sorted = {};
371
+ for (const key of Object.keys(obj).sort()) sorted[key] = obj[key];
372
+ return sorted;
373
+ }
374
+
375
+ // src/lib/workspace.ts
376
+ function findWorkspaceRoot(from = process3.cwd()) {
377
+ let currentDir = from;
378
+ while (currentDir !== path2.parse(currentDir).root) {
379
+ if (fs3.existsSync(path2.join(currentDir, "package.json"))) return currentDir;
380
+ currentDir = path2.dirname(currentDir);
381
+ }
382
+ return null;
383
+ }
384
+ function hasBackend(from = process3.cwd()) {
385
+ const root = findWorkspaceRoot(from);
386
+ return root !== null && fs3.existsSync(path2.join(root, DATA_DIR));
387
+ }
388
+ async function getWorkspace() {
389
+ const workspaceRootDir = findWorkspaceRoot();
390
+ if (!workspaceRootDir) {
391
+ throw new Error("Could not find workspace root (no package.json found)");
392
+ }
393
+ const routesDir = path2.join("src", "routes");
394
+ const fullRoutesPath = path2.join(workspaceRootDir, routesDir);
395
+ if (!fs3.existsSync(fullRoutesPath)) {
396
+ throw new Error("Could not find src/routes directory");
397
+ }
398
+ let publicRoutesDir = path2.join(routesDir, PUBLIC_DIR);
399
+ if (!fs3.existsSync(path2.join(workspaceRootDir, publicRoutesDir))) {
400
+ publicRoutesDir = routesDir;
401
+ }
402
+ let appRoutesDir;
403
+ const appRoutesPath = path2.join(fullRoutesPath, APP_DIR);
404
+ const isAppMode = fs3.existsSync(appRoutesPath);
405
+ if (isAppMode) appRoutesDir = path2.join(routesDir, APP_DIR);
406
+ const isPaymentsMode = fs3.existsSync(
407
+ path2.join(workspaceRootDir, routesDir, "webhooks", "stripe")
408
+ );
409
+ const features = detectFeatures(workspaceRootDir, { isAppMode, isPaymentsMode });
410
+ return {
411
+ workspaceRootDir,
412
+ routesDir,
413
+ publicRoutesDir,
414
+ appRoutesDir,
415
+ isAppMode,
416
+ isPaymentsMode,
417
+ features
418
+ };
419
+ }
420
+ function detectFeatures(root, { isAppMode, isPaymentsMode }) {
421
+ const has = (rel) => fs3.existsSync(path2.join(root, rel));
422
+ const pkg = readPackageJson(path2.join(root, "package.json"));
423
+ const hasDep = (name) => Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
424
+ return {
425
+ auth: isAppMode,
426
+ api: has("src/routes/api"),
427
+ apiKeys: has("src/routes/api/api-keys") || has("src/routes/(app)/api-keys"),
428
+ backend: has(DATA_DIR),
429
+ i18n: has("src/lib/i18n") || has("messages"),
430
+ teams: has("src/routes/(app)/teams") || has("src/lib/teams"),
431
+ payments: isPaymentsMode,
432
+ blog: hasDep("mdsvex"),
433
+ contentNegotiation: hasDep("sveltekit-negotiate")
434
+ };
435
+ }
436
+
282
437
  // src/commands/bless.ts
283
438
  import fs12 from "node:fs";
284
439
  import path11 from "node:path";
@@ -317,32 +472,32 @@ function toFlag(key) {
317
472
  }
318
473
 
319
474
  // src/lib/templates.ts
320
- import fs2 from "node:fs";
321
- import path2 from "node:path";
475
+ import fs4 from "node:fs";
476
+ import path3 from "node:path";
322
477
  import { fileURLToPath } from "node:url";
323
478
  var TEMPLATE_MANIFEST = "template.json";
324
479
  var DEFAULT_TEMPLATE = "minimal";
325
480
  var cached;
326
481
  function templatesDir() {
327
- let dir = path2.dirname(fileURLToPath(import.meta.url));
328
- const { root } = path2.parse(dir);
482
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
483
+ const { root } = path3.parse(dir);
329
484
  while (dir !== root) {
330
- const candidate = path2.join(dir, "templates");
485
+ const candidate = path3.join(dir, "templates");
331
486
  if (holdsManifest(candidate)) return candidate;
332
- dir = path2.dirname(dir);
487
+ dir = path3.dirname(dir);
333
488
  }
334
489
  throw new Error("Could not locate the templates directory");
335
490
  }
336
491
  function holdsManifest(dir) {
337
- if (!fs2.existsSync(dir)) return false;
338
- return fs2.readdirSync(dir, { withFileTypes: true }).some(
339
- (entry) => entry.isDirectory() && fs2.existsSync(path2.join(dir, entry.name, TEMPLATE_MANIFEST))
492
+ if (!fs4.existsSync(dir)) return false;
493
+ return fs4.readdirSync(dir, { withFileTypes: true }).some(
494
+ (entry) => entry.isDirectory() && fs4.existsSync(path3.join(dir, entry.name, TEMPLATE_MANIFEST))
340
495
  );
341
496
  }
342
497
  function listProjectTemplates() {
343
498
  if (cached) return cached;
344
499
  const root = templatesDir();
345
- cached = fs2.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readManifest(root, entry.name)).filter((template) => template !== void 0).sort((a, b) => a.name.localeCompare(b.name));
500
+ cached = fs4.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readManifest(root, entry.name)).filter((template) => template !== void 0).sort((a, b) => a.name.localeCompare(b.name));
346
501
  return cached;
347
502
  }
348
503
  function projectTemplateNames(options = {}) {
@@ -356,11 +511,11 @@ function findProjectTemplate(name) {
356
511
  return template;
357
512
  }
358
513
  function readManifest(root, name) {
359
- const manifestPath = path2.join(root, name, TEMPLATE_MANIFEST);
360
- if (!fs2.existsSync(manifestPath)) return void 0;
514
+ const manifestPath = path3.join(root, name, TEMPLATE_MANIFEST);
515
+ if (!fs4.existsSync(manifestPath)) return void 0;
361
516
  let parsed;
362
517
  try {
363
- parsed = JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
518
+ parsed = JSON.parse(fs4.readFileSync(manifestPath, "utf8"));
364
519
  } catch (e) {
365
520
  throw new Error(
366
521
  `Template ${name} has an unreadable ${TEMPLATE_MANIFEST}: ${e.message}`
@@ -376,14 +531,14 @@ function readManifest(root, name) {
376
531
  name,
377
532
  description: manifest.description,
378
533
  backend: manifest.backend,
379
- dir: path2.join(root, name)
534
+ dir: path3.join(root, name)
380
535
  };
381
536
  }
382
537
 
383
538
  // src/lib/package-manager.ts
384
- import fs3 from "node:fs";
385
- import path3 from "node:path";
386
- import process3 from "node:process";
539
+ import fs5 from "node:fs";
540
+ import path4 from "node:path";
541
+ import process4 from "node:process";
387
542
  import { exec } from "tinyexec";
388
543
  import { Option } from "commander";
389
544
  import * as p2 from "@clack/prompts";
@@ -399,7 +554,7 @@ var installOption = new Option(
399
554
  "installs dependencies with a specified package manager"
400
555
  ).choices(AGENT_NAMES);
401
556
  function getUserAgent() {
402
- const userAgent = process3.env.npm_config_user_agent;
557
+ const userAgent = process4.env.npm_config_user_agent;
403
558
  if (!userAgent) return void 0;
404
559
  const pmSpec = userAgent.split(" ")[0];
405
560
  const separatorPos = pmSpec.lastIndexOf("/");
@@ -409,7 +564,7 @@ function getUserAgent() {
409
564
  async function packageManagerPrompt(cwd) {
410
565
  const detected = await detect({ cwd });
411
566
  const agent = detected?.name ?? getUserAgent();
412
- if (!process3.stdout.isTTY) return agent;
567
+ if (!process4.stdout.isTTY) return agent;
413
568
  const options = [
414
569
  { label: "None", value: void 0 },
415
570
  ...AGENT_NAMES.map((pm2) => ({ value: pm2, label: pm2 }))
@@ -421,14 +576,14 @@ async function packageManagerPrompt(cwd) {
421
576
  });
422
577
  if (p2.isCancel(pm)) {
423
578
  p2.cancel("Operation cancelled.");
424
- process3.exit(1);
579
+ process4.exit(1);
425
580
  }
426
581
  return pm;
427
582
  }
428
583
  async function installDependencies(agent, cwd) {
429
584
  const task = p2.taskLog({
430
585
  title: `Installing dependencies with ${agent}...`,
431
- limit: Math.ceil(process3.stdout.rows / 2),
586
+ limit: Math.ceil(process4.stdout.rows / 2),
432
587
  spacing: 0,
433
588
  retainLog: true
434
589
  });
@@ -445,14 +600,14 @@ async function installDependencies(agent, cwd) {
445
600
  } catch {
446
601
  task.error("Failed to install dependencies");
447
602
  p2.cancel("Operation failed.");
448
- process3.exit(2);
603
+ process4.exit(2);
449
604
  }
450
605
  }
451
606
  function addPnpmBuildDependencies(cwd, packageManager, allowedPackages) {
452
607
  if (!packageManager || packageManager !== "pnpm") return;
453
- const pkgPath = path3.join(cwd, "package.json");
454
- if (!fs3.existsSync(pkgPath)) return;
455
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
608
+ const pkgPath = path4.join(cwd, "package.json");
609
+ if (!fs5.existsSync(pkgPath)) return;
610
+ const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
456
611
  pkg.pnpm ??= {};
457
612
  pkg.pnpm.onlyBuiltDependencies ??= [];
458
613
  for (const name of allowedPackages) {
@@ -460,30 +615,19 @@ function addPnpmBuildDependencies(cwd, packageManager, allowedPackages) {
460
615
  pkg.pnpm.onlyBuiltDependencies.push(name);
461
616
  }
462
617
  }
463
- fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, " ") + "\n");
618
+ fs5.writeFileSync(pkgPath, JSON.stringify(pkg, null, " ") + "\n");
464
619
  }
465
620
 
466
621
  // src/lib/pocketbase.ts
467
- import fs4 from "node:fs";
622
+ import fs6 from "node:fs";
468
623
  import net from "node:net";
469
- import path4 from "node:path";
470
- import process4 from "node:process";
624
+ import path5 from "node:path";
625
+ import process5 from "node:process";
471
626
  import { spawn } from "node:child_process";
472
627
  import PocketBase from "pocketbase";
473
628
  import { detect as detect2 } from "package-manager-detector";
474
629
  import { resolveCommand } from "package-manager-detector/commands";
475
630
  import { x } from "tinyexec";
476
-
477
- // src/lib/constants.ts
478
- var DATA_DIR = "data";
479
- var MIGRATIONS_DIR = "migrations";
480
- var PUBLIC_DIR = "(public)";
481
- var APP_DIR = "(app)";
482
- var LEGAL_DIR = "(legal)";
483
- var API_URL = "https://velastack.dev";
484
- var FIXTURE_PREFIX = "vela";
485
-
486
- // src/lib/pocketbase.ts
487
631
  function findFreePort(host = "localhost") {
488
632
  return new Promise((resolve, reject) => {
489
633
  const server = net.createServer();
@@ -589,9 +733,9 @@ async function authWithRetries(pb, email3, password8, attempts = 3) {
589
733
  }
590
734
  }
591
735
  function getPocketbaseMetadata(cwd) {
592
- const metadataPath = path4.join(cwd, "node_modules", ".vite", "_pocketbase_metadata.json");
593
- if (fs4.existsSync(metadataPath)) {
594
- return JSON.parse(fs4.readFileSync(metadataPath, "utf8"));
736
+ const metadataPath = path5.join(cwd, "node_modules", ".vite", "_pocketbase_metadata.json");
737
+ if (fs6.existsSync(metadataPath)) {
738
+ return JSON.parse(fs6.readFileSync(metadataPath, "utf8"));
595
739
  }
596
740
  return null;
597
741
  }
@@ -604,12 +748,12 @@ async function execPackageBin(cwd, args, stdio = "pipe") {
604
748
  return x(command, resolvedArgs, { nodeOptions: { cwd, stdio }, throwOnError: true });
605
749
  }
606
750
  async function withPocketbase(cwd, fn, creds) {
607
- const dir = path4.join(cwd, DATA_DIR);
608
- const migrationsDir = path4.join(cwd, MIGRATIONS_DIR);
751
+ const dir = path5.join(cwd, DATA_DIR);
752
+ const migrationsDir = path5.join(cwd, MIGRATIONS_DIR);
609
753
  const host = "localhost";
610
- const email3 = creds?.email ?? process4.env.POCKETBASE_SUPERUSER_EMAIL;
611
- const password8 = creds?.password ?? process4.env.POCKETBASE_SUPERUSER_PASSWORD;
612
- if (!fs4.existsSync(dir)) {
754
+ const email3 = creds?.email ?? process5.env.POCKETBASE_SUPERUSER_EMAIL;
755
+ const password8 = creds?.password ?? process5.env.POCKETBASE_SUPERUSER_PASSWORD;
756
+ if (!fs6.existsSync(dir)) {
613
757
  throw new Error("PocketBase data directory does not exist");
614
758
  }
615
759
  const metadata = getPocketbaseMetadata(cwd);
@@ -634,10 +778,10 @@ async function withPocketbase(cwd, fn, creds) {
634
778
  }
635
779
  }
636
780
  async function createSuperuser(cwd, email3, password8) {
637
- const dir = path4.join(cwd, DATA_DIR);
638
- const migrationsDir = path4.join(cwd, MIGRATIONS_DIR);
639
- fs4.mkdirSync(dir, { recursive: true });
640
- fs4.mkdirSync(migrationsDir, { recursive: true });
781
+ const dir = path5.join(cwd, DATA_DIR);
782
+ const migrationsDir = path5.join(cwd, MIGRATIONS_DIR);
783
+ fs6.mkdirSync(dir, { recursive: true });
784
+ fs6.mkdirSync(migrationsDir, { recursive: true });
641
785
  await execPackageBin(
642
786
  cwd,
643
787
  [
@@ -661,8 +805,8 @@ async function launchPocketbase(cwd, {
661
805
  password: password8
662
806
  }) {
663
807
  const host = "localhost";
664
- fs4.mkdirSync(dir, { recursive: true });
665
- fs4.mkdirSync(migrationsDir, { recursive: true });
808
+ fs6.mkdirSync(dir, { recursive: true });
809
+ fs6.mkdirSync(migrationsDir, { recursive: true });
666
810
  await execPackageBin(
667
811
  cwd,
668
812
  [
@@ -696,8 +840,8 @@ async function launchPocketbase(cwd, {
696
840
  }
697
841
 
698
842
  // src/lib/env.ts
699
- import fs5 from "node:fs";
700
- import path5 from "node:path";
843
+ import fs7 from "node:fs";
844
+ import path6 from "node:path";
701
845
  function addEnvVar(content, key, value) {
702
846
  if (content.includes(`${key}=`)) return content;
703
847
  return appendLine(content, `${key}=${value}`);
@@ -712,99 +856,20 @@ function appendLine(existing, line) {
712
856
  return withNewline + line + "\n";
713
857
  }
714
858
  function writeEnvFile(cwd, vars, comments = []) {
715
- const envPath = path5.join(cwd, ".env");
716
- let content = fs5.existsSync(envPath) ? fs5.readFileSync(envPath, "utf8") : "";
859
+ const envPath = path6.join(cwd, ".env");
860
+ let content = fs7.existsSync(envPath) ? fs7.readFileSync(envPath, "utf8") : "";
717
861
  for (const comment of comments) content = addEnvComment(content, comment);
718
862
  for (const [key, value] of Object.entries(vars)) content = addEnvVar(content, key, value);
719
- fs5.writeFileSync(envPath, content);
720
- }
721
-
722
- // src/lib/package-json.ts
723
- import fs6 from "node:fs";
724
- var DEP_KINDS = ["dependencies", "devDependencies"];
725
- function mergePackageJson(user, template) {
726
- const merged = structuredClone(user);
727
- const added = [];
728
- const conflicts = [];
729
- const replaced = [];
730
- for (const kind of DEP_KINDS) {
731
- const templateDeps = template[kind];
732
- if (!templateDeps) continue;
733
- const userDeps = merged[kind] ??= {};
734
- const otherKind = kind === "dependencies" ? "devDependencies" : "dependencies";
735
- const userOther = user[otherKind] ?? {};
736
- for (const [name, templateValue] of Object.entries(templateDeps)) {
737
- if (name in userDeps) {
738
- if (userDeps[name] !== templateValue) {
739
- conflicts.push({ kind, name, templateValue, userValue: userDeps[name] });
740
- }
741
- continue;
742
- }
743
- if (name in userOther) {
744
- continue;
745
- }
746
- userDeps[name] = templateValue;
747
- added.push({ kind, name, templateValue });
748
- }
749
- }
750
- const templateScripts = template.scripts;
751
- if (templateScripts) {
752
- const userScripts = merged.scripts ??= {};
753
- for (const [name, templateValue] of Object.entries(templateScripts)) {
754
- const existing = userScripts[name];
755
- if (existing === void 0) {
756
- userScripts[name] = templateValue;
757
- added.push({ kind: "scripts", name, templateValue });
758
- continue;
759
- }
760
- if (existing === templateValue) continue;
761
- userScripts[name] = templateValue;
762
- replaced.push({ kind: "scripts", name, templateValue, userValue: existing });
763
- }
764
- }
765
- for (const kind of DEP_KINDS) {
766
- const deps = merged[kind];
767
- if (deps) merged[kind] = sortKeys(deps);
768
- }
769
- return { merged, added, conflicts, replaced };
770
- }
771
- function readPackageJson(path36) {
772
- return JSON.parse(fs6.readFileSync(path36, "utf8"));
773
- }
774
- var PACKAGE_NAME_PLACEHOLDER = /~TODO~/g;
775
- var APP_NAME_PLACEHOLDER = /~APP_NAME~/g;
776
- var CLI_VERSION_PLACEHOLDER = /~VELA_VERSION~/g;
777
- function fillTemplatePlaceholders(raw, values) {
778
- const packageName = toValidPackageName(values.appName);
779
- const appName = escapeSingleQuoted(values.appName);
780
- return raw.replace(PACKAGE_NAME_PLACEHOLDER, () => packageName).replace(APP_NAME_PLACEHOLDER, () => appName).replace(CLI_VERSION_PLACEHOLDER, () => values.cliVersion);
781
- }
782
- function escapeSingleQuoted(value) {
783
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
784
- }
785
- function readTemplatePackageJson(path36, values) {
786
- const raw = fillTemplatePlaceholders(fs6.readFileSync(path36, "utf8"), values);
787
- return JSON.parse(raw);
788
- }
789
- function writePackageJson(path36, pkg) {
790
- fs6.writeFileSync(path36, JSON.stringify(pkg, null, " ") + "\n");
791
- }
792
- function toValidPackageName(name) {
793
- return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9~.-]+/g, "-");
794
- }
795
- function sortKeys(obj) {
796
- const sorted = {};
797
- for (const key of Object.keys(obj).sort()) sorted[key] = obj[key];
798
- return sorted;
863
+ fs7.writeFileSync(envPath, content);
799
864
  }
800
865
 
801
866
  // src/lib/config-merge.ts
802
- import fs8 from "node:fs";
803
- import path7 from "node:path";
867
+ import fs9 from "node:fs";
868
+ import path8 from "node:path";
804
869
 
805
870
  // src/lib/config-target.ts
806
- import fs7 from "node:fs";
807
- import path6 from "node:path";
871
+ import fs8 from "node:fs";
872
+ import path7 from "node:path";
808
873
  import {
809
874
  Project,
810
875
  QuoteKind,
@@ -824,8 +889,8 @@ var SVELTE_CONFIG_CANDIDATES = [
824
889
  ];
825
890
  function probeFirstExisting(root, candidates) {
826
891
  for (const rel of candidates) {
827
- const abs = path6.join(root, rel);
828
- if (fs7.existsSync(abs)) return abs;
892
+ const abs = path7.join(root, rel);
893
+ if (fs8.existsSync(abs)) return abs;
829
894
  }
830
895
  return null;
831
896
  }
@@ -901,7 +966,7 @@ function mergeSvelteConfig(projectRoot) {
901
966
  };
902
967
  }
903
968
  function mergeRunesIntoViteArg(vite, arg) {
904
- const file = path7.basename(vite.filePath);
969
+ const file = path8.basename(vite.filePath);
905
970
  const compilerOptions = getOrCreateObjectLiteralProperty(arg, "compilerOptions", "{}");
906
971
  if (!compilerOptions) {
907
972
  return {
@@ -920,8 +985,8 @@ function mergeRunesIntoViteArg(vite, arg) {
920
985
  return { applied: true, reason: "added runes compilerOption", file };
921
986
  }
922
987
  function mergeRunesIntoSvelteConfig(filePath) {
923
- const file = path7.basename(filePath);
924
- const original = fs8.readFileSync(filePath, "utf8");
988
+ const file = path8.basename(filePath);
989
+ const original = fs9.readFileSync(filePath, "utf8");
925
990
  if (/runes\s*:/m.test(original)) {
926
991
  return { applied: false, reason: "runes already configured", file };
927
992
  }
@@ -944,11 +1009,11 @@ function mergeRunesIntoSvelteConfig(filePath) {
944
1009
  }
945
1010
  const insertAt = anchor.index + anchor[0].length;
946
1011
  const updated = original.slice(0, insertAt) + RUNES_SNIPPET + original.slice(insertAt);
947
- fs8.writeFileSync(filePath, updated);
1012
+ fs9.writeFileSync(filePath, updated);
948
1013
  return { applied: true, reason: "added runes compilerOption", file };
949
1014
  }
950
1015
  function mergeViteConfig(filePath) {
951
- if (!fs8.existsSync(filePath)) {
1016
+ if (!fs9.existsSync(filePath)) {
952
1017
  return {
953
1018
  applied: false,
954
1019
  reason: "vite.config.ts not found",
@@ -956,7 +1021,7 @@ function mergeViteConfig(filePath) {
956
1021
  // then add tailwindcss() to the plugins array`
957
1022
  };
958
1023
  }
959
- const original = fs8.readFileSync(filePath, "utf8");
1024
+ const original = fs9.readFileSync(filePath, "utf8");
960
1025
  if (original.includes("@tailwindcss/vite")) {
961
1026
  return { applied: false, reason: "tailwindcss plugin already present" };
962
1027
  }
@@ -975,14 +1040,14 @@ function mergeViteConfig(filePath) {
975
1040
  const trailing = withImport.slice(insertAt);
976
1041
  const prefix = /^\s*\]/.test(trailing) ? "tailwindcss()" : "tailwindcss(), ";
977
1042
  const updated = withImport.slice(0, insertAt) + prefix + withImport.slice(insertAt);
978
- fs8.writeFileSync(filePath, updated);
1043
+ fs9.writeFileSync(filePath, updated);
979
1044
  return { applied: true, reason: "added @tailwindcss/vite plugin" };
980
1045
  }
981
1046
  function mergeTsconfig(filePath) {
982
- if (!fs8.existsSync(filePath)) {
1047
+ if (!fs9.existsSync(filePath)) {
983
1048
  return { applied: false, reason: "tsconfig.json not found" };
984
1049
  }
985
- const original = fs8.readFileSync(filePath, "utf8");
1050
+ const original = fs9.readFileSync(filePath, "utf8");
986
1051
  if (/rewriteRelativeImportExtensions/.test(original)) {
987
1052
  return { applied: false, reason: "rewriteRelativeImportExtensions already set" };
988
1053
  }
@@ -998,7 +1063,7 @@ function mergeTsconfig(filePath) {
998
1063
  const indent = detectIndent(original, insertAt);
999
1064
  const updated = original.slice(0, insertAt) + `
1000
1065
  ${indent}"rewriteRelativeImportExtensions": true,` + original.slice(insertAt);
1001
- fs8.writeFileSync(filePath, updated);
1066
+ fs9.writeFileSync(filePath, updated);
1002
1067
  return { applied: true, reason: "added rewriteRelativeImportExtensions" };
1003
1068
  }
1004
1069
  var GITIGNORE_ENTRIES = [
@@ -1010,7 +1075,7 @@ var GITIGNORE_ENTRIES = [
1010
1075
  "vite.config.ts.timestamp-*"
1011
1076
  ];
1012
1077
  function mergeGitignore(filePath) {
1013
- const existing = fs8.existsSync(filePath) ? fs8.readFileSync(filePath, "utf8") : "";
1078
+ const existing = fs9.existsSync(filePath) ? fs9.readFileSync(filePath, "utf8") : "";
1014
1079
  const lines = existing.split("\n").map((l) => l.trim());
1015
1080
  const missing = GITIGNORE_ENTRIES.filter((entry) => !lines.includes(entry));
1016
1081
  if (missing.length === 0) {
@@ -1019,7 +1084,7 @@ function mergeGitignore(filePath) {
1019
1084
  const needsNewline = existing.length > 0 && !existing.endsWith("\n");
1020
1085
  const appended = `${existing}${needsNewline ? "\n" : ""}${missing.join("\n")}
1021
1086
  `;
1022
- fs8.writeFileSync(filePath, appended);
1087
+ fs9.writeFileSync(filePath, appended);
1023
1088
  return { applied: true, reason: `added ${missing.length} gitignore entries` };
1024
1089
  }
1025
1090
  function addImport(source, importLine) {
@@ -1044,75 +1109,14 @@ function detectIndent(source, atOffset) {
1044
1109
  }
1045
1110
 
1046
1111
  // src/lib/scaffold-detect.ts
1047
- import fs9 from "node:fs";
1048
- import path8 from "node:path";
1049
- var VANILLA_MARKER = "Welcome to SvelteKit";
1050
- var PAGE_REL = path8.join("src", "routes", "+page.svelte");
1051
- function isVanillaRoutes(cwd) {
1052
- const pagePath = path8.join(cwd, PAGE_REL);
1053
- if (!fs9.existsSync(pagePath)) return false;
1054
- return fs9.readFileSync(pagePath, "utf8").includes(VANILLA_MARKER);
1055
- }
1056
-
1057
- // src/lib/workspace.ts
1058
1112
  import fs10 from "node:fs";
1059
1113
  import path9 from "node:path";
1060
- import process5 from "node:process";
1061
- async function getWorkspace() {
1062
- let currentDir = process5.cwd();
1063
- let workspaceRootDir = "";
1064
- while (currentDir !== path9.parse(currentDir).root) {
1065
- if (fs10.existsSync(path9.join(currentDir, "package.json"))) {
1066
- workspaceRootDir = currentDir;
1067
- break;
1068
- }
1069
- currentDir = path9.dirname(currentDir);
1070
- }
1071
- if (!workspaceRootDir) {
1072
- throw new Error("Could not find workspace root (no package.json found)");
1073
- }
1074
- const routesDir = path9.join("src", "routes");
1075
- const fullRoutesPath = path9.join(workspaceRootDir, routesDir);
1076
- if (!fs10.existsSync(fullRoutesPath)) {
1077
- throw new Error("Could not find src/routes directory");
1078
- }
1079
- let publicRoutesDir = path9.join(routesDir, PUBLIC_DIR);
1080
- if (!fs10.existsSync(path9.join(workspaceRootDir, publicRoutesDir))) {
1081
- publicRoutesDir = routesDir;
1082
- }
1083
- let appRoutesDir;
1084
- const appRoutesPath = path9.join(fullRoutesPath, APP_DIR);
1085
- const isAppMode = fs10.existsSync(appRoutesPath);
1086
- if (isAppMode) appRoutesDir = path9.join(routesDir, APP_DIR);
1087
- const isPaymentsMode = fs10.existsSync(
1088
- path9.join(workspaceRootDir, routesDir, "webhooks", "stripe")
1089
- );
1090
- const features = detectFeatures(workspaceRootDir, { isAppMode, isPaymentsMode });
1091
- return {
1092
- workspaceRootDir,
1093
- routesDir,
1094
- publicRoutesDir,
1095
- appRoutesDir,
1096
- isAppMode,
1097
- isPaymentsMode,
1098
- features
1099
- };
1100
- }
1101
- function detectFeatures(root, { isAppMode, isPaymentsMode }) {
1102
- const has = (rel) => fs10.existsSync(path9.join(root, rel));
1103
- const pkg = readPackageJson(path9.join(root, "package.json"));
1104
- const hasDep = (name) => Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
1105
- return {
1106
- auth: isAppMode,
1107
- api: has("src/routes/api"),
1108
- apiKeys: has("src/routes/api/api-keys") || has("src/routes/(app)/api-keys"),
1109
- backend: has(DATA_DIR),
1110
- i18n: has("src/lib/i18n") || has("messages"),
1111
- teams: has("src/routes/(app)/teams") || has("src/lib/teams"),
1112
- payments: isPaymentsMode,
1113
- blog: hasDep("mdsvex"),
1114
- contentNegotiation: hasDep("sveltekit-negotiate")
1115
- };
1114
+ var VANILLA_MARKER = "Welcome to SvelteKit";
1115
+ var PAGE_REL = path9.join("src", "routes", "+page.svelte");
1116
+ function isVanillaRoutes(cwd) {
1117
+ const pagePath = path9.join(cwd, PAGE_REL);
1118
+ if (!fs10.existsSync(pagePath)) return false;
1119
+ return fs10.readFileSync(pagePath, "utf8").includes(VANILLA_MARKER);
1116
1120
  }
1117
1121
 
1118
1122
  // src/lib/result-report.ts
@@ -5605,17 +5609,25 @@ import fs27 from "node:fs";
5605
5609
  import path29 from "node:path";
5606
5610
  import process17 from "node:process";
5607
5611
  import { performance } from "node:perf_hooks";
5608
- import { Command as Command65 } from "commander";
5612
+ import { Command as Command65, InvalidArgumentError as InvalidArgumentError5 } from "commander";
5609
5613
  import pc5 from "picocolors";
5610
5614
  import PocketBase2 from "pocketbase";
5611
- var dev = new Command65("dev").description("start the development server").configureHelp(helpConfig).action(async () => {
5615
+ function parsePort(value) {
5616
+ const port = Number(value);
5617
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
5618
+ throw new InvalidArgumentError5("must be a whole number between 1 and 65535.");
5619
+ }
5620
+ return port;
5621
+ }
5622
+ var dev = new Command65("dev").description("start the development server").option("--open", "open the app in a browser once the server is ready").option("--host [host]", "expose the server on the network").option("--port <port>", "port to listen on", parsePort).configureHelp(helpConfig).action(async (options) => {
5612
5623
  const cwd = process17.cwd();
5613
5624
  const startTime = performance.now();
5614
5625
  const { createServer, version } = await import("vite");
5615
5626
  const viteMetadataDir = path29.join(cwd, "node_modules", ".vite");
5616
5627
  const viteMetadataFile = path29.join(viteMetadataDir, "_pocketbase_metadata.json");
5617
5628
  let pbProc;
5618
- const needsStart = !process17.env.POCKETBASE_URL;
5629
+ const backend3 = hasBackend(cwd);
5630
+ const needsStart = backend3 && !process17.env.POCKETBASE_URL;
5619
5631
  const cleanup = () => {
5620
5632
  if (pbProc?.pid) pbProc.kill();
5621
5633
  if (fs27.existsSync(viteMetadataFile)) fs27.rmSync(viteMetadataFile);
@@ -5641,8 +5653,13 @@ var dev = new Command65("dev").description("start the development server").confi
5641
5653
  process17.exit(0);
5642
5654
  });
5643
5655
  }
5644
- const server = await createServer();
5656
+ const serverOptions = {};
5657
+ if (options.open !== void 0) serverOptions.open = options.open;
5658
+ if (options.host !== void 0) serverOptions.host = options.host;
5659
+ if (options.port !== void 0) serverOptions.port = options.port;
5660
+ const server = await createServer({ server: serverOptions });
5645
5661
  server.httpServer?.on("listening", async () => {
5662
+ if (!backend3) return;
5646
5663
  const { address, port: vitePort } = server.httpServer.address();
5647
5664
  const viteHost = address === "::1" ? "localhost" : address;
5648
5665
  await fs27.promises.mkdir(viteMetadataDir, { recursive: true });
@@ -5711,7 +5728,7 @@ var build = new Command66("build").description("build the app").configureHelp(he
5711
5728
  process18.env.VITE_BUILD = "true";
5712
5729
  const cwd = process18.cwd();
5713
5730
  let pbProc;
5714
- const needsStart = !process18.env.POCKETBASE_URL;
5731
+ const needsStart = hasBackend(cwd) && !process18.env.POCKETBASE_URL;
5715
5732
  const cleanup = () => {
5716
5733
  if (pbProc?.pid) pbProc.kill();
5717
5734
  };
@@ -5755,7 +5772,7 @@ import { resolveCommand as resolveCommand6 } from "package-manager-detector/comm
5755
5772
  var preview = new Command67("preview").description("preview the built app").configureHelp(helpConfig).action(async () => {
5756
5773
  const cwd = process19.cwd();
5757
5774
  let pbProc;
5758
- const needsStart = !process19.env.POCKETBASE_URL;
5775
+ const needsStart = hasBackend(cwd) && !process19.env.POCKETBASE_URL;
5759
5776
  const cleanup = () => {
5760
5777
  if (pbProc?.pid) pbProc.kill();
5761
5778
  };
@@ -6177,6 +6194,7 @@ var NO_BACKEND_COMMMANDS = /* @__PURE__ */ new Set([
6177
6194
  "generate schema",
6178
6195
  "generate form"
6179
6196
  ]);
6197
+ var BACKEND_OPTIONAL_COMMANDS = /* @__PURE__ */ new Set(["dev", "build", "preview", "deploy"]);
6180
6198
  var program = new Command73().name(package_default.name).description(package_default.description).version(package_default.version, "-v, --version").configureHelp(helpConfig);
6181
6199
  program.hook("preAction", (_thisCommand, actionCommand) => {
6182
6200
  if (isStub(actionCommand)) return;
@@ -6185,6 +6203,19 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
6185
6203
  if (NO_BACKEND_COMMMANDS.has(path36)) return;
6186
6204
  const top = path36.split(" ", 1)[0];
6187
6205
  if (NO_BACKEND_COMMMANDS.has(top)) return;
6206
+ if (!hasBackend()) {
6207
+ if (BACKEND_OPTIONAL_COMMANDS.has(top)) return;
6208
+ p29.log.error(
6209
+ `${pc7.cyan(`vela ${path36}`)} needs a backend, and this project does not have one.
6210
+
6211
+ Static projects have no database to talk to.
6212
+
6213
+ To add a backend to this project, run ${pc7.cyan("vela bless")}.`
6214
+ );
6215
+ p29.log.message();
6216
+ p29.cancel("Operation failed.");
6217
+ process23.exit(1);
6218
+ }
6188
6219
  if (!process23.env.POCKETBASE_SUPERUSER_EMAIL || !process23.env.POCKETBASE_SUPERUSER_PASSWORD) {
6189
6220
  p29.log.error(
6190
6221
  `PocketBase superuser credentials are required.