create-better-t-stack 2.33.8-canary.98a850ad → 2.33.9-canary.7db03e6d
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/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-D0MPVT22.js → src-CEmXO3U_.js} +101 -34
- package/package.json +1 -1
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import { createCli, trpcServer } from "trpc-cli";
|
|
|
5
5
|
import z from "zod";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import consola, { consola as consola$1 } from "consola";
|
|
8
|
-
import * as fs$1 from "fs-extra";
|
|
9
8
|
import fs from "fs-extra";
|
|
10
9
|
import { fileURLToPath } from "node:url";
|
|
11
10
|
import gradient from "gradient-string";
|
|
@@ -137,7 +136,8 @@ const ADDON_COMPATIBILITY = {
|
|
|
137
136
|
"react-router",
|
|
138
137
|
"nuxt",
|
|
139
138
|
"svelte",
|
|
140
|
-
"solid"
|
|
139
|
+
"solid",
|
|
140
|
+
"next"
|
|
141
141
|
],
|
|
142
142
|
biome: [],
|
|
143
143
|
husky: [],
|
|
@@ -565,6 +565,20 @@ function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
|
565
565
|
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
566
566
|
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
567
567
|
}
|
|
568
|
+
function validateAddonsAgainstFrontends(addons = [], frontends = []) {
|
|
569
|
+
for (const addon of addons) {
|
|
570
|
+
if (addon === "none") continue;
|
|
571
|
+
const { isCompatible, reason } = validateAddonCompatibility(addon, frontends);
|
|
572
|
+
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function validateExamplesCompatibility(examples, backend, database, frontend) {
|
|
576
|
+
const examplesArr = examples ?? [];
|
|
577
|
+
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
578
|
+
if (examplesArr.includes("todo") && backend !== "convex" && backend !== "none" && database === "none") exitWithError("The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.");
|
|
579
|
+
if (examplesArr.includes("ai") && backend === "elysia") exitWithError("The 'ai' example is not compatible with the Elysia backend.");
|
|
580
|
+
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
581
|
+
}
|
|
568
582
|
|
|
569
583
|
//#endregion
|
|
570
584
|
//#region src/prompts/api.ts
|
|
@@ -1298,9 +1312,9 @@ async function getProjectName(initialName) {
|
|
|
1298
1312
|
|
|
1299
1313
|
//#endregion
|
|
1300
1314
|
//#region src/utils/get-latest-cli-version.ts
|
|
1301
|
-
const getLatestCLIVersion =
|
|
1315
|
+
const getLatestCLIVersion = () => {
|
|
1302
1316
|
const packageJsonPath = path.join(PKG_ROOT, "package.json");
|
|
1303
|
-
const packageJsonContent =
|
|
1317
|
+
const packageJsonContent = fs.readJSONSync(packageJsonPath);
|
|
1304
1318
|
return packageJsonContent.version ?? "1.0.0";
|
|
1305
1319
|
};
|
|
1306
1320
|
|
|
@@ -1662,6 +1676,43 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
1662
1676
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
1663
1677
|
return config;
|
|
1664
1678
|
}
|
|
1679
|
+
function validateConfigCompatibility(config) {
|
|
1680
|
+
const effectiveDatabase = config.database;
|
|
1681
|
+
const effectiveBackend = config.backend;
|
|
1682
|
+
const effectiveFrontend = config.frontend;
|
|
1683
|
+
const effectiveApi = config.api;
|
|
1684
|
+
validateApiFrontendCompatibility(effectiveApi, effectiveFrontend);
|
|
1685
|
+
if (config.addons && config.addons.length > 0) {
|
|
1686
|
+
validateAddonsAgainstFrontends(config.addons, effectiveFrontend);
|
|
1687
|
+
config.addons = [...new Set(config.addons)];
|
|
1688
|
+
}
|
|
1689
|
+
validateExamplesCompatibility(config.examples ?? [], effectiveBackend, effectiveDatabase, effectiveFrontend ?? []);
|
|
1690
|
+
}
|
|
1691
|
+
function processProvidedFlagsWithoutValidation(options, projectName) {
|
|
1692
|
+
const config = {};
|
|
1693
|
+
if (options.api) config.api = options.api;
|
|
1694
|
+
if (options.backend) config.backend = options.backend;
|
|
1695
|
+
if (options.database) config.database = options.database;
|
|
1696
|
+
if (options.orm) config.orm = options.orm;
|
|
1697
|
+
if (options.auth !== void 0) config.auth = options.auth;
|
|
1698
|
+
if (options.git !== void 0) config.git = options.git;
|
|
1699
|
+
if (options.install !== void 0) config.install = options.install;
|
|
1700
|
+
if (options.runtime) config.runtime = options.runtime;
|
|
1701
|
+
if (options.dbSetup) config.dbSetup = options.dbSetup;
|
|
1702
|
+
if (options.packageManager) config.packageManager = options.packageManager;
|
|
1703
|
+
if (options.webDeploy) config.webDeploy = options.webDeploy;
|
|
1704
|
+
const derivedName = deriveProjectName(projectName, options.projectDirectory);
|
|
1705
|
+
if (derivedName) {
|
|
1706
|
+
const nameToValidate = projectName ? path.basename(projectName) : derivedName;
|
|
1707
|
+
const result = ProjectNameSchema.safeParse(nameToValidate);
|
|
1708
|
+
if (!result.success) throw new Error(`Invalid project name: ${result.error.issues[0]?.message}`);
|
|
1709
|
+
config.projectName = projectName || derivedName;
|
|
1710
|
+
}
|
|
1711
|
+
if (options.frontend && options.frontend.length > 0) config.frontend = processArrayOption(options.frontend);
|
|
1712
|
+
if (options.addons && options.addons.length > 0) config.addons = processArrayOption(options.addons);
|
|
1713
|
+
if (options.examples && options.examples.length > 0) config.examples = processArrayOption(options.examples);
|
|
1714
|
+
return config;
|
|
1715
|
+
}
|
|
1665
1716
|
function getProvidedFlags(options) {
|
|
1666
1717
|
return new Set(Object.keys(options).filter((key) => options[key] !== void 0));
|
|
1667
1718
|
}
|
|
@@ -2216,20 +2267,16 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
2216
2267
|
}
|
|
2217
2268
|
} else {
|
|
2218
2269
|
if (context.webDeploy === "alchemy") {
|
|
2270
|
+
const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
|
|
2219
2271
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
2220
|
-
if (await fs.pathExists(webAppDir))
|
|
2221
|
-
const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
|
|
2222
|
-
if (await fs.pathExists(alchemyTemplateSrc)) await processAndCopyFiles("**/*", alchemyTemplateSrc, webAppDir, context);
|
|
2223
|
-
}
|
|
2272
|
+
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
|
|
2224
2273
|
}
|
|
2225
2274
|
if (context.serverDeploy === "alchemy") {
|
|
2275
|
+
const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
|
|
2226
2276
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2227
|
-
if (await fs.pathExists(serverAppDir)) {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2231
|
-
await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2232
|
-
}
|
|
2277
|
+
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
|
|
2278
|
+
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2279
|
+
await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2233
2280
|
}
|
|
2234
2281
|
}
|
|
2235
2282
|
}
|
|
@@ -2501,7 +2548,7 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2501
2548
|
];
|
|
2502
2549
|
if (editors.length > 0) ultraciteArgs.push("--editors", ...editors);
|
|
2503
2550
|
if (rules.length > 0) ultraciteArgs.push("--rules", ...rules);
|
|
2504
|
-
if (hasHusky) ultraciteArgs.push("--
|
|
2551
|
+
if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
|
|
2505
2552
|
const ultraciteArgsString = ultraciteArgs.join(" ");
|
|
2506
2553
|
const commandWithArgs = `ultracite@latest ${ultraciteArgsString} --skip-install`;
|
|
2507
2554
|
const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
@@ -3342,15 +3389,15 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3342
3389
|
//#region src/helpers/deployment/workers/workers-next-setup.ts
|
|
3343
3390
|
async function setupNextWorkersDeploy(projectDir, _packageManager) {
|
|
3344
3391
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3345
|
-
if (!await fs
|
|
3392
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
3346
3393
|
await addPackageDependency({
|
|
3347
3394
|
dependencies: ["@opennextjs/cloudflare"],
|
|
3348
3395
|
devDependencies: ["wrangler"],
|
|
3349
3396
|
projectDir: webAppDir
|
|
3350
3397
|
});
|
|
3351
3398
|
const packageJsonPath = path.join(webAppDir, "package.json");
|
|
3352
|
-
if (await fs
|
|
3353
|
-
const pkg = await fs
|
|
3399
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
3400
|
+
const pkg = await fs.readJson(packageJsonPath);
|
|
3354
3401
|
pkg.scripts = {
|
|
3355
3402
|
...pkg.scripts,
|
|
3356
3403
|
preview: "opennextjs-cloudflare build && opennextjs-cloudflare preview",
|
|
@@ -3358,7 +3405,7 @@ async function setupNextWorkersDeploy(projectDir, _packageManager) {
|
|
|
3358
3405
|
upload: "opennextjs-cloudflare build && opennextjs-cloudflare upload",
|
|
3359
3406
|
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
|
|
3360
3407
|
};
|
|
3361
|
-
await fs
|
|
3408
|
+
await fs.writeJson(packageJsonPath, pkg, { spaces: 2 });
|
|
3362
3409
|
}
|
|
3363
3410
|
}
|
|
3364
3411
|
|
|
@@ -5471,13 +5518,14 @@ async function displayPostInstallInstructions(config) {
|
|
|
5471
5518
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager;
|
|
5472
5519
|
const cdCmd = `cd ${relativePath}`;
|
|
5473
5520
|
const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
|
|
5474
|
-
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup) : "";
|
|
5521
|
+
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
|
|
5475
5522
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
5476
5523
|
const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
|
|
5477
5524
|
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
|
|
5478
5525
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
5479
5526
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
5480
|
-
const
|
|
5527
|
+
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
5528
|
+
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
5481
5529
|
const hasWeb = frontend?.some((f) => [
|
|
5482
5530
|
"tanstack-router",
|
|
5483
5531
|
"react-router",
|
|
@@ -5520,7 +5568,8 @@ async function displayPostInstallInstructions(config) {
|
|
|
5520
5568
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
5521
5569
|
if (lintingInstructions) output += `\n${lintingInstructions.trim()}\n`;
|
|
5522
5570
|
if (pwaInstructions) output += `\n${pwaInstructions.trim()}\n`;
|
|
5523
|
-
if (
|
|
5571
|
+
if (wranglerDeployInstructions) output += `\n${wranglerDeployInstructions.trim()}\n`;
|
|
5572
|
+
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
5524
5573
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
5525
5574
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
5526
5575
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
@@ -5541,7 +5590,7 @@ function getNativeInstructions(isConvex) {
|
|
|
5541
5590
|
function getLintingInstructions(runCmd) {
|
|
5542
5591
|
return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
|
|
5543
5592
|
}
|
|
5544
|
-
async function getDatabaseInstructions(database, orm, runCmd,
|
|
5593
|
+
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, serverDeploy) {
|
|
5545
5594
|
const instructions = [];
|
|
5546
5595
|
if (dbSetup === "docker") {
|
|
5547
5596
|
const dockerStatus = await getDockerStatus(database);
|
|
@@ -5550,7 +5599,7 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup)
|
|
|
5550
5599
|
instructions.push("");
|
|
5551
5600
|
}
|
|
5552
5601
|
}
|
|
5553
|
-
if (
|
|
5602
|
+
if (serverDeploy === "wrangler" && dbSetup === "d1") {
|
|
5554
5603
|
const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
|
|
5555
5604
|
instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
|
|
5556
5605
|
instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
|
|
@@ -5560,6 +5609,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup)
|
|
|
5560
5609
|
instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
|
|
5561
5610
|
instructions.push("");
|
|
5562
5611
|
}
|
|
5612
|
+
if (dbSetup === "d1" && serverDeploy === "alchemy") {
|
|
5613
|
+
instructions.push(`${pc.cyan("•")} Generate migrations: ${pc.white(`${runCmd} db:generate`)}`);
|
|
5614
|
+
instructions.push("");
|
|
5615
|
+
}
|
|
5563
5616
|
if (orm === "prisma") {
|
|
5564
5617
|
if (dbSetup === "turso") instructions.push(`${pc.yellow("NOTE:")} Turso support with Prisma is in Early Access and requires\n additional setup. Learn more at:\n https://www.prisma.io/docs/orm/overview/databases/turso`);
|
|
5565
5618
|
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
|
|
@@ -5591,8 +5644,18 @@ function getNoOrmWarning() {
|
|
|
5591
5644
|
function getBunWebNativeWarning() {
|
|
5592
5645
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
5593
5646
|
}
|
|
5594
|
-
function
|
|
5595
|
-
|
|
5647
|
+
function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
5648
|
+
const instructions = [];
|
|
5649
|
+
if (webDeploy === "wrangler") instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} run deploy`}`);
|
|
5650
|
+
if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
|
|
5651
|
+
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
5652
|
+
}
|
|
5653
|
+
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
5654
|
+
const instructions = [];
|
|
5655
|
+
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web to Alchemy:")}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}`);
|
|
5656
|
+
else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server to Alchemy:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}`);
|
|
5657
|
+
else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy to Alchemy:")}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}`);
|
|
5658
|
+
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
5596
5659
|
}
|
|
5597
5660
|
|
|
5598
5661
|
//#endregion
|
|
@@ -5902,15 +5965,9 @@ async function createProjectHandler(input) {
|
|
|
5902
5965
|
projectDirectory: input.projectName
|
|
5903
5966
|
};
|
|
5904
5967
|
const providedFlags = getProvidedFlags(cliInput);
|
|
5905
|
-
const flagConfig = processAndValidateFlags(cliInput, providedFlags, finalBaseName);
|
|
5906
|
-
const { projectName: _projectNameFromFlags,...otherFlags } = flagConfig;
|
|
5907
|
-
if (!input.yes && Object.keys(otherFlags).length > 0) {
|
|
5908
|
-
log.info(pc.yellow("Using these pre-selected options:"));
|
|
5909
|
-
log.message(displayConfig(otherFlags));
|
|
5910
|
-
log.message("");
|
|
5911
|
-
}
|
|
5912
5968
|
let config;
|
|
5913
5969
|
if (input.yes) {
|
|
5970
|
+
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
5914
5971
|
config = {
|
|
5915
5972
|
...DEFAULT_CONFIG,
|
|
5916
5973
|
...flagConfig,
|
|
@@ -5918,12 +5975,22 @@ async function createProjectHandler(input) {
|
|
|
5918
5975
|
projectDir: finalResolvedPath,
|
|
5919
5976
|
relativePath: finalPathInput
|
|
5920
5977
|
};
|
|
5978
|
+
validateConfigCompatibility(config);
|
|
5921
5979
|
if (config.backend === "convex") log.info("Due to '--backend convex' flag, the following options have been automatically set: auth=false, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo");
|
|
5922
5980
|
else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none");
|
|
5923
5981
|
log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
|
|
5924
5982
|
log.message(displayConfig(config));
|
|
5925
5983
|
log.message("");
|
|
5926
|
-
} else
|
|
5984
|
+
} else {
|
|
5985
|
+
const flagConfig = processAndValidateFlags(cliInput, providedFlags, finalBaseName);
|
|
5986
|
+
const { projectName: _projectNameFromFlags,...otherFlags } = flagConfig;
|
|
5987
|
+
if (Object.keys(otherFlags).length > 0) {
|
|
5988
|
+
log.info(pc.yellow("Using these pre-selected options:"));
|
|
5989
|
+
log.message(displayConfig(otherFlags));
|
|
5990
|
+
log.message("");
|
|
5991
|
+
}
|
|
5992
|
+
config = await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput);
|
|
5993
|
+
}
|
|
5927
5994
|
await createProject(config);
|
|
5928
5995
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
5929
5996
|
log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.33.
|
|
3
|
+
"version": "2.33.9-canary.7db03e6d",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|