betterstart-cli 0.0.92 → 0.0.94
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/assets/adapters/next/templates/init/pages/settings/forms/form-notifications-drawer.tsx +146 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-columns.tsx +31 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page-content.tsx +39 -90
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-table.tsx +87 -0
- package/dist/cli.js +1433 -1212
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/edit-form-notifications-dialog.tsx +0 -128
package/dist/cli.js
CHANGED
|
@@ -52,6 +52,9 @@ async function loadConfigFile(configPath) {
|
|
|
52
52
|
function isInteractiveSession() {
|
|
53
53
|
return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
|
|
54
54
|
}
|
|
55
|
+
function isInteractiveTerminalSession() {
|
|
56
|
+
return isInteractiveSession() && Boolean(process.stdout.isTTY);
|
|
57
|
+
}
|
|
55
58
|
|
|
56
59
|
// core-engine/commands/runtime.ts
|
|
57
60
|
import { Argument, Command } from "commander";
|
|
@@ -120,7 +123,10 @@ function createInitCommand(runtime) {
|
|
|
120
123
|
).option("--database-provider <provider>", "Database provider: vercel, railway, or manual").option(
|
|
121
124
|
"--storage-provider <provider>",
|
|
122
125
|
"Storage provider: vercel-blob, railway-bucket, r2, or local"
|
|
123
|
-
).option("--deploy-provider <provider>", "Deploy provider: vercel, railway, or none").option(
|
|
126
|
+
).option("--deploy-provider <provider>", "Deploy provider: vercel, railway, or none").option(
|
|
127
|
+
"--use-existing-resources",
|
|
128
|
+
"Require database/storage credentials from existing project env files; never provision them"
|
|
129
|
+
).option("--database-plan <id>", "Neon plan id for a Vercel-provisioned database").option("--railway-workspace <id-or-name>", "Railway workspace for a new project").option("--railway-project <id-or-name>", "Existing Railway project to intentionally reuse").option("--railway-environment <id-or-name>", "Railway environment (default: production)").option("--railway-service <id-or-name>", "Railway app service (default: web)").option(
|
|
124
130
|
"--railway-bucket-region <region>",
|
|
125
131
|
"Railway bucket region: sjc, iad, ams, or sin (default: iad)"
|
|
126
132
|
).option("--force", "Overwrite all existing Admin files (nuclear option)").addHelpText(
|
|
@@ -185,15 +191,18 @@ function createUpdateStylesCommand(runtime) {
|
|
|
185
191
|
// core-engine/commands/default-action.ts
|
|
186
192
|
var ADD_COMMAND = "add";
|
|
187
193
|
var CREATE_COMMAND = "create";
|
|
194
|
+
var GENERATE_COMMAND = "generate";
|
|
188
195
|
var INIT_COMMAND = "init";
|
|
189
196
|
var REMOVE_COMMAND = "remove";
|
|
190
197
|
var REMOVE_SCHEMA_COMMAND = "remove-schema";
|
|
198
|
+
var UPDATE_COMMAND = "update";
|
|
199
|
+
var ALL_FLAG = "--all";
|
|
191
200
|
var CANCELLED = /* @__PURE__ */ Symbol("cancelled");
|
|
192
201
|
function cancelled(value) {
|
|
193
202
|
return value === CANCELLED;
|
|
194
203
|
}
|
|
195
204
|
async function runDefaultAction(program2, runtime) {
|
|
196
|
-
if (!
|
|
205
|
+
if (!isInteractiveTerminalSession()) {
|
|
197
206
|
program2.outputHelp();
|
|
198
207
|
return;
|
|
199
208
|
}
|
|
@@ -238,6 +247,24 @@ async function promptRequiredArguments(name, runtime, cwd) {
|
|
|
238
247
|
if (name === ADD_COMMAND || name === REMOVE_COMMAND) {
|
|
239
248
|
return promptInstallables(name, runtime, cwd);
|
|
240
249
|
}
|
|
250
|
+
if (name === GENERATE_COMMAND) {
|
|
251
|
+
const schemas = await runtime.listSchemaChoices(cwd);
|
|
252
|
+
if (schemas.length === 0) {
|
|
253
|
+
p.log.warn("No schemas to generate.");
|
|
254
|
+
return CANCELLED;
|
|
255
|
+
}
|
|
256
|
+
const target = await p.select({
|
|
257
|
+
message: "What do you want to generate?",
|
|
258
|
+
options: [
|
|
259
|
+
...schemas.map((value) => ({ value, label: value })),
|
|
260
|
+
{ value: ALL_FLAG, label: "All schemas", hint: "regenerate everything" }
|
|
261
|
+
]
|
|
262
|
+
});
|
|
263
|
+
return p.isCancel(target) ? CANCELLED : [target];
|
|
264
|
+
}
|
|
265
|
+
if (name === UPDATE_COMMAND) {
|
|
266
|
+
return promptComponents(runtime, cwd);
|
|
267
|
+
}
|
|
241
268
|
if (name === REMOVE_SCHEMA_COMMAND) {
|
|
242
269
|
const schemas = await runtime.listSchemaChoices(cwd);
|
|
243
270
|
if (schemas.length === 0) {
|
|
@@ -252,6 +279,31 @@ async function promptRequiredArguments(name, runtime, cwd) {
|
|
|
252
279
|
}
|
|
253
280
|
return [];
|
|
254
281
|
}
|
|
282
|
+
async function promptComponents(runtime, cwd) {
|
|
283
|
+
const scope = await p.select({
|
|
284
|
+
message: "What do you want to update?",
|
|
285
|
+
options: [
|
|
286
|
+
{ value: ALL_FLAG, label: "All components", hint: "every installed component" },
|
|
287
|
+
{ value: "pick", label: "Choose components" }
|
|
288
|
+
]
|
|
289
|
+
});
|
|
290
|
+
if (p.isCancel(scope)) {
|
|
291
|
+
return CANCELLED;
|
|
292
|
+
}
|
|
293
|
+
if (scope === ALL_FLAG) {
|
|
294
|
+
return [ALL_FLAG];
|
|
295
|
+
}
|
|
296
|
+
const components = await runtime.listComponentChoices(cwd);
|
|
297
|
+
if (components.length === 0) {
|
|
298
|
+
p.log.warn("No components available to update.");
|
|
299
|
+
return CANCELLED;
|
|
300
|
+
}
|
|
301
|
+
const selected = await p.multiselect({
|
|
302
|
+
message: "Components to update",
|
|
303
|
+
options: components.map((value) => ({ value, label: value }))
|
|
304
|
+
});
|
|
305
|
+
return p.isCancel(selected) ? CANCELLED : selected;
|
|
306
|
+
}
|
|
255
307
|
async function promptInstallables(name, runtime, cwd) {
|
|
256
308
|
const removing = name === REMOVE_COMMAND;
|
|
257
309
|
const choices = await runtime.listInstallableChoices(cwd);
|
|
@@ -1729,6 +1781,21 @@ function spawnAsync(cmd, args, cwd) {
|
|
|
1729
1781
|
child.on("error", reject);
|
|
1730
1782
|
});
|
|
1731
1783
|
}
|
|
1784
|
+
function isUnexpectedPnpmStoreError(error) {
|
|
1785
|
+
return error instanceof Error && error.message.includes("ERR_PNPM_UNEXPECTED_STORE");
|
|
1786
|
+
}
|
|
1787
|
+
async function runDependencyInstall(pm, args, cwd, recovery) {
|
|
1788
|
+
try {
|
|
1789
|
+
await spawnAsync(pm, args, cwd);
|
|
1790
|
+
} catch (error) {
|
|
1791
|
+
if (pm !== "pnpm" || recovery.attempted || !isUnexpectedPnpmStoreError(error)) {
|
|
1792
|
+
throw error;
|
|
1793
|
+
}
|
|
1794
|
+
recovery.attempted = true;
|
|
1795
|
+
await spawnAsync("pnpm", ["install", "--force"], cwd);
|
|
1796
|
+
await spawnAsync(pm, args, cwd);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1732
1799
|
function unique(values) {
|
|
1733
1800
|
return Array.from(new Set(values));
|
|
1734
1801
|
}
|
|
@@ -1775,11 +1842,22 @@ async function installDependenciesAsync({
|
|
|
1775
1842
|
ensurePnpmAllowedBuilds(cwd, installPlan.pnpmAllowedBuilds ?? []);
|
|
1776
1843
|
ensurePnpmPackageExtensions(cwd, installPlan.dependencies);
|
|
1777
1844
|
}
|
|
1845
|
+
const pnpmStoreRecovery = { attempted: false };
|
|
1778
1846
|
if (installPlan.dependencies.length > 0) {
|
|
1779
|
-
await
|
|
1847
|
+
await runDependencyInstall(
|
|
1848
|
+
pm,
|
|
1849
|
+
buildAddArgs(pm, installPlan.dependencies, false),
|
|
1850
|
+
cwd,
|
|
1851
|
+
pnpmStoreRecovery
|
|
1852
|
+
);
|
|
1780
1853
|
}
|
|
1781
1854
|
if (installPlan.devDependencies.length > 0) {
|
|
1782
|
-
await
|
|
1855
|
+
await runDependencyInstall(
|
|
1856
|
+
pm,
|
|
1857
|
+
buildAddArgs(pm, installPlan.devDependencies, true),
|
|
1858
|
+
cwd,
|
|
1859
|
+
pnpmStoreRecovery
|
|
1860
|
+
);
|
|
1783
1861
|
}
|
|
1784
1862
|
return {
|
|
1785
1863
|
dependencies: installPlan.dependencies,
|
|
@@ -3240,6 +3318,13 @@ function walkSlot(field, fieldPath, errors) {
|
|
|
3240
3318
|
// core-engine/utils/env.ts
|
|
3241
3319
|
import fs10 from "fs";
|
|
3242
3320
|
import path12 from "path";
|
|
3321
|
+
import { parseEnv } from "util";
|
|
3322
|
+
var DEVELOPMENT_ENV_FILES = [
|
|
3323
|
+
".env.development.local",
|
|
3324
|
+
".env.local",
|
|
3325
|
+
".env.development",
|
|
3326
|
+
".env"
|
|
3327
|
+
];
|
|
3243
3328
|
function readEnvVar(cwd, key) {
|
|
3244
3329
|
const envPath = path12.join(cwd, ".env.local");
|
|
3245
3330
|
if (!fs10.existsSync(envPath)) {
|
|
@@ -3259,6 +3344,21 @@ function readEnvVar(cwd, key) {
|
|
|
3259
3344
|
}
|
|
3260
3345
|
return void 0;
|
|
3261
3346
|
}
|
|
3347
|
+
function resolveProjectEnvVar(cwd, key) {
|
|
3348
|
+
for (const envFile of DEVELOPMENT_ENV_FILES) {
|
|
3349
|
+
const envPath = path12.join(cwd, envFile);
|
|
3350
|
+
if (!fs10.existsSync(envPath)) continue;
|
|
3351
|
+
try {
|
|
3352
|
+
const value = parseEnv(fs10.readFileSync(envPath, "utf-8"))[key]?.trim();
|
|
3353
|
+
if (value) return { source: envFile, value };
|
|
3354
|
+
} catch {
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
return void 0;
|
|
3358
|
+
}
|
|
3359
|
+
function readProjectEnvVar(cwd, key) {
|
|
3360
|
+
return resolveProjectEnvVar(cwd, key)?.value;
|
|
3361
|
+
}
|
|
3262
3362
|
function parseEnvValue(rawValue) {
|
|
3263
3363
|
const value = rawValue.trim();
|
|
3264
3364
|
const quote2 = value[0];
|
|
@@ -3645,10 +3745,10 @@ function hasRequiredIntegrationEnv(definition, cwd) {
|
|
|
3645
3745
|
const requiredKeys = definition.envSections.flatMap(
|
|
3646
3746
|
(section) => section.vars.filter((entry) => entry.value === "").map((entry) => entry.key)
|
|
3647
3747
|
);
|
|
3648
|
-
return requiredKeys.every((key) => Boolean(
|
|
3748
|
+
return requiredKeys.every((key) => Boolean(readProjectEnvVar(cwd, key)));
|
|
3649
3749
|
}
|
|
3650
3750
|
function readNonEmptyEnvVar(cwd, key) {
|
|
3651
|
-
const value =
|
|
3751
|
+
const value = readProjectEnvVar(cwd, key)?.trim();
|
|
3652
3752
|
return value ? value : void 0;
|
|
3653
3753
|
}
|
|
3654
3754
|
async function resolveTextEnvValue(options) {
|
|
@@ -4270,7 +4370,8 @@ async function installIntegrations({
|
|
|
4270
4370
|
pm,
|
|
4271
4371
|
integrationIds,
|
|
4272
4372
|
interactive,
|
|
4273
|
-
includeBiome
|
|
4373
|
+
includeBiome,
|
|
4374
|
+
dependenciesInstalled = false
|
|
4274
4375
|
}) {
|
|
4275
4376
|
const orderedIntegrationIds = resolveIntegrationInstallOrder(integrationIds);
|
|
4276
4377
|
const installedIntegrationIds = normalizeInstalledIntegrations(config);
|
|
@@ -4348,7 +4449,7 @@ async function installIntegrations({
|
|
|
4348
4449
|
(dependency) => !currentPlan.devDependencies.includes(dependency)
|
|
4349
4450
|
)
|
|
4350
4451
|
};
|
|
4351
|
-
if (dependencyDiff.dependencies.length > 0 || dependencyDiff.devDependencies.length > 0) {
|
|
4452
|
+
if (!dependenciesInstalled && (dependencyDiff.dependencies.length > 0 || dependencyDiff.devDependencies.length > 0)) {
|
|
4352
4453
|
const result = await installDependenciesAsync({
|
|
4353
4454
|
cwd,
|
|
4354
4455
|
pm,
|
|
@@ -19395,7 +19496,8 @@ async function installPresets({
|
|
|
19395
19496
|
config,
|
|
19396
19497
|
pm,
|
|
19397
19498
|
presetIds,
|
|
19398
|
-
includeBiome
|
|
19499
|
+
includeBiome,
|
|
19500
|
+
dependenciesInstalled = false
|
|
19399
19501
|
}) {
|
|
19400
19502
|
const orderedPresetIds = resolvePresetInstallOrder(presetIds);
|
|
19401
19503
|
const installedPresetIds = normalizeInstalledPresets(config);
|
|
@@ -19430,7 +19532,7 @@ async function installPresets({
|
|
|
19430
19532
|
(dependency) => !currentPlan.devDependencies.includes(dependency)
|
|
19431
19533
|
)
|
|
19432
19534
|
};
|
|
19433
|
-
if (dependencyDiff.dependencies.length > 0 || dependencyDiff.devDependencies.length > 0) {
|
|
19535
|
+
if (!dependenciesInstalled && (dependencyDiff.dependencies.length > 0 || dependencyDiff.devDependencies.length > 0)) {
|
|
19434
19536
|
const result = await installDependenciesAsync({
|
|
19435
19537
|
cwd,
|
|
19436
19538
|
pm,
|
|
@@ -22785,7 +22887,7 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22785
22887
|
mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["railway-bucket"]));
|
|
22786
22888
|
}
|
|
22787
22889
|
} else if (storage === "vercel-blob") {
|
|
22788
|
-
const existingToken =
|
|
22890
|
+
const existingToken = readProjectEnvVar(cwd, "BLOB_READ_WRITE_TOKEN")?.trim();
|
|
22789
22891
|
const flow = !existingToken && options.provisionVercelBlob ? await options.provisionVercelBlob() : void 0;
|
|
22790
22892
|
if (flow?.ok && flow.token) {
|
|
22791
22893
|
persistBlobReadWriteToken(cwd, flow.token);
|
|
@@ -22898,6 +23000,14 @@ function validateInitProviderFlags(options) {
|
|
|
22898
23000
|
if (options.databasePlan && options.databaseProvider && options.databaseProvider !== "vercel") {
|
|
22899
23001
|
throw new Error("--database-plan can only be used with --database-provider vercel.");
|
|
22900
23002
|
}
|
|
23003
|
+
if (options.useExistingResources && !options.yes) {
|
|
23004
|
+
throw new Error("--use-existing-resources requires --yes.");
|
|
23005
|
+
}
|
|
23006
|
+
if (options.useExistingResources && options.databaseProvider && options.databaseProvider !== "manual") {
|
|
23007
|
+
throw new Error(
|
|
23008
|
+
"--use-existing-resources requires --database-provider manual for an existing DATABASE_URL."
|
|
23009
|
+
);
|
|
23010
|
+
}
|
|
22901
23011
|
}
|
|
22902
23012
|
function resolveFlagStorageProvider(explicitProvider, integrations) {
|
|
22903
23013
|
const inferred = Array.from(
|
|
@@ -25055,8 +25165,16 @@ function scaffoldLayout({ cwd, config }) {
|
|
|
25055
25165
|
readTemplate("pages/settings/forms/forms-settings-page-content.tsx")
|
|
25056
25166
|
);
|
|
25057
25167
|
write(
|
|
25058
|
-
path43.join(settingsFormsDir, "
|
|
25059
|
-
readTemplate("pages/settings/forms/
|
|
25168
|
+
path43.join(settingsFormsDir, "forms-settings-columns.tsx"),
|
|
25169
|
+
readTemplate("pages/settings/forms/forms-settings-columns.tsx")
|
|
25170
|
+
);
|
|
25171
|
+
write(
|
|
25172
|
+
path43.join(settingsFormsDir, "forms-settings-table.tsx"),
|
|
25173
|
+
readTemplate("pages/settings/forms/forms-settings-table.tsx")
|
|
25174
|
+
);
|
|
25175
|
+
write(
|
|
25176
|
+
path43.join(settingsFormsDir, "form-notifications-drawer.tsx"),
|
|
25177
|
+
readTemplate("pages/settings/forms/form-notifications-drawer.tsx")
|
|
25060
25178
|
);
|
|
25061
25179
|
const settingsWebhooksDir = path43.join(settingsDir, "webhooks");
|
|
25062
25180
|
write(
|
|
@@ -27436,20 +27554,62 @@ function removeExistingAdminPaths(cwd, namespaces) {
|
|
|
27436
27554
|
}
|
|
27437
27555
|
return removed;
|
|
27438
27556
|
}
|
|
27557
|
+
function writeInitJson(context, payload) {
|
|
27558
|
+
context.restoreStdout?.();
|
|
27559
|
+
context.restoreStdout = void 0;
|
|
27560
|
+
context.written = true;
|
|
27561
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
27562
|
+
}
|
|
27439
27563
|
async function runInitCommand(name, options) {
|
|
27440
|
-
|
|
27564
|
+
const jsonContext = {
|
|
27565
|
+
restoreStdout: options.json ? redirectStdoutToStderr() : void 0,
|
|
27566
|
+
written: false
|
|
27567
|
+
};
|
|
27568
|
+
try {
|
|
27569
|
+
await runInitCommandInternal(name, options, jsonContext);
|
|
27570
|
+
} catch (error) {
|
|
27571
|
+
if (options.json && !jsonContext.written) {
|
|
27572
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27573
|
+
writeInitJson(jsonContext, {
|
|
27574
|
+
success: false,
|
|
27575
|
+
phase: "initialization",
|
|
27576
|
+
error: {
|
|
27577
|
+
code: "UNEXPECTED_INIT_ERROR",
|
|
27578
|
+
message: redactSecrets(message)
|
|
27579
|
+
}
|
|
27580
|
+
});
|
|
27581
|
+
}
|
|
27582
|
+
throw error;
|
|
27583
|
+
} finally {
|
|
27584
|
+
jsonContext.restoreStdout?.();
|
|
27585
|
+
}
|
|
27586
|
+
}
|
|
27587
|
+
async function runInitCommandInternal(name, options, jsonContext) {
|
|
27588
|
+
const exitInit = (phase, message, code = "INIT_FAILED") => {
|
|
27589
|
+
if (options.json) {
|
|
27590
|
+
writeInitJson(jsonContext, {
|
|
27591
|
+
success: false,
|
|
27592
|
+
phase,
|
|
27593
|
+
error: {
|
|
27594
|
+
code,
|
|
27595
|
+
message: redactSecrets(message)
|
|
27596
|
+
}
|
|
27597
|
+
});
|
|
27598
|
+
}
|
|
27599
|
+
process.exit(1);
|
|
27600
|
+
};
|
|
27441
27601
|
if (options.json) {
|
|
27442
27602
|
if (!options.yes) {
|
|
27443
|
-
|
|
27444
|
-
|
|
27603
|
+
const message = "--json requires --yes.";
|
|
27604
|
+
p26.log.error(message);
|
|
27605
|
+
exitInit("validation", message, "JSON_REQUIRES_YES");
|
|
27445
27606
|
}
|
|
27446
|
-
restoreStdout = redirectStdoutToStderr();
|
|
27447
27607
|
}
|
|
27448
27608
|
installPromptCheckmarks();
|
|
27449
27609
|
const disposeCancelGuard = installSetupCancelGuard();
|
|
27450
27610
|
renderInitBanner();
|
|
27451
|
-
let selectedPresets;
|
|
27452
|
-
let selectedIntegrations;
|
|
27611
|
+
let selectedPresets = [];
|
|
27612
|
+
let selectedIntegrations = [];
|
|
27453
27613
|
let flagStorageProvider;
|
|
27454
27614
|
try {
|
|
27455
27615
|
validateInitProviderFlags(options);
|
|
@@ -27461,8 +27621,9 @@ async function runInitCommand(name, options) {
|
|
|
27461
27621
|
}
|
|
27462
27622
|
} catch (error) {
|
|
27463
27623
|
disposeCancelGuard();
|
|
27464
|
-
|
|
27465
|
-
|
|
27624
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27625
|
+
p26.log.error(message);
|
|
27626
|
+
exitInit("validation", message, "INVALID_OPTIONS");
|
|
27466
27627
|
}
|
|
27467
27628
|
let cwd = process.cwd();
|
|
27468
27629
|
let projectName = path52.basename(cwd);
|
|
@@ -27472,8 +27633,9 @@ async function runInitCommand(name, options) {
|
|
|
27472
27633
|
try {
|
|
27473
27634
|
namespace = validateAdminNamespace(options.namespace);
|
|
27474
27635
|
} catch (error) {
|
|
27475
|
-
|
|
27476
|
-
|
|
27636
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27637
|
+
p26.log.error(message);
|
|
27638
|
+
exitInit("validation", message, "INVALID_NAMESPACE");
|
|
27477
27639
|
}
|
|
27478
27640
|
}
|
|
27479
27641
|
let projectPrompt;
|
|
@@ -27481,8 +27643,9 @@ async function runInitCommand(name, options) {
|
|
|
27481
27643
|
try {
|
|
27482
27644
|
projectPrompt = options.yes ? resolveNonInteractiveProject(name) : await promptProject(name);
|
|
27483
27645
|
} catch (error) {
|
|
27484
|
-
|
|
27485
|
-
|
|
27646
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27647
|
+
p26.log.error(message);
|
|
27648
|
+
exitInit("project", message, "PROJECT_SETUP_FAILED");
|
|
27486
27649
|
}
|
|
27487
27650
|
}
|
|
27488
27651
|
if (!options.yes && !options.namespace) {
|
|
@@ -27513,8 +27676,9 @@ async function runInitCommand(name, options) {
|
|
|
27513
27676
|
if (project2.isExisting) {
|
|
27514
27677
|
srcDir = project2.hasSrcDir;
|
|
27515
27678
|
if (!project2.hasTypeScript) {
|
|
27516
|
-
|
|
27517
|
-
|
|
27679
|
+
const message = "TypeScript is required. Please add a tsconfig.json first.";
|
|
27680
|
+
p26.log.error(message);
|
|
27681
|
+
exitInit("project", message, "TYPESCRIPT_REQUIRED");
|
|
27518
27682
|
}
|
|
27519
27683
|
if (forceMode) {
|
|
27520
27684
|
const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
|
|
@@ -27530,10 +27694,9 @@ async function runInitCommand(name, options) {
|
|
|
27530
27694
|
);
|
|
27531
27695
|
p26.note(conflictLines.join("\n"), pc10.yellow("Conflicts"));
|
|
27532
27696
|
if (options.yes) {
|
|
27533
|
-
|
|
27534
|
-
|
|
27535
|
-
);
|
|
27536
|
-
process.exit(1);
|
|
27697
|
+
const message = "Can't continue with --yes while admin files conflict. Re-run with --force to remove them first.";
|
|
27698
|
+
p26.log.error(message);
|
|
27699
|
+
exitInit("project", message, "ADMIN_FILES_CONFLICT");
|
|
27537
27700
|
}
|
|
27538
27701
|
const proceed = await p26.confirm({
|
|
27539
27702
|
message: [
|
|
@@ -27608,7 +27771,7 @@ async function runInitCommand(name, options) {
|
|
|
27608
27771
|
${pc10.cyan(`npx create-next-app@latest ${freshProject.projectName} --typescript --tailwind --app`)}
|
|
27609
27772
|
Then run ${pc10.cyan("betterstart init")} inside it.`
|
|
27610
27773
|
);
|
|
27611
|
-
|
|
27774
|
+
exitInit("project", createNextAppResult.error, "CREATE_NEXT_APP_FAILED");
|
|
27612
27775
|
}
|
|
27613
27776
|
cwd = path52.resolve(cwd, freshProject.projectName);
|
|
27614
27777
|
const hasPackageJson = fs41.existsSync(path52.join(cwd, "package.json"));
|
|
@@ -27626,7 +27789,11 @@ async function runInitCommand(name, options) {
|
|
|
27626
27789
|
${pc10.cyan(manualCmd)}
|
|
27627
27790
|
Then run ${pc10.cyan("betterstart init")} inside it.`
|
|
27628
27791
|
);
|
|
27629
|
-
|
|
27792
|
+
exitInit(
|
|
27793
|
+
"project",
|
|
27794
|
+
"create-next-app completed but the project was not created.",
|
|
27795
|
+
"CREATE_NEXT_APP_INCOMPLETE"
|
|
27796
|
+
);
|
|
27630
27797
|
}
|
|
27631
27798
|
createNextAppSpinner.clear();
|
|
27632
27799
|
project2 = detectProject(cwd, namespace);
|
|
@@ -27638,7 +27805,8 @@ async function runInitCommand(name, options) {
|
|
|
27638
27805
|
let railwayDatabaseServiceName;
|
|
27639
27806
|
let railwayBucketResourceName;
|
|
27640
27807
|
let railwayBucketUrlStyle;
|
|
27641
|
-
const
|
|
27808
|
+
const existingDatabase = readExistingDbUrl(cwd);
|
|
27809
|
+
const existingDbUrl = existingDatabase?.value;
|
|
27642
27810
|
const getRailwaySession = () => {
|
|
27643
27811
|
railwaySessionPromise ??= createRailwaySession({
|
|
27644
27812
|
cwd,
|
|
@@ -27670,24 +27838,28 @@ async function runInitCommand(name, options) {
|
|
|
27670
27838
|
try {
|
|
27671
27839
|
validateResolvedDatabaseProvider(databaseProvider, options);
|
|
27672
27840
|
} catch (error) {
|
|
27673
|
-
|
|
27674
|
-
|
|
27841
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27842
|
+
p26.log.error(message);
|
|
27843
|
+
exitInit("providers", message, "INVALID_DATABASE_PROVIDER");
|
|
27675
27844
|
}
|
|
27676
27845
|
if (databaseProvider === "manual") {
|
|
27677
27846
|
const candidate = options.databaseUrl ?? existingDbUrl ?? promptedManualUrl;
|
|
27678
27847
|
if (candidate && !isValidDbUrl(candidate)) {
|
|
27679
|
-
|
|
27680
|
-
|
|
27681
|
-
);
|
|
27682
|
-
process.exit(1);
|
|
27848
|
+
const message = "Invalid database URL. Must start with postgres:// or postgresql://";
|
|
27849
|
+
p26.log.error(message);
|
|
27850
|
+
exitInit("providers", message, "INVALID_DATABASE_URL");
|
|
27683
27851
|
}
|
|
27684
27852
|
if (candidate) {
|
|
27685
27853
|
databaseUrl = candidate;
|
|
27686
27854
|
if (existingDbUrl === candidate && !options.databaseUrl) {
|
|
27687
27855
|
p26.log.info(
|
|
27688
|
-
`Using the existing DATABASE_URL from
|
|
27856
|
+
`Using the existing DATABASE_URL from ${existingDatabase?.source ?? "project env"} ${pc10.dim(`(${maskDbUrl(candidate)})`)}`
|
|
27689
27857
|
);
|
|
27690
27858
|
}
|
|
27859
|
+
} else if (options.useExistingResources) {
|
|
27860
|
+
const message = "DATABASE_URL is missing from the project environment files.";
|
|
27861
|
+
p26.log.error(message);
|
|
27862
|
+
exitInit("providers", message, "MISSING_DATABASE_URL");
|
|
27691
27863
|
} else if (!options.yes) {
|
|
27692
27864
|
databaseUrl = await promptConnectionString();
|
|
27693
27865
|
}
|
|
@@ -27704,10 +27876,9 @@ async function runInitCommand(name, options) {
|
|
|
27704
27876
|
persistDatabaseUrl(cwd, databaseUrl);
|
|
27705
27877
|
dismissVercelSignedInNote = flow.dismissSignedInNote;
|
|
27706
27878
|
} else if (options.yes) {
|
|
27707
|
-
|
|
27708
|
-
|
|
27709
|
-
);
|
|
27710
|
-
process.exit(1);
|
|
27879
|
+
const message = flow.ok ? "Created a Neon database, but DATABASE_URL could not be retrieved from Vercel." : "Vercel database provisioning did not complete.";
|
|
27880
|
+
p26.log.error(message);
|
|
27881
|
+
exitInit("providers", message, "DATABASE_PROVISIONING_FAILED");
|
|
27711
27882
|
} else if (flow.ok) {
|
|
27712
27883
|
openBrowserVercelNeonResource(flow.dashboardUrl ?? flow.resourceUrl);
|
|
27713
27884
|
databaseUrl = await promptConnectionString();
|
|
@@ -27727,8 +27898,9 @@ async function runInitCommand(name, options) {
|
|
|
27727
27898
|
} catch (error) {
|
|
27728
27899
|
const message = error instanceof Error ? error.message : String(error);
|
|
27729
27900
|
if (options.yes) {
|
|
27730
|
-
|
|
27731
|
-
|
|
27901
|
+
const failureMessage = `Railway database provisioning failed: ${message}`;
|
|
27902
|
+
p26.log.error(failureMessage);
|
|
27903
|
+
exitInit("providers", failureMessage, "DATABASE_PROVISIONING_FAILED");
|
|
27732
27904
|
}
|
|
27733
27905
|
p26.log.warn(`Railway database provisioning failed: ${message}`);
|
|
27734
27906
|
p26.log.info("Falling back to a manual database connection string.");
|
|
@@ -27773,27 +27945,39 @@ async function runInitCommand(name, options) {
|
|
|
27773
27945
|
storage
|
|
27774
27946
|
};
|
|
27775
27947
|
if (storage === "r2") {
|
|
27776
|
-
const missingR2Keys = R2_ENV_KEYS.filter((key) => !
|
|
27948
|
+
const missingR2Keys = R2_ENV_KEYS.filter((key) => !readProjectEnvVar(cwd, key)?.trim());
|
|
27777
27949
|
if (options.yes && missingR2Keys.length > 0) {
|
|
27778
|
-
|
|
27779
|
-
|
|
27780
|
-
);
|
|
27781
|
-
process.exit(1);
|
|
27950
|
+
const message = `Cloudflare R2 is missing required environment variables: ${missingR2Keys.join(", ")}.`;
|
|
27951
|
+
p26.log.error(message);
|
|
27952
|
+
exitInit("providers", message, "MISSING_STORAGE_CREDENTIALS");
|
|
27782
27953
|
}
|
|
27783
27954
|
mergeCollectedIntegrationConfig(await collectIntegrationConfig(cwd, ["r2"]));
|
|
27784
27955
|
} else if (storage === "railway-bucket") {
|
|
27785
27956
|
if (hasRailwayBucketConfig(cwd)) {
|
|
27786
27957
|
mergeCollectedIntegrationConfig(await collectIntegrationConfig(cwd, ["railway-bucket"]));
|
|
27958
|
+
} else if (options.useExistingResources) {
|
|
27959
|
+
const missingKeys = RAILWAY_BUCKET_ENV_KEYS.filter(
|
|
27960
|
+
(key) => !readProjectEnvVar(cwd, key)?.trim()
|
|
27961
|
+
);
|
|
27962
|
+
const message = `Railway Bucket is missing required environment variables: ${missingKeys.join(", ")}.`;
|
|
27963
|
+
p26.log.error(message);
|
|
27964
|
+
exitInit("providers", message, "MISSING_STORAGE_CREDENTIALS");
|
|
27787
27965
|
} else {
|
|
27788
27966
|
const flow = await provisionRailwayBucket();
|
|
27789
27967
|
if (flow.ok && flow.config) {
|
|
27790
27968
|
mergeCollectedIntegrationConfig(flow.config);
|
|
27791
27969
|
} else if (options.yes) {
|
|
27792
|
-
|
|
27793
|
-
|
|
27970
|
+
const message = "Railway bucket provisioning did not complete.";
|
|
27971
|
+
p26.log.error(message);
|
|
27972
|
+
exitInit("providers", message, "STORAGE_PROVISIONING_FAILED");
|
|
27794
27973
|
}
|
|
27795
27974
|
}
|
|
27796
|
-
} else if (storage === "vercel-blob" && !
|
|
27975
|
+
} else if (storage === "vercel-blob" && !readProjectEnvVar(cwd, "BLOB_READ_WRITE_TOKEN")?.trim()) {
|
|
27976
|
+
if (options.useExistingResources) {
|
|
27977
|
+
const message = "BLOB_READ_WRITE_TOKEN is missing from the project environment files.";
|
|
27978
|
+
p26.log.error(message);
|
|
27979
|
+
exitInit("providers", message, "MISSING_STORAGE_CREDENTIALS");
|
|
27980
|
+
}
|
|
27797
27981
|
const flow = await runVercelBlobFlow({
|
|
27798
27982
|
cwd,
|
|
27799
27983
|
projectName,
|
|
@@ -27808,8 +27992,9 @@ async function runInitCommand(name, options) {
|
|
|
27808
27992
|
});
|
|
27809
27993
|
collectedIntegrationConfig.overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
|
|
27810
27994
|
} else if (options.yes) {
|
|
27811
|
-
|
|
27812
|
-
|
|
27995
|
+
const message = "Vercel Blob provisioning did not complete.";
|
|
27996
|
+
p26.log.error(message);
|
|
27997
|
+
exitInit("providers", message, "STORAGE_PROVISIONING_FAILED");
|
|
27813
27998
|
} else {
|
|
27814
27999
|
p26.log.warn(
|
|
27815
28000
|
"Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
|
|
@@ -27846,6 +28031,45 @@ async function runInitCommand(name, options) {
|
|
|
27846
28031
|
config.paths = config.frameworkConfig.next.paths;
|
|
27847
28032
|
config.database.migrationsDir = deriveMigrationsDir(namespace);
|
|
27848
28033
|
const s = spinner2();
|
|
28034
|
+
const coreDependencyPlan = getDependencyPlan(
|
|
28035
|
+
presetSelection.presets,
|
|
28036
|
+
presetSelection.integrations,
|
|
28037
|
+
project2.linter.type === "none"
|
|
28038
|
+
);
|
|
28039
|
+
const cliDependencyPlan = getCliDependencySyncPlan(cwd);
|
|
28040
|
+
s.start("Installing dependencies, This may take a moment");
|
|
28041
|
+
const depsResult = await installDependenciesAsync({
|
|
28042
|
+
cwd,
|
|
28043
|
+
pm,
|
|
28044
|
+
...isFreshProject && pm === "pnpm" ? { pnpmAllowedBuilds: [...FRESH_PNPM_ALLOWED_BUILDS] } : {},
|
|
28045
|
+
dependencies: Array.from(
|
|
28046
|
+
/* @__PURE__ */ new Set([...coreDependencyPlan.dependencies, ...cliDependencyPlan?.dependencies ?? []])
|
|
28047
|
+
),
|
|
28048
|
+
devDependencies: Array.from(
|
|
28049
|
+
/* @__PURE__ */ new Set([
|
|
28050
|
+
...coreDependencyPlan.devDependencies,
|
|
28051
|
+
...cliDependencyPlan?.devDependencies ?? []
|
|
28052
|
+
])
|
|
28053
|
+
)
|
|
28054
|
+
});
|
|
28055
|
+
if (depsResult.success) {
|
|
28056
|
+
s.stop("");
|
|
28057
|
+
} else {
|
|
28058
|
+
const message = redactSecrets(depsResult.error ?? "Unknown dependency installation error");
|
|
28059
|
+
s.stop("Failed to install dependencies");
|
|
28060
|
+
p26.log.warn(message);
|
|
28061
|
+
p26.log.info(
|
|
28062
|
+
`You can install them manually:
|
|
28063
|
+
${pc10.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
|
|
28064
|
+
${pc10.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
|
|
28065
|
+
);
|
|
28066
|
+
disposeCancelGuard();
|
|
28067
|
+
exitInit(
|
|
28068
|
+
"dependencies",
|
|
28069
|
+
message,
|
|
28070
|
+
message.includes("ERR_PNPM_UNEXPECTED_STORE") ? "PNPM_UNEXPECTED_STORE" : "DEPENDENCY_INSTALL_FAILED"
|
|
28071
|
+
);
|
|
28072
|
+
}
|
|
27849
28073
|
s.start("Directory structure");
|
|
27850
28074
|
scaffoldBase({
|
|
27851
28075
|
cwd,
|
|
@@ -27913,36 +28137,6 @@ async function runInitCommand(name, options) {
|
|
|
27913
28137
|
}
|
|
27914
28138
|
}
|
|
27915
28139
|
}
|
|
27916
|
-
const coreDependencyPlan = getDependencyPlan([], [], project2.linter.type === "none");
|
|
27917
|
-
const cliDependencyPlan = getCliDependencySyncPlan(cwd);
|
|
27918
|
-
s.start("Installing dependencies, This may take a moment");
|
|
27919
|
-
const depsResult = await installDependenciesAsync({
|
|
27920
|
-
cwd,
|
|
27921
|
-
pm,
|
|
27922
|
-
...isFreshProject && pm === "pnpm" ? { pnpmAllowedBuilds: [...FRESH_PNPM_ALLOWED_BUILDS] } : {},
|
|
27923
|
-
dependencies: Array.from(
|
|
27924
|
-
/* @__PURE__ */ new Set([...coreDependencyPlan.dependencies, ...cliDependencyPlan?.dependencies ?? []])
|
|
27925
|
-
),
|
|
27926
|
-
devDependencies: Array.from(
|
|
27927
|
-
/* @__PURE__ */ new Set([
|
|
27928
|
-
...coreDependencyPlan.devDependencies,
|
|
27929
|
-
...cliDependencyPlan?.devDependencies ?? []
|
|
27930
|
-
])
|
|
27931
|
-
)
|
|
27932
|
-
});
|
|
27933
|
-
if (depsResult.success) {
|
|
27934
|
-
s.stop("");
|
|
27935
|
-
} else {
|
|
27936
|
-
s.stop("Failed to install dependencies");
|
|
27937
|
-
p26.log.warn(depsResult.error ?? "Unknown error");
|
|
27938
|
-
p26.log.info(
|
|
27939
|
-
`You can install them manually:
|
|
27940
|
-
${pc10.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
|
|
27941
|
-
${pc10.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
|
|
27942
|
-
);
|
|
27943
|
-
disposeCancelGuard();
|
|
27944
|
-
process.exit(1);
|
|
27945
|
-
}
|
|
27946
28140
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
27947
28141
|
s.start("Generating core schemas");
|
|
27948
28142
|
const coreSchemasResult = scaffoldCoreSchemas({ cwd, config });
|
|
@@ -27956,7 +28150,8 @@ async function runInitCommand(name, options) {
|
|
|
27956
28150
|
pm,
|
|
27957
28151
|
presetIds: presetSelection.presets,
|
|
27958
28152
|
interactive: !options.yes,
|
|
27959
|
-
includeBiome: project2.linter.type === "none"
|
|
28153
|
+
includeBiome: project2.linter.type === "none",
|
|
28154
|
+
dependenciesInstalled: true
|
|
27960
28155
|
});
|
|
27961
28156
|
})() : Promise.resolve({ installed: [], skipped: [], warnings: [], config });
|
|
27962
28157
|
const resolvedPresetInstallResult = await presetInstallResult;
|
|
@@ -27975,7 +28170,8 @@ async function runInitCommand(name, options) {
|
|
|
27975
28170
|
// .env.local, so the install runs without prompting (rule: no
|
|
27976
28171
|
// mid-scaffold input).
|
|
27977
28172
|
interactive: false,
|
|
27978
|
-
includeBiome: project2.linter.type === "none"
|
|
28173
|
+
includeBiome: project2.linter.type === "none",
|
|
28174
|
+
dependenciesInstalled: true
|
|
27979
28175
|
});
|
|
27980
28176
|
})() : Promise.resolve({
|
|
27981
28177
|
installed: [],
|
|
@@ -28051,8 +28247,9 @@ async function runInitCommand(name, options) {
|
|
|
28051
28247
|
clearDbSpinner();
|
|
28052
28248
|
if (!verification.success) {
|
|
28053
28249
|
p26.log.warn(verification.error);
|
|
28054
|
-
|
|
28055
|
-
|
|
28250
|
+
const message = "Database was not reachable. Aborting setup.";
|
|
28251
|
+
p26.log.error(message);
|
|
28252
|
+
exitInit("database", verification.error, "DATABASE_UNREACHABLE");
|
|
28056
28253
|
}
|
|
28057
28254
|
} else {
|
|
28058
28255
|
clearDbSpinner();
|
|
@@ -28066,8 +28263,9 @@ async function runInitCommand(name, options) {
|
|
|
28066
28263
|
const pushError = pushResult.error ?? "Unknown error";
|
|
28067
28264
|
p26.log.warn(pushError);
|
|
28068
28265
|
if (isDatabaseReachabilityError(pushError)) {
|
|
28069
|
-
|
|
28070
|
-
|
|
28266
|
+
const message = "Database was not reachable. Aborting setup.";
|
|
28267
|
+
p26.log.error(message);
|
|
28268
|
+
exitInit("database", pushError, "DATABASE_UNREACHABLE");
|
|
28071
28269
|
}
|
|
28072
28270
|
p26.log.info(`You can run it manually: ${pc10.cyan(drizzlePushCommand(pm))}`);
|
|
28073
28271
|
}
|
|
@@ -28092,8 +28290,9 @@ async function runInitCommand(name, options) {
|
|
|
28092
28290
|
if (adminCheck.error) {
|
|
28093
28291
|
p26.log.warn(`Could not verify existing admin account ${pc10.dim(`(${adminCheck.error})`)}`);
|
|
28094
28292
|
if (isDatabaseReachabilityError(adminCheck.error)) {
|
|
28095
|
-
|
|
28096
|
-
|
|
28293
|
+
const message = "Database was not reachable. Aborting setup.";
|
|
28294
|
+
p26.log.error(message);
|
|
28295
|
+
exitInit("database", adminCheck.error, "DATABASE_UNREACHABLE");
|
|
28097
28296
|
}
|
|
28098
28297
|
} else if (adminCheck.existingAdmin) {
|
|
28099
28298
|
const existingAdminLabel = formatAdminIdentity(adminCheck.existingAdmin);
|
|
@@ -28202,8 +28401,9 @@ Run manually: ${pc10.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
28202
28401
|
pc10.red("Seed failed")
|
|
28203
28402
|
);
|
|
28204
28403
|
if (isDatabaseReachabilityError(seedResult.error)) {
|
|
28205
|
-
|
|
28206
|
-
|
|
28404
|
+
const message = "Database was not reachable. Aborting setup.";
|
|
28405
|
+
p26.log.error(message);
|
|
28406
|
+
exitInit("database", seedResult.error, "DATABASE_UNREACHABLE");
|
|
28207
28407
|
}
|
|
28208
28408
|
}
|
|
28209
28409
|
}
|
|
@@ -28244,8 +28444,9 @@ Run manually: ${pc10.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
28244
28444
|
deployedUrl = deployFlow.url;
|
|
28245
28445
|
if (!deployFlow.ok) {
|
|
28246
28446
|
if (options.yes) {
|
|
28247
|
-
|
|
28248
|
-
|
|
28447
|
+
const message = "Vercel deploy did not complete.";
|
|
28448
|
+
p26.log.error(message);
|
|
28449
|
+
exitInit("deployment", message, "DEPLOYMENT_FAILED");
|
|
28249
28450
|
}
|
|
28250
28451
|
p26.log.warn("Vercel deploy did not complete; continuing.");
|
|
28251
28452
|
}
|
|
@@ -28267,16 +28468,18 @@ Run manually: ${pc10.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
28267
28468
|
if (!deployFlow.ok) {
|
|
28268
28469
|
if (deployFlow.detail) p26.log.message(pc10.dim(redactSecrets(deployFlow.detail)));
|
|
28269
28470
|
if (options.yes) {
|
|
28270
|
-
|
|
28271
|
-
|
|
28471
|
+
const message = "Railway deploy did not complete.";
|
|
28472
|
+
p26.log.error(message);
|
|
28473
|
+
exitInit("deployment", message, "DEPLOYMENT_FAILED");
|
|
28272
28474
|
}
|
|
28273
28475
|
p26.log.warn("Railway deploy did not complete; continuing.");
|
|
28274
28476
|
}
|
|
28275
28477
|
} catch (error) {
|
|
28276
28478
|
const message = error instanceof Error ? error.message : String(error);
|
|
28277
28479
|
if (options.yes) {
|
|
28278
|
-
|
|
28279
|
-
|
|
28480
|
+
const failureMessage = `Railway deploy failed: ${message}`;
|
|
28481
|
+
p26.log.error(failureMessage);
|
|
28482
|
+
exitInit("deployment", failureMessage, "DEPLOYMENT_FAILED");
|
|
28280
28483
|
}
|
|
28281
28484
|
p26.log.warn(`Railway deploy failed: ${message}`);
|
|
28282
28485
|
}
|
|
@@ -28297,26 +28500,20 @@ Run manually: ${pc10.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
28297
28500
|
}
|
|
28298
28501
|
}
|
|
28299
28502
|
disposeCancelGuard();
|
|
28300
|
-
if (options.json
|
|
28301
|
-
|
|
28302
|
-
|
|
28303
|
-
|
|
28304
|
-
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
|
|
28308
|
-
|
|
28309
|
-
|
|
28310
|
-
|
|
28311
|
-
|
|
28312
|
-
|
|
28313
|
-
|
|
28314
|
-
deployedUrl: deployedUrl ?? null
|
|
28315
|
-
},
|
|
28316
|
-
null,
|
|
28317
|
-
2
|
|
28318
|
-
)
|
|
28319
|
-
);
|
|
28503
|
+
if (options.json) {
|
|
28504
|
+
writeInitJson(jsonContext, {
|
|
28505
|
+
success: true,
|
|
28506
|
+
namespace,
|
|
28507
|
+
configPath: CONFIG_FILE_NAME,
|
|
28508
|
+
databaseProvider,
|
|
28509
|
+
storageProvider: presetSelection.storage,
|
|
28510
|
+
deployProvider,
|
|
28511
|
+
presets: presetSelection.presets,
|
|
28512
|
+
integrations: presetSelection.integrations,
|
|
28513
|
+
adminUrl: adminLoginUrl,
|
|
28514
|
+
adminEmail: seedEmail ?? null,
|
|
28515
|
+
deployedUrl: deployedUrl ?? null
|
|
28516
|
+
});
|
|
28320
28517
|
return;
|
|
28321
28518
|
}
|
|
28322
28519
|
p26.outro(`Admin ready at ${adminNamespace.routePath}`);
|
|
@@ -28325,21 +28522,13 @@ function isValidDbUrl(url) {
|
|
|
28325
28522
|
return url.startsWith("postgres://") || url.startsWith("postgresql://");
|
|
28326
28523
|
}
|
|
28327
28524
|
function readExistingDbUrl(cwd) {
|
|
28328
|
-
const
|
|
28329
|
-
if (!
|
|
28330
|
-
const
|
|
28331
|
-
|
|
28332
|
-
|
|
28333
|
-
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
28334
|
-
const [key, ...rest] = trimmed.split("=");
|
|
28335
|
-
if (key?.trim() === "DATABASE_URL") {
|
|
28336
|
-
const val = rest.join("=").replace(/^['"]|['"]$/g, "").trim();
|
|
28337
|
-
if (val.length > 0 && !val.startsWith("your_") && val !== "postgresql://..." && isValidDbUrl(val)) {
|
|
28338
|
-
return val;
|
|
28339
|
-
}
|
|
28340
|
-
}
|
|
28525
|
+
const resolved = resolveProjectEnvVar(cwd, "DATABASE_URL");
|
|
28526
|
+
if (!resolved) return void 0;
|
|
28527
|
+
const value = resolved.value.trim();
|
|
28528
|
+
if (value.startsWith("your_") || value === "postgresql://..." || !isValidDbUrl(value)) {
|
|
28529
|
+
return void 0;
|
|
28341
28530
|
}
|
|
28342
|
-
return
|
|
28531
|
+
return { ...resolved, value };
|
|
28343
28532
|
}
|
|
28344
28533
|
function maskDbUrl(url) {
|
|
28345
28534
|
try {
|
|
@@ -28381,7 +28570,7 @@ var R2_ENV_KEYS = [
|
|
|
28381
28570
|
"BETTERSTART_R2_PUBLIC_URL"
|
|
28382
28571
|
];
|
|
28383
28572
|
function hasRailwayBucketConfig(cwd) {
|
|
28384
|
-
return RAILWAY_BUCKET_ENV_KEYS.every((key) => Boolean(
|
|
28573
|
+
return RAILWAY_BUCKET_ENV_KEYS.every((key) => Boolean(readProjectEnvVar(cwd, key)?.trim()));
|
|
28385
28574
|
}
|
|
28386
28575
|
function railwayBucketIntegrationConfig(credentials) {
|
|
28387
28576
|
return {
|
|
@@ -28501,7 +28690,7 @@ main().catch((error) => {
|
|
|
28501
28690
|
cwd,
|
|
28502
28691
|
timeoutMs: 15e3,
|
|
28503
28692
|
env: {
|
|
28504
|
-
DATABASE_URL: databaseUrl
|
|
28693
|
+
DATABASE_URL: databaseUrl.value
|
|
28505
28694
|
}
|
|
28506
28695
|
}
|
|
28507
28696
|
);
|
|
@@ -28914,581 +29103,13 @@ async function runListPresetsCommand(options) {
|
|
|
28914
29103
|
}
|
|
28915
29104
|
|
|
28916
29105
|
// adapters/next/commands/menu-choices.ts
|
|
28917
|
-
import path55 from "path";
|
|
28918
|
-
async function listInstallableChoices(cwd) {
|
|
28919
|
-
const config = await resolveConfigOrExit(cwd);
|
|
28920
|
-
const installedPresets = new Set(config.presets.installed);
|
|
28921
|
-
const installedIntegrations = new Set(config.integrations.installed);
|
|
28922
|
-
return {
|
|
28923
|
-
presets: listAvailablePresets().map((preset) => ({
|
|
28924
|
-
id: preset.id,
|
|
28925
|
-
description: preset.description,
|
|
28926
|
-
installed: installedPresets.has(preset.id)
|
|
28927
|
-
})),
|
|
28928
|
-
integrations: listAvailableIntegrations().map((integration) => ({
|
|
28929
|
-
id: integration.id,
|
|
28930
|
-
description: integration.description,
|
|
28931
|
-
installed: installedIntegrations.has(integration.id)
|
|
28932
|
-
}))
|
|
28933
|
-
};
|
|
28934
|
-
}
|
|
28935
|
-
async function listSchemaChoices(cwd) {
|
|
28936
|
-
const config = await resolveConfigOrExit(cwd);
|
|
28937
|
-
const paths = resolveProjectPaths(config);
|
|
28938
|
-
return listSchemaNames(path55.join(cwd, ...paths.schemasDir.split("/")));
|
|
28939
|
-
}
|
|
28940
|
-
|
|
28941
|
-
// adapters/next/commands/remove.ts
|
|
28942
29106
|
import path56 from "path";
|
|
28943
|
-
import * as p29 from "@clack/prompts";
|
|
28944
|
-
async function runRemoveCommand(items, options) {
|
|
28945
|
-
const removeIntegrationsMode = Boolean(options.integration);
|
|
28946
|
-
if (!removeIntegrationsMode && items.includes("core")) {
|
|
28947
|
-
p29.log.error("The core Admin cannot be removed.");
|
|
28948
|
-
process.exit(1);
|
|
28949
|
-
}
|
|
28950
|
-
const presetIds = items.filter(isPresetId);
|
|
28951
|
-
const integrationIds = items.filter(isIntegrationId);
|
|
28952
|
-
if (!removeIntegrationsMode && integrationIds.length > 0) {
|
|
28953
|
-
p29.log.error(
|
|
28954
|
-
`Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
|
|
28955
|
-
);
|
|
28956
|
-
process.exit(1);
|
|
28957
|
-
}
|
|
28958
|
-
if (removeIntegrationsMode && presetIds.length > 0) {
|
|
28959
|
-
p29.log.error(`Preset IDs cannot be removed with --integration: ${presetIds.join(", ")}`);
|
|
28960
|
-
process.exit(1);
|
|
28961
|
-
}
|
|
28962
|
-
const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((presetId) => !isPresetId(presetId));
|
|
28963
|
-
if (invalidItems.length > 0) {
|
|
28964
|
-
p29.log.error(
|
|
28965
|
-
removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPresetMessage(invalidItems)
|
|
28966
|
-
);
|
|
28967
|
-
process.exit(1);
|
|
28968
|
-
}
|
|
28969
|
-
const cwd = options.cwd ? path56.resolve(options.cwd) : process.cwd();
|
|
28970
|
-
const config = await resolveConfigOrExit(cwd);
|
|
28971
|
-
const pm = detectPackageManager(cwd);
|
|
28972
|
-
if (!options.force) {
|
|
28973
|
-
const confirmed = await p29.confirm({
|
|
28974
|
-
message: `Remove ${removeIntegrationsMode ? "integration" : "preset"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
|
|
28975
|
-
initialValue: false
|
|
28976
|
-
});
|
|
28977
|
-
if (p29.isCancel(confirmed) || !confirmed) {
|
|
28978
|
-
p29.cancel(`${removeIntegrationsMode ? "Integration" : "Preset"} removal cancelled.`);
|
|
28979
|
-
process.exit(0);
|
|
28980
|
-
}
|
|
28981
|
-
}
|
|
28982
|
-
if (removeIntegrationsMode) {
|
|
28983
|
-
const result2 = await removeIntegrations({
|
|
28984
|
-
cwd,
|
|
28985
|
-
config,
|
|
28986
|
-
pm,
|
|
28987
|
-
integrationIds
|
|
28988
|
-
});
|
|
28989
|
-
writeConfigFile(cwd, result2.config);
|
|
28990
|
-
if (result2.removed.length === 0) {
|
|
28991
|
-
p29.outro("No integrations were removed.");
|
|
28992
|
-
return;
|
|
28993
|
-
}
|
|
28994
|
-
if (result2.warnings.length > 0) {
|
|
28995
|
-
p29.note(result2.warnings.join("\n"), "Warnings");
|
|
28996
|
-
}
|
|
28997
|
-
p29.outro(
|
|
28998
|
-
`Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
|
|
28999
|
-
);
|
|
29000
|
-
return;
|
|
29001
|
-
}
|
|
29002
|
-
const result = await removePresets({
|
|
29003
|
-
cwd,
|
|
29004
|
-
config,
|
|
29005
|
-
pm,
|
|
29006
|
-
presetIds
|
|
29007
|
-
});
|
|
29008
|
-
writeConfigFile(cwd, result.config);
|
|
29009
|
-
if (result.removed.length === 0) {
|
|
29010
|
-
p29.outro("No presets were removed.");
|
|
29011
|
-
return;
|
|
29012
|
-
}
|
|
29013
|
-
if (result.warnings.length > 0) {
|
|
29014
|
-
p29.note(result.warnings.join("\n"), "Warnings");
|
|
29015
|
-
}
|
|
29016
|
-
p29.outro(`Removed preset${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
|
|
29017
|
-
}
|
|
29018
|
-
|
|
29019
|
-
// adapters/next/commands/remove-schema.ts
|
|
29020
|
-
import fs42 from "fs";
|
|
29021
|
-
import path57 from "path";
|
|
29022
|
-
import * as clack3 from "@clack/prompts";
|
|
29023
|
-
function removePath2(cwd, filePath) {
|
|
29024
|
-
const fullPath = path57.join(cwd, ...filePath.split("/"));
|
|
29025
|
-
const existed = fs42.existsSync(fullPath);
|
|
29026
|
-
fs42.rmSync(fullPath, { recursive: true, force: true });
|
|
29027
|
-
return existed;
|
|
29028
|
-
}
|
|
29029
|
-
function cleanupEmptyDirs3(cwd, deletedPaths, configPaths) {
|
|
29030
|
-
const stopRoots = /* @__PURE__ */ new Set([
|
|
29031
|
-
path57.join(cwd, ...configPaths.adminDir.split("/")),
|
|
29032
|
-
path57.join(cwd, ...configPaths.adminNavigationDir.split("/")),
|
|
29033
|
-
path57.join(cwd, ...configPaths.adminWebhookEventsDir.split("/")),
|
|
29034
|
-
path57.join(cwd, ...configPaths.pagesDir.split("/"))
|
|
29035
|
-
]);
|
|
29036
|
-
for (const deletedPath of deletedPaths) {
|
|
29037
|
-
let current = path57.dirname(path57.join(cwd, ...deletedPath.split("/")));
|
|
29038
|
-
while (!stopRoots.has(current)) {
|
|
29039
|
-
if (!fs42.existsSync(current)) {
|
|
29040
|
-
current = path57.dirname(current);
|
|
29041
|
-
continue;
|
|
29042
|
-
}
|
|
29043
|
-
const entries = fs42.readdirSync(current);
|
|
29044
|
-
if (entries.length > 0) {
|
|
29045
|
-
break;
|
|
29046
|
-
}
|
|
29047
|
-
fs42.rmdirSync(current);
|
|
29048
|
-
current = path57.dirname(current);
|
|
29049
|
-
}
|
|
29050
|
-
}
|
|
29051
|
-
}
|
|
29052
|
-
function resolveSchemaOwnerForRemoval(cwd, schemaName) {
|
|
29053
|
-
const explicitOwner = getSchemaOwner(cwd, schemaName);
|
|
29054
|
-
if (explicitOwner) {
|
|
29055
|
-
return explicitOwner;
|
|
29056
|
-
}
|
|
29057
|
-
if (schemaName === "settings") {
|
|
29058
|
-
return "core";
|
|
29059
|
-
}
|
|
29060
|
-
return "user";
|
|
29061
|
-
}
|
|
29062
|
-
async function runRemoveSchemaCommand(schemaName, options) {
|
|
29063
|
-
const owner = resolveSchemaOwnerForRemoval(
|
|
29064
|
-
options.cwd ? path57.resolve(options.cwd) : process.cwd(),
|
|
29065
|
-
schemaName
|
|
29066
|
-
);
|
|
29067
|
-
if (owner === "core") {
|
|
29068
|
-
clack3.log.error(
|
|
29069
|
-
`"${schemaName}" is a core Admin module and cannot be removed with remove-schema.`
|
|
29070
|
-
);
|
|
29071
|
-
process.exit(1);
|
|
29072
|
-
}
|
|
29073
|
-
if (owner.startsWith("preset:")) {
|
|
29074
|
-
clack3.log.error(`"${schemaName}" is owned by ${owner}. Remove the owning preset instead.`);
|
|
29075
|
-
process.exit(1);
|
|
29076
|
-
}
|
|
29077
|
-
const cwd = options.cwd ? path57.resolve(options.cwd) : process.cwd();
|
|
29078
|
-
const config = await resolveConfigOrExit(cwd);
|
|
29079
|
-
const paths = resolveProjectPaths(config);
|
|
29080
|
-
const manifest = loadManifest(cwd, schemaName);
|
|
29081
|
-
if (!snapshotRootExists(cwd)) {
|
|
29082
|
-
clack3.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
29083
|
-
process.exit(1);
|
|
29084
|
-
}
|
|
29085
|
-
if (!manifest) {
|
|
29086
|
-
clack3.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
29087
|
-
process.exit(1);
|
|
29088
|
-
}
|
|
29089
|
-
if (!options.force) {
|
|
29090
|
-
if (!isInteractiveSession()) {
|
|
29091
|
-
clack3.log.error(
|
|
29092
|
-
`Removing generated files for ${schemaName} needs confirmation. Re-run with --force.`
|
|
29093
|
-
);
|
|
29094
|
-
process.exit(1);
|
|
29095
|
-
}
|
|
29096
|
-
const confirmed = await clack3.confirm({
|
|
29097
|
-
message: `Remove generated files for ${schemaName}?`,
|
|
29098
|
-
initialValue: false
|
|
29099
|
-
});
|
|
29100
|
-
if (clack3.isCancel(confirmed) || !confirmed) {
|
|
29101
|
-
clack3.cancel("Cancelled.");
|
|
29102
|
-
return;
|
|
29103
|
-
}
|
|
29104
|
-
}
|
|
29105
|
-
const deletedPaths = [];
|
|
29106
|
-
for (const file of [...manifest.files.map((entry) => entry.path), ...manifest.skipped]) {
|
|
29107
|
-
if (removePath2(cwd, file)) {
|
|
29108
|
-
deletedPaths.push(file);
|
|
29109
|
-
}
|
|
29110
|
-
}
|
|
29111
|
-
const loaded = (() => {
|
|
29112
|
-
try {
|
|
29113
|
-
return loadSchema(path57.join(cwd, ...paths.schemasDir.split("/")), schemaName);
|
|
29114
|
-
} catch {
|
|
29115
|
-
return null;
|
|
29116
|
-
}
|
|
29117
|
-
})();
|
|
29118
|
-
const kebabName = toKebabCase(schemaName);
|
|
29119
|
-
if (loaded?.type === "form") {
|
|
29120
|
-
if (removePath2(cwd, `${paths.adminActionsDir}/${schemaName}`)) {
|
|
29121
|
-
deletedPaths.push(`${paths.adminActionsDir}/${schemaName}`);
|
|
29122
|
-
}
|
|
29123
|
-
if (removePath2(cwd, `${paths.pagesDir}/forms/${kebabName}`)) {
|
|
29124
|
-
deletedPaths.push(`${paths.pagesDir}/forms/${kebabName}`);
|
|
29125
|
-
}
|
|
29126
|
-
} else {
|
|
29127
|
-
if (removePath2(cwd, `${paths.adminActionsDir}/${schemaName}`)) {
|
|
29128
|
-
deletedPaths.push(`${paths.adminActionsDir}/${schemaName}`);
|
|
29129
|
-
}
|
|
29130
|
-
if (removePath2(cwd, `${paths.pagesDir}/${schemaName}`)) {
|
|
29131
|
-
deletedPaths.push(`${paths.pagesDir}/${schemaName}`);
|
|
29132
|
-
}
|
|
29133
|
-
}
|
|
29134
|
-
cleanupEmptyDirs3(cwd, deletedPaths, paths);
|
|
29135
|
-
deleteSnapshot(cwd, schemaName);
|
|
29136
|
-
if (hasTombstone(cwd, schemaName)) {
|
|
29137
|
-
clearTombstone(cwd, schemaName);
|
|
29138
|
-
}
|
|
29139
|
-
writeTombstone(cwd, schemaName);
|
|
29140
|
-
await applyGeneratedFiles({
|
|
29141
|
-
cwd,
|
|
29142
|
-
config,
|
|
29143
|
-
scope: BARREL_SCOPE,
|
|
29144
|
-
schemaJson: { name: BARREL_SCOPE },
|
|
29145
|
-
generatedFiles: renderBarrelFiles(cwd, config),
|
|
29146
|
-
force: false,
|
|
29147
|
-
interactive: false
|
|
29148
|
-
});
|
|
29149
|
-
clack3.log.info(
|
|
29150
|
-
`Tombstone written: .betterstart/snapshots/_removed/${schemaName}
|
|
29151
|
-
Schema JSON preserved.`
|
|
29152
|
-
);
|
|
29153
|
-
clack3.outro(`Removed generated files for ${schemaName}`);
|
|
29154
|
-
}
|
|
29155
|
-
|
|
29156
|
-
// adapters/next/commands/uninstall.ts
|
|
29157
|
-
import fs44 from "fs";
|
|
29158
|
-
import path58 from "path";
|
|
29159
|
-
import * as p30 from "@clack/prompts";
|
|
29160
|
-
import pc11 from "picocolors";
|
|
29161
|
-
|
|
29162
|
-
// adapters/next/commands/uninstall-cleaners.ts
|
|
29163
|
-
import fs43 from "fs";
|
|
29164
|
-
function stripJsonComments2(input) {
|
|
29165
|
-
let result = "";
|
|
29166
|
-
let i = 0;
|
|
29167
|
-
while (i < input.length) {
|
|
29168
|
-
if (input[i] === '"') {
|
|
29169
|
-
let j = i + 1;
|
|
29170
|
-
while (j < input.length) {
|
|
29171
|
-
if (input[j] === "\\") {
|
|
29172
|
-
j += 2;
|
|
29173
|
-
continue;
|
|
29174
|
-
}
|
|
29175
|
-
if (input[j] === '"') {
|
|
29176
|
-
j++;
|
|
29177
|
-
break;
|
|
29178
|
-
}
|
|
29179
|
-
j++;
|
|
29180
|
-
}
|
|
29181
|
-
result += input.slice(i, j);
|
|
29182
|
-
i = j;
|
|
29183
|
-
} else if (input[i] === "/" && input[i + 1] === "/") {
|
|
29184
|
-
const nl = input.indexOf("\n", i);
|
|
29185
|
-
i = nl === -1 ? input.length : nl;
|
|
29186
|
-
} else if (input[i] === "/" && input[i + 1] === "*") {
|
|
29187
|
-
const end = input.indexOf("*/", i + 2);
|
|
29188
|
-
i = end === -1 ? input.length : end + 2;
|
|
29189
|
-
} else {
|
|
29190
|
-
result += input[i];
|
|
29191
|
-
i++;
|
|
29192
|
-
}
|
|
29193
|
-
}
|
|
29194
|
-
return result;
|
|
29195
|
-
}
|
|
29196
|
-
function cleanTsconfig(tsconfigPath, aliasRoot = "@admin") {
|
|
29197
|
-
if (!fs43.existsSync(tsconfigPath)) return [];
|
|
29198
|
-
const raw = fs43.readFileSync(tsconfigPath, "utf-8");
|
|
29199
|
-
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
29200
|
-
let tsconfig;
|
|
29201
|
-
try {
|
|
29202
|
-
tsconfig = JSON.parse(stripped);
|
|
29203
|
-
} catch {
|
|
29204
|
-
return [];
|
|
29205
|
-
}
|
|
29206
|
-
const compilerOptions = tsconfig.compilerOptions ?? {};
|
|
29207
|
-
const paths = compilerOptions.paths ?? {};
|
|
29208
|
-
const removed = [];
|
|
29209
|
-
for (const key of Object.keys(paths)) {
|
|
29210
|
-
if (key.startsWith("@admin/") || key === "@admin/*" || key.startsWith(`${aliasRoot}/`) || key === `${aliasRoot}/*`) {
|
|
29211
|
-
removed.push(key);
|
|
29212
|
-
delete paths[key];
|
|
29213
|
-
}
|
|
29214
|
-
}
|
|
29215
|
-
if (removed.length === 0) return [];
|
|
29216
|
-
if (Object.keys(paths).length === 0) {
|
|
29217
|
-
compilerOptions.paths = void 0;
|
|
29218
|
-
} else {
|
|
29219
|
-
compilerOptions.paths = paths;
|
|
29220
|
-
}
|
|
29221
|
-
tsconfig.compilerOptions = compilerOptions;
|
|
29222
|
-
fs43.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
29223
|
-
`, "utf-8");
|
|
29224
|
-
return removed;
|
|
29225
|
-
}
|
|
29226
|
-
function cleanCss(cssPath, namespace = "admin") {
|
|
29227
|
-
if (!fs43.existsSync(cssPath)) return [];
|
|
29228
|
-
const content = fs43.readFileSync(cssPath, "utf-8");
|
|
29229
|
-
const lines = content.split("\n");
|
|
29230
|
-
const sourcePattern = new RegExp(`^@source\\s+"[^"]*(?:admin|${namespace})[^"]*";\\s*$`);
|
|
29231
|
-
const removed = [];
|
|
29232
|
-
const kept = [];
|
|
29233
|
-
for (const line of lines) {
|
|
29234
|
-
if (sourcePattern.test(line)) {
|
|
29235
|
-
removed.push(line.trim());
|
|
29236
|
-
} else {
|
|
29237
|
-
kept.push(line);
|
|
29238
|
-
}
|
|
29239
|
-
}
|
|
29240
|
-
if (removed.length === 0) return [];
|
|
29241
|
-
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
29242
|
-
fs43.writeFileSync(cssPath, cleaned, "utf-8");
|
|
29243
|
-
return removed;
|
|
29244
|
-
}
|
|
29245
|
-
function cleanEnvFile(envPath) {
|
|
29246
|
-
if (!fs43.existsSync(envPath)) return [];
|
|
29247
|
-
const content = fs43.readFileSync(envPath, "utf-8");
|
|
29248
|
-
const lines = content.split("\n");
|
|
29249
|
-
const removed = [];
|
|
29250
|
-
const kept = [];
|
|
29251
|
-
const headerPattern = /^# =+$/;
|
|
29252
|
-
const headerTextPattern = /^# BetterStart Admin$/;
|
|
29253
|
-
for (let i = 0; i < lines.length; i++) {
|
|
29254
|
-
const line = lines[i];
|
|
29255
|
-
const trimmed = line.trim();
|
|
29256
|
-
if (trimmed.match(/^BETTERSTART_\w+=/)) {
|
|
29257
|
-
const key = trimmed.split("=")[0];
|
|
29258
|
-
removed.push(key);
|
|
29259
|
-
continue;
|
|
29260
|
-
}
|
|
29261
|
-
if (headerPattern.test(trimmed)) {
|
|
29262
|
-
const next = lines[i + 1]?.trim();
|
|
29263
|
-
const afterNext = lines[i + 2]?.trim();
|
|
29264
|
-
if (next && headerTextPattern.test(next) && afterNext && headerPattern.test(afterNext)) {
|
|
29265
|
-
i += 2;
|
|
29266
|
-
continue;
|
|
29267
|
-
}
|
|
29268
|
-
}
|
|
29269
|
-
if (trimmed.startsWith("#") && !headerPattern.test(trimmed)) {
|
|
29270
|
-
const nextNonEmpty = findNextNonEmptyLine(lines, i + 1);
|
|
29271
|
-
if (nextNonEmpty?.match(/^BETTERSTART_\w+=/)) {
|
|
29272
|
-
continue;
|
|
29273
|
-
}
|
|
29274
|
-
}
|
|
29275
|
-
kept.push(line);
|
|
29276
|
-
}
|
|
29277
|
-
if (removed.length === 0) return [];
|
|
29278
|
-
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
29279
|
-
if (result === "") {
|
|
29280
|
-
fs43.unlinkSync(envPath);
|
|
29281
|
-
} else {
|
|
29282
|
-
fs43.writeFileSync(envPath, `${result}
|
|
29283
|
-
`, "utf-8");
|
|
29284
|
-
}
|
|
29285
|
-
return removed;
|
|
29286
|
-
}
|
|
29287
|
-
function findNextNonEmptyLine(lines, startIndex) {
|
|
29288
|
-
for (let i = startIndex; i < lines.length; i++) {
|
|
29289
|
-
const trimmed = lines[i].trim();
|
|
29290
|
-
if (trimmed !== "") return trimmed;
|
|
29291
|
-
}
|
|
29292
|
-
return null;
|
|
29293
|
-
}
|
|
29294
|
-
|
|
29295
|
-
// adapters/next/commands/uninstall.ts
|
|
29296
|
-
function findMainCss2(cwd) {
|
|
29297
|
-
const candidates = [
|
|
29298
|
-
"src/app/globals.css",
|
|
29299
|
-
"app/globals.css",
|
|
29300
|
-
"src/app/global.css",
|
|
29301
|
-
"app/global.css",
|
|
29302
|
-
"src/app/app.css",
|
|
29303
|
-
"app/app.css",
|
|
29304
|
-
"src/globals.css",
|
|
29305
|
-
"globals.css"
|
|
29306
|
-
];
|
|
29307
|
-
for (const candidate of candidates) {
|
|
29308
|
-
const filePath = path58.join(cwd, candidate);
|
|
29309
|
-
if (fs44.existsSync(filePath)) return filePath;
|
|
29310
|
-
}
|
|
29311
|
-
return void 0;
|
|
29312
|
-
}
|
|
29313
|
-
function isCLICreatedBiome(biomePath) {
|
|
29314
|
-
if (!fs44.existsSync(biomePath)) return false;
|
|
29315
|
-
try {
|
|
29316
|
-
const content = JSON.parse(fs44.readFileSync(biomePath, "utf-8"));
|
|
29317
|
-
return content.$schema?.includes("biomejs.dev") && content.formatter?.indentStyle === "space" && content.javascript?.formatter?.quoteStyle === "single" && Array.isArray(content.files?.ignore) && content.files.ignore.includes(".next");
|
|
29318
|
-
} catch {
|
|
29319
|
-
return false;
|
|
29320
|
-
}
|
|
29321
|
-
}
|
|
29322
|
-
function buildUninstallPlan(cwd, namespaceValue) {
|
|
29323
|
-
const steps = [];
|
|
29324
|
-
const namespace = resolveAdminNamespace(namespaceValue);
|
|
29325
|
-
const hasSrc = fs44.existsSync(path58.join(cwd, "src"));
|
|
29326
|
-
const appBase = hasSrc ? "src/app" : "app";
|
|
29327
|
-
const dirs = [];
|
|
29328
|
-
const adminDir = path58.join(cwd, namespace.segment);
|
|
29329
|
-
const legacyAdminDir = path58.join(cwd, "admin");
|
|
29330
|
-
const adminRouteGroup = path58.join(cwd, appBase, namespace.routeGroup);
|
|
29331
|
-
const legacyAdminRouteGroup = path58.join(cwd, appBase, "(admin)");
|
|
29332
|
-
if (fs44.existsSync(adminDir)) dirs.push(`${namespace.segment}/`);
|
|
29333
|
-
if (namespace.segment !== "admin" && fs44.existsSync(legacyAdminDir)) dirs.push("admin/");
|
|
29334
|
-
if (fs44.existsSync(adminRouteGroup)) dirs.push(`${appBase}/${namespace.routeGroup}/`);
|
|
29335
|
-
if (namespace.segment !== "admin" && fs44.existsSync(legacyAdminRouteGroup))
|
|
29336
|
-
dirs.push(`${appBase}/(admin)/`);
|
|
29337
|
-
if (dirs.length > 0) {
|
|
29338
|
-
steps.push({
|
|
29339
|
-
label: "Admin directories",
|
|
29340
|
-
items: dirs,
|
|
29341
|
-
count: dirs.length,
|
|
29342
|
-
unit: dirs.length === 1 ? "directory" : "directories",
|
|
29343
|
-
execute() {
|
|
29344
|
-
if (fs44.existsSync(adminDir)) fs44.rmSync(adminDir, { recursive: true, force: true });
|
|
29345
|
-
if (fs44.existsSync(legacyAdminDir))
|
|
29346
|
-
fs44.rmSync(legacyAdminDir, { recursive: true, force: true });
|
|
29347
|
-
if (fs44.existsSync(adminRouteGroup)) {
|
|
29348
|
-
fs44.rmSync(adminRouteGroup, { recursive: true, force: true });
|
|
29349
|
-
}
|
|
29350
|
-
if (fs44.existsSync(legacyAdminRouteGroup)) {
|
|
29351
|
-
fs44.rmSync(legacyAdminRouteGroup, { recursive: true, force: true });
|
|
29352
|
-
}
|
|
29353
|
-
}
|
|
29354
|
-
});
|
|
29355
|
-
}
|
|
29356
|
-
const configFiles = [];
|
|
29357
|
-
const configPaths = [];
|
|
29358
|
-
const candidates = [
|
|
29359
|
-
[CONFIG_FILE_NAME, path58.join(cwd, CONFIG_FILE_NAME)],
|
|
29360
|
-
["drizzle.config.ts", path58.join(cwd, "drizzle.config.ts")],
|
|
29361
|
-
["ADMIN.md", path58.join(cwd, "ADMIN.md")]
|
|
29362
|
-
];
|
|
29363
|
-
for (const [label, fullPath] of candidates) {
|
|
29364
|
-
if (fs44.existsSync(fullPath)) {
|
|
29365
|
-
configFiles.push(label);
|
|
29366
|
-
configPaths.push(fullPath);
|
|
29367
|
-
}
|
|
29368
|
-
}
|
|
29369
|
-
const biomePath = path58.join(cwd, "biome.json");
|
|
29370
|
-
if (isCLICreatedBiome(biomePath)) {
|
|
29371
|
-
configFiles.push("biome.json (CLI-created)");
|
|
29372
|
-
configPaths.push(biomePath);
|
|
29373
|
-
}
|
|
29374
|
-
if (configFiles.length > 0) {
|
|
29375
|
-
steps.push({
|
|
29376
|
-
label: "Config files",
|
|
29377
|
-
items: configFiles,
|
|
29378
|
-
count: configFiles.length,
|
|
29379
|
-
unit: configFiles.length === 1 ? "file" : "files",
|
|
29380
|
-
execute() {
|
|
29381
|
-
for (const p32 of configPaths) {
|
|
29382
|
-
if (fs44.existsSync(p32)) fs44.unlinkSync(p32);
|
|
29383
|
-
}
|
|
29384
|
-
}
|
|
29385
|
-
});
|
|
29386
|
-
}
|
|
29387
|
-
const tsconfigPath = path58.join(cwd, "tsconfig.json");
|
|
29388
|
-
if (fs44.existsSync(tsconfigPath)) {
|
|
29389
|
-
const content = fs44.readFileSync(tsconfigPath, "utf-8");
|
|
29390
|
-
const aliasMatches = [
|
|
29391
|
-
...content.match(/"@admin\//g) ?? [],
|
|
29392
|
-
...content.match(new RegExp(`"${namespace.alias}/`, "g")) ?? []
|
|
29393
|
-
];
|
|
29394
|
-
if (aliasMatches && aliasMatches.length > 0) {
|
|
29395
|
-
const aliasCount = aliasMatches.length;
|
|
29396
|
-
steps.push({
|
|
29397
|
-
label: "tsconfig.json path aliases",
|
|
29398
|
-
items: [`${namespace.alias}/* aliases in tsconfig.json`],
|
|
29399
|
-
count: aliasCount,
|
|
29400
|
-
unit: aliasCount === 1 ? "alias" : "aliases",
|
|
29401
|
-
execute() {
|
|
29402
|
-
cleanTsconfig(tsconfigPath, namespace.alias);
|
|
29403
|
-
}
|
|
29404
|
-
});
|
|
29405
|
-
}
|
|
29406
|
-
}
|
|
29407
|
-
const cssFile = findMainCss2(cwd);
|
|
29408
|
-
if (cssFile) {
|
|
29409
|
-
const cssContent = fs44.readFileSync(cssFile, "utf-8");
|
|
29410
|
-
const sourceLines = cssContent.split("\n").filter(
|
|
29411
|
-
(l) => new RegExp(`^@source\\s+"[^"]*(?:admin|${namespace.segment})[^"]*";\\s*$`).test(l)
|
|
29412
|
-
);
|
|
29413
|
-
if (sourceLines.length > 0) {
|
|
29414
|
-
const relCss = path58.relative(cwd, cssFile);
|
|
29415
|
-
steps.push({
|
|
29416
|
-
label: `CSS @source lines (${relCss})`,
|
|
29417
|
-
items: [`@source lines in ${relCss}`],
|
|
29418
|
-
count: sourceLines.length,
|
|
29419
|
-
unit: sourceLines.length === 1 ? "line" : "lines",
|
|
29420
|
-
execute() {
|
|
29421
|
-
cleanCss(cssFile, namespace.segment);
|
|
29422
|
-
}
|
|
29423
|
-
});
|
|
29424
|
-
}
|
|
29425
|
-
}
|
|
29426
|
-
const envPath = path58.join(cwd, ".env.local");
|
|
29427
|
-
if (fs44.existsSync(envPath)) {
|
|
29428
|
-
const envContent = fs44.readFileSync(envPath, "utf-8");
|
|
29429
|
-
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
29430
|
-
if (bsVars.length > 0) {
|
|
29431
|
-
steps.push({
|
|
29432
|
-
label: ".env.local variables",
|
|
29433
|
-
items: ["BETTERSTART_* vars in .env.local"],
|
|
29434
|
-
count: bsVars.length,
|
|
29435
|
-
unit: bsVars.length === 1 ? "variable" : "variables",
|
|
29436
|
-
execute() {
|
|
29437
|
-
cleanEnvFile(envPath);
|
|
29438
|
-
}
|
|
29439
|
-
});
|
|
29440
|
-
}
|
|
29441
|
-
}
|
|
29442
|
-
return steps;
|
|
29443
|
-
}
|
|
29444
|
-
async function runUninstallCommand(options) {
|
|
29445
|
-
const cwd = options.cwd ? path58.resolve(options.cwd) : process.cwd();
|
|
29446
|
-
p30.intro(pc11.bgRed(pc11.white(" BetterStart Uninstall ")));
|
|
29447
|
-
let namespace = DEFAULT_ADMIN_NAMESPACE;
|
|
29448
|
-
try {
|
|
29449
|
-
const config = await resolveConfig(cwd);
|
|
29450
|
-
namespace = config.frameworkConfig.next.namespace;
|
|
29451
|
-
} catch {
|
|
29452
|
-
}
|
|
29453
|
-
const steps = buildUninstallPlan(cwd, namespace);
|
|
29454
|
-
if (steps.length === 0) {
|
|
29455
|
-
p30.log.success(`${pc11.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
|
|
29456
|
-
p30.outro("Project already clean");
|
|
29457
|
-
return;
|
|
29458
|
-
}
|
|
29459
|
-
const planLines = steps.map((step) => {
|
|
29460
|
-
const names = step.items.join(" ");
|
|
29461
|
-
const countLabel = pc11.dim(`${step.count} ${step.unit}`);
|
|
29462
|
-
return `${pc11.red("\u2717")} ${names} ${countLabel}`;
|
|
29463
|
-
});
|
|
29464
|
-
p30.note(planLines.join("\n"), "Uninstall plan");
|
|
29465
|
-
if (!options.force) {
|
|
29466
|
-
const confirmed = await p30.confirm({
|
|
29467
|
-
message: "Proceed with uninstall?",
|
|
29468
|
-
initialValue: false
|
|
29469
|
-
});
|
|
29470
|
-
if (p30.isCancel(confirmed) || !confirmed) {
|
|
29471
|
-
p30.cancel("Uninstall cancelled.");
|
|
29472
|
-
process.exit(0);
|
|
29473
|
-
}
|
|
29474
|
-
}
|
|
29475
|
-
const s = spinner2();
|
|
29476
|
-
s.start(steps[0].label);
|
|
29477
|
-
for (const step of steps) {
|
|
29478
|
-
s.message(step.label);
|
|
29479
|
-
step.execute();
|
|
29480
|
-
}
|
|
29481
|
-
const parts = steps.map((step) => `${step.count} ${step.unit}`);
|
|
29482
|
-
s.stop(`Removed ${parts.join(", ")}`);
|
|
29483
|
-
p30.note(pc11.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
|
|
29484
|
-
p30.outro("Uninstall complete");
|
|
29485
|
-
}
|
|
29486
29107
|
|
|
29487
29108
|
// adapters/next/commands/update-component.ts
|
|
29488
29109
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
29489
|
-
import
|
|
29490
|
-
import
|
|
29491
|
-
import * as
|
|
29110
|
+
import fs42 from "fs";
|
|
29111
|
+
import path55 from "path";
|
|
29112
|
+
import * as clack3 from "@clack/prompts";
|
|
29492
29113
|
import fsExtra from "fs-extra";
|
|
29493
29114
|
var STATIC_CUSTOM_DEPENDENCIES = {
|
|
29494
29115
|
"content-editor": [
|
|
@@ -29738,24 +29359,24 @@ function applyNamespaceToTemplateEntry(entry, config, cwd) {
|
|
|
29738
29359
|
};
|
|
29739
29360
|
}
|
|
29740
29361
|
function writeNamespacedFile(srcPath, destPath, namespace) {
|
|
29741
|
-
|
|
29362
|
+
fs42.writeFileSync(
|
|
29742
29363
|
destPath,
|
|
29743
|
-
applyAdminNamespaceToContent(
|
|
29364
|
+
applyAdminNamespaceToContent(fs42.readFileSync(srcPath, "utf-8"), namespace),
|
|
29744
29365
|
"utf-8"
|
|
29745
29366
|
);
|
|
29746
29367
|
}
|
|
29747
29368
|
function copyNamespacedDirectory(srcDir, destDir, namespace) {
|
|
29748
|
-
const entries =
|
|
29369
|
+
const entries = fs42.readdirSync(srcDir, { withFileTypes: true });
|
|
29749
29370
|
for (const entry of entries) {
|
|
29750
29371
|
const namespacedName = applyAdminNamespaceToPath(entry.name, namespace);
|
|
29751
|
-
const srcPath =
|
|
29752
|
-
const destPath =
|
|
29372
|
+
const srcPath = path55.join(srcDir, entry.name);
|
|
29373
|
+
const destPath = path55.join(destDir, namespacedName);
|
|
29753
29374
|
if (entry.isDirectory()) {
|
|
29754
29375
|
fsExtra.ensureDirSync(destPath);
|
|
29755
29376
|
copyNamespacedDirectory(srcPath, destPath, namespace);
|
|
29756
29377
|
continue;
|
|
29757
29378
|
}
|
|
29758
|
-
fsExtra.ensureDirSync(
|
|
29379
|
+
fsExtra.ensureDirSync(path55.dirname(destPath));
|
|
29759
29380
|
writeNamespacedFile(srcPath, destPath, namespace);
|
|
29760
29381
|
}
|
|
29761
29382
|
}
|
|
@@ -29766,10 +29387,10 @@ function hasIntegration(config, integrationId) {
|
|
|
29766
29387
|
return config.integrations.installed.includes(integrationId);
|
|
29767
29388
|
}
|
|
29768
29389
|
function readProjectPackageJson2(cwd) {
|
|
29769
|
-
const pkgPath =
|
|
29770
|
-
if (!
|
|
29390
|
+
const pkgPath = path55.join(cwd, "package.json");
|
|
29391
|
+
if (!fs42.existsSync(pkgPath)) return null;
|
|
29771
29392
|
try {
|
|
29772
|
-
return JSON.parse(
|
|
29393
|
+
return JSON.parse(fs42.readFileSync(pkgPath, "utf-8"));
|
|
29773
29394
|
} catch {
|
|
29774
29395
|
return null;
|
|
29775
29396
|
}
|
|
@@ -30653,13 +30274,38 @@ var TEMPLATE_REGISTRY = {
|
|
|
30653
30274
|
relPath: "app/(admin)/admin/(authenticated)/settings/forms/forms-settings-page-content.tsx",
|
|
30654
30275
|
content: () => readTemplate("pages/settings/forms/forms-settings-page-content.tsx"),
|
|
30655
30276
|
base: "cwd",
|
|
30656
|
-
dependencies: [
|
|
30277
|
+
dependencies: [
|
|
30278
|
+
"form-notifications-drawer",
|
|
30279
|
+
"forms-settings-columns",
|
|
30280
|
+
"forms-settings-table",
|
|
30281
|
+
"page-header",
|
|
30282
|
+
"use-webhooks"
|
|
30283
|
+
]
|
|
30284
|
+
},
|
|
30285
|
+
"forms-settings-columns": {
|
|
30286
|
+
relPath: "app/(admin)/admin/(authenticated)/settings/forms/forms-settings-columns.tsx",
|
|
30287
|
+
content: () => readTemplate("pages/settings/forms/forms-settings-columns.tsx"),
|
|
30288
|
+
base: "cwd"
|
|
30289
|
+
},
|
|
30290
|
+
"forms-settings-table": {
|
|
30291
|
+
relPath: "app/(admin)/admin/(authenticated)/settings/forms/forms-settings-table.tsx",
|
|
30292
|
+
content: () => readTemplate("pages/settings/forms/forms-settings-table.tsx"),
|
|
30293
|
+
base: "cwd",
|
|
30294
|
+
dependencies: ["data-grid"]
|
|
30657
30295
|
},
|
|
30658
|
-
"
|
|
30659
|
-
relPath: "app/(admin)/admin/(authenticated)/settings/forms/
|
|
30660
|
-
content: () => readTemplate("pages/settings/forms/
|
|
30296
|
+
"form-notifications-drawer": {
|
|
30297
|
+
relPath: "app/(admin)/admin/(authenticated)/settings/forms/form-notifications-drawer.tsx",
|
|
30298
|
+
content: () => readTemplate("pages/settings/forms/form-notifications-drawer.tsx"),
|
|
30661
30299
|
base: "cwd",
|
|
30662
|
-
dependencies: [
|
|
30300
|
+
dependencies: [
|
|
30301
|
+
"button",
|
|
30302
|
+
"card",
|
|
30303
|
+
"drawer",
|
|
30304
|
+
"form",
|
|
30305
|
+
"form-settings-action",
|
|
30306
|
+
"scroll-area",
|
|
30307
|
+
"textarea"
|
|
30308
|
+
]
|
|
30663
30309
|
},
|
|
30664
30310
|
"webhooks-page": {
|
|
30665
30311
|
relPath: "app/(admin)/admin/(authenticated)/settings/webhooks/page.tsx",
|
|
@@ -31277,577 +30923,1151 @@ function getStaticUiComponents() {
|
|
|
31277
30923
|
function getStaticUiComponentEntries() {
|
|
31278
30924
|
return getStaticAssetComponentEntries("ui");
|
|
31279
30925
|
}
|
|
31280
|
-
function getStaticCustomComponents() {
|
|
31281
|
-
return getStaticAssetComponents("custom");
|
|
30926
|
+
function getStaticCustomComponents() {
|
|
30927
|
+
return getStaticAssetComponents("custom");
|
|
30928
|
+
}
|
|
30929
|
+
function getStaticCustomComponentEntries() {
|
|
30930
|
+
return getStaticAssetComponentEntries("custom");
|
|
30931
|
+
}
|
|
30932
|
+
function getStaticAssetComponents(assetDirectory) {
|
|
30933
|
+
return getStaticAssetComponentEntries(assetDirectory).map((entry) => entry.name);
|
|
30934
|
+
}
|
|
30935
|
+
function getStaticAssetComponentEntries(assetDirectory) {
|
|
30936
|
+
const assetDir = resolveCliAssetPath("shared-assets", "react-admin", assetDirectory);
|
|
30937
|
+
if (!fs42.existsSync(assetDir)) return [];
|
|
30938
|
+
const components = [];
|
|
30939
|
+
for (const entry of fs42.readdirSync(assetDir, { withFileTypes: true })) {
|
|
30940
|
+
if (entry.isFile() && (entry.name.endsWith(".tsx") || entry.name.endsWith(".ts"))) {
|
|
30941
|
+
components.push({
|
|
30942
|
+
name: entry.name.replace(/\.(tsx|ts)$/, ""),
|
|
30943
|
+
file: entry.name
|
|
30944
|
+
});
|
|
30945
|
+
continue;
|
|
30946
|
+
}
|
|
30947
|
+
if (!entry.isDirectory()) {
|
|
30948
|
+
continue;
|
|
30949
|
+
}
|
|
30950
|
+
const indexFile = ["index.tsx", "index.ts"].find(
|
|
30951
|
+
(file) => fs42.existsSync(path55.join(assetDir, entry.name, file))
|
|
30952
|
+
);
|
|
30953
|
+
if (indexFile) {
|
|
30954
|
+
components.push({
|
|
30955
|
+
name: entry.name,
|
|
30956
|
+
file: path55.join(entry.name, indexFile)
|
|
30957
|
+
});
|
|
30958
|
+
}
|
|
30959
|
+
}
|
|
30960
|
+
return components.sort((a, b) => a.name.localeCompare(b.name));
|
|
30961
|
+
}
|
|
30962
|
+
function findStaticAssetFile(assetDir, componentName) {
|
|
30963
|
+
if (!fs42.existsSync(assetDir)) return void 0;
|
|
30964
|
+
const isNestedComponentName = /[\\/]/.test(componentName);
|
|
30965
|
+
const nestedComponentName = componentName.split(/[\\/]/).filter(Boolean).join(path55.sep);
|
|
30966
|
+
if (isNestedComponentName && nestedComponentName && !path55.isAbsolute(componentName) && !nestedComponentName.split(path55.sep).includes("..")) {
|
|
30967
|
+
for (const extension of [".tsx", ".ts"]) {
|
|
30968
|
+
const relPath = `${nestedComponentName}${extension}`;
|
|
30969
|
+
const filePath = path55.join(assetDir, relPath);
|
|
30970
|
+
if (fs42.existsSync(filePath) && fs42.statSync(filePath).isFile()) {
|
|
30971
|
+
return relPath;
|
|
30972
|
+
}
|
|
30973
|
+
}
|
|
30974
|
+
}
|
|
30975
|
+
if (!isNestedComponentName && !componentName.includes("..") && !path55.isAbsolute(componentName)) {
|
|
30976
|
+
for (const extension of [".tsx", ".ts"]) {
|
|
30977
|
+
const relPath = path55.join(componentName, `index${extension}`);
|
|
30978
|
+
const filePath = path55.join(assetDir, relPath);
|
|
30979
|
+
if (fs42.existsSync(filePath) && fs42.statSync(filePath).isFile()) {
|
|
30980
|
+
return relPath;
|
|
30981
|
+
}
|
|
30982
|
+
}
|
|
30983
|
+
}
|
|
30984
|
+
return fs42.readdirSync(assetDir, { withFileTypes: true }).find(
|
|
30985
|
+
(entry) => entry.isFile() && (entry.name.endsWith(".tsx") || entry.name.endsWith(".ts")) && entry.name.replace(/\.(tsx|ts)$/, "") === componentName
|
|
30986
|
+
)?.name;
|
|
31282
30987
|
}
|
|
31283
|
-
function
|
|
31284
|
-
|
|
30988
|
+
function getAllComponentNames() {
|
|
30989
|
+
const staticUi = getStaticUiComponents();
|
|
30990
|
+
const staticCustom = getStaticCustomComponents();
|
|
30991
|
+
const templateKeys = Object.keys(TEMPLATE_REGISTRY);
|
|
30992
|
+
return [.../* @__PURE__ */ new Set([...staticUi, ...staticCustom, ...templateKeys, "tiptap"])].sort();
|
|
31285
30993
|
}
|
|
31286
|
-
function
|
|
31287
|
-
|
|
30994
|
+
function getAllComponentNamesForConfig(config) {
|
|
30995
|
+
const staticUi = getStaticUiComponents();
|
|
30996
|
+
const staticCustom = getStaticCustomComponents();
|
|
30997
|
+
const templateKeys = Object.entries(TEMPLATE_REGISTRY).filter(
|
|
30998
|
+
([, entry]) => (!entry.requiredPreset || hasPreset(config, entry.requiredPreset)) && (!entry.requiredIntegration || hasIntegration(config, entry.requiredIntegration))
|
|
30999
|
+
).map(([name]) => name);
|
|
31000
|
+
return [.../* @__PURE__ */ new Set([...staticUi, ...staticCustom, ...templateKeys, "tiptap"])].sort();
|
|
31288
31001
|
}
|
|
31289
|
-
function
|
|
31290
|
-
const
|
|
31291
|
-
|
|
31292
|
-
|
|
31293
|
-
|
|
31294
|
-
|
|
31295
|
-
|
|
31296
|
-
|
|
31297
|
-
|
|
31002
|
+
async function runUpdateCommand(components, options) {
|
|
31003
|
+
const cwd = options.cwd ? path55.resolve(options.cwd) : process.cwd();
|
|
31004
|
+
const normalizedOnly = normalizeShadcnPresetOnly(options.only);
|
|
31005
|
+
validateShadcnPresetOptions(components, options);
|
|
31006
|
+
if (options.json && !options.list) {
|
|
31007
|
+
clack3.cancel("--json can only be used with --list.");
|
|
31008
|
+
process.exit(1);
|
|
31009
|
+
}
|
|
31010
|
+
if (options.list) {
|
|
31011
|
+
const uiComponents = getStaticUiComponentEntries();
|
|
31012
|
+
const customComponents = getStaticCustomComponentEntries();
|
|
31013
|
+
const templateKeys = Object.keys(TEMPLATE_REGISTRY).sort();
|
|
31014
|
+
const templatePath = (name) => {
|
|
31015
|
+
const entry = TEMPLATE_REGISTRY[name];
|
|
31016
|
+
return entry.displayPath ?? (typeof entry.relPath === "string" ? entry.relPath : "");
|
|
31017
|
+
};
|
|
31018
|
+
if (options.json) {
|
|
31019
|
+
const items = [
|
|
31020
|
+
...uiComponents.map((component) => ({
|
|
31021
|
+
name: component.name,
|
|
31022
|
+
path: `components/ui/${component.file}`,
|
|
31023
|
+
kind: "ui"
|
|
31024
|
+
})),
|
|
31025
|
+
...customComponents.map((component) => ({
|
|
31026
|
+
name: component.name,
|
|
31027
|
+
path: `components/custom/${component.file}`,
|
|
31028
|
+
kind: "custom"
|
|
31029
|
+
})),
|
|
31030
|
+
...templateKeys.map((name) => ({ name, path: templatePath(name), kind: "template" })),
|
|
31031
|
+
{ name: "tiptap", path: "components/custom/content-editor/tiptap-*/", kind: "special" }
|
|
31032
|
+
];
|
|
31033
|
+
console.log(JSON.stringify(items, null, 2));
|
|
31034
|
+
return;
|
|
31035
|
+
}
|
|
31036
|
+
const all = getAllComponentNames();
|
|
31037
|
+
clack3.intro("Available components");
|
|
31038
|
+
clack3.note(
|
|
31039
|
+
renderTableRows(
|
|
31040
|
+
uiComponents.map((component) => [component.name, `components/ui/${component.file}`])
|
|
31041
|
+
).join("\n"),
|
|
31042
|
+
`Shadcn UI Components (${uiComponents.length})`
|
|
31043
|
+
);
|
|
31044
|
+
clack3.note(
|
|
31045
|
+
renderTableRows(
|
|
31046
|
+
customComponents.map((component) => [component.name, `components/custom/${component.file}`])
|
|
31047
|
+
).join("\n"),
|
|
31048
|
+
`Custom Components (${customComponents.length})`
|
|
31049
|
+
);
|
|
31050
|
+
clack3.note(
|
|
31051
|
+
renderTableRows(templateKeys.map((name) => [name, templatePath(name)])).join("\n"),
|
|
31052
|
+
`Template Components (${templateKeys.length})`
|
|
31053
|
+
);
|
|
31054
|
+
clack3.note(
|
|
31055
|
+
renderTableRows([["tiptap", "components/custom/content-editor/tiptap-*/ (all files)"]]).join(
|
|
31056
|
+
"\n"
|
|
31057
|
+
),
|
|
31058
|
+
"Special"
|
|
31059
|
+
);
|
|
31060
|
+
clack3.outro(`${all.length} components available`);
|
|
31061
|
+
return;
|
|
31062
|
+
}
|
|
31063
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31064
|
+
const admin = path55.resolve(cwd, config.paths.admin);
|
|
31065
|
+
if (!fs42.existsSync(admin)) {
|
|
31066
|
+
clack3.cancel(
|
|
31067
|
+
`Admin directory not found at ${config.paths.admin}. Run 'betterstart init' first.`
|
|
31068
|
+
);
|
|
31069
|
+
process.exit(1);
|
|
31070
|
+
}
|
|
31071
|
+
if (options.shadcnPreset) {
|
|
31072
|
+
runShadcnPresetUpdate({
|
|
31073
|
+
cwd,
|
|
31074
|
+
config,
|
|
31075
|
+
preset: options.shadcnPreset.trim(),
|
|
31076
|
+
only: normalizedOnly
|
|
31077
|
+
});
|
|
31078
|
+
return;
|
|
31079
|
+
}
|
|
31080
|
+
if (!options.all && components.length === 0) {
|
|
31081
|
+
clack3.log.error(
|
|
31082
|
+
"Provide component names or use --all. Run with --list to see available components."
|
|
31083
|
+
);
|
|
31084
|
+
process.exit(1);
|
|
31085
|
+
}
|
|
31086
|
+
clack3.intro("BetterStart Update Components");
|
|
31087
|
+
const toUpdate = options.all ? getAllComponentNamesForConfig(config) : components;
|
|
31088
|
+
const uiDir = resolveCliAssetPath("shared-assets", "react-admin", "ui");
|
|
31089
|
+
const customDir = resolveCliAssetPath("shared-assets", "react-admin", "custom");
|
|
31090
|
+
let updated = 0;
|
|
31091
|
+
let skipped = 0;
|
|
31092
|
+
const updatedTemplateNames = /* @__PURE__ */ new Set();
|
|
31093
|
+
const updatedStaticNames = /* @__PURE__ */ new Set();
|
|
31094
|
+
const requiredPackageDependencies = /* @__PURE__ */ new Set();
|
|
31095
|
+
const pendingWrites = [];
|
|
31096
|
+
function trackPackageDependencies(name) {
|
|
31097
|
+
for (const dependency of COMPONENT_PACKAGE_DEPENDENCIES[name] ?? []) {
|
|
31098
|
+
requiredPackageDependencies.add(dependency);
|
|
31099
|
+
}
|
|
31100
|
+
}
|
|
31101
|
+
function writeTemplateEntry(name, entry) {
|
|
31102
|
+
if (updatedTemplateNames.has(name)) {
|
|
31103
|
+
return false;
|
|
31104
|
+
}
|
|
31105
|
+
if (entry.requiredPreset && !hasPreset(config, entry.requiredPreset)) {
|
|
31106
|
+
clack3.log.warn(`${name} requires the ${entry.requiredPreset} preset and was skipped.`);
|
|
31107
|
+
skipped++;
|
|
31108
|
+
return false;
|
|
31109
|
+
}
|
|
31110
|
+
if (entry.requiredIntegration && !hasIntegration(config, entry.requiredIntegration)) {
|
|
31111
|
+
clack3.log.warn(
|
|
31112
|
+
`${name} requires the ${entry.requiredIntegration} integration and was skipped.`
|
|
31113
|
+
);
|
|
31114
|
+
skipped++;
|
|
31115
|
+
return false;
|
|
31116
|
+
}
|
|
31117
|
+
const { relPath, content } = applyNamespaceToTemplateEntry(entry, config, cwd);
|
|
31118
|
+
const baseDir = entry.base === "cwd" ? cwd : admin;
|
|
31119
|
+
const destPath = path55.join(baseDir, relPath);
|
|
31120
|
+
if (entry.preserveExisting && fs42.existsSync(destPath)) {
|
|
31121
|
+
clack3.log.info(`Preserved ${relPath}`);
|
|
31122
|
+
updatedTemplateNames.add(name);
|
|
31123
|
+
skipped++;
|
|
31124
|
+
return false;
|
|
31125
|
+
}
|
|
31126
|
+
pendingWrites.push({
|
|
31127
|
+
displayPath: relPath,
|
|
31128
|
+
write: () => {
|
|
31129
|
+
fsExtra.ensureDirSync(path55.dirname(destPath));
|
|
31130
|
+
fs42.writeFileSync(destPath, content, "utf-8");
|
|
31131
|
+
clack3.log.success(`Updated ${relPath}`);
|
|
31132
|
+
}
|
|
31133
|
+
});
|
|
31134
|
+
updatedTemplateNames.add(name);
|
|
31135
|
+
trackPackageDependencies(name);
|
|
31136
|
+
updated++;
|
|
31137
|
+
return true;
|
|
31138
|
+
}
|
|
31139
|
+
function writeTemplateEntryWithDependencies(name, entry) {
|
|
31140
|
+
const wroteEntry = writeTemplateEntry(name, entry);
|
|
31141
|
+
if (!wroteEntry) {
|
|
31142
|
+
return false;
|
|
31143
|
+
}
|
|
31144
|
+
for (const dependencyName of entry.dependencies ?? []) {
|
|
31145
|
+
writeNamedDependency(dependencyName);
|
|
31146
|
+
}
|
|
31147
|
+
return true;
|
|
31148
|
+
}
|
|
31149
|
+
function writeStaticAssetEntry(assetDirectory, name) {
|
|
31150
|
+
const key = `${assetDirectory}:${name}`;
|
|
31151
|
+
if (updatedStaticNames.has(key)) {
|
|
31152
|
+
return false;
|
|
31153
|
+
}
|
|
31154
|
+
const assetDir = assetDirectory === "ui" ? uiDir : customDir;
|
|
31155
|
+
const assetFile = findStaticAssetFile(assetDir, name);
|
|
31156
|
+
if (!assetFile) {
|
|
31157
|
+
return false;
|
|
31158
|
+
}
|
|
31159
|
+
const namespace = config.frameworkConfig.next.namespace;
|
|
31160
|
+
const namespacedAssetFile = applyAdminNamespaceToPath(assetFile, namespace);
|
|
31161
|
+
const destPath = path55.join(admin, "components", assetDirectory, namespacedAssetFile);
|
|
31162
|
+
pendingWrites.push({
|
|
31163
|
+
displayPath: `components/${assetDirectory}/${namespacedAssetFile}`,
|
|
31164
|
+
write: () => {
|
|
31165
|
+
fsExtra.ensureDirSync(path55.dirname(destPath));
|
|
31166
|
+
writeNamespacedFile(path55.join(assetDir, assetFile), destPath, namespace);
|
|
31167
|
+
clack3.log.success(`Updated components/${assetDirectory}/${namespacedAssetFile}`);
|
|
31168
|
+
}
|
|
31169
|
+
});
|
|
31170
|
+
if (assetDirectory === "custom") {
|
|
31171
|
+
const assetSubdir = path55.join(assetDir, name);
|
|
31172
|
+
if (fs42.existsSync(assetSubdir) && fs42.statSync(assetSubdir).isDirectory()) {
|
|
31173
|
+
const namespacedName = applyAdminNamespaceToPath(name, namespace);
|
|
31174
|
+
const destSubdir = path55.join(admin, "components", assetDirectory, namespacedName);
|
|
31175
|
+
pendingWrites.push({
|
|
31176
|
+
displayPath: `components/${assetDirectory}/${namespacedName}/ (template files)`,
|
|
31177
|
+
write: () => {
|
|
31178
|
+
fsExtra.ensureDirSync(destSubdir);
|
|
31179
|
+
copyNamespacedDirectory(assetSubdir, destSubdir, namespace);
|
|
31180
|
+
clack3.log.success(
|
|
31181
|
+
`Updated components/${assetDirectory}/${namespacedName}/ (template files)`
|
|
31182
|
+
);
|
|
31183
|
+
}
|
|
31184
|
+
});
|
|
31185
|
+
}
|
|
31186
|
+
}
|
|
31187
|
+
updatedStaticNames.add(key);
|
|
31188
|
+
trackPackageDependencies(name);
|
|
31189
|
+
updated++;
|
|
31190
|
+
if (assetDirectory === "custom") {
|
|
31191
|
+
for (const dependencyName of STATIC_CUSTOM_DEPENDENCIES[name] ?? []) {
|
|
31192
|
+
writeNamedDependency(dependencyName);
|
|
31193
|
+
}
|
|
31194
|
+
}
|
|
31195
|
+
return true;
|
|
31196
|
+
}
|
|
31197
|
+
function writeTiptapTemplates() {
|
|
31198
|
+
const key = "custom:tiptap";
|
|
31199
|
+
if (updatedStaticNames.has(key)) {
|
|
31200
|
+
return true;
|
|
31201
|
+
}
|
|
31202
|
+
const srcBaseDir = resolveCliAssetPath(
|
|
31203
|
+
"shared-assets",
|
|
31204
|
+
"react-admin",
|
|
31205
|
+
"custom",
|
|
31206
|
+
"content-editor"
|
|
31207
|
+
);
|
|
31208
|
+
const namespace = config.frameworkConfig.next.namespace;
|
|
31209
|
+
const destBaseDir = path55.join(admin, "components", "custom", "content-editor");
|
|
31210
|
+
if (!fs42.existsSync(srcBaseDir)) {
|
|
31211
|
+
return false;
|
|
31212
|
+
}
|
|
31213
|
+
const dirsToCopy = [];
|
|
31214
|
+
for (const directory of TIPTAP_CONTENT_EDITOR_DIRECTORIES) {
|
|
31215
|
+
const srcDir = path55.join(srcBaseDir, directory);
|
|
31216
|
+
if (!fs42.existsSync(srcDir)) {
|
|
31217
|
+
continue;
|
|
31218
|
+
}
|
|
31219
|
+
dirsToCopy.push({
|
|
31220
|
+
srcDir,
|
|
31221
|
+
destDir: path55.join(destBaseDir, applyAdminNamespaceToPath(directory, namespace))
|
|
31298
31222
|
});
|
|
31223
|
+
}
|
|
31224
|
+
if (dirsToCopy.length === 0) {
|
|
31225
|
+
return false;
|
|
31226
|
+
}
|
|
31227
|
+
pendingWrites.push({
|
|
31228
|
+
displayPath: "components/custom/content-editor/tiptap-*/ (template files)",
|
|
31229
|
+
write: () => {
|
|
31230
|
+
for (const { srcDir, destDir } of dirsToCopy) {
|
|
31231
|
+
fsExtra.ensureDirSync(destDir);
|
|
31232
|
+
copyNamespacedDirectory(srcDir, destDir, namespace);
|
|
31233
|
+
}
|
|
31234
|
+
clack3.log.success("Updated components/custom/content-editor/tiptap-*/ (template files)");
|
|
31235
|
+
removeLegacyDirectory(path55.join(admin, "components", "custom", "tiptap"));
|
|
31236
|
+
}
|
|
31237
|
+
});
|
|
31238
|
+
updatedStaticNames.add(key);
|
|
31239
|
+
trackPackageDependencies("tiptap");
|
|
31240
|
+
updated++;
|
|
31241
|
+
for (const dependencyName of TIPTAP_TEMPLATE_DEPENDENCIES) {
|
|
31242
|
+
writeNamedDependency(dependencyName);
|
|
31243
|
+
}
|
|
31244
|
+
return true;
|
|
31245
|
+
}
|
|
31246
|
+
function writeNamedDependency(name) {
|
|
31247
|
+
const entry = TEMPLATE_REGISTRY[name];
|
|
31248
|
+
if (entry) {
|
|
31249
|
+
writeTemplateEntryWithDependencies(name, entry);
|
|
31250
|
+
return;
|
|
31251
|
+
}
|
|
31252
|
+
if (writeStaticAssetEntry("ui", name)) {
|
|
31253
|
+
return;
|
|
31254
|
+
}
|
|
31255
|
+
if (writeStaticAssetEntry("custom", name)) {
|
|
31256
|
+
return;
|
|
31257
|
+
}
|
|
31258
|
+
if (name === "tiptap") {
|
|
31259
|
+
writeTiptapTemplates();
|
|
31260
|
+
}
|
|
31261
|
+
}
|
|
31262
|
+
for (const name of toUpdate) {
|
|
31263
|
+
if (TEMPLATE_REGISTRY[name]) {
|
|
31264
|
+
const entry = TEMPLATE_REGISTRY[name];
|
|
31265
|
+
writeTemplateEntryWithDependencies(name, entry);
|
|
31299
31266
|
continue;
|
|
31300
31267
|
}
|
|
31301
|
-
if (
|
|
31268
|
+
if (writeStaticAssetEntry("ui", name)) {
|
|
31302
31269
|
continue;
|
|
31303
31270
|
}
|
|
31304
|
-
|
|
31305
|
-
|
|
31306
|
-
);
|
|
31307
|
-
if (indexFile) {
|
|
31308
|
-
components.push({
|
|
31309
|
-
name: entry.name,
|
|
31310
|
-
file: path59.join(entry.name, indexFile)
|
|
31311
|
-
});
|
|
31271
|
+
if (writeStaticAssetEntry("custom", name)) {
|
|
31272
|
+
continue;
|
|
31312
31273
|
}
|
|
31313
|
-
|
|
31314
|
-
|
|
31315
|
-
|
|
31316
|
-
|
|
31317
|
-
if (!fs45.existsSync(assetDir)) return void 0;
|
|
31318
|
-
const isNestedComponentName = /[\\/]/.test(componentName);
|
|
31319
|
-
const nestedComponentName = componentName.split(/[\\/]/).filter(Boolean).join(path59.sep);
|
|
31320
|
-
if (isNestedComponentName && nestedComponentName && !path59.isAbsolute(componentName) && !nestedComponentName.split(path59.sep).includes("..")) {
|
|
31321
|
-
for (const extension of [".tsx", ".ts"]) {
|
|
31322
|
-
const relPath = `${nestedComponentName}${extension}`;
|
|
31323
|
-
const filePath = path59.join(assetDir, relPath);
|
|
31324
|
-
if (fs45.existsSync(filePath) && fs45.statSync(filePath).isFile()) {
|
|
31325
|
-
return relPath;
|
|
31274
|
+
if (name === "tiptap") {
|
|
31275
|
+
if (!writeTiptapTemplates()) {
|
|
31276
|
+
clack3.log.warn("tiptap templates not found");
|
|
31277
|
+
skipped++;
|
|
31326
31278
|
}
|
|
31279
|
+
continue;
|
|
31327
31280
|
}
|
|
31281
|
+
clack3.log.warn(`Unknown component: ${name}`);
|
|
31282
|
+
skipped++;
|
|
31328
31283
|
}
|
|
31329
|
-
if (
|
|
31330
|
-
|
|
31331
|
-
|
|
31332
|
-
|
|
31333
|
-
|
|
31334
|
-
|
|
31284
|
+
if (pendingWrites.length > 0) {
|
|
31285
|
+
const displayPaths = pendingWrites.map((entry) => entry.displayPath);
|
|
31286
|
+
const preview = displayPaths.slice(0, MAX_OVERWRITE_PREVIEW_ROWS);
|
|
31287
|
+
if (displayPaths.length > preview.length) {
|
|
31288
|
+
preview.push(`\u2026 and ${displayPaths.length - preview.length} more`);
|
|
31289
|
+
}
|
|
31290
|
+
clack3.note(
|
|
31291
|
+
preview.join("\n"),
|
|
31292
|
+
`Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"}`
|
|
31293
|
+
);
|
|
31294
|
+
if (!options.yes) {
|
|
31295
|
+
if (!isInteractiveSession()) {
|
|
31296
|
+
clack3.log.error(
|
|
31297
|
+
`Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"} needs confirmation. Re-run with --yes.`
|
|
31298
|
+
);
|
|
31299
|
+
process.exit(1);
|
|
31300
|
+
}
|
|
31301
|
+
const proceed = await clack3.confirm({
|
|
31302
|
+
message: "Overwrite these files with the latest templates?",
|
|
31303
|
+
initialValue: true
|
|
31304
|
+
});
|
|
31305
|
+
if (clack3.isCancel(proceed) || !proceed) {
|
|
31306
|
+
clack3.cancel("Update cancelled.");
|
|
31307
|
+
process.exit(0);
|
|
31335
31308
|
}
|
|
31336
31309
|
}
|
|
31310
|
+
for (const entry of pendingWrites) {
|
|
31311
|
+
entry.write();
|
|
31312
|
+
}
|
|
31337
31313
|
}
|
|
31338
|
-
|
|
31339
|
-
|
|
31340
|
-
)
|
|
31341
|
-
|
|
31342
|
-
|
|
31343
|
-
|
|
31344
|
-
|
|
31345
|
-
|
|
31346
|
-
|
|
31314
|
+
syncInstalledPresetManifests(cwd, config);
|
|
31315
|
+
syncInstalledIntegrationManifests(cwd, config);
|
|
31316
|
+
const projectPackageJson = readProjectPackageJson2(cwd);
|
|
31317
|
+
const missingPackageDependencies = Array.from(requiredPackageDependencies).filter(
|
|
31318
|
+
(dependency) => !hasDeclaredPackage2(projectPackageJson, dependency)
|
|
31319
|
+
);
|
|
31320
|
+
if (missingPackageDependencies.length > 0) {
|
|
31321
|
+
clack3.log.warn(
|
|
31322
|
+
`Updated components require package dependencies that are not declared: ${missingPackageDependencies.join(", ")}. Run 'pnpm betterstart update-deps' before building.`
|
|
31323
|
+
);
|
|
31324
|
+
}
|
|
31325
|
+
clack3.outro(
|
|
31326
|
+
`Updated ${updated} component${updated !== 1 ? "s" : ""}${skipped > 0 ? `, ${skipped} skipped` : ""}`
|
|
31327
|
+
);
|
|
31347
31328
|
}
|
|
31348
|
-
function
|
|
31349
|
-
|
|
31350
|
-
|
|
31351
|
-
|
|
31352
|
-
([, entry]) => (!entry.requiredPreset || hasPreset(config, entry.requiredPreset)) && (!entry.requiredIntegration || hasIntegration(config, entry.requiredIntegration))
|
|
31353
|
-
).map(([name]) => name);
|
|
31354
|
-
return [.../* @__PURE__ */ new Set([...staticUi, ...staticCustom, ...templateKeys, "tiptap"])].sort();
|
|
31329
|
+
function removeLegacyDirectory(dirPath) {
|
|
31330
|
+
if (fs42.existsSync(dirPath)) {
|
|
31331
|
+
fs42.rmSync(dirPath, { recursive: true, force: true });
|
|
31332
|
+
}
|
|
31355
31333
|
}
|
|
31356
|
-
|
|
31357
|
-
|
|
31358
|
-
|
|
31359
|
-
validateShadcnPresetOptions(components, options);
|
|
31360
|
-
if (options.json && !options.list) {
|
|
31361
|
-
clack4.cancel("--json can only be used with --list.");
|
|
31334
|
+
function validateShadcnPresetOptions(components, options) {
|
|
31335
|
+
if (options.only && !options.shadcnPreset) {
|
|
31336
|
+
clack3.cancel("--only can only be used with --shadcn-preset.");
|
|
31362
31337
|
process.exit(1);
|
|
31363
31338
|
}
|
|
31339
|
+
if (!options.shadcnPreset) {
|
|
31340
|
+
return;
|
|
31341
|
+
}
|
|
31364
31342
|
if (options.list) {
|
|
31365
|
-
|
|
31366
|
-
|
|
31367
|
-
|
|
31368
|
-
|
|
31369
|
-
|
|
31370
|
-
|
|
31371
|
-
|
|
31372
|
-
|
|
31373
|
-
|
|
31374
|
-
|
|
31375
|
-
|
|
31376
|
-
|
|
31377
|
-
|
|
31378
|
-
|
|
31379
|
-
|
|
31380
|
-
|
|
31381
|
-
|
|
31382
|
-
|
|
31383
|
-
|
|
31384
|
-
|
|
31385
|
-
{ name: "tiptap", path: "components/custom/content-editor/tiptap-*/", kind: "special" }
|
|
31386
|
-
];
|
|
31387
|
-
console.log(JSON.stringify(items, null, 2));
|
|
31388
|
-
return;
|
|
31389
|
-
}
|
|
31390
|
-
const all = getAllComponentNames();
|
|
31391
|
-
clack4.intro("Available components");
|
|
31392
|
-
clack4.note(
|
|
31393
|
-
renderTableRows(
|
|
31394
|
-
uiComponents.map((component) => [component.name, `components/ui/${component.file}`])
|
|
31395
|
-
).join("\n"),
|
|
31396
|
-
`Shadcn UI Components (${uiComponents.length})`
|
|
31343
|
+
clack3.cancel("--list cannot be combined with --shadcn-preset.");
|
|
31344
|
+
process.exit(1);
|
|
31345
|
+
}
|
|
31346
|
+
if (options.all) {
|
|
31347
|
+
clack3.cancel("--all cannot be combined with --shadcn-preset.");
|
|
31348
|
+
process.exit(1);
|
|
31349
|
+
}
|
|
31350
|
+
if (components.length > 0) {
|
|
31351
|
+
clack3.cancel("Component names cannot be combined with --shadcn-preset.");
|
|
31352
|
+
process.exit(1);
|
|
31353
|
+
}
|
|
31354
|
+
}
|
|
31355
|
+
function normalizeShadcnPresetOnly(value) {
|
|
31356
|
+
if (!value) {
|
|
31357
|
+
return void 0;
|
|
31358
|
+
}
|
|
31359
|
+
const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
|
|
31360
|
+
if (parts.length !== 1 || parts[0] !== "theme") {
|
|
31361
|
+
clack3.cancel(
|
|
31362
|
+
"Admin-only shadcn preset updates currently support --only theme. Omit --only to apply the full preset."
|
|
31397
31363
|
);
|
|
31398
|
-
|
|
31399
|
-
|
|
31400
|
-
|
|
31401
|
-
|
|
31402
|
-
|
|
31364
|
+
process.exit(1);
|
|
31365
|
+
}
|
|
31366
|
+
return "theme";
|
|
31367
|
+
}
|
|
31368
|
+
function runShadcnPresetUpdate({
|
|
31369
|
+
cwd,
|
|
31370
|
+
config,
|
|
31371
|
+
preset,
|
|
31372
|
+
only
|
|
31373
|
+
}) {
|
|
31374
|
+
const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
|
|
31375
|
+
const adminGlobalsPath = path55.join(cwd, config.paths.admin, namespace.globalsFile);
|
|
31376
|
+
const componentsJsonPath = path55.join(cwd, "components.json");
|
|
31377
|
+
const shadcnBackupPath = `${componentsJsonPath}.bak`;
|
|
31378
|
+
const restoreAfterApplyPaths = [
|
|
31379
|
+
componentsJsonPath,
|
|
31380
|
+
shadcnBackupPath,
|
|
31381
|
+
path55.join(cwd, config.paths.admin, "lib", "utils.ts"),
|
|
31382
|
+
...getHostProjectFilesToRestore(cwd)
|
|
31383
|
+
];
|
|
31384
|
+
if (!preset) {
|
|
31385
|
+
clack3.cancel("--shadcn-preset requires a preset code, preset name, or preset URL.");
|
|
31386
|
+
process.exit(1);
|
|
31387
|
+
}
|
|
31388
|
+
if (!fs42.existsSync(adminGlobalsPath)) {
|
|
31389
|
+
clack3.cancel(
|
|
31390
|
+
`Admin globals file not found at ${path55.relative(cwd, adminGlobalsPath)}. Run 'betterstart update admin-globals' first.`
|
|
31403
31391
|
);
|
|
31404
|
-
|
|
31405
|
-
|
|
31406
|
-
|
|
31392
|
+
process.exit(1);
|
|
31393
|
+
}
|
|
31394
|
+
const shadcnBin = resolveLocalShadcnBin(cwd);
|
|
31395
|
+
const restoreSnapshots = restoreAfterApplyPaths.map((filePath) => ({
|
|
31396
|
+
filePath,
|
|
31397
|
+
snapshot: snapshotFile(filePath)
|
|
31398
|
+
}));
|
|
31399
|
+
clack3.intro("BetterStart Shadcn Preset");
|
|
31400
|
+
clack3.log.info(`Applying preset to ${path55.join(config.paths.admin, "components/ui")}`);
|
|
31401
|
+
let failed = false;
|
|
31402
|
+
try {
|
|
31403
|
+
fs42.writeFileSync(
|
|
31404
|
+
componentsJsonPath,
|
|
31405
|
+
`${JSON.stringify(createAdminShadcnComponentsJson(config), null, 2)}
|
|
31406
|
+
`,
|
|
31407
|
+
"utf-8"
|
|
31407
31408
|
);
|
|
31408
|
-
|
|
31409
|
-
|
|
31410
|
-
|
|
31411
|
-
|
|
31412
|
-
|
|
31409
|
+
const args = ["apply", "--preset", preset, "--cwd", cwd, "--yes"];
|
|
31410
|
+
if (only) {
|
|
31411
|
+
args.push("--only", only);
|
|
31412
|
+
}
|
|
31413
|
+
execFileSync5(shadcnBin, args, { cwd, stdio: "inherit" });
|
|
31414
|
+
} catch {
|
|
31415
|
+
failed = true;
|
|
31416
|
+
} finally {
|
|
31417
|
+
for (const { filePath, snapshot } of restoreSnapshots.reverse()) {
|
|
31418
|
+
restoreFile(filePath, snapshot);
|
|
31419
|
+
}
|
|
31420
|
+
}
|
|
31421
|
+
if (failed) {
|
|
31422
|
+
clack3.cancel("shadcn preset application failed.");
|
|
31423
|
+
process.exit(1);
|
|
31424
|
+
}
|
|
31425
|
+
clack3.outro(
|
|
31426
|
+
only ? `Applied shadcn preset parts (${only}) to the Admin.` : "Applied shadcn preset to Admin UI components and styles."
|
|
31427
|
+
);
|
|
31428
|
+
}
|
|
31429
|
+
function createAdminShadcnComponentsJson(config) {
|
|
31430
|
+
const adminPath = toPosixPath(config.paths.admin);
|
|
31431
|
+
const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
|
|
31432
|
+
return {
|
|
31433
|
+
$schema: "https://ui.shadcn.com/schema.json",
|
|
31434
|
+
style: "new-york",
|
|
31435
|
+
rsc: true,
|
|
31436
|
+
tsx: true,
|
|
31437
|
+
tailwind: {
|
|
31438
|
+
config: "",
|
|
31439
|
+
css: `${adminPath}/${namespace.globalsFile}`,
|
|
31440
|
+
baseColor: "neutral",
|
|
31441
|
+
cssVariables: true,
|
|
31442
|
+
prefix: ""
|
|
31443
|
+
},
|
|
31444
|
+
iconLibrary: "lucide",
|
|
31445
|
+
aliases: {
|
|
31446
|
+
components: `${namespace.alias}/components`,
|
|
31447
|
+
ui: `${namespace.alias}/components/ui`,
|
|
31448
|
+
hooks: `${namespace.alias}/hooks`,
|
|
31449
|
+
lib: `${namespace.alias}/lib`,
|
|
31450
|
+
utils: `${namespace.alias}/utils/shared/cn`
|
|
31451
|
+
}
|
|
31452
|
+
};
|
|
31453
|
+
}
|
|
31454
|
+
function toPosixPath(value) {
|
|
31455
|
+
return value.replace(/\\/g, "/");
|
|
31456
|
+
}
|
|
31457
|
+
function resolveLocalShadcnBin(cwd) {
|
|
31458
|
+
const binName = process.platform === "win32" ? "shadcn.cmd" : "shadcn";
|
|
31459
|
+
const shadcnBin = path55.join(cwd, "node_modules", ".bin", binName);
|
|
31460
|
+
if (!fs42.existsSync(shadcnBin)) {
|
|
31461
|
+
clack3.cancel(
|
|
31462
|
+
`shadcn is not installed in this project. Run 'betterstart update-deps' and try again.`
|
|
31413
31463
|
);
|
|
31414
|
-
|
|
31464
|
+
process.exit(1);
|
|
31465
|
+
}
|
|
31466
|
+
return shadcnBin;
|
|
31467
|
+
}
|
|
31468
|
+
function getHostProjectFilesToRestore(cwd) {
|
|
31469
|
+
const hostRelativePaths = [
|
|
31470
|
+
"app/layout.tsx",
|
|
31471
|
+
"app/layout.ts",
|
|
31472
|
+
"app/layout.jsx",
|
|
31473
|
+
"app/layout.js",
|
|
31474
|
+
"src/app/layout.tsx",
|
|
31475
|
+
"src/app/layout.ts",
|
|
31476
|
+
"src/app/layout.jsx",
|
|
31477
|
+
"src/app/layout.js",
|
|
31478
|
+
"app/globals.css",
|
|
31479
|
+
"src/app/globals.css"
|
|
31480
|
+
];
|
|
31481
|
+
return hostRelativePaths.map((relativePath) => path55.join(cwd, relativePath));
|
|
31482
|
+
}
|
|
31483
|
+
function snapshotFile(filePath) {
|
|
31484
|
+
if (!fs42.existsSync(filePath)) {
|
|
31485
|
+
return { existed: false };
|
|
31486
|
+
}
|
|
31487
|
+
return { existed: true, content: fs42.readFileSync(filePath, "utf-8") };
|
|
31488
|
+
}
|
|
31489
|
+
function restoreFile(filePath, snapshot) {
|
|
31490
|
+
if (snapshot.existed) {
|
|
31491
|
+
fs42.writeFileSync(filePath, snapshot.content ?? "", "utf-8");
|
|
31415
31492
|
return;
|
|
31416
31493
|
}
|
|
31494
|
+
if (fs42.existsSync(filePath)) {
|
|
31495
|
+
fs42.rmSync(filePath, { force: true });
|
|
31496
|
+
}
|
|
31497
|
+
}
|
|
31498
|
+
|
|
31499
|
+
// adapters/next/commands/menu-choices.ts
|
|
31500
|
+
async function listInstallableChoices(cwd) {
|
|
31417
31501
|
const config = await resolveConfigOrExit(cwd);
|
|
31418
|
-
const
|
|
31419
|
-
|
|
31420
|
-
|
|
31421
|
-
|
|
31502
|
+
const installedPresets = new Set(config.presets.installed);
|
|
31503
|
+
const installedIntegrations = new Set(config.integrations.installed);
|
|
31504
|
+
return {
|
|
31505
|
+
presets: listAvailablePresets().map((preset) => ({
|
|
31506
|
+
id: preset.id,
|
|
31507
|
+
description: preset.description,
|
|
31508
|
+
installed: installedPresets.has(preset.id)
|
|
31509
|
+
})),
|
|
31510
|
+
integrations: listAvailableIntegrations().map((integration) => ({
|
|
31511
|
+
id: integration.id,
|
|
31512
|
+
description: integration.description,
|
|
31513
|
+
installed: installedIntegrations.has(integration.id)
|
|
31514
|
+
}))
|
|
31515
|
+
};
|
|
31516
|
+
}
|
|
31517
|
+
async function listSchemaChoices(cwd) {
|
|
31518
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31519
|
+
const paths = resolveProjectPaths(config);
|
|
31520
|
+
return listSchemaNames(path56.join(cwd, ...paths.schemasDir.split("/")));
|
|
31521
|
+
}
|
|
31522
|
+
async function listComponentChoices(cwd) {
|
|
31523
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31524
|
+
return getAllComponentNamesForConfig(config);
|
|
31525
|
+
}
|
|
31526
|
+
|
|
31527
|
+
// adapters/next/commands/remove.ts
|
|
31528
|
+
import path57 from "path";
|
|
31529
|
+
import * as p29 from "@clack/prompts";
|
|
31530
|
+
async function runRemoveCommand(items, options) {
|
|
31531
|
+
const removeIntegrationsMode = Boolean(options.integration);
|
|
31532
|
+
if (!removeIntegrationsMode && items.includes("core")) {
|
|
31533
|
+
p29.log.error("The core Admin cannot be removed.");
|
|
31534
|
+
process.exit(1);
|
|
31535
|
+
}
|
|
31536
|
+
const presetIds = items.filter(isPresetId);
|
|
31537
|
+
const integrationIds = items.filter(isIntegrationId);
|
|
31538
|
+
if (!removeIntegrationsMode && integrationIds.length > 0) {
|
|
31539
|
+
p29.log.error(
|
|
31540
|
+
`Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
|
|
31422
31541
|
);
|
|
31423
31542
|
process.exit(1);
|
|
31424
31543
|
}
|
|
31425
|
-
if (
|
|
31426
|
-
|
|
31427
|
-
|
|
31428
|
-
config,
|
|
31429
|
-
preset: options.shadcnPreset.trim(),
|
|
31430
|
-
only: normalizedOnly
|
|
31431
|
-
});
|
|
31432
|
-
return;
|
|
31544
|
+
if (removeIntegrationsMode && presetIds.length > 0) {
|
|
31545
|
+
p29.log.error(`Preset IDs cannot be removed with --integration: ${presetIds.join(", ")}`);
|
|
31546
|
+
process.exit(1);
|
|
31433
31547
|
}
|
|
31434
|
-
|
|
31435
|
-
|
|
31436
|
-
|
|
31548
|
+
const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((presetId) => !isPresetId(presetId));
|
|
31549
|
+
if (invalidItems.length > 0) {
|
|
31550
|
+
p29.log.error(
|
|
31551
|
+
removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPresetMessage(invalidItems)
|
|
31437
31552
|
);
|
|
31438
31553
|
process.exit(1);
|
|
31439
31554
|
}
|
|
31440
|
-
|
|
31441
|
-
const
|
|
31442
|
-
const
|
|
31443
|
-
|
|
31444
|
-
|
|
31445
|
-
|
|
31446
|
-
|
|
31447
|
-
const updatedStaticNames = /* @__PURE__ */ new Set();
|
|
31448
|
-
const requiredPackageDependencies = /* @__PURE__ */ new Set();
|
|
31449
|
-
const pendingWrites = [];
|
|
31450
|
-
function trackPackageDependencies(name) {
|
|
31451
|
-
for (const dependency of COMPONENT_PACKAGE_DEPENDENCIES[name] ?? []) {
|
|
31452
|
-
requiredPackageDependencies.add(dependency);
|
|
31453
|
-
}
|
|
31454
|
-
}
|
|
31455
|
-
function writeTemplateEntry(name, entry) {
|
|
31456
|
-
if (updatedTemplateNames.has(name)) {
|
|
31457
|
-
return false;
|
|
31458
|
-
}
|
|
31459
|
-
if (entry.requiredPreset && !hasPreset(config, entry.requiredPreset)) {
|
|
31460
|
-
clack4.log.warn(`${name} requires the ${entry.requiredPreset} preset and was skipped.`);
|
|
31461
|
-
skipped++;
|
|
31462
|
-
return false;
|
|
31463
|
-
}
|
|
31464
|
-
if (entry.requiredIntegration && !hasIntegration(config, entry.requiredIntegration)) {
|
|
31465
|
-
clack4.log.warn(
|
|
31466
|
-
`${name} requires the ${entry.requiredIntegration} integration and was skipped.`
|
|
31467
|
-
);
|
|
31468
|
-
skipped++;
|
|
31469
|
-
return false;
|
|
31470
|
-
}
|
|
31471
|
-
const { relPath, content } = applyNamespaceToTemplateEntry(entry, config, cwd);
|
|
31472
|
-
const baseDir = entry.base === "cwd" ? cwd : admin;
|
|
31473
|
-
const destPath = path59.join(baseDir, relPath);
|
|
31474
|
-
if (entry.preserveExisting && fs45.existsSync(destPath)) {
|
|
31475
|
-
clack4.log.info(`Preserved ${relPath}`);
|
|
31476
|
-
updatedTemplateNames.add(name);
|
|
31477
|
-
skipped++;
|
|
31478
|
-
return false;
|
|
31479
|
-
}
|
|
31480
|
-
pendingWrites.push({
|
|
31481
|
-
displayPath: relPath,
|
|
31482
|
-
write: () => {
|
|
31483
|
-
fsExtra.ensureDirSync(path59.dirname(destPath));
|
|
31484
|
-
fs45.writeFileSync(destPath, content, "utf-8");
|
|
31485
|
-
clack4.log.success(`Updated ${relPath}`);
|
|
31486
|
-
}
|
|
31555
|
+
const cwd = options.cwd ? path57.resolve(options.cwd) : process.cwd();
|
|
31556
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31557
|
+
const pm = detectPackageManager(cwd);
|
|
31558
|
+
if (!options.force) {
|
|
31559
|
+
const confirmed = await p29.confirm({
|
|
31560
|
+
message: `Remove ${removeIntegrationsMode ? "integration" : "preset"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
|
|
31561
|
+
initialValue: false
|
|
31487
31562
|
});
|
|
31488
|
-
|
|
31489
|
-
|
|
31490
|
-
|
|
31491
|
-
return true;
|
|
31492
|
-
}
|
|
31493
|
-
function writeTemplateEntryWithDependencies(name, entry) {
|
|
31494
|
-
const wroteEntry = writeTemplateEntry(name, entry);
|
|
31495
|
-
if (!wroteEntry) {
|
|
31496
|
-
return false;
|
|
31497
|
-
}
|
|
31498
|
-
for (const dependencyName of entry.dependencies ?? []) {
|
|
31499
|
-
writeNamedDependency(dependencyName);
|
|
31563
|
+
if (p29.isCancel(confirmed) || !confirmed) {
|
|
31564
|
+
p29.cancel(`${removeIntegrationsMode ? "Integration" : "Preset"} removal cancelled.`);
|
|
31565
|
+
process.exit(0);
|
|
31500
31566
|
}
|
|
31501
|
-
return true;
|
|
31502
31567
|
}
|
|
31503
|
-
|
|
31504
|
-
const
|
|
31505
|
-
|
|
31506
|
-
|
|
31507
|
-
|
|
31508
|
-
|
|
31509
|
-
const assetFile = findStaticAssetFile(assetDir, name);
|
|
31510
|
-
if (!assetFile) {
|
|
31511
|
-
return false;
|
|
31512
|
-
}
|
|
31513
|
-
const namespace = config.frameworkConfig.next.namespace;
|
|
31514
|
-
const namespacedAssetFile = applyAdminNamespaceToPath(assetFile, namespace);
|
|
31515
|
-
const destPath = path59.join(admin, "components", assetDirectory, namespacedAssetFile);
|
|
31516
|
-
pendingWrites.push({
|
|
31517
|
-
displayPath: `components/${assetDirectory}/${namespacedAssetFile}`,
|
|
31518
|
-
write: () => {
|
|
31519
|
-
fsExtra.ensureDirSync(path59.dirname(destPath));
|
|
31520
|
-
writeNamespacedFile(path59.join(assetDir, assetFile), destPath, namespace);
|
|
31521
|
-
clack4.log.success(`Updated components/${assetDirectory}/${namespacedAssetFile}`);
|
|
31522
|
-
}
|
|
31568
|
+
if (removeIntegrationsMode) {
|
|
31569
|
+
const result2 = await removeIntegrations({
|
|
31570
|
+
cwd,
|
|
31571
|
+
config,
|
|
31572
|
+
pm,
|
|
31573
|
+
integrationIds
|
|
31523
31574
|
});
|
|
31524
|
-
|
|
31525
|
-
|
|
31526
|
-
|
|
31527
|
-
|
|
31528
|
-
const destSubdir = path59.join(admin, "components", assetDirectory, namespacedName);
|
|
31529
|
-
pendingWrites.push({
|
|
31530
|
-
displayPath: `components/${assetDirectory}/${namespacedName}/ (template files)`,
|
|
31531
|
-
write: () => {
|
|
31532
|
-
fsExtra.ensureDirSync(destSubdir);
|
|
31533
|
-
copyNamespacedDirectory(assetSubdir, destSubdir, namespace);
|
|
31534
|
-
clack4.log.success(
|
|
31535
|
-
`Updated components/${assetDirectory}/${namespacedName}/ (template files)`
|
|
31536
|
-
);
|
|
31537
|
-
}
|
|
31538
|
-
});
|
|
31539
|
-
}
|
|
31540
|
-
}
|
|
31541
|
-
updatedStaticNames.add(key);
|
|
31542
|
-
trackPackageDependencies(name);
|
|
31543
|
-
updated++;
|
|
31544
|
-
if (assetDirectory === "custom") {
|
|
31545
|
-
for (const dependencyName of STATIC_CUSTOM_DEPENDENCIES[name] ?? []) {
|
|
31546
|
-
writeNamedDependency(dependencyName);
|
|
31547
|
-
}
|
|
31575
|
+
writeConfigFile(cwd, result2.config);
|
|
31576
|
+
if (result2.removed.length === 0) {
|
|
31577
|
+
p29.outro("No integrations were removed.");
|
|
31578
|
+
return;
|
|
31548
31579
|
}
|
|
31549
|
-
|
|
31550
|
-
|
|
31551
|
-
function writeTiptapTemplates() {
|
|
31552
|
-
const key = "custom:tiptap";
|
|
31553
|
-
if (updatedStaticNames.has(key)) {
|
|
31554
|
-
return true;
|
|
31580
|
+
if (result2.warnings.length > 0) {
|
|
31581
|
+
p29.note(result2.warnings.join("\n"), "Warnings");
|
|
31555
31582
|
}
|
|
31556
|
-
|
|
31557
|
-
"
|
|
31558
|
-
"react-admin",
|
|
31559
|
-
"custom",
|
|
31560
|
-
"content-editor"
|
|
31583
|
+
p29.outro(
|
|
31584
|
+
`Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
|
|
31561
31585
|
);
|
|
31562
|
-
|
|
31563
|
-
|
|
31564
|
-
|
|
31565
|
-
|
|
31566
|
-
|
|
31567
|
-
|
|
31568
|
-
|
|
31569
|
-
|
|
31570
|
-
|
|
31586
|
+
return;
|
|
31587
|
+
}
|
|
31588
|
+
const result = await removePresets({
|
|
31589
|
+
cwd,
|
|
31590
|
+
config,
|
|
31591
|
+
pm,
|
|
31592
|
+
presetIds
|
|
31593
|
+
});
|
|
31594
|
+
writeConfigFile(cwd, result.config);
|
|
31595
|
+
if (result.removed.length === 0) {
|
|
31596
|
+
p29.outro("No presets were removed.");
|
|
31597
|
+
return;
|
|
31598
|
+
}
|
|
31599
|
+
if (result.warnings.length > 0) {
|
|
31600
|
+
p29.note(result.warnings.join("\n"), "Warnings");
|
|
31601
|
+
}
|
|
31602
|
+
p29.outro(`Removed preset${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
|
|
31603
|
+
}
|
|
31604
|
+
|
|
31605
|
+
// adapters/next/commands/remove-schema.ts
|
|
31606
|
+
import fs43 from "fs";
|
|
31607
|
+
import path58 from "path";
|
|
31608
|
+
import * as clack4 from "@clack/prompts";
|
|
31609
|
+
function removePath2(cwd, filePath) {
|
|
31610
|
+
const fullPath = path58.join(cwd, ...filePath.split("/"));
|
|
31611
|
+
const existed = fs43.existsSync(fullPath);
|
|
31612
|
+
fs43.rmSync(fullPath, { recursive: true, force: true });
|
|
31613
|
+
return existed;
|
|
31614
|
+
}
|
|
31615
|
+
function cleanupEmptyDirs3(cwd, deletedPaths, configPaths) {
|
|
31616
|
+
const stopRoots = /* @__PURE__ */ new Set([
|
|
31617
|
+
path58.join(cwd, ...configPaths.adminDir.split("/")),
|
|
31618
|
+
path58.join(cwd, ...configPaths.adminNavigationDir.split("/")),
|
|
31619
|
+
path58.join(cwd, ...configPaths.adminWebhookEventsDir.split("/")),
|
|
31620
|
+
path58.join(cwd, ...configPaths.pagesDir.split("/"))
|
|
31621
|
+
]);
|
|
31622
|
+
for (const deletedPath of deletedPaths) {
|
|
31623
|
+
let current = path58.dirname(path58.join(cwd, ...deletedPath.split("/")));
|
|
31624
|
+
while (!stopRoots.has(current)) {
|
|
31625
|
+
if (!fs43.existsSync(current)) {
|
|
31626
|
+
current = path58.dirname(current);
|
|
31571
31627
|
continue;
|
|
31572
31628
|
}
|
|
31573
|
-
|
|
31574
|
-
|
|
31575
|
-
|
|
31576
|
-
}
|
|
31577
|
-
|
|
31578
|
-
|
|
31579
|
-
return false;
|
|
31580
|
-
}
|
|
31581
|
-
pendingWrites.push({
|
|
31582
|
-
displayPath: "components/custom/content-editor/tiptap-*/ (template files)",
|
|
31583
|
-
write: () => {
|
|
31584
|
-
for (const { srcDir, destDir } of dirsToCopy) {
|
|
31585
|
-
fsExtra.ensureDirSync(destDir);
|
|
31586
|
-
copyNamespacedDirectory(srcDir, destDir, namespace);
|
|
31587
|
-
}
|
|
31588
|
-
clack4.log.success("Updated components/custom/content-editor/tiptap-*/ (template files)");
|
|
31589
|
-
removeLegacyDirectory(path59.join(admin, "components", "custom", "tiptap"));
|
|
31590
|
-
}
|
|
31591
|
-
});
|
|
31592
|
-
updatedStaticNames.add(key);
|
|
31593
|
-
trackPackageDependencies("tiptap");
|
|
31594
|
-
updated++;
|
|
31595
|
-
for (const dependencyName of TIPTAP_TEMPLATE_DEPENDENCIES) {
|
|
31596
|
-
writeNamedDependency(dependencyName);
|
|
31597
|
-
}
|
|
31598
|
-
return true;
|
|
31599
|
-
}
|
|
31600
|
-
function writeNamedDependency(name) {
|
|
31601
|
-
const entry = TEMPLATE_REGISTRY[name];
|
|
31602
|
-
if (entry) {
|
|
31603
|
-
writeTemplateEntryWithDependencies(name, entry);
|
|
31604
|
-
return;
|
|
31629
|
+
const entries = fs43.readdirSync(current);
|
|
31630
|
+
if (entries.length > 0) {
|
|
31631
|
+
break;
|
|
31632
|
+
}
|
|
31633
|
+
fs43.rmdirSync(current);
|
|
31634
|
+
current = path58.dirname(current);
|
|
31605
31635
|
}
|
|
31606
|
-
|
|
31607
|
-
|
|
31636
|
+
}
|
|
31637
|
+
}
|
|
31638
|
+
function resolveSchemaOwnerForRemoval(cwd, schemaName) {
|
|
31639
|
+
const explicitOwner = getSchemaOwner(cwd, schemaName);
|
|
31640
|
+
if (explicitOwner) {
|
|
31641
|
+
return explicitOwner;
|
|
31642
|
+
}
|
|
31643
|
+
if (schemaName === "settings") {
|
|
31644
|
+
return "core";
|
|
31645
|
+
}
|
|
31646
|
+
return "user";
|
|
31647
|
+
}
|
|
31648
|
+
async function runRemoveSchemaCommand(schemaName, options) {
|
|
31649
|
+
const owner = resolveSchemaOwnerForRemoval(
|
|
31650
|
+
options.cwd ? path58.resolve(options.cwd) : process.cwd(),
|
|
31651
|
+
schemaName
|
|
31652
|
+
);
|
|
31653
|
+
if (owner === "core") {
|
|
31654
|
+
clack4.log.error(
|
|
31655
|
+
`"${schemaName}" is a core Admin module and cannot be removed with remove-schema.`
|
|
31656
|
+
);
|
|
31657
|
+
process.exit(1);
|
|
31658
|
+
}
|
|
31659
|
+
if (owner.startsWith("preset:")) {
|
|
31660
|
+
clack4.log.error(`"${schemaName}" is owned by ${owner}. Remove the owning preset instead.`);
|
|
31661
|
+
process.exit(1);
|
|
31662
|
+
}
|
|
31663
|
+
const cwd = options.cwd ? path58.resolve(options.cwd) : process.cwd();
|
|
31664
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31665
|
+
const paths = resolveProjectPaths(config);
|
|
31666
|
+
const manifest = loadManifest(cwd, schemaName);
|
|
31667
|
+
if (!snapshotRootExists(cwd)) {
|
|
31668
|
+
clack4.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
31669
|
+
process.exit(1);
|
|
31670
|
+
}
|
|
31671
|
+
if (!manifest) {
|
|
31672
|
+
clack4.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
31673
|
+
process.exit(1);
|
|
31674
|
+
}
|
|
31675
|
+
if (!options.force) {
|
|
31676
|
+
if (!isInteractiveSession()) {
|
|
31677
|
+
clack4.log.error(
|
|
31678
|
+
`Removing generated files for ${schemaName} needs confirmation. Re-run with --force.`
|
|
31679
|
+
);
|
|
31680
|
+
process.exit(1);
|
|
31608
31681
|
}
|
|
31609
|
-
|
|
31682
|
+
const confirmed = await clack4.confirm({
|
|
31683
|
+
message: `Remove generated files for ${schemaName}?`,
|
|
31684
|
+
initialValue: false
|
|
31685
|
+
});
|
|
31686
|
+
if (clack4.isCancel(confirmed) || !confirmed) {
|
|
31687
|
+
clack4.cancel("Cancelled.");
|
|
31610
31688
|
return;
|
|
31611
31689
|
}
|
|
31612
|
-
if (name === "tiptap") {
|
|
31613
|
-
writeTiptapTemplates();
|
|
31614
|
-
}
|
|
31615
31690
|
}
|
|
31616
|
-
|
|
31617
|
-
|
|
31618
|
-
|
|
31619
|
-
|
|
31620
|
-
continue;
|
|
31621
|
-
}
|
|
31622
|
-
if (writeStaticAssetEntry("ui", name)) {
|
|
31623
|
-
continue;
|
|
31691
|
+
const deletedPaths = [];
|
|
31692
|
+
for (const file of [...manifest.files.map((entry) => entry.path), ...manifest.skipped]) {
|
|
31693
|
+
if (removePath2(cwd, file)) {
|
|
31694
|
+
deletedPaths.push(file);
|
|
31624
31695
|
}
|
|
31625
|
-
|
|
31626
|
-
|
|
31696
|
+
}
|
|
31697
|
+
const loaded = (() => {
|
|
31698
|
+
try {
|
|
31699
|
+
return loadSchema(path58.join(cwd, ...paths.schemasDir.split("/")), schemaName);
|
|
31700
|
+
} catch {
|
|
31701
|
+
return null;
|
|
31627
31702
|
}
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
}
|
|
31633
|
-
continue;
|
|
31703
|
+
})();
|
|
31704
|
+
const kebabName = toKebabCase(schemaName);
|
|
31705
|
+
if (loaded?.type === "form") {
|
|
31706
|
+
if (removePath2(cwd, `${paths.adminActionsDir}/${schemaName}`)) {
|
|
31707
|
+
deletedPaths.push(`${paths.adminActionsDir}/${schemaName}`);
|
|
31634
31708
|
}
|
|
31635
|
-
|
|
31636
|
-
|
|
31637
|
-
}
|
|
31638
|
-
if (pendingWrites.length > 0) {
|
|
31639
|
-
const displayPaths = pendingWrites.map((entry) => entry.displayPath);
|
|
31640
|
-
const preview = displayPaths.slice(0, MAX_OVERWRITE_PREVIEW_ROWS);
|
|
31641
|
-
if (displayPaths.length > preview.length) {
|
|
31642
|
-
preview.push(`\u2026 and ${displayPaths.length - preview.length} more`);
|
|
31709
|
+
if (removePath2(cwd, `${paths.pagesDir}/forms/${kebabName}`)) {
|
|
31710
|
+
deletedPaths.push(`${paths.pagesDir}/forms/${kebabName}`);
|
|
31643
31711
|
}
|
|
31644
|
-
|
|
31645
|
-
|
|
31646
|
-
|
|
31647
|
-
);
|
|
31648
|
-
if (!options.yes) {
|
|
31649
|
-
if (!isInteractiveSession()) {
|
|
31650
|
-
clack4.log.error(
|
|
31651
|
-
`Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"} needs confirmation. Re-run with --yes.`
|
|
31652
|
-
);
|
|
31653
|
-
process.exit(1);
|
|
31654
|
-
}
|
|
31655
|
-
const proceed = await clack4.confirm({
|
|
31656
|
-
message: "Overwrite these files with the latest templates?",
|
|
31657
|
-
initialValue: true
|
|
31658
|
-
});
|
|
31659
|
-
if (clack4.isCancel(proceed) || !proceed) {
|
|
31660
|
-
clack4.cancel("Update cancelled.");
|
|
31661
|
-
process.exit(0);
|
|
31662
|
-
}
|
|
31712
|
+
} else {
|
|
31713
|
+
if (removePath2(cwd, `${paths.adminActionsDir}/${schemaName}`)) {
|
|
31714
|
+
deletedPaths.push(`${paths.adminActionsDir}/${schemaName}`);
|
|
31663
31715
|
}
|
|
31664
|
-
|
|
31665
|
-
|
|
31716
|
+
if (removePath2(cwd, `${paths.pagesDir}/${schemaName}`)) {
|
|
31717
|
+
deletedPaths.push(`${paths.pagesDir}/${schemaName}`);
|
|
31666
31718
|
}
|
|
31667
31719
|
}
|
|
31668
|
-
|
|
31669
|
-
|
|
31670
|
-
|
|
31671
|
-
|
|
31672
|
-
(dependency) => !hasDeclaredPackage2(projectPackageJson, dependency)
|
|
31673
|
-
);
|
|
31674
|
-
if (missingPackageDependencies.length > 0) {
|
|
31675
|
-
clack4.log.warn(
|
|
31676
|
-
`Updated components require package dependencies that are not declared: ${missingPackageDependencies.join(", ")}. Run 'pnpm betterstart update-deps' before building.`
|
|
31677
|
-
);
|
|
31720
|
+
cleanupEmptyDirs3(cwd, deletedPaths, paths);
|
|
31721
|
+
deleteSnapshot(cwd, schemaName);
|
|
31722
|
+
if (hasTombstone(cwd, schemaName)) {
|
|
31723
|
+
clearTombstone(cwd, schemaName);
|
|
31678
31724
|
}
|
|
31679
|
-
|
|
31680
|
-
|
|
31725
|
+
writeTombstone(cwd, schemaName);
|
|
31726
|
+
await applyGeneratedFiles({
|
|
31727
|
+
cwd,
|
|
31728
|
+
config,
|
|
31729
|
+
scope: BARREL_SCOPE,
|
|
31730
|
+
schemaJson: { name: BARREL_SCOPE },
|
|
31731
|
+
generatedFiles: renderBarrelFiles(cwd, config),
|
|
31732
|
+
force: false,
|
|
31733
|
+
interactive: false
|
|
31734
|
+
});
|
|
31735
|
+
clack4.log.info(
|
|
31736
|
+
`Tombstone written: .betterstart/snapshots/_removed/${schemaName}
|
|
31737
|
+
Schema JSON preserved.`
|
|
31681
31738
|
);
|
|
31739
|
+
clack4.outro(`Removed generated files for ${schemaName}`);
|
|
31682
31740
|
}
|
|
31683
|
-
|
|
31684
|
-
|
|
31685
|
-
|
|
31741
|
+
|
|
31742
|
+
// adapters/next/commands/uninstall.ts
|
|
31743
|
+
import fs45 from "fs";
|
|
31744
|
+
import path59 from "path";
|
|
31745
|
+
import * as p30 from "@clack/prompts";
|
|
31746
|
+
import pc11 from "picocolors";
|
|
31747
|
+
|
|
31748
|
+
// adapters/next/commands/uninstall-cleaners.ts
|
|
31749
|
+
import fs44 from "fs";
|
|
31750
|
+
function stripJsonComments2(input) {
|
|
31751
|
+
let result = "";
|
|
31752
|
+
let i = 0;
|
|
31753
|
+
while (i < input.length) {
|
|
31754
|
+
if (input[i] === '"') {
|
|
31755
|
+
let j = i + 1;
|
|
31756
|
+
while (j < input.length) {
|
|
31757
|
+
if (input[j] === "\\") {
|
|
31758
|
+
j += 2;
|
|
31759
|
+
continue;
|
|
31760
|
+
}
|
|
31761
|
+
if (input[j] === '"') {
|
|
31762
|
+
j++;
|
|
31763
|
+
break;
|
|
31764
|
+
}
|
|
31765
|
+
j++;
|
|
31766
|
+
}
|
|
31767
|
+
result += input.slice(i, j);
|
|
31768
|
+
i = j;
|
|
31769
|
+
} else if (input[i] === "/" && input[i + 1] === "/") {
|
|
31770
|
+
const nl = input.indexOf("\n", i);
|
|
31771
|
+
i = nl === -1 ? input.length : nl;
|
|
31772
|
+
} else if (input[i] === "/" && input[i + 1] === "*") {
|
|
31773
|
+
const end = input.indexOf("*/", i + 2);
|
|
31774
|
+
i = end === -1 ? input.length : end + 2;
|
|
31775
|
+
} else {
|
|
31776
|
+
result += input[i];
|
|
31777
|
+
i++;
|
|
31778
|
+
}
|
|
31686
31779
|
}
|
|
31780
|
+
return result;
|
|
31687
31781
|
}
|
|
31688
|
-
function
|
|
31689
|
-
if (
|
|
31690
|
-
|
|
31691
|
-
|
|
31782
|
+
function cleanTsconfig(tsconfigPath, aliasRoot = "@admin") {
|
|
31783
|
+
if (!fs44.existsSync(tsconfigPath)) return [];
|
|
31784
|
+
const raw = fs44.readFileSync(tsconfigPath, "utf-8");
|
|
31785
|
+
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
31786
|
+
let tsconfig;
|
|
31787
|
+
try {
|
|
31788
|
+
tsconfig = JSON.parse(stripped);
|
|
31789
|
+
} catch {
|
|
31790
|
+
return [];
|
|
31692
31791
|
}
|
|
31693
|
-
|
|
31694
|
-
|
|
31792
|
+
const compilerOptions = tsconfig.compilerOptions ?? {};
|
|
31793
|
+
const paths = compilerOptions.paths ?? {};
|
|
31794
|
+
const removed = [];
|
|
31795
|
+
for (const key of Object.keys(paths)) {
|
|
31796
|
+
if (key.startsWith("@admin/") || key === "@admin/*" || key.startsWith(`${aliasRoot}/`) || key === `${aliasRoot}/*`) {
|
|
31797
|
+
removed.push(key);
|
|
31798
|
+
delete paths[key];
|
|
31799
|
+
}
|
|
31695
31800
|
}
|
|
31696
|
-
if (
|
|
31697
|
-
|
|
31698
|
-
|
|
31801
|
+
if (removed.length === 0) return [];
|
|
31802
|
+
if (Object.keys(paths).length === 0) {
|
|
31803
|
+
compilerOptions.paths = void 0;
|
|
31804
|
+
} else {
|
|
31805
|
+
compilerOptions.paths = paths;
|
|
31699
31806
|
}
|
|
31700
|
-
|
|
31701
|
-
|
|
31702
|
-
|
|
31807
|
+
tsconfig.compilerOptions = compilerOptions;
|
|
31808
|
+
fs44.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
31809
|
+
`, "utf-8");
|
|
31810
|
+
return removed;
|
|
31811
|
+
}
|
|
31812
|
+
function cleanCss(cssPath, namespace = "admin") {
|
|
31813
|
+
if (!fs44.existsSync(cssPath)) return [];
|
|
31814
|
+
const content = fs44.readFileSync(cssPath, "utf-8");
|
|
31815
|
+
const lines = content.split("\n");
|
|
31816
|
+
const sourcePattern = new RegExp(`^@source\\s+"[^"]*(?:admin|${namespace})[^"]*";\\s*$`);
|
|
31817
|
+
const removed = [];
|
|
31818
|
+
const kept = [];
|
|
31819
|
+
for (const line of lines) {
|
|
31820
|
+
if (sourcePattern.test(line)) {
|
|
31821
|
+
removed.push(line.trim());
|
|
31822
|
+
} else {
|
|
31823
|
+
kept.push(line);
|
|
31824
|
+
}
|
|
31703
31825
|
}
|
|
31704
|
-
if (
|
|
31705
|
-
|
|
31706
|
-
|
|
31826
|
+
if (removed.length === 0) return [];
|
|
31827
|
+
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
31828
|
+
fs44.writeFileSync(cssPath, cleaned, "utf-8");
|
|
31829
|
+
return removed;
|
|
31830
|
+
}
|
|
31831
|
+
function cleanEnvFile(envPath) {
|
|
31832
|
+
if (!fs44.existsSync(envPath)) return [];
|
|
31833
|
+
const content = fs44.readFileSync(envPath, "utf-8");
|
|
31834
|
+
const lines = content.split("\n");
|
|
31835
|
+
const removed = [];
|
|
31836
|
+
const kept = [];
|
|
31837
|
+
const headerPattern = /^# =+$/;
|
|
31838
|
+
const headerTextPattern = /^# BetterStart Admin$/;
|
|
31839
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31840
|
+
const line = lines[i];
|
|
31841
|
+
const trimmed = line.trim();
|
|
31842
|
+
if (trimmed.match(/^BETTERSTART_\w+=/)) {
|
|
31843
|
+
const key = trimmed.split("=")[0];
|
|
31844
|
+
removed.push(key);
|
|
31845
|
+
continue;
|
|
31846
|
+
}
|
|
31847
|
+
if (headerPattern.test(trimmed)) {
|
|
31848
|
+
const next = lines[i + 1]?.trim();
|
|
31849
|
+
const afterNext = lines[i + 2]?.trim();
|
|
31850
|
+
if (next && headerTextPattern.test(next) && afterNext && headerPattern.test(afterNext)) {
|
|
31851
|
+
i += 2;
|
|
31852
|
+
continue;
|
|
31853
|
+
}
|
|
31854
|
+
}
|
|
31855
|
+
if (trimmed.startsWith("#") && !headerPattern.test(trimmed)) {
|
|
31856
|
+
const nextNonEmpty = findNextNonEmptyLine(lines, i + 1);
|
|
31857
|
+
if (nextNonEmpty?.match(/^BETTERSTART_\w+=/)) {
|
|
31858
|
+
continue;
|
|
31859
|
+
}
|
|
31860
|
+
}
|
|
31861
|
+
kept.push(line);
|
|
31862
|
+
}
|
|
31863
|
+
if (removed.length === 0) return [];
|
|
31864
|
+
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
31865
|
+
if (result === "") {
|
|
31866
|
+
fs44.unlinkSync(envPath);
|
|
31867
|
+
} else {
|
|
31868
|
+
fs44.writeFileSync(envPath, `${result}
|
|
31869
|
+
`, "utf-8");
|
|
31707
31870
|
}
|
|
31871
|
+
return removed;
|
|
31708
31872
|
}
|
|
31709
|
-
function
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
31713
|
-
const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
|
|
31714
|
-
if (parts.length !== 1 || parts[0] !== "theme") {
|
|
31715
|
-
clack4.cancel(
|
|
31716
|
-
"Admin-only shadcn preset updates currently support --only theme. Omit --only to apply the full preset."
|
|
31717
|
-
);
|
|
31718
|
-
process.exit(1);
|
|
31873
|
+
function findNextNonEmptyLine(lines, startIndex) {
|
|
31874
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
31875
|
+
const trimmed = lines[i].trim();
|
|
31876
|
+
if (trimmed !== "") return trimmed;
|
|
31719
31877
|
}
|
|
31720
|
-
return
|
|
31878
|
+
return null;
|
|
31721
31879
|
}
|
|
31722
|
-
|
|
31723
|
-
|
|
31724
|
-
|
|
31725
|
-
|
|
31726
|
-
|
|
31727
|
-
|
|
31728
|
-
|
|
31729
|
-
|
|
31730
|
-
|
|
31731
|
-
|
|
31732
|
-
|
|
31733
|
-
|
|
31734
|
-
shadcnBackupPath,
|
|
31735
|
-
path59.join(cwd, config.paths.admin, "lib", "utils.ts"),
|
|
31736
|
-
...getHostProjectFilesToRestore(cwd)
|
|
31880
|
+
|
|
31881
|
+
// adapters/next/commands/uninstall.ts
|
|
31882
|
+
function findMainCss2(cwd) {
|
|
31883
|
+
const candidates = [
|
|
31884
|
+
"src/app/globals.css",
|
|
31885
|
+
"app/globals.css",
|
|
31886
|
+
"src/app/global.css",
|
|
31887
|
+
"app/global.css",
|
|
31888
|
+
"src/app/app.css",
|
|
31889
|
+
"app/app.css",
|
|
31890
|
+
"src/globals.css",
|
|
31891
|
+
"globals.css"
|
|
31737
31892
|
];
|
|
31738
|
-
|
|
31739
|
-
|
|
31740
|
-
|
|
31741
|
-
}
|
|
31742
|
-
if (!fs45.existsSync(adminGlobalsPath)) {
|
|
31743
|
-
clack4.cancel(
|
|
31744
|
-
`Admin globals file not found at ${path59.relative(cwd, adminGlobalsPath)}. Run 'betterstart update admin-globals' first.`
|
|
31745
|
-
);
|
|
31746
|
-
process.exit(1);
|
|
31893
|
+
for (const candidate of candidates) {
|
|
31894
|
+
const filePath = path59.join(cwd, candidate);
|
|
31895
|
+
if (fs45.existsSync(filePath)) return filePath;
|
|
31747
31896
|
}
|
|
31748
|
-
|
|
31749
|
-
|
|
31750
|
-
|
|
31751
|
-
|
|
31752
|
-
}));
|
|
31753
|
-
clack4.intro("BetterStart Shadcn Preset");
|
|
31754
|
-
clack4.log.info(`Applying preset to ${path59.join(config.paths.admin, "components/ui")}`);
|
|
31755
|
-
let failed = false;
|
|
31897
|
+
return void 0;
|
|
31898
|
+
}
|
|
31899
|
+
function isCLICreatedBiome(biomePath) {
|
|
31900
|
+
if (!fs45.existsSync(biomePath)) return false;
|
|
31756
31901
|
try {
|
|
31757
|
-
fs45.
|
|
31758
|
-
|
|
31759
|
-
`${JSON.stringify(createAdminShadcnComponentsJson(config), null, 2)}
|
|
31760
|
-
`,
|
|
31761
|
-
"utf-8"
|
|
31762
|
-
);
|
|
31763
|
-
const args = ["apply", "--preset", preset, "--cwd", cwd, "--yes"];
|
|
31764
|
-
if (only) {
|
|
31765
|
-
args.push("--only", only);
|
|
31766
|
-
}
|
|
31767
|
-
execFileSync5(shadcnBin, args, { cwd, stdio: "inherit" });
|
|
31902
|
+
const content = JSON.parse(fs45.readFileSync(biomePath, "utf-8"));
|
|
31903
|
+
return content.$schema?.includes("biomejs.dev") && content.formatter?.indentStyle === "space" && content.javascript?.formatter?.quoteStyle === "single" && Array.isArray(content.files?.ignore) && content.files.ignore.includes(".next");
|
|
31768
31904
|
} catch {
|
|
31769
|
-
|
|
31770
|
-
}
|
|
31771
|
-
|
|
31772
|
-
|
|
31905
|
+
return false;
|
|
31906
|
+
}
|
|
31907
|
+
}
|
|
31908
|
+
function buildUninstallPlan(cwd, namespaceValue) {
|
|
31909
|
+
const steps = [];
|
|
31910
|
+
const namespace = resolveAdminNamespace(namespaceValue);
|
|
31911
|
+
const hasSrc = fs45.existsSync(path59.join(cwd, "src"));
|
|
31912
|
+
const appBase = hasSrc ? "src/app" : "app";
|
|
31913
|
+
const dirs = [];
|
|
31914
|
+
const adminDir = path59.join(cwd, namespace.segment);
|
|
31915
|
+
const legacyAdminDir = path59.join(cwd, "admin");
|
|
31916
|
+
const adminRouteGroup = path59.join(cwd, appBase, namespace.routeGroup);
|
|
31917
|
+
const legacyAdminRouteGroup = path59.join(cwd, appBase, "(admin)");
|
|
31918
|
+
if (fs45.existsSync(adminDir)) dirs.push(`${namespace.segment}/`);
|
|
31919
|
+
if (namespace.segment !== "admin" && fs45.existsSync(legacyAdminDir)) dirs.push("admin/");
|
|
31920
|
+
if (fs45.existsSync(adminRouteGroup)) dirs.push(`${appBase}/${namespace.routeGroup}/`);
|
|
31921
|
+
if (namespace.segment !== "admin" && fs45.existsSync(legacyAdminRouteGroup))
|
|
31922
|
+
dirs.push(`${appBase}/(admin)/`);
|
|
31923
|
+
if (dirs.length > 0) {
|
|
31924
|
+
steps.push({
|
|
31925
|
+
label: "Admin directories",
|
|
31926
|
+
items: dirs,
|
|
31927
|
+
count: dirs.length,
|
|
31928
|
+
unit: dirs.length === 1 ? "directory" : "directories",
|
|
31929
|
+
execute() {
|
|
31930
|
+
if (fs45.existsSync(adminDir)) fs45.rmSync(adminDir, { recursive: true, force: true });
|
|
31931
|
+
if (fs45.existsSync(legacyAdminDir))
|
|
31932
|
+
fs45.rmSync(legacyAdminDir, { recursive: true, force: true });
|
|
31933
|
+
if (fs45.existsSync(adminRouteGroup)) {
|
|
31934
|
+
fs45.rmSync(adminRouteGroup, { recursive: true, force: true });
|
|
31935
|
+
}
|
|
31936
|
+
if (fs45.existsSync(legacyAdminRouteGroup)) {
|
|
31937
|
+
fs45.rmSync(legacyAdminRouteGroup, { recursive: true, force: true });
|
|
31938
|
+
}
|
|
31939
|
+
}
|
|
31940
|
+
});
|
|
31941
|
+
}
|
|
31942
|
+
const configFiles = [];
|
|
31943
|
+
const configPaths = [];
|
|
31944
|
+
const candidates = [
|
|
31945
|
+
[CONFIG_FILE_NAME, path59.join(cwd, CONFIG_FILE_NAME)],
|
|
31946
|
+
["drizzle.config.ts", path59.join(cwd, "drizzle.config.ts")],
|
|
31947
|
+
["ADMIN.md", path59.join(cwd, "ADMIN.md")]
|
|
31948
|
+
];
|
|
31949
|
+
for (const [label, fullPath] of candidates) {
|
|
31950
|
+
if (fs45.existsSync(fullPath)) {
|
|
31951
|
+
configFiles.push(label);
|
|
31952
|
+
configPaths.push(fullPath);
|
|
31773
31953
|
}
|
|
31774
31954
|
}
|
|
31775
|
-
|
|
31776
|
-
|
|
31777
|
-
|
|
31955
|
+
const biomePath = path59.join(cwd, "biome.json");
|
|
31956
|
+
if (isCLICreatedBiome(biomePath)) {
|
|
31957
|
+
configFiles.push("biome.json (CLI-created)");
|
|
31958
|
+
configPaths.push(biomePath);
|
|
31778
31959
|
}
|
|
31779
|
-
|
|
31780
|
-
|
|
31781
|
-
|
|
31782
|
-
|
|
31783
|
-
|
|
31784
|
-
|
|
31785
|
-
|
|
31786
|
-
|
|
31787
|
-
|
|
31788
|
-
|
|
31789
|
-
|
|
31790
|
-
|
|
31791
|
-
|
|
31792
|
-
|
|
31793
|
-
|
|
31794
|
-
|
|
31795
|
-
|
|
31796
|
-
|
|
31797
|
-
|
|
31798
|
-
|
|
31799
|
-
|
|
31800
|
-
|
|
31801
|
-
|
|
31802
|
-
|
|
31803
|
-
|
|
31804
|
-
|
|
31960
|
+
if (configFiles.length > 0) {
|
|
31961
|
+
steps.push({
|
|
31962
|
+
label: "Config files",
|
|
31963
|
+
items: configFiles,
|
|
31964
|
+
count: configFiles.length,
|
|
31965
|
+
unit: configFiles.length === 1 ? "file" : "files",
|
|
31966
|
+
execute() {
|
|
31967
|
+
for (const p32 of configPaths) {
|
|
31968
|
+
if (fs45.existsSync(p32)) fs45.unlinkSync(p32);
|
|
31969
|
+
}
|
|
31970
|
+
}
|
|
31971
|
+
});
|
|
31972
|
+
}
|
|
31973
|
+
const tsconfigPath = path59.join(cwd, "tsconfig.json");
|
|
31974
|
+
if (fs45.existsSync(tsconfigPath)) {
|
|
31975
|
+
const content = fs45.readFileSync(tsconfigPath, "utf-8");
|
|
31976
|
+
const aliasMatches = [
|
|
31977
|
+
...content.match(/"@admin\//g) ?? [],
|
|
31978
|
+
...content.match(new RegExp(`"${namespace.alias}/`, "g")) ?? []
|
|
31979
|
+
];
|
|
31980
|
+
if (aliasMatches && aliasMatches.length > 0) {
|
|
31981
|
+
const aliasCount = aliasMatches.length;
|
|
31982
|
+
steps.push({
|
|
31983
|
+
label: "tsconfig.json path aliases",
|
|
31984
|
+
items: [`${namespace.alias}/* aliases in tsconfig.json`],
|
|
31985
|
+
count: aliasCount,
|
|
31986
|
+
unit: aliasCount === 1 ? "alias" : "aliases",
|
|
31987
|
+
execute() {
|
|
31988
|
+
cleanTsconfig(tsconfigPath, namespace.alias);
|
|
31989
|
+
}
|
|
31990
|
+
});
|
|
31805
31991
|
}
|
|
31806
|
-
}
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31810
|
-
|
|
31811
|
-
|
|
31812
|
-
const binName = process.platform === "win32" ? "shadcn.cmd" : "shadcn";
|
|
31813
|
-
const shadcnBin = path59.join(cwd, "node_modules", ".bin", binName);
|
|
31814
|
-
if (!fs45.existsSync(shadcnBin)) {
|
|
31815
|
-
clack4.cancel(
|
|
31816
|
-
`shadcn is not installed in this project. Run 'betterstart update-deps' and try again.`
|
|
31992
|
+
}
|
|
31993
|
+
const cssFile = findMainCss2(cwd);
|
|
31994
|
+
if (cssFile) {
|
|
31995
|
+
const cssContent = fs45.readFileSync(cssFile, "utf-8");
|
|
31996
|
+
const sourceLines = cssContent.split("\n").filter(
|
|
31997
|
+
(l) => new RegExp(`^@source\\s+"[^"]*(?:admin|${namespace.segment})[^"]*";\\s*$`).test(l)
|
|
31817
31998
|
);
|
|
31818
|
-
|
|
31999
|
+
if (sourceLines.length > 0) {
|
|
32000
|
+
const relCss = path59.relative(cwd, cssFile);
|
|
32001
|
+
steps.push({
|
|
32002
|
+
label: `CSS @source lines (${relCss})`,
|
|
32003
|
+
items: [`@source lines in ${relCss}`],
|
|
32004
|
+
count: sourceLines.length,
|
|
32005
|
+
unit: sourceLines.length === 1 ? "line" : "lines",
|
|
32006
|
+
execute() {
|
|
32007
|
+
cleanCss(cssFile, namespace.segment);
|
|
32008
|
+
}
|
|
32009
|
+
});
|
|
32010
|
+
}
|
|
31819
32011
|
}
|
|
31820
|
-
|
|
31821
|
-
|
|
31822
|
-
|
|
31823
|
-
|
|
31824
|
-
|
|
31825
|
-
|
|
31826
|
-
|
|
31827
|
-
|
|
31828
|
-
|
|
31829
|
-
|
|
31830
|
-
|
|
31831
|
-
|
|
31832
|
-
|
|
31833
|
-
|
|
31834
|
-
|
|
31835
|
-
return hostRelativePaths.map((relativePath) => path59.join(cwd, relativePath));
|
|
31836
|
-
}
|
|
31837
|
-
function snapshotFile(filePath) {
|
|
31838
|
-
if (!fs45.existsSync(filePath)) {
|
|
31839
|
-
return { existed: false };
|
|
32012
|
+
const envPath = path59.join(cwd, ".env.local");
|
|
32013
|
+
if (fs45.existsSync(envPath)) {
|
|
32014
|
+
const envContent = fs45.readFileSync(envPath, "utf-8");
|
|
32015
|
+
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
32016
|
+
if (bsVars.length > 0) {
|
|
32017
|
+
steps.push({
|
|
32018
|
+
label: ".env.local variables",
|
|
32019
|
+
items: ["BETTERSTART_* vars in .env.local"],
|
|
32020
|
+
count: bsVars.length,
|
|
32021
|
+
unit: bsVars.length === 1 ? "variable" : "variables",
|
|
32022
|
+
execute() {
|
|
32023
|
+
cleanEnvFile(envPath);
|
|
32024
|
+
}
|
|
32025
|
+
});
|
|
32026
|
+
}
|
|
31840
32027
|
}
|
|
31841
|
-
return
|
|
32028
|
+
return steps;
|
|
31842
32029
|
}
|
|
31843
|
-
function
|
|
31844
|
-
|
|
31845
|
-
|
|
32030
|
+
async function runUninstallCommand(options) {
|
|
32031
|
+
const cwd = options.cwd ? path59.resolve(options.cwd) : process.cwd();
|
|
32032
|
+
p30.intro(pc11.bgRed(pc11.white(" BetterStart Uninstall ")));
|
|
32033
|
+
let namespace = DEFAULT_ADMIN_NAMESPACE;
|
|
32034
|
+
try {
|
|
32035
|
+
const config = await resolveConfig(cwd);
|
|
32036
|
+
namespace = config.frameworkConfig.next.namespace;
|
|
32037
|
+
} catch {
|
|
32038
|
+
}
|
|
32039
|
+
const steps = buildUninstallPlan(cwd, namespace);
|
|
32040
|
+
if (steps.length === 0) {
|
|
32041
|
+
p30.log.success(`${pc11.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
|
|
32042
|
+
p30.outro("Project already clean");
|
|
31846
32043
|
return;
|
|
31847
32044
|
}
|
|
31848
|
-
|
|
31849
|
-
|
|
32045
|
+
const planLines = steps.map((step) => {
|
|
32046
|
+
const names = step.items.join(" ");
|
|
32047
|
+
const countLabel = pc11.dim(`${step.count} ${step.unit}`);
|
|
32048
|
+
return `${pc11.red("\u2717")} ${names} ${countLabel}`;
|
|
32049
|
+
});
|
|
32050
|
+
p30.note(planLines.join("\n"), "Uninstall plan");
|
|
32051
|
+
if (!options.force) {
|
|
32052
|
+
const confirmed = await p30.confirm({
|
|
32053
|
+
message: "Proceed with uninstall?",
|
|
32054
|
+
initialValue: false
|
|
32055
|
+
});
|
|
32056
|
+
if (p30.isCancel(confirmed) || !confirmed) {
|
|
32057
|
+
p30.cancel("Uninstall cancelled.");
|
|
32058
|
+
process.exit(0);
|
|
32059
|
+
}
|
|
32060
|
+
}
|
|
32061
|
+
const s = spinner2();
|
|
32062
|
+
s.start(steps[0].label);
|
|
32063
|
+
for (const step of steps) {
|
|
32064
|
+
s.message(step.label);
|
|
32065
|
+
step.execute();
|
|
31850
32066
|
}
|
|
32067
|
+
const parts = steps.map((step) => `${step.count} ${step.unit}`);
|
|
32068
|
+
s.stop(`Removed ${parts.join(", ")}`);
|
|
32069
|
+
p30.note(pc11.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
|
|
32070
|
+
p30.outro("Uninstall complete");
|
|
31851
32071
|
}
|
|
31852
32072
|
|
|
31853
32073
|
// adapters/next/commands/update-deps.ts
|
|
@@ -31913,6 +32133,7 @@ async function runUpdateStylesCommand(options) {
|
|
|
31913
32133
|
|
|
31914
32134
|
// adapters/next/commands-runtime.ts
|
|
31915
32135
|
var nextCommandRuntime = {
|
|
32136
|
+
listComponentChoices,
|
|
31916
32137
|
listInstallableChoices,
|
|
31917
32138
|
listSchemaChoices,
|
|
31918
32139
|
runAdd: runAddCommand,
|