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 +261 -230
- package/dist/bin.js.map +4 -4
- package/package.json +1 -1
- package/templates/minimal/package.template.json +2 -2
- package/templates/minimal/src/routes/(public)/+page.svelte +28 -12
- package/templates/minimal/src/routes/(public)/root-layout.svelte +1 -0
- package/templates/minimal/src/routes/+error.svelte +11 -4
- package/templates/static/package.template.json +4 -3
- package/templates/static/src/hooks.server.ts +6 -22
- package/templates/static/src/routes/(public)/+page.svelte +30 -13
- package/templates/static/src/routes/(public)/root-layout.svelte +1 -0
- package/templates/static/src/routes/+error.svelte +11 -4
- package/templates/static/vite.config.ts +0 -12
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.
|
|
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
|
|
321
|
-
import
|
|
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 =
|
|
328
|
-
const { root } =
|
|
482
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
483
|
+
const { root } = path3.parse(dir);
|
|
329
484
|
while (dir !== root) {
|
|
330
|
-
const candidate =
|
|
485
|
+
const candidate = path3.join(dir, "templates");
|
|
331
486
|
if (holdsManifest(candidate)) return candidate;
|
|
332
|
-
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 (!
|
|
338
|
-
return
|
|
339
|
-
(entry) => entry.isDirectory() &&
|
|
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 =
|
|
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 =
|
|
360
|
-
if (!
|
|
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(
|
|
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:
|
|
534
|
+
dir: path3.join(root, name)
|
|
380
535
|
};
|
|
381
536
|
}
|
|
382
537
|
|
|
383
538
|
// src/lib/package-manager.ts
|
|
384
|
-
import
|
|
385
|
-
import
|
|
386
|
-
import
|
|
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 =
|
|
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 (!
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
454
|
-
if (!
|
|
455
|
-
const pkg = JSON.parse(
|
|
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
|
-
|
|
618
|
+
fs5.writeFileSync(pkgPath, JSON.stringify(pkg, null, " ") + "\n");
|
|
464
619
|
}
|
|
465
620
|
|
|
466
621
|
// src/lib/pocketbase.ts
|
|
467
|
-
import
|
|
622
|
+
import fs6 from "node:fs";
|
|
468
623
|
import net from "node:net";
|
|
469
|
-
import
|
|
470
|
-
import
|
|
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 =
|
|
593
|
-
if (
|
|
594
|
-
return JSON.parse(
|
|
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 =
|
|
608
|
-
const migrationsDir =
|
|
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 ??
|
|
611
|
-
const password8 = creds?.password ??
|
|
612
|
-
if (!
|
|
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 =
|
|
638
|
-
const migrationsDir =
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
665
|
-
|
|
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
|
|
700
|
-
import
|
|
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 =
|
|
716
|
-
let content =
|
|
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
|
-
|
|
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
|
|
803
|
-
import
|
|
867
|
+
import fs9 from "node:fs";
|
|
868
|
+
import path8 from "node:path";
|
|
804
869
|
|
|
805
870
|
// src/lib/config-target.ts
|
|
806
|
-
import
|
|
807
|
-
import
|
|
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 =
|
|
828
|
-
if (
|
|
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 =
|
|
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 =
|
|
924
|
-
const original =
|
|
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
|
-
|
|
1012
|
+
fs9.writeFileSync(filePath, updated);
|
|
948
1013
|
return { applied: true, reason: "added runes compilerOption", file };
|
|
949
1014
|
}
|
|
950
1015
|
function mergeViteConfig(filePath) {
|
|
951
|
-
if (!
|
|
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 =
|
|
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
|
-
|
|
1043
|
+
fs9.writeFileSync(filePath, updated);
|
|
979
1044
|
return { applied: true, reason: "added @tailwindcss/vite plugin" };
|
|
980
1045
|
}
|
|
981
1046
|
function mergeTsconfig(filePath) {
|
|
982
|
-
if (!
|
|
1047
|
+
if (!fs9.existsSync(filePath)) {
|
|
983
1048
|
return { applied: false, reason: "tsconfig.json not found" };
|
|
984
1049
|
}
|
|
985
|
-
const original =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|