vela 0.15.4 → 0.15.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +225 -157
- package/dist/bin.js.map +4 -4
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -21,7 +21,7 @@ import pc46 from "picocolors";
|
|
|
21
21
|
// package.json
|
|
22
22
|
var package_default = {
|
|
23
23
|
name: "vela",
|
|
24
|
-
version: "0.15.
|
|
24
|
+
version: "0.15.6",
|
|
25
25
|
type: "module",
|
|
26
26
|
description: "A CLI for creating and updating SvelteKit projects",
|
|
27
27
|
license: "MIT",
|
|
@@ -73,7 +73,7 @@ var package_default = {
|
|
|
73
73
|
pocketbase: "^0.28.0",
|
|
74
74
|
"pocketbase-server": "^0.40.5-beta.5",
|
|
75
75
|
stripe: "^22.6.2",
|
|
76
|
-
sv: "~1.
|
|
76
|
+
sv: "~1.1.1",
|
|
77
77
|
svelte: "^5.56.10",
|
|
78
78
|
tar: "^7.5.22",
|
|
79
79
|
tinyexec: "^1.3.0",
|
|
@@ -601,10 +601,10 @@ function detectFeatures(root, { isAppMode, isPaymentsMode }) {
|
|
|
601
601
|
// src/commands/bless.ts
|
|
602
602
|
import fs18 from "node:fs";
|
|
603
603
|
import path16 from "node:path";
|
|
604
|
-
import
|
|
604
|
+
import process9 from "node:process";
|
|
605
605
|
import * as v4 from "valibot";
|
|
606
606
|
import { Command } from "commander";
|
|
607
|
-
import * as
|
|
607
|
+
import * as p6 from "@clack/prompts";
|
|
608
608
|
import pc3 from "picocolors";
|
|
609
609
|
import { resolveCommand as resolveCommand2 } from "package-manager-detector";
|
|
610
610
|
|
|
@@ -1533,9 +1533,122 @@ function quoteEnvValue(value) {
|
|
|
1533
1533
|
}
|
|
1534
1534
|
|
|
1535
1535
|
// src/lib/superuser.ts
|
|
1536
|
-
import
|
|
1536
|
+
import process8 from "node:process";
|
|
1537
1537
|
import * as v3 from "valibot";
|
|
1538
|
+
import * as p4 from "@clack/prompts";
|
|
1539
|
+
|
|
1540
|
+
// src/lib/providers.ts
|
|
1541
|
+
import process7 from "node:process";
|
|
1538
1542
|
import * as p3 from "@clack/prompts";
|
|
1543
|
+
import { bySlug } from "@velastack/patterns";
|
|
1544
|
+
function capabilityName(slug2) {
|
|
1545
|
+
return slug2.replace(/^enable-/, "");
|
|
1546
|
+
}
|
|
1547
|
+
function choices(providers) {
|
|
1548
|
+
return `--provider <${providers.map((provider) => provider.id).join("|")}>`;
|
|
1549
|
+
}
|
|
1550
|
+
function unknownProviderMessage(name, raw, providers) {
|
|
1551
|
+
return [
|
|
1552
|
+
`Unknown provider "${raw}" for ${name}.`,
|
|
1553
|
+
"",
|
|
1554
|
+
"Available providers:",
|
|
1555
|
+
...providers.map((provider) => ` ${provider.id}`)
|
|
1556
|
+
].join("\n");
|
|
1557
|
+
}
|
|
1558
|
+
function decideProvider(request) {
|
|
1559
|
+
const { patternName, providers, flagValue, interactive } = request;
|
|
1560
|
+
if (flagValue !== void 0) {
|
|
1561
|
+
const provider = providers.find((candidate) => candidate.id === flagValue);
|
|
1562
|
+
if (!provider) {
|
|
1563
|
+
return { kind: "error", message: unknownProviderMessage(patternName, flagValue, providers) };
|
|
1564
|
+
}
|
|
1565
|
+
return { kind: "use", provider };
|
|
1566
|
+
}
|
|
1567
|
+
if (interactive) return { kind: "prompt" };
|
|
1568
|
+
return {
|
|
1569
|
+
kind: "error",
|
|
1570
|
+
message: `Choose ${article(patternName)} ${patternName} provider: pass ${choices(providers)}. There is no terminal to ask on.`
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
function article(word) {
|
|
1574
|
+
return /^[aeiou]/i.test(word) ? "an" : "a";
|
|
1575
|
+
}
|
|
1576
|
+
function isInteractive() {
|
|
1577
|
+
return Boolean(process7.stdout.isTTY) && !process7.env.CI;
|
|
1578
|
+
}
|
|
1579
|
+
function providerFlagInArgv(argv) {
|
|
1580
|
+
return argv.some((arg) => arg === "--provider" || arg.startsWith("--provider="));
|
|
1581
|
+
}
|
|
1582
|
+
function checkProviderInput(pattern, argv, input) {
|
|
1583
|
+
const providers = pattern.providers ?? [];
|
|
1584
|
+
if (providers.length > 0) {
|
|
1585
|
+
if (typeof input.provider !== "string" || input.provider.length === 0) {
|
|
1586
|
+
throw new Error(`${pattern.title} needs a provider. Pass ${choices(providers)}.`);
|
|
1587
|
+
}
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
if (providerFlagInArgv(argv)) {
|
|
1591
|
+
throw new Error(`${pattern.title} does not support --provider.`);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
async function resolveProvider(slug2, flagValue) {
|
|
1595
|
+
const pattern = bySlug[slug2];
|
|
1596
|
+
const providers = pattern.providers ?? [];
|
|
1597
|
+
if (providers.length === 0) {
|
|
1598
|
+
throw new Error(`${pattern.title} does not support --provider.`);
|
|
1599
|
+
}
|
|
1600
|
+
const patternName = capabilityName(slug2);
|
|
1601
|
+
const decision = decideProvider({
|
|
1602
|
+
patternName,
|
|
1603
|
+
providers,
|
|
1604
|
+
flagValue,
|
|
1605
|
+
interactive: isInteractive()
|
|
1606
|
+
});
|
|
1607
|
+
if (decision.kind === "error") throw new Error(decision.message);
|
|
1608
|
+
if (decision.kind === "use") return decision.provider;
|
|
1609
|
+
const selected = await p3.select({
|
|
1610
|
+
message: `Select ${patternName} provider`,
|
|
1611
|
+
options: providers.map((provider) => ({ value: provider.id, label: provider.label }))
|
|
1612
|
+
});
|
|
1613
|
+
if (p3.isCancel(selected)) {
|
|
1614
|
+
p3.cancel("Operation cancelled.");
|
|
1615
|
+
process7.exit(0);
|
|
1616
|
+
}
|
|
1617
|
+
return providers.find((provider) => provider.id === selected);
|
|
1618
|
+
}
|
|
1619
|
+
async function collectProviderEnv(provider) {
|
|
1620
|
+
const values = {};
|
|
1621
|
+
const interactive = isInteractive();
|
|
1622
|
+
for (const variable of provider.env ?? []) {
|
|
1623
|
+
const existing = process7.env[variable.key];
|
|
1624
|
+
if (existing) {
|
|
1625
|
+
values[variable.key] = existing;
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
if (!interactive) {
|
|
1629
|
+
values[variable.key] = variable.default ?? "";
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
const value = variable.secret ? await p3.password({
|
|
1633
|
+
message: variable.placeholder ? `${variable.label} (${variable.placeholder})` : variable.label
|
|
1634
|
+
}) : await p3.text({
|
|
1635
|
+
message: variable.label,
|
|
1636
|
+
placeholder: variable.placeholder,
|
|
1637
|
+
initialValue: variable.default
|
|
1638
|
+
});
|
|
1639
|
+
if (p3.isCancel(value)) {
|
|
1640
|
+
p3.cancel("Operation cancelled.");
|
|
1641
|
+
process7.exit(0);
|
|
1642
|
+
}
|
|
1643
|
+
values[variable.key] = (value ?? "").trim();
|
|
1644
|
+
}
|
|
1645
|
+
return values;
|
|
1646
|
+
}
|
|
1647
|
+
function missingEnvKeys(provider, values) {
|
|
1648
|
+
return (provider.env ?? []).filter((variable) => !(values[variable.key] ?? "").trim() && !variable.default).map((variable) => variable.key);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// src/lib/superuser.ts
|
|
1539
1652
|
var emailFlag = v3.optional(v3.pipe(v3.string(), v3.email("must be a valid email address")));
|
|
1540
1653
|
var passwordFlag = v3.optional(
|
|
1541
1654
|
v3.pipe(v3.string(), v3.minLength(8, "must be at least 8 characters long"))
|
|
@@ -1564,17 +1677,23 @@ function decideSuperuser(flags, env2, interactive) {
|
|
|
1564
1677
|
message: `Pass ${missing.join(" and ")} for the PocketBase admin user. There is no terminal to ask on.`
|
|
1565
1678
|
};
|
|
1566
1679
|
}
|
|
1680
|
+
async function requireSuperuser(flags, env2, onCancel2) {
|
|
1681
|
+
const decision = decideSuperuser(flags, env2, isInteractive());
|
|
1682
|
+
if (decision.kind === "error") throw new Error(decision.message);
|
|
1683
|
+
if (decision.kind === "use") return decision.credentials;
|
|
1684
|
+
return promptSuperuser(decision.known, onCancel2);
|
|
1685
|
+
}
|
|
1567
1686
|
function superuserFromEnv() {
|
|
1568
|
-
const email2 =
|
|
1569
|
-
const password9 =
|
|
1687
|
+
const email2 = process8.env.POCKETBASE_SUPERUSER_EMAIL?.trim();
|
|
1688
|
+
const password9 = process8.env.POCKETBASE_SUPERUSER_PASSWORD;
|
|
1570
1689
|
return { ...email2 ? { email: email2 } : {}, ...password9 ? { password: password9 } : {} };
|
|
1571
1690
|
}
|
|
1572
1691
|
function promptSuperuser(known, onCancel2) {
|
|
1573
|
-
return
|
|
1692
|
+
return p4.group(
|
|
1574
1693
|
{
|
|
1575
1694
|
email: () => {
|
|
1576
1695
|
if (known.email) return Promise.resolve(known.email);
|
|
1577
|
-
return
|
|
1696
|
+
return p4.text({
|
|
1578
1697
|
message: "Enter an email for the admin user",
|
|
1579
1698
|
initialValue: "admin@example.com",
|
|
1580
1699
|
validate: (value) => !value ? "Email is required" : !value.includes("@") ? "Invalid email" : void 0
|
|
@@ -1582,7 +1701,7 @@ function promptSuperuser(known, onCancel2) {
|
|
|
1582
1701
|
},
|
|
1583
1702
|
password: () => {
|
|
1584
1703
|
if (known.password) return Promise.resolve(known.password);
|
|
1585
|
-
return
|
|
1704
|
+
return p4.password({
|
|
1586
1705
|
message: "Enter a password for the admin user (at least 8 characters)",
|
|
1587
1706
|
validate: (value) => !value ? "Password is required" : value.length < 8 ? "Password must be at least 8 characters long" : void 0
|
|
1588
1707
|
});
|
|
@@ -2490,7 +2609,7 @@ async function ensureEnvDeclarations(root, specs) {
|
|
|
2490
2609
|
}
|
|
2491
2610
|
|
|
2492
2611
|
// src/lib/result-report.ts
|
|
2493
|
-
import * as
|
|
2612
|
+
import * as p5 from "@clack/prompts";
|
|
2494
2613
|
function reportResult(report5) {
|
|
2495
2614
|
const sections = [
|
|
2496
2615
|
["Files created", report5.filesCreated],
|
|
@@ -2514,20 +2633,20 @@ function reportResult(report5) {
|
|
|
2514
2633
|
}
|
|
2515
2634
|
const failures = report5.failures ?? [];
|
|
2516
2635
|
if (body.length > 0) {
|
|
2517
|
-
|
|
2636
|
+
p5.log.success(`${report5.summary}
|
|
2518
2637
|
|
|
2519
2638
|
${body.join("\n")}`);
|
|
2520
2639
|
} else if (failures.length === 0) {
|
|
2521
|
-
|
|
2640
|
+
p5.log.success(report5.summary);
|
|
2522
2641
|
}
|
|
2523
2642
|
for (const failure of failures) {
|
|
2524
2643
|
const headline = failure.status === "not-found" ? `Could not find ${failure.path}` : `Could not update ${failure.path}`;
|
|
2525
|
-
|
|
2644
|
+
p5.log.warn(failure.message ? `${headline}
|
|
2526
2645
|
|
|
2527
2646
|
${failure.message}` : headline);
|
|
2528
2647
|
}
|
|
2529
2648
|
if (report5.nextSteps && report5.nextSteps.length > 0) {
|
|
2530
|
-
|
|
2649
|
+
p5.note(report5.nextSteps.map((s) => `- ${s}`).join("\n"), "Next steps", {
|
|
2531
2650
|
format: (line) => line
|
|
2532
2651
|
});
|
|
2533
2652
|
}
|
|
@@ -2537,7 +2656,11 @@ ${failure.message}` : headline);
|
|
|
2537
2656
|
function optionsSchema() {
|
|
2538
2657
|
const templates = projectTemplateNames({ backend: true });
|
|
2539
2658
|
return v4.strictObject({
|
|
2540
|
-
install
|
|
2659
|
+
// Unset when neither `--install` nor `--no-install` is passed (commander 15).
|
|
2660
|
+
install: v4.optional(
|
|
2661
|
+
v4.union([v4.boolean(), v4.picklist(AGENT_NAMES)], "must be a package manager"),
|
|
2662
|
+
true
|
|
2663
|
+
),
|
|
2541
2664
|
template: v4.optional(v4.picklist(templates, `must be one of: ${templates.join(", ")}`)),
|
|
2542
2665
|
email: emailFlag,
|
|
2543
2666
|
password: passwordFlag,
|
|
@@ -2586,9 +2709,9 @@ async function blessProject(cwdArg, options) {
|
|
|
2586
2709
|
assertKit3(projectPath, "vela bless");
|
|
2587
2710
|
assertNotAlreadyBlessed(projectPath);
|
|
2588
2711
|
const templateDir = findProjectTemplate(options.template ?? DEFAULT_TEMPLATE).dir;
|
|
2589
|
-
const { email: email2, password: password9 } = await
|
|
2590
|
-
|
|
2591
|
-
|
|
2712
|
+
const { email: email2, password: password9 } = await requireSuperuser(options, {}, () => {
|
|
2713
|
+
p6.cancel("Operation cancelled.");
|
|
2714
|
+
process9.exit(0);
|
|
2592
2715
|
});
|
|
2593
2716
|
mergeDependencies(projectPath, templateDir);
|
|
2594
2717
|
copyVelaOnlyFiles(templateDir, projectPath);
|
|
@@ -2599,7 +2722,7 @@ async function blessProject(cwdArg, options) {
|
|
|
2599
2722
|
mergeConfigFiles(projectPath);
|
|
2600
2723
|
mergeAppDts(templateDir, projectPath);
|
|
2601
2724
|
maybeReplaceRoutes(projectPath, templateDir, options);
|
|
2602
|
-
|
|
2725
|
+
p6.log.success("Vela files in place");
|
|
2603
2726
|
let packageManager2;
|
|
2604
2727
|
if (options.install !== false) {
|
|
2605
2728
|
const pm = typeof options.install === "string" ? options.install : await packageManagerPrompt(projectPath);
|
|
@@ -2609,7 +2732,7 @@ async function blessProject(cwdArg, options) {
|
|
|
2609
2732
|
packageManager2 = pm;
|
|
2610
2733
|
}
|
|
2611
2734
|
}
|
|
2612
|
-
|
|
2735
|
+
p6.log.step("Initializing PocketBase...");
|
|
2613
2736
|
await createSuperuser(projectPath, email2, password9);
|
|
2614
2737
|
writeEnvFile(
|
|
2615
2738
|
projectPath,
|
|
@@ -2619,12 +2742,12 @@ async function blessProject(cwdArg, options) {
|
|
|
2619
2742
|
},
|
|
2620
2743
|
["PocketBase superuser credentials \u2014 used by `vela` commands"]
|
|
2621
2744
|
);
|
|
2622
|
-
|
|
2745
|
+
p6.log.success("PocketBase initialized");
|
|
2623
2746
|
printNextSteps(projectPath, packageManager2);
|
|
2624
2747
|
}
|
|
2625
2748
|
function ensureShadcnCss(projectPath) {
|
|
2626
2749
|
if (ensureShadcnImport(path16.join(projectPath, "src", "app.css"))) {
|
|
2627
|
-
|
|
2750
|
+
p6.log.info(
|
|
2628
2751
|
"src/app.css: added the shadcn-svelte/tailwind.css import its registry components rely on."
|
|
2629
2752
|
);
|
|
2630
2753
|
}
|
|
@@ -2632,7 +2755,7 @@ function ensureShadcnCss(projectPath) {
|
|
|
2632
2755
|
function hintComponentsJson(projectPath) {
|
|
2633
2756
|
const config = readComponentsJson(projectPath);
|
|
2634
2757
|
if (!config) return;
|
|
2635
|
-
for (const hint of componentsJsonHints(config))
|
|
2758
|
+
for (const hint of componentsJsonHints(config)) p6.log.warn(hint);
|
|
2636
2759
|
}
|
|
2637
2760
|
function resolveProjectPath(cwdArg) {
|
|
2638
2761
|
const projectPath = path16.resolve(cwdArg);
|
|
@@ -2671,14 +2794,14 @@ function mergeDependencies(projectPath, templateDir) {
|
|
|
2671
2794
|
dropTemplateAdapters(userPkg, templatePkg)
|
|
2672
2795
|
);
|
|
2673
2796
|
const pinned = applyTemplatePins(merged, templatePkg, conflicts);
|
|
2674
|
-
if (ensureLibImports(merged))
|
|
2797
|
+
if (ensureLibImports(merged)) p6.log.info("package.json: added the #lib imports.");
|
|
2675
2798
|
writePackageJson(userPkgPath, merged);
|
|
2676
2799
|
if (pinned.length > 0) {
|
|
2677
|
-
|
|
2800
|
+
p6.log.info(`Pinned what SvelteKit 3 needs exactly:
|
|
2678
2801
|
${pinned.map((l) => ` ${l}`).join("\n")}`);
|
|
2679
2802
|
}
|
|
2680
2803
|
if (added.length > 0) {
|
|
2681
|
-
|
|
2804
|
+
p6.log.info(
|
|
2682
2805
|
`Added ${added.length} entries to package.json (${summarize(added.map((c) => c.name))}).`
|
|
2683
2806
|
);
|
|
2684
2807
|
}
|
|
@@ -2686,14 +2809,14 @@ ${pinned.map((l) => ` ${l}`).join("\n")}`);
|
|
|
2686
2809
|
const lines = replaced.map(
|
|
2687
2810
|
(c) => ` script ${pc3.bold(c.name)}: ${pc3.gray(c.userValue ?? "")} \u2192 ${pc3.cyan(c.templateValue)}`
|
|
2688
2811
|
);
|
|
2689
|
-
|
|
2812
|
+
p6.log.info(`Updated ${replaced.length} script(s):
|
|
2690
2813
|
${lines.join("\n")}`);
|
|
2691
2814
|
}
|
|
2692
2815
|
if (conflicts.length > 0) {
|
|
2693
2816
|
const lines = conflicts.map(
|
|
2694
2817
|
(c) => ` dep ${pc3.bold(c.name)}: kept ${pc3.cyan(c.userValue ?? "")} (template wants ${pc3.gray(c.templateValue)})`
|
|
2695
2818
|
);
|
|
2696
|
-
|
|
2819
|
+
p6.log.warn(`Kept ${conflicts.length} dependency version(s) on conflict:
|
|
2697
2820
|
${lines.join("\n")}`);
|
|
2698
2821
|
}
|
|
2699
2822
|
}
|
|
@@ -2734,9 +2857,9 @@ function applyTemplatePins(merged, template, conflicts) {
|
|
|
2734
2857
|
async function declareEnvVars(projectPath) {
|
|
2735
2858
|
const outcome = await ensureEnvDeclarations(projectPath, VELA_ENV_VARS);
|
|
2736
2859
|
if (outcome.failure) {
|
|
2737
|
-
|
|
2860
|
+
p6.log.warn(`${outcome.file}: ${outcome.failure}`);
|
|
2738
2861
|
} else if (outcome.changed) {
|
|
2739
|
-
|
|
2862
|
+
p6.log.info(`${outcome.file}: declared ${outcome.declared.join(", ")}.`);
|
|
2740
2863
|
}
|
|
2741
2864
|
}
|
|
2742
2865
|
function copyVelaOnlyFiles(templateDir, projectPath) {
|
|
@@ -2765,7 +2888,7 @@ function writeSiteFile(templateDir, projectPath) {
|
|
|
2765
2888
|
const dest = path16.join(projectPath, SITE_FILE);
|
|
2766
2889
|
if (!fs18.existsSync(source)) return;
|
|
2767
2890
|
if (fs18.existsSync(dest)) {
|
|
2768
|
-
|
|
2891
|
+
p6.log.info(
|
|
2769
2892
|
`Kept your ${pc3.bold(SITE_FILE)}. Vela's layouts read ${pc3.cyan("name")} and ${pc3.cyan("url")} from its ${pc3.cyan("site")} export.`
|
|
2770
2893
|
);
|
|
2771
2894
|
return;
|
|
@@ -2780,13 +2903,13 @@ function writeSiteFile(templateDir, projectPath) {
|
|
|
2780
2903
|
cliVersion: package_default.version
|
|
2781
2904
|
})
|
|
2782
2905
|
);
|
|
2783
|
-
|
|
2906
|
+
p6.log.info(`Wrote ${pc3.bold(SITE_FILE)}, naming the app ${pc3.cyan(appName)}. Edit it to rename.`);
|
|
2784
2907
|
}
|
|
2785
2908
|
function reportKeptFiles(kept) {
|
|
2786
2909
|
if (kept.length === 0) return;
|
|
2787
2910
|
const padding = Math.max(...kept.map((f) => f.path.length));
|
|
2788
2911
|
const lines = kept.map((f) => ` ${pc3.bold(f.path.padEnd(padding))} ${pc3.gray(f.adds)}`);
|
|
2789
|
-
|
|
2912
|
+
p6.log.info(
|
|
2790
2913
|
`Kept ${kept.length} file(s) you already had. Merge by hand to pick up what vela adds:
|
|
2791
2914
|
${lines.join("\n")}`
|
|
2792
2915
|
);
|
|
@@ -2814,11 +2937,11 @@ function mergeConfigFiles(projectPath) {
|
|
|
2814
2937
|
];
|
|
2815
2938
|
for (const [name, outcome] of outcomes) {
|
|
2816
2939
|
if (outcome.applied) {
|
|
2817
|
-
|
|
2940
|
+
p6.log.info(`${name}: ${outcome.reason}`);
|
|
2818
2941
|
continue;
|
|
2819
2942
|
}
|
|
2820
2943
|
if (outcome.snippet) {
|
|
2821
|
-
|
|
2944
|
+
p6.log.warn(
|
|
2822
2945
|
`${name}: ${outcome.reason}
|
|
2823
2946
|
${pc3.gray("Add this manually:")}
|
|
2824
2947
|
${pc3.cyan(outcome.snippet)}`
|
|
@@ -2845,7 +2968,7 @@ function mergeAppDts(templateDir, projectPath) {
|
|
|
2845
2968
|
`;
|
|
2846
2969
|
const match = current.match(/declare global\s*\{\s*\n/);
|
|
2847
2970
|
if (!match || match.index === void 0) {
|
|
2848
|
-
|
|
2971
|
+
p6.log.warn(
|
|
2849
2972
|
`src/app.d.ts: non-standard shape \u2014 add this namespace manually:
|
|
2850
2973
|
${pc3.cyan(block)}`
|
|
2851
2974
|
);
|
|
@@ -2857,11 +2980,11 @@ ${pc3.cyan(block)}`
|
|
|
2857
2980
|
}
|
|
2858
2981
|
function maybeReplaceRoutes(projectPath, templateDir, options) {
|
|
2859
2982
|
if (options.skipRoutes) {
|
|
2860
|
-
|
|
2983
|
+
p6.log.info("Skipping src/routes (--skip-routes).");
|
|
2861
2984
|
return;
|
|
2862
2985
|
}
|
|
2863
2986
|
if (!options.forceRoutes && !isVanillaRoutes(projectPath)) {
|
|
2864
|
-
|
|
2987
|
+
p6.log.info("Leaving src/routes alone (looks customized).");
|
|
2865
2988
|
return;
|
|
2866
2989
|
}
|
|
2867
2990
|
const target = path16.join(projectPath, "src", "routes");
|
|
@@ -2871,14 +2994,14 @@ function maybeReplaceRoutes(projectPath, templateDir, options) {
|
|
|
2871
2994
|
recursive: true,
|
|
2872
2995
|
filter: (s) => path16.basename(s) !== ".DS_Store"
|
|
2873
2996
|
});
|
|
2874
|
-
|
|
2997
|
+
p6.log.success("Replaced src/routes with the vela template.");
|
|
2875
2998
|
}
|
|
2876
2999
|
function summarize(names) {
|
|
2877
3000
|
if (names.length <= 4) return names.join(", ");
|
|
2878
3001
|
return `${names.slice(0, 3).join(", ")}, and ${names.length - 3} more`;
|
|
2879
3002
|
}
|
|
2880
3003
|
function printNextSteps(projectPath, packageManager2) {
|
|
2881
|
-
const relative = path16.relative(
|
|
3004
|
+
const relative = path16.relative(process9.cwd(), projectPath);
|
|
2882
3005
|
const pm = packageManager2 ?? getUserAgent() ?? "npm";
|
|
2883
3006
|
const nextSteps = [];
|
|
2884
3007
|
if (relative !== "") {
|
|
@@ -2910,117 +3033,6 @@ import { Command as Command3 } from "commander";
|
|
|
2910
3033
|
import * as p10 from "@clack/prompts";
|
|
2911
3034
|
import { detect as detect5, resolveCommand as resolveCommand3 } from "package-manager-detector";
|
|
2912
3035
|
|
|
2913
|
-
// src/lib/providers.ts
|
|
2914
|
-
import process9 from "node:process";
|
|
2915
|
-
import * as p6 from "@clack/prompts";
|
|
2916
|
-
import { bySlug } from "@velastack/patterns";
|
|
2917
|
-
function capabilityName(slug2) {
|
|
2918
|
-
return slug2.replace(/^enable-/, "");
|
|
2919
|
-
}
|
|
2920
|
-
function choices(providers) {
|
|
2921
|
-
return `--provider <${providers.map((provider) => provider.id).join("|")}>`;
|
|
2922
|
-
}
|
|
2923
|
-
function unknownProviderMessage(name, raw, providers) {
|
|
2924
|
-
return [
|
|
2925
|
-
`Unknown provider "${raw}" for ${name}.`,
|
|
2926
|
-
"",
|
|
2927
|
-
"Available providers:",
|
|
2928
|
-
...providers.map((provider) => ` ${provider.id}`)
|
|
2929
|
-
].join("\n");
|
|
2930
|
-
}
|
|
2931
|
-
function decideProvider(request) {
|
|
2932
|
-
const { patternName, providers, flagValue, interactive } = request;
|
|
2933
|
-
if (flagValue !== void 0) {
|
|
2934
|
-
const provider = providers.find((candidate) => candidate.id === flagValue);
|
|
2935
|
-
if (!provider) {
|
|
2936
|
-
return { kind: "error", message: unknownProviderMessage(patternName, flagValue, providers) };
|
|
2937
|
-
}
|
|
2938
|
-
return { kind: "use", provider };
|
|
2939
|
-
}
|
|
2940
|
-
if (interactive) return { kind: "prompt" };
|
|
2941
|
-
return {
|
|
2942
|
-
kind: "error",
|
|
2943
|
-
message: `Choose ${article(patternName)} ${patternName} provider: pass ${choices(providers)}. There is no terminal to ask on.`
|
|
2944
|
-
};
|
|
2945
|
-
}
|
|
2946
|
-
function article(word) {
|
|
2947
|
-
return /^[aeiou]/i.test(word) ? "an" : "a";
|
|
2948
|
-
}
|
|
2949
|
-
function isInteractive() {
|
|
2950
|
-
return Boolean(process9.stdout.isTTY) && !process9.env.CI;
|
|
2951
|
-
}
|
|
2952
|
-
function providerFlagInArgv(argv) {
|
|
2953
|
-
return argv.some((arg) => arg === "--provider" || arg.startsWith("--provider="));
|
|
2954
|
-
}
|
|
2955
|
-
function checkProviderInput(pattern, argv, input) {
|
|
2956
|
-
const providers = pattern.providers ?? [];
|
|
2957
|
-
if (providers.length > 0) {
|
|
2958
|
-
if (typeof input.provider !== "string" || input.provider.length === 0) {
|
|
2959
|
-
throw new Error(`${pattern.title} needs a provider. Pass ${choices(providers)}.`);
|
|
2960
|
-
}
|
|
2961
|
-
return;
|
|
2962
|
-
}
|
|
2963
|
-
if (providerFlagInArgv(argv)) {
|
|
2964
|
-
throw new Error(`${pattern.title} does not support --provider.`);
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
|
-
async function resolveProvider(slug2, flagValue) {
|
|
2968
|
-
const pattern = bySlug[slug2];
|
|
2969
|
-
const providers = pattern.providers ?? [];
|
|
2970
|
-
if (providers.length === 0) {
|
|
2971
|
-
throw new Error(`${pattern.title} does not support --provider.`);
|
|
2972
|
-
}
|
|
2973
|
-
const patternName = capabilityName(slug2);
|
|
2974
|
-
const decision = decideProvider({
|
|
2975
|
-
patternName,
|
|
2976
|
-
providers,
|
|
2977
|
-
flagValue,
|
|
2978
|
-
interactive: isInteractive()
|
|
2979
|
-
});
|
|
2980
|
-
if (decision.kind === "error") throw new Error(decision.message);
|
|
2981
|
-
if (decision.kind === "use") return decision.provider;
|
|
2982
|
-
const selected = await p6.select({
|
|
2983
|
-
message: `Select ${patternName} provider`,
|
|
2984
|
-
options: providers.map((provider) => ({ value: provider.id, label: provider.label }))
|
|
2985
|
-
});
|
|
2986
|
-
if (p6.isCancel(selected)) {
|
|
2987
|
-
p6.cancel("Operation cancelled.");
|
|
2988
|
-
process9.exit(0);
|
|
2989
|
-
}
|
|
2990
|
-
return providers.find((provider) => provider.id === selected);
|
|
2991
|
-
}
|
|
2992
|
-
async function collectProviderEnv(provider) {
|
|
2993
|
-
const values = {};
|
|
2994
|
-
const interactive = isInteractive();
|
|
2995
|
-
for (const variable of provider.env ?? []) {
|
|
2996
|
-
const existing = process9.env[variable.key];
|
|
2997
|
-
if (existing) {
|
|
2998
|
-
values[variable.key] = existing;
|
|
2999
|
-
continue;
|
|
3000
|
-
}
|
|
3001
|
-
if (!interactive) {
|
|
3002
|
-
values[variable.key] = variable.default ?? "";
|
|
3003
|
-
continue;
|
|
3004
|
-
}
|
|
3005
|
-
const value = variable.secret ? await p6.password({
|
|
3006
|
-
message: variable.placeholder ? `${variable.label} (${variable.placeholder})` : variable.label
|
|
3007
|
-
}) : await p6.text({
|
|
3008
|
-
message: variable.label,
|
|
3009
|
-
placeholder: variable.placeholder,
|
|
3010
|
-
initialValue: variable.default
|
|
3011
|
-
});
|
|
3012
|
-
if (p6.isCancel(value)) {
|
|
3013
|
-
p6.cancel("Operation cancelled.");
|
|
3014
|
-
process9.exit(0);
|
|
3015
|
-
}
|
|
3016
|
-
values[variable.key] = (value ?? "").trim();
|
|
3017
|
-
}
|
|
3018
|
-
return values;
|
|
3019
|
-
}
|
|
3020
|
-
function missingEnvKeys(provider, values) {
|
|
3021
|
-
return (provider.env ?? []).filter((variable) => !(values[variable.key] ?? "").trim() && !variable.default).map((variable) => variable.key);
|
|
3022
|
-
}
|
|
3023
|
-
|
|
3024
3036
|
// src/lib/link-on-create.ts
|
|
3025
3037
|
var FREE_CMS_HINT = "Get a free CMS at https://velastack.dev";
|
|
3026
3038
|
var PROJECT_ID_RE = /^[a-z0-9]{15}$/;
|
|
@@ -3048,13 +3060,24 @@ function normalizeCmsUrl(url) {
|
|
|
3048
3060
|
}
|
|
3049
3061
|
function linkNextSteps(outcome) {
|
|
3050
3062
|
const steps = [];
|
|
3051
|
-
const { linked, cmsEndpoint, cmsSource, templateCms, loggedIn, interactive } = outcome;
|
|
3063
|
+
const { linked, cmsEndpoint, cmsSource, templateCms, loggedIn, interactive, seed } = outcome;
|
|
3052
3064
|
if (templateCms && linked && cmsSource === "linked") {
|
|
3065
|
+
if (seed?.status === "seeded" || seed?.status === "already-seeded") {
|
|
3066
|
+
steps.push(
|
|
3067
|
+
`The site's content is already in its CMS, ready to edit: navigation, hours, contact details and every page.`
|
|
3068
|
+
);
|
|
3069
|
+
} else if (seed?.status === "failed") {
|
|
3070
|
+
steps.push(
|
|
3071
|
+
`Loading the template's content into the CMS failed (${seed.error}). Retry with "Load template content" at ${linked.dashboardUrl}/cms/settings; until then the site shows the copy built into its components.`
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3053
3074
|
steps.push(
|
|
3054
3075
|
`Add an editor at ${linked.dashboardUrl}/cms/editors, then open any page with \`?edit\` and sign in from the admin bar.`
|
|
3055
3076
|
);
|
|
3056
3077
|
} else if (cmsEndpoint) {
|
|
3057
|
-
steps.push(
|
|
3078
|
+
steps.push(
|
|
3079
|
+
templateCms ? "Open any page with `?edit` and sign in from the admin bar. The site shows its fallback copy until content is published to that CMS." : "Open any page with `?edit` and sign in from the admin bar."
|
|
3080
|
+
);
|
|
3058
3081
|
} else if (templateCms) {
|
|
3059
3082
|
steps.push(
|
|
3060
3083
|
`When you have a CMS, set \`cmsEndpoint\` in \`src/lib/site.ts\`. ${FREE_CMS_HINT}.`
|
|
@@ -3166,6 +3189,12 @@ async function startSiteDeploy(apiKey, projectId) {
|
|
|
3166
3189
|
method: "POST"
|
|
3167
3190
|
});
|
|
3168
3191
|
}
|
|
3192
|
+
async function seedSite(apiKey, projectId, input) {
|
|
3193
|
+
return apiFetch(apiKey, `/v1/projects/${projectId}/site/seed`, {
|
|
3194
|
+
method: "POST",
|
|
3195
|
+
body: JSON.stringify(input)
|
|
3196
|
+
});
|
|
3197
|
+
}
|
|
3169
3198
|
async function destroyEnvironment(apiKey, projectId, envTag) {
|
|
3170
3199
|
return apiFetch(apiKey, `/v1/projects/${projectId}/environments/${encodeURIComponent(envTag)}`, {
|
|
3171
3200
|
method: "DELETE"
|
|
@@ -3234,6 +3263,16 @@ function toLinked(project, team) {
|
|
|
3234
3263
|
dashboardUrl: dashboardUrl(team?.slug, project.slug)
|
|
3235
3264
|
};
|
|
3236
3265
|
}
|
|
3266
|
+
async function seedLinkedProject(apiKey, projectId, template) {
|
|
3267
|
+
try {
|
|
3268
|
+
return await seedSite(apiKey, projectId, {
|
|
3269
|
+
template: template.name,
|
|
3270
|
+
...template.version ? { version: template.version } : {}
|
|
3271
|
+
});
|
|
3272
|
+
} catch (error) {
|
|
3273
|
+
return { status: "failed", error: error instanceof Error ? error.message : String(error) };
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3237
3276
|
|
|
3238
3277
|
// src/lib/project-config.ts
|
|
3239
3278
|
import fs19 from "node:fs";
|
|
@@ -5505,7 +5544,13 @@ function optionsSchema2(listing) {
|
|
|
5505
5544
|
choices2 += ` (could not reach the template registry: ${listing.registryError})`;
|
|
5506
5545
|
}
|
|
5507
5546
|
return v5.strictObject({
|
|
5508
|
-
install
|
|
5547
|
+
// `--no-install` and `--install <pm>` share a key, and commander 15 leaves it
|
|
5548
|
+
// unset when neither is passed: installing with the detected package
|
|
5549
|
+
// manager is the default.
|
|
5550
|
+
install: v5.optional(
|
|
5551
|
+
v5.union([v5.boolean(), v5.picklist(AGENT_NAMES)], "must be a package manager"),
|
|
5552
|
+
true
|
|
5553
|
+
),
|
|
5509
5554
|
template: v5.optional(v5.picklist(names, choices2)),
|
|
5510
5555
|
name: v5.optional(v5.pipe(v5.string(), v5.trim(), v5.minLength(1, "must not be empty"))),
|
|
5511
5556
|
email: emailFlag,
|
|
@@ -5661,9 +5706,12 @@ async function resolveCreatableTemplate(template) {
|
|
|
5661
5706
|
return resolved;
|
|
5662
5707
|
}
|
|
5663
5708
|
async function scaffoldProject(cwdArg, options, template, resolved, onCancel2) {
|
|
5709
|
+
const interactive = isInteractive();
|
|
5664
5710
|
let directory;
|
|
5665
5711
|
if (cwdArg) {
|
|
5666
5712
|
directory = path27.resolve(cwdArg);
|
|
5713
|
+
} else if (!interactive) {
|
|
5714
|
+
throw new Error("Pass the directory to create the project in, e.g. `vela create my-app`.");
|
|
5667
5715
|
} else {
|
|
5668
5716
|
const answer = await p10.text({
|
|
5669
5717
|
message: "Where would you like your project to be created?",
|
|
@@ -5674,6 +5722,7 @@ async function scaffoldProject(cwdArg, options, template, resolved, onCancel2) {
|
|
|
5674
5722
|
directory = path27.resolve(answer);
|
|
5675
5723
|
}
|
|
5676
5724
|
if (fs29.existsSync(directory) && fs29.readdirSync(directory).filter((f) => !f.startsWith(".git")).length > 0) {
|
|
5725
|
+
if (!interactive) throw new Error(`${directory} is not empty.`);
|
|
5677
5726
|
const force = await p10.confirm({
|
|
5678
5727
|
message: "Directory not empty. Continue?",
|
|
5679
5728
|
initialValue: false
|
|
@@ -5685,6 +5734,7 @@ async function scaffoldProject(cwdArg, options, template, resolved, onCancel2) {
|
|
|
5685
5734
|
{
|
|
5686
5735
|
name: () => {
|
|
5687
5736
|
if (options.name) return Promise.resolve(options.name);
|
|
5737
|
+
if (!interactive) return Promise.resolve(dirName || "SvelteKit");
|
|
5688
5738
|
return p10.text({
|
|
5689
5739
|
message: "App name (written to src/lib/site.ts)",
|
|
5690
5740
|
initialValue: dirName || "SvelteKit",
|
|
@@ -5694,7 +5744,7 @@ async function scaffoldProject(cwdArg, options, template, resolved, onCancel2) {
|
|
|
5694
5744
|
},
|
|
5695
5745
|
{ onCancel: onCancel2 }
|
|
5696
5746
|
);
|
|
5697
|
-
const credentials = template.backend ? await
|
|
5747
|
+
const credentials = template.backend ? await requireSuperuser(options, {}, onCancel2) : void 0;
|
|
5698
5748
|
const link2 = await linkOnCreate(name, template, options, onCancel2);
|
|
5699
5749
|
copyTemplate(resolved, directory);
|
|
5700
5750
|
applyTemplateFiles(directory, {
|
|
@@ -5796,10 +5846,24 @@ async function linkOnCreate(name, template, options, onCancel2) {
|
|
|
5796
5846
|
outcome.cmsEndpoint = cmsEndpointFor(linked.projectId);
|
|
5797
5847
|
outcome.cmsSource = "linked";
|
|
5798
5848
|
p10.log.success(`CMS: ${outcome.cmsEndpoint}`);
|
|
5849
|
+
outcome.seed = await seedCms(apiKey ?? requireApiKey(), linked, template);
|
|
5799
5850
|
}
|
|
5800
5851
|
}
|
|
5801
5852
|
return outcome;
|
|
5802
5853
|
}
|
|
5854
|
+
async function seedCms(apiKey, linked, template) {
|
|
5855
|
+
if (template.source !== "remote") return void 0;
|
|
5856
|
+
const version = template.entry.version;
|
|
5857
|
+
const seed = await seedLinkedProject(apiKey, linked.projectId, { name: template.name, version });
|
|
5858
|
+
if (seed.status === "seeded") {
|
|
5859
|
+
p10.log.success(`CMS content loaded (${seed.locales.join(", ")})`);
|
|
5860
|
+
} else if (seed.status === "already-seeded") {
|
|
5861
|
+
p10.log.info("The CMS already has content; left as it is.");
|
|
5862
|
+
} else if (seed.status === "failed") {
|
|
5863
|
+
p10.log.warn(`Could not load the template's content into the CMS: ${seed.error}`);
|
|
5864
|
+
}
|
|
5865
|
+
return seed;
|
|
5866
|
+
}
|
|
5803
5867
|
async function promptCms(onCancel2) {
|
|
5804
5868
|
p10.note(`This template reads its copy from a hosted CMS.
|
|
5805
5869
|
${FREE_CMS_HINT}.`, "CMS");
|
|
@@ -14524,7 +14588,11 @@ var testServer = new Command101("test:server").description("run server tests").a
|
|
|
14524
14588
|
vite = await createServer({
|
|
14525
14589
|
mode: "test",
|
|
14526
14590
|
plugins: [stubPagesPlugin()],
|
|
14527
|
-
optimizeDeps: { noDiscovery: true }
|
|
14591
|
+
optimizeDeps: { noDiscovery: true },
|
|
14592
|
+
// This config differs from `vela dev`'s, so sharing node_modules/.vite
|
|
14593
|
+
// would make Vite re-optimize dependencies under a running dev server,
|
|
14594
|
+
// which then loads two copies of Svelte and fails reading `page`.
|
|
14595
|
+
cacheDir: "node_modules/.vite-test"
|
|
14528
14596
|
});
|
|
14529
14597
|
const vitePort = await findFreePort();
|
|
14530
14598
|
await vite.listen(vitePort);
|