betterstart-cli 0.0.40 → 0.0.42
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 +55 -158
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1508,8 +1508,15 @@ function runDrizzlePush(cwd, options = {}) {
|
|
|
1508
1508
|
});
|
|
1509
1509
|
let stdout = "";
|
|
1510
1510
|
let stderr = "";
|
|
1511
|
+
let outputNotified = false;
|
|
1512
|
+
const notifyOutput = () => {
|
|
1513
|
+
if (outputNotified) return;
|
|
1514
|
+
outputNotified = true;
|
|
1515
|
+
options.onOutput?.();
|
|
1516
|
+
};
|
|
1511
1517
|
const stderrFilter = createDrizzleNoiseFilter((chunk) => {
|
|
1512
1518
|
if (options.interactive) {
|
|
1519
|
+
notifyOutput();
|
|
1513
1520
|
process.stderr.write(chunk);
|
|
1514
1521
|
return;
|
|
1515
1522
|
}
|
|
@@ -1517,6 +1524,7 @@ function runDrizzlePush(cwd, options = {}) {
|
|
|
1517
1524
|
});
|
|
1518
1525
|
const stdoutFilter = createDrizzleNoiseFilter((chunk) => {
|
|
1519
1526
|
if (options.interactive) {
|
|
1527
|
+
notifyOutput();
|
|
1520
1528
|
process.stdout.write(chunk);
|
|
1521
1529
|
return;
|
|
1522
1530
|
}
|
|
@@ -22241,7 +22249,7 @@ async function promptPlugins(cwd, options = {}) {
|
|
|
22241
22249
|
} else {
|
|
22242
22250
|
if (existingToken) {
|
|
22243
22251
|
p12.log.info(
|
|
22244
|
-
`Using the existing
|
|
22252
|
+
`Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${styleText(
|
|
22245
22253
|
"dim",
|
|
22246
22254
|
`(${maskBlobToken(existingToken)})`
|
|
22247
22255
|
)}`
|
|
@@ -22270,30 +22278,13 @@ async function promptPlugins(cwd, options = {}) {
|
|
|
22270
22278
|
process.exit(0);
|
|
22271
22279
|
}
|
|
22272
22280
|
const integrations = [];
|
|
22273
|
-
|
|
22274
|
-
|
|
22275
|
-
|
|
22276
|
-
"
|
|
22277
|
-
|
|
22278
|
-
|
|
22279
|
-
initialValue: true
|
|
22281
|
+
sections.push({
|
|
22282
|
+
header: "Email (Resend)",
|
|
22283
|
+
vars: [
|
|
22284
|
+
{ key: "BETTERSTART_RESEND_API_KEY", value: "" },
|
|
22285
|
+
{ key: "BETTERSTART_EMAIL_FROM", value: "" }
|
|
22286
|
+
]
|
|
22280
22287
|
});
|
|
22281
|
-
if (p12.isCancel(setupResend)) {
|
|
22282
|
-
p12.cancel("Setup cancelled.");
|
|
22283
|
-
process.exit(0);
|
|
22284
|
-
}
|
|
22285
|
-
if (setupResend) {
|
|
22286
|
-
integrations.push("resend");
|
|
22287
|
-
mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["resend"]));
|
|
22288
|
-
} else {
|
|
22289
|
-
sections.push({
|
|
22290
|
-
header: "Email (Resend)",
|
|
22291
|
-
vars: [
|
|
22292
|
-
{ key: "BETTERSTART_RESEND_API_KEY", value: "" },
|
|
22293
|
-
{ key: "BETTERSTART_EMAIL_FROM", value: "" }
|
|
22294
|
-
]
|
|
22295
|
-
});
|
|
22296
|
-
}
|
|
22297
22288
|
if (storage === "r2") {
|
|
22298
22289
|
integrations.push("r2");
|
|
22299
22290
|
} else if (storage === "vercel-blob") {
|
|
@@ -25505,18 +25496,6 @@ function readNamespaceFromConfigSource(source) {
|
|
|
25505
25496
|
const match = source.match(/\bnamespace\s*:\s*(['"`])([^'"`]+)\1/);
|
|
25506
25497
|
return normalizeNamespaceForRemoval(match?.[2]);
|
|
25507
25498
|
}
|
|
25508
|
-
function formatStorageProviderLabel(provider) {
|
|
25509
|
-
switch (provider) {
|
|
25510
|
-
case "vercel-blob":
|
|
25511
|
-
return "Vercel Blob";
|
|
25512
|
-
case "r2":
|
|
25513
|
-
return "Cloudflare R2";
|
|
25514
|
-
case "local":
|
|
25515
|
-
return "local filesystem";
|
|
25516
|
-
default:
|
|
25517
|
-
return provider;
|
|
25518
|
-
}
|
|
25519
|
-
}
|
|
25520
25499
|
function resolveNonInteractiveProject(name) {
|
|
25521
25500
|
const projectName = name?.trim() || "my-app";
|
|
25522
25501
|
if (projectName !== "." && !/^[a-z0-9_-]+$/i.test(projectName)) {
|
|
@@ -25808,7 +25787,7 @@ async function runInitCommand(name, options) {
|
|
|
25808
25787
|
}
|
|
25809
25788
|
} else if (existingDbUrl) {
|
|
25810
25789
|
const masked = maskDbUrl(existingDbUrl);
|
|
25811
|
-
p18.log.info(`Using existing
|
|
25790
|
+
p18.log.info(`Using the existing DATABASE_URL from .env.local ${pc7.dim(`(${masked})`)}`);
|
|
25812
25791
|
databaseUrl = existingDbUrl;
|
|
25813
25792
|
} else {
|
|
25814
25793
|
const databaseStep = deferNextPromptCheck();
|
|
@@ -25913,35 +25892,21 @@ async function runInitCommand(name, options) {
|
|
|
25913
25892
|
config.frameworkConfig.next.paths = deriveNamespacePaths(namespace, srcDir);
|
|
25914
25893
|
config.paths = config.frameworkConfig.next.paths;
|
|
25915
25894
|
config.database.migrationsDir = deriveMigrationsDir(namespace);
|
|
25916
|
-
const results = [];
|
|
25917
25895
|
const s = spinner2();
|
|
25918
25896
|
s.start("Directory structure");
|
|
25919
|
-
|
|
25897
|
+
scaffoldBase({
|
|
25920
25898
|
cwd,
|
|
25921
25899
|
config,
|
|
25922
25900
|
includeEmailTemplatesDir: pluginSelection.integrations.some(isEmailProviderIntegration)
|
|
25923
25901
|
});
|
|
25924
|
-
results.push({ label: "Directory structure", result: `${baseFiles.length} files` });
|
|
25925
25902
|
s.message("TypeScript aliases");
|
|
25926
|
-
|
|
25927
|
-
results.push({
|
|
25928
|
-
label: "TypeScript aliases",
|
|
25929
|
-
result: tsResult.added.length > 0 ? `${tsResult.added.length} paths` : "already set"
|
|
25930
|
-
});
|
|
25903
|
+
scaffoldTsconfig(cwd, config);
|
|
25931
25904
|
s.message("Tailwind CSS");
|
|
25932
|
-
|
|
25933
|
-
results.push({
|
|
25934
|
-
label: "Tailwind CSS",
|
|
25935
|
-
result: twResult.appended ? "updated" : twResult.file ? "already set" : "no CSS file"
|
|
25936
|
-
});
|
|
25905
|
+
scaffoldTailwind(cwd, srcDir, namespace);
|
|
25937
25906
|
s.message("Dev server");
|
|
25938
|
-
|
|
25939
|
-
results.push({
|
|
25940
|
-
label: "Dev server",
|
|
25941
|
-
result: formatDevServerResult(devServerResult)
|
|
25942
|
-
});
|
|
25907
|
+
ensureDefaultDevScriptPort(cwd);
|
|
25943
25908
|
s.message("Environment variables");
|
|
25944
|
-
|
|
25909
|
+
scaffoldEnv(cwd, { databaseUrl, namespace });
|
|
25945
25910
|
if (collectedIntegrationConfig.sections.length > 0) {
|
|
25946
25911
|
appendEnvVars(
|
|
25947
25912
|
cwd,
|
|
@@ -25949,49 +25914,26 @@ async function runInitCommand(name, options) {
|
|
|
25949
25914
|
collectedIntegrationConfig.overwriteKeys
|
|
25950
25915
|
);
|
|
25951
25916
|
}
|
|
25952
|
-
const envCount = envResult.added.length + envResult.updated.length;
|
|
25953
|
-
results.push({
|
|
25954
|
-
label: "Environment variables",
|
|
25955
|
-
result: envCount > 0 ? `${envCount} vars` : "already set"
|
|
25956
|
-
});
|
|
25957
25917
|
s.message("Database");
|
|
25958
25918
|
const dbFiles = scaffoldDatabase({ cwd, config });
|
|
25959
|
-
results.push({ label: "Database", result: `${dbFiles.length} files` });
|
|
25960
25919
|
s.message("Authentication");
|
|
25961
|
-
|
|
25962
|
-
results.push({ label: "Authentication", result: `${authFiles.length} files` });
|
|
25920
|
+
scaffoldAuth({ cwd, config });
|
|
25963
25921
|
s.message("Components");
|
|
25964
|
-
|
|
25965
|
-
results.push({ label: "Components", result: `${compFiles.length} files` });
|
|
25922
|
+
scaffoldComponents({ cwd, config });
|
|
25966
25923
|
s.message("Pages & layouts");
|
|
25967
|
-
|
|
25968
|
-
results.push({ label: "Pages & layouts", result: `${layoutFiles.length} files` });
|
|
25924
|
+
scaffoldLayout({ cwd, config });
|
|
25969
25925
|
s.message("API routes");
|
|
25970
|
-
|
|
25971
|
-
results.push({ label: "API routes", result: `${apiFiles.length} routes` });
|
|
25926
|
+
scaffoldApiRoutes({ cwd, config });
|
|
25972
25927
|
s.message("Linter");
|
|
25973
|
-
let linterResult;
|
|
25974
25928
|
if (project2.linter.type === "none") {
|
|
25975
|
-
|
|
25976
|
-
linterResult = biomeResult.installed ? "biome (new)" : "none";
|
|
25977
|
-
} else {
|
|
25978
|
-
linterResult = project2.linter.type;
|
|
25929
|
+
scaffoldBiome(cwd, project2.linter);
|
|
25979
25930
|
}
|
|
25980
|
-
results.push({ label: "Linter", result: linterResult });
|
|
25981
25931
|
s.message("Next.js config");
|
|
25982
25932
|
const nextConfigResult = scaffoldNextConfig({ cwd, nextMajorVersion });
|
|
25983
|
-
|
|
25984
|
-
|
|
25985
|
-
|
|
25986
|
-
}
|
|
25987
|
-
const maxLabel = Math.max(...results.map((r) => r.label.length));
|
|
25988
|
-
const noteLines = results.map((r) => {
|
|
25989
|
-
const padded = r.label.padEnd(maxLabel + 3);
|
|
25990
|
-
return `${pc7.green("\u2713")} ${padded}${pc7.dim(r.result)}`;
|
|
25991
|
-
});
|
|
25992
|
-
s.stop("");
|
|
25993
|
-
process.stdout.write("\x1B[2A\x1B[J");
|
|
25994
|
-
p18.note(noteLines.join("\n"), "Scaffolded admin");
|
|
25933
|
+
s.clear();
|
|
25934
|
+
if (nextConfigResult.status === "unsupported") {
|
|
25935
|
+
p18.log.warn("The Next.js config could not be updated automatically \u2014 review it manually.");
|
|
25936
|
+
}
|
|
25995
25937
|
const drizzleConfigPath = path50.join(cwd, "drizzle.config.ts");
|
|
25996
25938
|
if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
|
|
25997
25939
|
if (forceMode) {
|
|
@@ -26096,42 +26038,20 @@ async function runInitCommand(name, options) {
|
|
|
26096
26038
|
}
|
|
26097
26039
|
writeConfigFile(cwd, resolvedIntegrationInstallResult.config);
|
|
26098
26040
|
const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider === "local";
|
|
26099
|
-
const
|
|
26100
|
-
|
|
26101
|
-
installLines.push(
|
|
26102
|
-
`${pc7.green("\u2713")} Dependencies ${pc7.dim(`${depsResult.dependencies.length} deps + ${depsResult.devDeps.length} dev deps`)}`
|
|
26103
|
-
);
|
|
26104
|
-
}
|
|
26105
|
-
if (coreSchemasResult.errors.length > 0) {
|
|
26106
|
-
installLines.push(
|
|
26107
|
-
`${pc7.yellow("\u25B2")} Core schemas ${pc7.dim(`${coreSchemasResult.errors.length} warning(s)`)}`
|
|
26108
|
-
);
|
|
26109
|
-
for (const err of coreSchemasResult.errors) {
|
|
26110
|
-
installLines.push(` ${pc7.dim(err)}`);
|
|
26111
|
-
}
|
|
26112
|
-
} else {
|
|
26113
|
-
installLines.push(
|
|
26114
|
-
`${pc7.green("\u2713")} Core schemas ${pc7.dim(`${coreSchemasResult.schemas.length} schemas, ${coreSchemasResult.generatedFiles.length} files`)}`
|
|
26115
|
-
);
|
|
26041
|
+
for (const err of coreSchemasResult.errors) {
|
|
26042
|
+
p18.log.warn(`Core schemas: ${err}`);
|
|
26116
26043
|
}
|
|
26117
|
-
installLines.push(
|
|
26118
|
-
`${pc7.green("\u2713")} Plugins ${pc7.dim(resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only")}`
|
|
26119
|
-
);
|
|
26120
26044
|
for (const warning of resolvedPluginInstallResult.warnings) {
|
|
26121
|
-
|
|
26045
|
+
p18.log.warn(warning);
|
|
26122
26046
|
}
|
|
26123
|
-
installLines.push(
|
|
26124
|
-
`${pc7.green("\u2713")} Integrations ${pc7.dim(resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none")}`
|
|
26125
|
-
);
|
|
26126
26047
|
for (const warning of resolvedIntegrationInstallResult.warnings) {
|
|
26127
|
-
|
|
26048
|
+
p18.log.warn(warning);
|
|
26128
26049
|
}
|
|
26129
26050
|
if (usesLocalStorage) {
|
|
26130
|
-
|
|
26131
|
-
|
|
26051
|
+
p18.log.warn(
|
|
26052
|
+
"Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob or Cloudflare R2 for hosted/serverless production."
|
|
26132
26053
|
);
|
|
26133
26054
|
}
|
|
26134
|
-
p18.note(installLines.join("\n"), "Installed");
|
|
26135
26055
|
let dbPushed = false;
|
|
26136
26056
|
if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
|
|
26137
26057
|
p18.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
|
|
@@ -26160,29 +26080,35 @@ async function runInitCommand(name, options) {
|
|
|
26160
26080
|
}
|
|
26161
26081
|
if (driverReady) {
|
|
26162
26082
|
const useTerminalForDbPush = shouldUseTerminalForDbPush(options);
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26083
|
+
let dbSpinnerVisible = true;
|
|
26084
|
+
const clearDbSpinner = () => {
|
|
26085
|
+
if (!dbSpinnerVisible) return;
|
|
26086
|
+
dbSpinnerVisible = false;
|
|
26087
|
+
s.clear();
|
|
26088
|
+
};
|
|
26089
|
+
s.start(
|
|
26090
|
+
useTerminalForDbPush ? "Setting up database" : "Pushing database schema (drizzle-kit push)"
|
|
26091
|
+
);
|
|
26092
|
+
const pushResult = await runDrizzlePush(cwd, {
|
|
26093
|
+
interactive: useTerminalForDbPush,
|
|
26094
|
+
onOutput: clearDbSpinner
|
|
26095
|
+
});
|
|
26169
26096
|
if (pushResult.success) {
|
|
26170
26097
|
if (useTerminalForDbPush) {
|
|
26171
26098
|
const verification = await verifyDatabaseReachable(cwd);
|
|
26099
|
+
clearDbSpinner();
|
|
26172
26100
|
if (!verification.success) {
|
|
26173
26101
|
p18.log.warning(verification.error);
|
|
26174
26102
|
p18.log.error("Database was not reachable. Aborting setup.");
|
|
26175
26103
|
process.exit(1);
|
|
26176
26104
|
}
|
|
26177
|
-
}
|
|
26178
|
-
if (useTerminalForDbPush) {
|
|
26179
|
-
p18.log.success("Database schema pushed");
|
|
26180
26105
|
} else {
|
|
26181
|
-
|
|
26106
|
+
clearDbSpinner();
|
|
26182
26107
|
}
|
|
26183
26108
|
dbPushed = true;
|
|
26184
26109
|
} else {
|
|
26185
|
-
if (
|
|
26110
|
+
if (dbSpinnerVisible) {
|
|
26111
|
+
dbSpinnerVisible = false;
|
|
26186
26112
|
s.stop("Database push failed");
|
|
26187
26113
|
}
|
|
26188
26114
|
const pushError = pushResult.error ?? "Unknown error";
|
|
@@ -26208,7 +26134,9 @@ async function runInitCommand(name, options) {
|
|
|
26208
26134
|
const adminDir = config.paths?.admin ?? "./admin";
|
|
26209
26135
|
const authBasePath = `${resolveAdminNamespace(namespace).apiPath}/auth`;
|
|
26210
26136
|
let replaceExistingAdmin = false;
|
|
26137
|
+
s.start("Setting up database");
|
|
26211
26138
|
const adminCheck = await checkExistingAdmin(cwd, adminDir, authBasePath);
|
|
26139
|
+
s.clear();
|
|
26212
26140
|
if (adminCheck.error) {
|
|
26213
26141
|
p18.log.warning(`Could not verify existing admin account ${pc7.dim(`(${adminCheck.error})`)}`);
|
|
26214
26142
|
if (isDatabaseReachabilityError(adminCheck.error)) {
|
|
@@ -26243,12 +26171,6 @@ async function runInitCommand(name, options) {
|
|
|
26243
26171
|
}
|
|
26244
26172
|
}
|
|
26245
26173
|
if (!adminAccountReady) {
|
|
26246
|
-
p18.note(
|
|
26247
|
-
pc7.dim(
|
|
26248
|
-
replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the dashboard."
|
|
26249
|
-
),
|
|
26250
|
-
"Admin account"
|
|
26251
|
-
);
|
|
26252
26174
|
const credentials = await p18.group(
|
|
26253
26175
|
{
|
|
26254
26176
|
email: () => p18.text({
|
|
@@ -26338,19 +26260,8 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
26338
26260
|
s.stop("Git commit skipped");
|
|
26339
26261
|
}
|
|
26340
26262
|
}
|
|
26341
|
-
const totalFiles = baseFiles.length + dbFiles.length + authFiles.length + compFiles.length + layoutFiles.length + apiFiles.length;
|
|
26342
26263
|
const adminNamespace = resolveAdminNamespace(namespace);
|
|
26343
26264
|
const adminLoginUrl = `http://localhost:${detectDevPort(cwd)}${adminNamespace.routePath}/login`;
|
|
26344
|
-
const installedPluginList = resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only";
|
|
26345
|
-
const installedIntegrationList = resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none";
|
|
26346
|
-
const summaryLines = [
|
|
26347
|
-
`Plugins: ${pc7.cyan(installedPluginList)}`,
|
|
26348
|
-
`Integrations: ${pc7.cyan(installedIntegrationList)}`,
|
|
26349
|
-
`Storage: ${pc7.cyan(formatStorageProviderLabel(resolvedIntegrationInstallResult.config.storage.provider))}`,
|
|
26350
|
-
`Files created: ${pc7.cyan(String(totalFiles))}`,
|
|
26351
|
-
`Env vars: ${envResult.added.length} added, ${envResult.skipped.length} skipped`
|
|
26352
|
-
];
|
|
26353
|
-
p18.note(summaryLines.join("\n"), "Admin scaffolded successfully");
|
|
26354
26265
|
if (options.vercel !== false && !options.skipDeploy) {
|
|
26355
26266
|
const linkedVercelProject = Boolean(readLinkedProjectId(cwd));
|
|
26356
26267
|
if (!options.yes) {
|
|
@@ -26463,20 +26374,6 @@ function detectInstalledNextVersion(cwd) {
|
|
|
26463
26374
|
return void 0;
|
|
26464
26375
|
}
|
|
26465
26376
|
}
|
|
26466
|
-
function formatDevServerResult(result) {
|
|
26467
|
-
switch (result.status) {
|
|
26468
|
-
case "updated":
|
|
26469
|
-
return `set to ${result.port}`;
|
|
26470
|
-
case "preserved":
|
|
26471
|
-
return `using ${result.port}`;
|
|
26472
|
-
case "skipped":
|
|
26473
|
-
return `custom script preserved; BetterStart uses ${result.port}`;
|
|
26474
|
-
case "missing-script":
|
|
26475
|
-
return `no dev script; BetterStart uses ${result.port}`;
|
|
26476
|
-
case "missing-package":
|
|
26477
|
-
return `package.json missing; BetterStart uses ${result.port}`;
|
|
26478
|
-
}
|
|
26479
|
-
}
|
|
26480
26377
|
function shouldUseTerminalForDbPush(options) {
|
|
26481
26378
|
return !options.yes && process.env.CI !== "true" && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
26482
26379
|
}
|