create-better-fullstack 1.4.16 → 1.5.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/README.md +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +1 -1
- package/dist/{src-BVbxA2WC.mjs → src-hfdQPH0o.mjs} +277 -38
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ Configure your stack visually — pick every option from a UI, preview your choi
|
|
|
38
38
|
--yolo # Scaffold a random stack — good for exploring
|
|
39
39
|
--template <name> # Use a preset (t3, mern, pern, uniwind)
|
|
40
40
|
--ecosystem <lang> # Start in rust, python, or go mode
|
|
41
|
+
--version-channel # Dependency channel: stable, latest, beta
|
|
41
42
|
--no-git # Skip git initialization
|
|
42
43
|
--no-install # Skip dependency installation
|
|
43
44
|
--package-manager # Package manager (bun, pnpm, npm, yarn)
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -278,6 +278,11 @@ declare const router: {
|
|
|
278
278
|
yarn: "yarn";
|
|
279
279
|
}>>;
|
|
280
280
|
install: z.ZodOptional<z.ZodBoolean>;
|
|
281
|
+
versionChannel: z.ZodOptional<z.ZodEnum<{
|
|
282
|
+
stable: "stable";
|
|
283
|
+
latest: "latest";
|
|
284
|
+
beta: "beta";
|
|
285
|
+
}>>;
|
|
281
286
|
dbSetup: z.ZodOptional<z.ZodEnum<{
|
|
282
287
|
none: "none";
|
|
283
288
|
turso: "turso";
|
|
@@ -543,6 +548,7 @@ declare const router: {
|
|
|
543
548
|
payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
|
|
544
549
|
git: boolean;
|
|
545
550
|
packageManager: "bun" | "npm" | "pnpm" | "yarn";
|
|
551
|
+
versionChannel: "stable" | "latest" | "beta";
|
|
546
552
|
install: boolean;
|
|
547
553
|
dbSetup: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "upstash" | "d1" | "docker";
|
|
548
554
|
api: "none" | "trpc" | "orpc" | "ts-rest" | "garph";
|
|
@@ -629,6 +635,7 @@ declare const router: {
|
|
|
629
635
|
payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
|
|
630
636
|
git: boolean;
|
|
631
637
|
packageManager: "bun" | "npm" | "pnpm" | "yarn";
|
|
638
|
+
versionChannel: "stable" | "latest" | "beta";
|
|
632
639
|
install: boolean;
|
|
633
640
|
dbSetup: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "upstash" | "d1" | "docker";
|
|
634
641
|
api: "none" | "trpc" | "orpc" | "ts-rest" | "garph";
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as builder, c as createVirtual, d as history, f as router, i as add, l as docs, n as TEMPLATE_COUNT, o as create, p as sponsors, r as VirtualFileSystem, s as createBtsCli, t as EMBEDDED_TEMPLATES, u as generateVirtualProject } from "./src-
|
|
2
|
+
import { a as builder, c as createVirtual, d as history, f as router, i as add, l as docs, n as TEMPLATE_COUNT, o as create, p as sponsors, r as VirtualFileSystem, s as createBtsCli, t as EMBEDDED_TEMPLATES, u as generateVirtualProject } from "./src-hfdQPH0o.mjs";
|
|
3
3
|
|
|
4
4
|
export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, history, router, sponsors };
|
|
@@ -67,6 +67,7 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
67
67
|
examples: [],
|
|
68
68
|
git: true,
|
|
69
69
|
install: true,
|
|
70
|
+
versionChannel: "stable",
|
|
70
71
|
dbSetup: "none",
|
|
71
72
|
backend: "hono",
|
|
72
73
|
runtime: "bun",
|
|
@@ -1288,10 +1289,9 @@ const ADDON_GROUPS = {
|
|
|
1288
1289
|
"tauri",
|
|
1289
1290
|
"opentui",
|
|
1290
1291
|
"wxt",
|
|
1291
|
-
"ruler"
|
|
1292
|
-
"msw",
|
|
1293
|
-
"storybook"
|
|
1292
|
+
"ruler"
|
|
1294
1293
|
],
|
|
1294
|
+
Integrations: ["msw", "storybook"],
|
|
1295
1295
|
"AI Agents": ["mcp", "skills"],
|
|
1296
1296
|
TanStack: [
|
|
1297
1297
|
"tanstack-query",
|
|
@@ -1308,6 +1308,7 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
1308
1308
|
Tooling: [],
|
|
1309
1309
|
Documentation: [],
|
|
1310
1310
|
Extensions: [],
|
|
1311
|
+
Integrations: [],
|
|
1311
1312
|
"AI Agents": [],
|
|
1312
1313
|
TanStack: []
|
|
1313
1314
|
};
|
|
@@ -1324,6 +1325,7 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
1324
1325
|
if (ADDON_GROUPS.Tooling.includes(addon)) groupedOptions.Tooling.push(option);
|
|
1325
1326
|
else if (ADDON_GROUPS.Documentation.includes(addon)) groupedOptions.Documentation.push(option);
|
|
1326
1327
|
else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
|
|
1328
|
+
else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
|
|
1327
1329
|
else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
|
|
1328
1330
|
else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
|
|
1329
1331
|
}
|
|
@@ -1350,6 +1352,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
|
1350
1352
|
Tooling: [],
|
|
1351
1353
|
Documentation: [],
|
|
1352
1354
|
Extensions: [],
|
|
1355
|
+
Integrations: [],
|
|
1353
1356
|
"AI Agents": [],
|
|
1354
1357
|
TanStack: []
|
|
1355
1358
|
};
|
|
@@ -1365,6 +1368,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
|
1365
1368
|
if (ADDON_GROUPS.Tooling.includes(addon)) groupedOptions.Tooling.push(option);
|
|
1366
1369
|
else if (ADDON_GROUPS.Documentation.includes(addon)) groupedOptions.Documentation.push(option);
|
|
1367
1370
|
else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
|
|
1371
|
+
else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
|
|
1368
1372
|
else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
|
|
1369
1373
|
else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
|
|
1370
1374
|
}
|
|
@@ -1413,6 +1417,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
1413
1417
|
forms: projectConfig.forms,
|
|
1414
1418
|
testing: projectConfig.testing,
|
|
1415
1419
|
packageManager: projectConfig.packageManager,
|
|
1420
|
+
versionChannel: projectConfig.versionChannel,
|
|
1416
1421
|
dbSetup: projectConfig.dbSetup,
|
|
1417
1422
|
api: projectConfig.api,
|
|
1418
1423
|
webDeploy: projectConfig.webDeploy,
|
|
@@ -1472,6 +1477,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
1472
1477
|
forms: btsConfig.forms,
|
|
1473
1478
|
testing: btsConfig.testing,
|
|
1474
1479
|
packageManager: btsConfig.packageManager,
|
|
1480
|
+
versionChannel: btsConfig.versionChannel,
|
|
1475
1481
|
dbSetup: btsConfig.dbSetup,
|
|
1476
1482
|
api: btsConfig.api,
|
|
1477
1483
|
webDeploy: btsConfig.webDeploy,
|
|
@@ -1558,6 +1564,166 @@ async function updateBtsConfig(projectDir, updates) {
|
|
|
1558
1564
|
} catch {}
|
|
1559
1565
|
}
|
|
1560
1566
|
|
|
1567
|
+
//#endregion
|
|
1568
|
+
//#region src/utils/dependency-version-channel.ts
|
|
1569
|
+
const VERSION_CACHE = /* @__PURE__ */ new Map();
|
|
1570
|
+
const PRERELEASE_TAG_PRIORITY = [
|
|
1571
|
+
"beta",
|
|
1572
|
+
"next",
|
|
1573
|
+
"rc",
|
|
1574
|
+
"canary",
|
|
1575
|
+
"alpha"
|
|
1576
|
+
];
|
|
1577
|
+
const REGISTRY_FETCH_TIMEOUT_MS = 1e4;
|
|
1578
|
+
const REGISTRY_CONCURRENCY = 10;
|
|
1579
|
+
function mapWithConcurrency(items, fn, concurrency) {
|
|
1580
|
+
const results = Array.from({ length: items.length });
|
|
1581
|
+
let index = 0;
|
|
1582
|
+
async function worker() {
|
|
1583
|
+
while (index < items.length) {
|
|
1584
|
+
const i = index++;
|
|
1585
|
+
results[i] = await fn(items[i], i);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker())).then(() => results);
|
|
1589
|
+
}
|
|
1590
|
+
function parseVersion(value) {
|
|
1591
|
+
const normalized = value.replace(/^[^\d]*/, "");
|
|
1592
|
+
const dashIdx = normalized.indexOf("-");
|
|
1593
|
+
const base = dashIdx === -1 ? normalized : normalized.slice(0, dashIdx);
|
|
1594
|
+
const prerelease = dashIdx === -1 ? "" : normalized.slice(dashIdx + 1);
|
|
1595
|
+
const [major = "0", minor = "0", patch = "0"] = base.split(".");
|
|
1596
|
+
return {
|
|
1597
|
+
major: Number(major) || 0,
|
|
1598
|
+
minor: Number(minor) || 0,
|
|
1599
|
+
patch: Number(patch) || 0,
|
|
1600
|
+
prerelease: prerelease ? prerelease.split(/[.-]/).map((part) => /^\d+$/.test(part) ? Number(part) : part) : []
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
function compareVersions(a, b) {
|
|
1604
|
+
const left = parseVersion(a);
|
|
1605
|
+
const right = parseVersion(b);
|
|
1606
|
+
if (left.major !== right.major) return left.major - right.major;
|
|
1607
|
+
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
1608
|
+
if (left.patch !== right.patch) return left.patch - right.patch;
|
|
1609
|
+
if (left.prerelease.length === 0 && right.prerelease.length === 0) return 0;
|
|
1610
|
+
if (left.prerelease.length === 0) return 1;
|
|
1611
|
+
if (right.prerelease.length === 0) return -1;
|
|
1612
|
+
const maxLength = Math.max(left.prerelease.length, right.prerelease.length);
|
|
1613
|
+
for (let index = 0; index < maxLength; index++) {
|
|
1614
|
+
const leftPart = left.prerelease[index];
|
|
1615
|
+
const rightPart = right.prerelease[index];
|
|
1616
|
+
if (leftPart === void 0) return -1;
|
|
1617
|
+
if (rightPart === void 0) return 1;
|
|
1618
|
+
if (leftPart === rightPart) continue;
|
|
1619
|
+
if (typeof leftPart === "number" && typeof rightPart === "number") return leftPart - rightPart;
|
|
1620
|
+
if (typeof leftPart === "number") return -1;
|
|
1621
|
+
if (typeof rightPart === "number") return 1;
|
|
1622
|
+
return leftPart.localeCompare(rightPart);
|
|
1623
|
+
}
|
|
1624
|
+
return 0;
|
|
1625
|
+
}
|
|
1626
|
+
function isPrerelease(version) {
|
|
1627
|
+
return /-(alpha|beta|rc|next|canary)/i.test(version);
|
|
1628
|
+
}
|
|
1629
|
+
function getVersionPrefix(version) {
|
|
1630
|
+
return version.match(/^[^\d]*/)?.[0] ?? "";
|
|
1631
|
+
}
|
|
1632
|
+
function applyVersionPrefix(currentVersion, resolvedVersion) {
|
|
1633
|
+
return `${getVersionPrefix(currentVersion)}${resolvedVersion}`;
|
|
1634
|
+
}
|
|
1635
|
+
function isRegistrySemverSpec(version) {
|
|
1636
|
+
return /^[~^]?\d/.test(version);
|
|
1637
|
+
}
|
|
1638
|
+
async function fetchPackageInfo(packageName) {
|
|
1639
|
+
const cached = VERSION_CACHE.get(packageName);
|
|
1640
|
+
if (cached) return cached;
|
|
1641
|
+
const encodedName = encodeURIComponent(packageName).replace("%40", "@");
|
|
1642
|
+
const response = await fetch(`https://registry.npmjs.org/${encodedName}`, {
|
|
1643
|
+
headers: { Accept: "application/vnd.npm.install-v1+json" },
|
|
1644
|
+
signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS)
|
|
1645
|
+
});
|
|
1646
|
+
if (!response.ok) throw new Error(`Package ${packageName} not found (${response.status})`);
|
|
1647
|
+
const raw = await response.json();
|
|
1648
|
+
const data = {
|
|
1649
|
+
"dist-tags": raw["dist-tags"],
|
|
1650
|
+
versions: raw["versions"]
|
|
1651
|
+
};
|
|
1652
|
+
VERSION_CACHE.set(packageName, data);
|
|
1653
|
+
return data;
|
|
1654
|
+
}
|
|
1655
|
+
function selectRegistryVersionForChannel(packageInfo, channel) {
|
|
1656
|
+
const tags = packageInfo["dist-tags"] ?? {};
|
|
1657
|
+
if (channel === "latest") return tags.latest ?? null;
|
|
1658
|
+
for (const tag of PRERELEASE_TAG_PRIORITY) if (tags[tag]) return tags[tag];
|
|
1659
|
+
const prereleases = Object.keys(packageInfo.versions ?? {}).filter(isPrerelease);
|
|
1660
|
+
if (prereleases.length > 0) return prereleases.sort((left, right) => compareVersions(right, left))[0] ?? null;
|
|
1661
|
+
return tags.latest ?? null;
|
|
1662
|
+
}
|
|
1663
|
+
async function resolveRegistryVersion(packageName, channel) {
|
|
1664
|
+
const version = selectRegistryVersionForChannel(await fetchPackageInfo(packageName), channel);
|
|
1665
|
+
if (!version) throw new Error(`No ${channel} version available for ${packageName}`);
|
|
1666
|
+
return version;
|
|
1667
|
+
}
|
|
1668
|
+
async function collectPackageJsonPaths$1(projectDir) {
|
|
1669
|
+
const results = [];
|
|
1670
|
+
async function walk(currentDir) {
|
|
1671
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
1672
|
+
for (const entry of entries) {
|
|
1673
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".turbo") continue;
|
|
1674
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
1675
|
+
if (entry.isDirectory()) {
|
|
1676
|
+
await walk(fullPath);
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
if (entry.isFile() && entry.name === "package.json") results.push(fullPath);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
await walk(projectDir);
|
|
1683
|
+
return results.sort();
|
|
1684
|
+
}
|
|
1685
|
+
async function applyDependencyVersionChannel(projectDir, channel) {
|
|
1686
|
+
if (channel === "stable") return;
|
|
1687
|
+
const packageJsonPaths = await collectPackageJsonPaths$1(projectDir);
|
|
1688
|
+
if (packageJsonPaths.length === 0) return;
|
|
1689
|
+
const packageNames = /* @__PURE__ */ new Set();
|
|
1690
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
1691
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1692
|
+
for (const [depName, depVersion] of Object.entries(packageJson.dependencies ?? {})) if (typeof depVersion === "string" && isRegistrySemverSpec(depVersion)) packageNames.add(depName);
|
|
1693
|
+
for (const [depName, depVersion] of Object.entries(packageJson.devDependencies ?? {})) if (typeof depVersion === "string" && isRegistrySemverSpec(depVersion)) packageNames.add(depName);
|
|
1694
|
+
}
|
|
1695
|
+
if (packageNames.size === 0) return;
|
|
1696
|
+
const resolvedVersions = /* @__PURE__ */ new Map();
|
|
1697
|
+
await mapWithConcurrency([...packageNames], async (packageName) => {
|
|
1698
|
+
try {
|
|
1699
|
+
const resolvedVersion = await resolveRegistryVersion(packageName, channel);
|
|
1700
|
+
resolvedVersions.set(packageName, resolvedVersion);
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
log.warn(`Failed to resolve ${channel} version for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1703
|
+
}
|
|
1704
|
+
}, REGISTRY_CONCURRENCY);
|
|
1705
|
+
if (resolvedVersions.size === 0) return;
|
|
1706
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
1707
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1708
|
+
let changed = false;
|
|
1709
|
+
for (const sectionName of ["dependencies", "devDependencies"]) {
|
|
1710
|
+
const section = packageJson[sectionName];
|
|
1711
|
+
if (!section) continue;
|
|
1712
|
+
for (const [packageName, currentVersion] of Object.entries(section)) {
|
|
1713
|
+
if (!isRegistrySemverSpec(currentVersion)) continue;
|
|
1714
|
+
const resolvedVersion = resolvedVersions.get(packageName);
|
|
1715
|
+
if (!resolvedVersion) continue;
|
|
1716
|
+
const nextVersion = applyVersionPrefix(currentVersion, resolvedVersion);
|
|
1717
|
+
if (nextVersion !== currentVersion) {
|
|
1718
|
+
section[packageName] = nextVersion;
|
|
1719
|
+
changed = true;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (changed) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1561
1727
|
//#endregion
|
|
1562
1728
|
//#region src/utils/add-package-deps.ts
|
|
1563
1729
|
const addPackageDependency = async (opts) => {
|
|
@@ -2946,6 +3112,7 @@ async function addHandlerInternal(input) {
|
|
|
2946
3112
|
config
|
|
2947
3113
|
}, projectDir);
|
|
2948
3114
|
await setupAddons(config);
|
|
3115
|
+
await applyDependencyVersionChannel(projectDir, config.versionChannel);
|
|
2949
3116
|
const configUpdates = { addons: [...new Set([...existingAddons, ...addonsToAdd])] };
|
|
2950
3117
|
if (input.webDeploy !== void 0) configUpdates.webDeploy = input.webDeploy;
|
|
2951
3118
|
if (input.serverDeploy !== void 0) configUpdates.serverDeploy = input.serverDeploy;
|
|
@@ -5039,6 +5206,12 @@ const ICON_LIBRARY_OPTIONS = [
|
|
|
5039
5206
|
}
|
|
5040
5207
|
];
|
|
5041
5208
|
const COLOR_THEME_OPTIONS = [
|
|
5209
|
+
{
|
|
5210
|
+
value: "neutral",
|
|
5211
|
+
label: "── Neutrals ──",
|
|
5212
|
+
hint: "",
|
|
5213
|
+
disabled: true
|
|
5214
|
+
},
|
|
5042
5215
|
{
|
|
5043
5216
|
value: "neutral",
|
|
5044
5217
|
label: "Neutral",
|
|
@@ -5059,20 +5232,52 @@ const COLOR_THEME_OPTIONS = [
|
|
|
5059
5232
|
label: "Gray",
|
|
5060
5233
|
hint: "Blue-tinted neutral"
|
|
5061
5234
|
},
|
|
5235
|
+
{
|
|
5236
|
+
value: "neutral",
|
|
5237
|
+
label: "── Cool ──",
|
|
5238
|
+
hint: "",
|
|
5239
|
+
disabled: true
|
|
5240
|
+
},
|
|
5062
5241
|
{
|
|
5063
5242
|
value: "blue",
|
|
5064
5243
|
label: "Blue",
|
|
5065
5244
|
hint: "Trust and reliability"
|
|
5066
5245
|
},
|
|
5246
|
+
{
|
|
5247
|
+
value: "indigo",
|
|
5248
|
+
label: "Indigo",
|
|
5249
|
+
hint: "Deep and focused"
|
|
5250
|
+
},
|
|
5067
5251
|
{
|
|
5068
5252
|
value: "violet",
|
|
5069
5253
|
label: "Violet",
|
|
5070
5254
|
hint: "Creative and modern"
|
|
5071
5255
|
},
|
|
5072
5256
|
{
|
|
5073
|
-
value: "
|
|
5074
|
-
label: "
|
|
5075
|
-
hint: "
|
|
5257
|
+
value: "purple",
|
|
5258
|
+
label: "Purple",
|
|
5259
|
+
hint: "Royal and elegant"
|
|
5260
|
+
},
|
|
5261
|
+
{
|
|
5262
|
+
value: "cyan",
|
|
5263
|
+
label: "Cyan",
|
|
5264
|
+
hint: "Cool and refreshing"
|
|
5265
|
+
},
|
|
5266
|
+
{
|
|
5267
|
+
value: "sky",
|
|
5268
|
+
label: "Sky",
|
|
5269
|
+
hint: "Light and airy"
|
|
5270
|
+
},
|
|
5271
|
+
{
|
|
5272
|
+
value: "teal",
|
|
5273
|
+
label: "Teal",
|
|
5274
|
+
hint: "Calm and sophisticated"
|
|
5275
|
+
},
|
|
5276
|
+
{
|
|
5277
|
+
value: "neutral",
|
|
5278
|
+
label: "── Warm ──",
|
|
5279
|
+
hint: "",
|
|
5280
|
+
disabled: true
|
|
5076
5281
|
},
|
|
5077
5282
|
{
|
|
5078
5283
|
value: "red",
|
|
@@ -5104,36 +5309,16 @@ const COLOR_THEME_OPTIONS = [
|
|
|
5104
5309
|
label: "Lime",
|
|
5105
5310
|
hint: "Fresh and lively"
|
|
5106
5311
|
},
|
|
5312
|
+
{
|
|
5313
|
+
value: "green",
|
|
5314
|
+
label: "Green",
|
|
5315
|
+
hint: "Growth and success"
|
|
5316
|
+
},
|
|
5107
5317
|
{
|
|
5108
5318
|
value: "emerald",
|
|
5109
5319
|
label: "Emerald",
|
|
5110
5320
|
hint: "Rich and luxurious"
|
|
5111
5321
|
},
|
|
5112
|
-
{
|
|
5113
|
-
value: "teal",
|
|
5114
|
-
label: "Teal",
|
|
5115
|
-
hint: "Calm and sophisticated"
|
|
5116
|
-
},
|
|
5117
|
-
{
|
|
5118
|
-
value: "cyan",
|
|
5119
|
-
label: "Cyan",
|
|
5120
|
-
hint: "Cool and refreshing"
|
|
5121
|
-
},
|
|
5122
|
-
{
|
|
5123
|
-
value: "sky",
|
|
5124
|
-
label: "Sky",
|
|
5125
|
-
hint: "Light and airy"
|
|
5126
|
-
},
|
|
5127
|
-
{
|
|
5128
|
-
value: "indigo",
|
|
5129
|
-
label: "Indigo",
|
|
5130
|
-
hint: "Deep and focused"
|
|
5131
|
-
},
|
|
5132
|
-
{
|
|
5133
|
-
value: "purple",
|
|
5134
|
-
label: "Purple",
|
|
5135
|
-
hint: "Royal and elegant"
|
|
5136
|
-
},
|
|
5137
5322
|
{
|
|
5138
5323
|
value: "fuchsia",
|
|
5139
5324
|
label: "Fuchsia",
|
|
@@ -5985,6 +6170,35 @@ async function getProjectName(initialName) {
|
|
|
5985
6170
|
return projectPath;
|
|
5986
6171
|
}
|
|
5987
6172
|
|
|
6173
|
+
//#endregion
|
|
6174
|
+
//#region src/prompts/version-channel.ts
|
|
6175
|
+
async function getVersionChannelChoice(versionChannel) {
|
|
6176
|
+
if (versionChannel !== void 0) return versionChannel;
|
|
6177
|
+
const response = await navigableSelect({
|
|
6178
|
+
message: "Choose dependency version channel",
|
|
6179
|
+
options: [
|
|
6180
|
+
{
|
|
6181
|
+
value: "stable",
|
|
6182
|
+
label: "Stable",
|
|
6183
|
+
hint: "Use Better Fullstack's curated pinned versions"
|
|
6184
|
+
},
|
|
6185
|
+
{
|
|
6186
|
+
value: "latest",
|
|
6187
|
+
label: "Latest",
|
|
6188
|
+
hint: "Resolve current npm latest tags during scaffolding"
|
|
6189
|
+
},
|
|
6190
|
+
{
|
|
6191
|
+
value: "beta",
|
|
6192
|
+
label: "Beta",
|
|
6193
|
+
hint: "Prefer npm beta/next prerelease tags when available"
|
|
6194
|
+
}
|
|
6195
|
+
],
|
|
6196
|
+
initialValue: "stable"
|
|
6197
|
+
});
|
|
6198
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6199
|
+
return response;
|
|
6200
|
+
}
|
|
6201
|
+
|
|
5988
6202
|
//#endregion
|
|
5989
6203
|
//#region src/utils/telemetry.ts
|
|
5990
6204
|
/**
|
|
@@ -6077,6 +6291,7 @@ function displayConfig(config) {
|
|
|
6077
6291
|
configDisplay.push(`${pc.blue("Git Init:")} ${gitText}`);
|
|
6078
6292
|
}
|
|
6079
6293
|
if (config.packageManager !== void 0) configDisplay.push(`${pc.blue("Package Manager:")} ${String(config.packageManager)}`);
|
|
6294
|
+
if (config.versionChannel !== void 0) configDisplay.push(`${pc.blue("Version Channel:")} ${String(config.versionChannel)}`);
|
|
6080
6295
|
if (config.install !== void 0) {
|
|
6081
6296
|
const installText = typeof config.install === "boolean" ? config.install ? "Yes" : "No" : String(config.install);
|
|
6082
6297
|
configDisplay.push(`${pc.blue("Install Dependencies:")} ${installText}`);
|
|
@@ -6109,6 +6324,7 @@ function appendCommonFlags(flags, config) {
|
|
|
6109
6324
|
else flags.push("--ai-docs none");
|
|
6110
6325
|
flags.push(config.git ? "--git" : "--no-git");
|
|
6111
6326
|
flags.push(`--package-manager ${config.packageManager}`);
|
|
6327
|
+
if (config.versionChannel !== "stable") flags.push(`--version-channel ${config.versionChannel}`);
|
|
6112
6328
|
flags.push(config.install ? "--install" : "--no-install");
|
|
6113
6329
|
}
|
|
6114
6330
|
function appendSharedNonTypeScriptFlags(flags, config) {
|
|
@@ -6432,6 +6648,7 @@ function processFlags(options, projectName) {
|
|
|
6432
6648
|
if (options.runtime) config.runtime = options.runtime;
|
|
6433
6649
|
if (options.dbSetup) config.dbSetup = options.dbSetup;
|
|
6434
6650
|
if (options.packageManager) config.packageManager = options.packageManager;
|
|
6651
|
+
if (options.versionChannel) config.versionChannel = options.versionChannel;
|
|
6435
6652
|
if (options.webDeploy) config.webDeploy = options.webDeploy;
|
|
6436
6653
|
if (options.serverDeploy) config.serverDeploy = options.serverDeploy;
|
|
6437
6654
|
const derivedName = deriveProjectName(projectName, options.projectDirectory);
|
|
@@ -6789,6 +7006,15 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6789
7006
|
function validateEcosystemAuthCompatibility(config, providedFlags) {
|
|
6790
7007
|
const auth = config.auth;
|
|
6791
7008
|
if (!auth || auth === "none") return;
|
|
7009
|
+
if (auth === "better-auth" && config.orm && [
|
|
7010
|
+
"typeorm",
|
|
7011
|
+
"sequelize",
|
|
7012
|
+
"mikroorm"
|
|
7013
|
+
].includes(config.orm)) {
|
|
7014
|
+
config.auth = "none";
|
|
7015
|
+
if (providedFlags?.has("auth") && !isSilent()) consola.warn(pc.yellow(`Unsupported auth selection '${auth}' with ${config.orm}: no Better Auth adapter exists. Falling back to '--auth none'.`));
|
|
7016
|
+
return;
|
|
7017
|
+
}
|
|
6792
7018
|
const normalized = (0, types_exports.normalizeCapabilitySelection)("auth", {
|
|
6793
7019
|
ecosystem: config.ecosystem,
|
|
6794
7020
|
backend: config.backend,
|
|
@@ -8321,7 +8547,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
8321
8547
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
8322
8548
|
let stepCounter = 2;
|
|
8323
8549
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
8324
|
-
if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH\n`;
|
|
8550
|
+
if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH.\n Install: ${pc.underline("https://docs.deno.com/runtime/getting_started/installation/")}\n`;
|
|
8325
8551
|
if (database === "sqlite" && dbSetup !== "d1") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} db:local\n${pc.dim(" (optional - starts local SQLite database)")}\n`;
|
|
8326
8552
|
if (isConvex) {
|
|
8327
8553
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
@@ -8409,7 +8635,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
8409
8635
|
}
|
|
8410
8636
|
if (dbSetup === "turso" && orm === "prisma") instructions.push(`${pc.yellow("NOTE:")} Follow Turso's Prisma guide for migrations via the Turso CLI:\n https://docs.turso.tech/sdk/ts/orm/prisma`);
|
|
8411
8637
|
if (orm === "prisma") {
|
|
8412
|
-
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination
|
|
8638
|
+
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker: this combination has known issues.\n Consider using MongoDB Atlas (${pc.underline("https://www.mongodb.com/atlas")}) for reliable Prisma + MongoDB support.`);
|
|
8413
8639
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
8414
8640
|
if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
|
|
8415
8641
|
instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
|
|
@@ -8607,6 +8833,7 @@ async function createProject(options, cliInput = {}) {
|
|
|
8607
8833
|
await setPackageManagerVersion(projectDir, options.packageManager);
|
|
8608
8834
|
if (!isConvex && options.database !== "none") await setupDatabase(options, cliInput);
|
|
8609
8835
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
8836
|
+
await applyDependencyVersionChannel(projectDir, options.versionChannel);
|
|
8610
8837
|
await writeBtsConfig(options);
|
|
8611
8838
|
await formatProject(projectDir);
|
|
8612
8839
|
if (!isSilent()) log.success("Project template successfully scaffolded!");
|
|
@@ -8651,6 +8878,10 @@ async function setPackageManagerVersion(projectDir, packageManager) {
|
|
|
8651
8878
|
|
|
8652
8879
|
//#endregion
|
|
8653
8880
|
//#region src/helpers/core/command-handlers.ts
|
|
8881
|
+
function shouldPromptForVersionChannel(input) {
|
|
8882
|
+
if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
|
|
8883
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true && process.env.CI !== "true";
|
|
8884
|
+
}
|
|
8654
8885
|
async function createProjectHandler(input, options = {}) {
|
|
8655
8886
|
const { silent = false } = options;
|
|
8656
8887
|
return runWithContextAsync({ silent }, async () => {
|
|
@@ -8672,6 +8903,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
8672
8903
|
}
|
|
8673
8904
|
currentPathInput = defaultName;
|
|
8674
8905
|
} else currentPathInput = await getProjectName(input.projectName);
|
|
8906
|
+
const versionChannel = shouldPromptForVersionChannel(input) ? await getVersionChannelChoice() : input.versionChannel ?? "stable";
|
|
8675
8907
|
let finalPathInput;
|
|
8676
8908
|
let shouldClearDirectory;
|
|
8677
8909
|
try {
|
|
@@ -8707,6 +8939,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
8707
8939
|
effect: "none",
|
|
8708
8940
|
git: false,
|
|
8709
8941
|
packageManager: "npm",
|
|
8942
|
+
versionChannel: "stable",
|
|
8710
8943
|
install: false,
|
|
8711
8944
|
dbSetup: "none",
|
|
8712
8945
|
api: "none",
|
|
@@ -8791,7 +9024,8 @@ async function createProjectHandler(input, options = {}) {
|
|
|
8791
9024
|
...flagConfig,
|
|
8792
9025
|
projectName: finalBaseName,
|
|
8793
9026
|
projectDir: finalResolvedPath,
|
|
8794
|
-
relativePath: finalPathInput
|
|
9027
|
+
relativePath: finalPathInput,
|
|
9028
|
+
versionChannel
|
|
8795
9029
|
};
|
|
8796
9030
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
8797
9031
|
const yesPreflight = validatePreflightConfig(config);
|
|
@@ -8808,7 +9042,10 @@ async function createProjectHandler(input, options = {}) {
|
|
|
8808
9042
|
log.message(displayConfig(otherFlags));
|
|
8809
9043
|
log.message("");
|
|
8810
9044
|
}
|
|
8811
|
-
config =
|
|
9045
|
+
config = {
|
|
9046
|
+
...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput),
|
|
9047
|
+
versionChannel
|
|
9048
|
+
};
|
|
8812
9049
|
}
|
|
8813
9050
|
const preflight = validatePreflightConfig(config);
|
|
8814
9051
|
if (preflight.hasWarnings && !isSilent()) displayPreflightWarnings(preflight);
|
|
@@ -9002,6 +9239,7 @@ const router = os.router({
|
|
|
9002
9239
|
git: z.boolean().optional(),
|
|
9003
9240
|
packageManager: types_exports.PackageManagerSchema.optional(),
|
|
9004
9241
|
install: z.boolean().optional(),
|
|
9242
|
+
versionChannel: types_exports.VersionChannelSchema.optional().describe("Dependency version channel (stable, latest, beta)"),
|
|
9005
9243
|
dbSetup: types_exports.DatabaseSetupSchema.optional(),
|
|
9006
9244
|
backend: types_exports.BackendSchema.optional(),
|
|
9007
9245
|
runtime: types_exports.RuntimeSchema.optional(),
|
|
@@ -9065,7 +9303,7 @@ const router = os.router({
|
|
|
9065
9303
|
log.message(`Please visit ${DOCS_URL}`);
|
|
9066
9304
|
}
|
|
9067
9305
|
}),
|
|
9068
|
-
builder: os.meta({ description: "Open the web-based stack builder" }).handler(async () => {
|
|
9306
|
+
builder: os.meta({ description: "Open the interactive web-based stack builder at better-fullstack.dev" }).handler(async () => {
|
|
9069
9307
|
const BUILDER_URL = "https://better-fullstack-web.vercel.app/new";
|
|
9070
9308
|
try {
|
|
9071
9309
|
await openUrl(BUILDER_URL);
|
|
@@ -9074,7 +9312,7 @@ const router = os.router({
|
|
|
9074
9312
|
log.message(`Please visit ${BUILDER_URL}`);
|
|
9075
9313
|
}
|
|
9076
9314
|
}),
|
|
9077
|
-
add: os.meta({ description: "Add addons to an existing Better Fullstack project" }).input(z.object({
|
|
9315
|
+
add: os.meta({ description: "Add addons or deployment targets to an existing Better Fullstack project" }).input(z.object({
|
|
9078
9316
|
addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
|
|
9079
9317
|
webDeploy: types_exports.WebDeploySchema.optional().describe("Web deployment option to set"),
|
|
9080
9318
|
serverDeploy: types_exports.ServerDeploySchema.optional().describe("Server deployment option to set"),
|
|
@@ -9084,7 +9322,7 @@ const router = os.router({
|
|
|
9084
9322
|
})).handler(async ({ input }) => {
|
|
9085
9323
|
await addHandler(input);
|
|
9086
9324
|
}),
|
|
9087
|
-
history: os.meta({ description: "Show
|
|
9325
|
+
history: os.meta({ description: "Show history of scaffolded projects with reproducible commands" }).input(z.object({
|
|
9088
9326
|
limit: z.number().optional().default(10).describe("Number of entries to show"),
|
|
9089
9327
|
clear: z.boolean().optional().default(false).describe("Clear all history"),
|
|
9090
9328
|
json: z.boolean().optional().default(false).describe("Output as JSON")
|
|
@@ -9226,6 +9464,7 @@ async function createVirtual(options) {
|
|
|
9226
9464
|
effect: options.effect || "none",
|
|
9227
9465
|
git: options.git ?? false,
|
|
9228
9466
|
packageManager: options.packageManager || "bun",
|
|
9467
|
+
versionChannel: options.versionChannel || "stable",
|
|
9229
9468
|
install: false,
|
|
9230
9469
|
dbSetup: options.dbSetup || "none",
|
|
9231
9470
|
api: options.api || "trpc",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-fullstack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Scaffold production-ready fullstack apps in seconds. Pick your stack from 270+ options — the CLI wires everything together.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -108,17 +108,17 @@
|
|
|
108
108
|
"prepublishOnly": "npm run build"
|
|
109
109
|
},
|
|
110
110
|
"dependencies": {
|
|
111
|
-
"@better-fullstack/template-generator": "^1.
|
|
112
|
-
"@better-fullstack/types": "^1.
|
|
111
|
+
"@better-fullstack/template-generator": "^1.5.1",
|
|
112
|
+
"@better-fullstack/types": "^1.5.1",
|
|
113
113
|
"@clack/core": "^0.5.0",
|
|
114
114
|
"@clack/prompts": "^1.1.0",
|
|
115
|
-
"@orpc/server": "^1.13.
|
|
115
|
+
"@orpc/server": "^1.13.13",
|
|
116
116
|
"consola": "^3.4.2",
|
|
117
117
|
"env-paths": "^4.0.0",
|
|
118
118
|
"execa": "^9.6.1",
|
|
119
119
|
"fs-extra": "^11.3.4",
|
|
120
120
|
"gradient-string": "^3.0.0",
|
|
121
|
-
"handlebars": "^4.7.
|
|
121
|
+
"handlebars": "^4.7.9",
|
|
122
122
|
"jsonc-parser": "^3.3.1",
|
|
123
123
|
"oxfmt": "^0.19.0",
|
|
124
124
|
"picocolors": "^1.1.1",
|