@betterstart/cli 0.1.25 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +408 -42
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-SAPJG4NO.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
|
-
import { Command as
|
|
7
|
+
import { Command as Command8 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/commands/generate.ts
|
|
10
10
|
import path22 from "path";
|
|
@@ -901,8 +901,8 @@ function toPascalCase3(str) {
|
|
|
901
901
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
902
902
|
}
|
|
903
903
|
function toCamelCase(str) {
|
|
904
|
-
const
|
|
905
|
-
return
|
|
904
|
+
const p6 = toPascalCase3(str);
|
|
905
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
906
906
|
}
|
|
907
907
|
function toKebabCase(str) {
|
|
908
908
|
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
@@ -993,7 +993,7 @@ function generateFormAdminPages(schema, cwd, pagesDir, options) {
|
|
|
993
993
|
const adminDir = path4.join(cwd, pagesDir, "forms", kebab);
|
|
994
994
|
if (!fs4.existsSync(adminDir)) fs4.mkdirSync(adminDir, { recursive: true });
|
|
995
995
|
const files = [];
|
|
996
|
-
const rel = (
|
|
996
|
+
const rel = (p6) => path4.relative(cwd, p6);
|
|
997
997
|
const pagePath = path4.join(adminDir, "page.tsx");
|
|
998
998
|
if (!fs4.existsSync(pagePath) || options.force) {
|
|
999
999
|
fs4.writeFileSync(pagePath, generatePage(pascal, kebab), "utf-8");
|
|
@@ -1981,8 +1981,8 @@ function toPascalCase4(str) {
|
|
|
1981
1981
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
1982
1982
|
}
|
|
1983
1983
|
function toCamelCase2(str) {
|
|
1984
|
-
const
|
|
1985
|
-
return
|
|
1984
|
+
const p6 = toPascalCase4(str);
|
|
1985
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
1986
1986
|
}
|
|
1987
1987
|
function toKebabCase3(str) {
|
|
1988
1988
|
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
@@ -2697,8 +2697,8 @@ function toPascalCase5(str) {
|
|
|
2697
2697
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
2698
2698
|
}
|
|
2699
2699
|
function toCamelCase3(str) {
|
|
2700
|
-
const
|
|
2701
|
-
return
|
|
2700
|
+
const p6 = toPascalCase5(str);
|
|
2701
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
2702
2702
|
}
|
|
2703
2703
|
function singularize2(str) {
|
|
2704
2704
|
if (str.endsWith("ies")) return `${str.slice(0, -3)}y`;
|
|
@@ -2758,11 +2758,11 @@ function generateActions(schema, cwd, actionsDir, options = {}) {
|
|
|
2758
2758
|
const listFieldsWithRels = findListFieldsWithRelationships(dbFields);
|
|
2759
2759
|
const hasListRels = listFieldsWithRels.length > 0;
|
|
2760
2760
|
const allListRelQueries = [];
|
|
2761
|
-
for (const { field: listField, path:
|
|
2761
|
+
for (const { field: listField, path: path42 } of listFieldsWithRels) {
|
|
2762
2762
|
const rels = (listField.fields || []).filter((f) => f.type === "relationship" && f.relationship);
|
|
2763
2763
|
for (const relField of rels) {
|
|
2764
2764
|
allListRelQueries.push({
|
|
2765
|
-
fieldPath:
|
|
2765
|
+
fieldPath: path42.join("_"),
|
|
2766
2766
|
relField,
|
|
2767
2767
|
relTable: toCamelCase3(relField.relationship),
|
|
2768
2768
|
listFieldName: listField.name
|
|
@@ -3421,8 +3421,8 @@ function toPascalCase6(str) {
|
|
|
3421
3421
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
3422
3422
|
}
|
|
3423
3423
|
function toCamelCase4(str) {
|
|
3424
|
-
const
|
|
3425
|
-
return
|
|
3424
|
+
const p6 = toPascalCase6(str);
|
|
3425
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
3426
3426
|
}
|
|
3427
3427
|
function singularize3(str) {
|
|
3428
3428
|
if (str.endsWith("ies")) return `${str.slice(0, -3)}y`;
|
|
@@ -4564,8 +4564,8 @@ function toPascalCase9(str) {
|
|
|
4564
4564
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
4565
4565
|
}
|
|
4566
4566
|
function toCamelCase5(str) {
|
|
4567
|
-
const
|
|
4568
|
-
return
|
|
4567
|
+
const p6 = toPascalCase9(str);
|
|
4568
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
4569
4569
|
}
|
|
4570
4570
|
function singularize6(str) {
|
|
4571
4571
|
if (str.endsWith("ies")) return `${str.slice(0, -3)}y`;
|
|
@@ -4813,8 +4813,8 @@ function toPascalCase10(str) {
|
|
|
4813
4813
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
4814
4814
|
}
|
|
4815
4815
|
function toCamelCase6(str) {
|
|
4816
|
-
const
|
|
4817
|
-
return
|
|
4816
|
+
const p6 = toPascalCase10(str);
|
|
4817
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
4818
4818
|
}
|
|
4819
4819
|
function singularize7(str) {
|
|
4820
4820
|
if (str.endsWith("ies")) return `${str.slice(0, -3)}y`;
|
|
@@ -7045,8 +7045,8 @@ function toPascalCase16(str) {
|
|
|
7045
7045
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
7046
7046
|
}
|
|
7047
7047
|
function toCamelCase7(str) {
|
|
7048
|
-
const
|
|
7049
|
-
return
|
|
7048
|
+
const p6 = toPascalCase16(str);
|
|
7049
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
7050
7050
|
}
|
|
7051
7051
|
function singularize12(str) {
|
|
7052
7052
|
if (str.endsWith("ies")) return `${str.slice(0, -3)}y`;
|
|
@@ -11675,7 +11675,6 @@ var CORE_DEPS = [
|
|
|
11675
11675
|
"input-otp",
|
|
11676
11676
|
"react-resizable-panels",
|
|
11677
11677
|
"recharts",
|
|
11678
|
-
"shadcn",
|
|
11679
11678
|
"tw-animate-css",
|
|
11680
11679
|
"usehooks-ts",
|
|
11681
11680
|
"vaul"
|
|
@@ -12018,6 +12017,7 @@ import { authClient } from '@cms/auth/client'
|
|
|
12018
12017
|
import { Button } from '@cms/components/ui/button'
|
|
12019
12018
|
import { Input } from '@cms/components/ui/input'
|
|
12020
12019
|
import { Label } from '@cms/components/ui/label'
|
|
12020
|
+
import { LoaderCircle } from 'lucide-react'
|
|
12021
12021
|
import { useRouter } from 'next/navigation'
|
|
12022
12022
|
import * as React from 'react'
|
|
12023
12023
|
|
|
@@ -12090,6 +12090,7 @@ export function LoginForm() {
|
|
|
12090
12090
|
</div>
|
|
12091
12091
|
|
|
12092
12092
|
<Button type="submit" className="w-full" size="lg" disabled={isLoading}>
|
|
12093
|
+
{isLoading && <LoaderCircle className="animate-spin" />}
|
|
12093
12094
|
{isLoading ? 'Signing in...' : 'Sign In'}
|
|
12094
12095
|
</Button>
|
|
12095
12096
|
</form>
|
|
@@ -13510,9 +13511,32 @@ const auth = betterAuth({
|
|
|
13510
13511
|
const EMAIL = process.env.SEED_EMAIL!
|
|
13511
13512
|
const PASSWORD = process.env.SEED_PASSWORD!
|
|
13512
13513
|
const NAME = process.env.SEED_NAME || 'Admin'
|
|
13514
|
+
const OVERWRITE = process.env.SEED_OVERWRITE === 'true'
|
|
13513
13515
|
|
|
13514
13516
|
async function main() {
|
|
13515
|
-
|
|
13517
|
+
// Check if user already exists
|
|
13518
|
+
const existing = await db
|
|
13519
|
+
.select({ id: schema.user.id, name: schema.user.name })
|
|
13520
|
+
.from(schema.user)
|
|
13521
|
+
.where(eq(schema.user.email, EMAIL))
|
|
13522
|
+
.then((rows: { id: string; name: string }[]) => rows[0])
|
|
13523
|
+
|
|
13524
|
+
if (existing && !OVERWRITE) {
|
|
13525
|
+
// Exit code 2 signals "user exists" to the CLI
|
|
13526
|
+
console.log(\`EXISTING_USER:\${existing.name}\`)
|
|
13527
|
+
process.exit(2)
|
|
13528
|
+
}
|
|
13529
|
+
|
|
13530
|
+
if (existing && OVERWRITE) {
|
|
13531
|
+
console.log('\\n Replacing existing admin user...')
|
|
13532
|
+
// Remove existing account + session rows first (foreign key refs)
|
|
13533
|
+
await db.delete(schema.session).where(eq(schema.session.userId, existing.id))
|
|
13534
|
+
await db.delete(schema.account).where(eq(schema.account.userId, existing.id))
|
|
13535
|
+
await db.delete(schema.user).where(eq(schema.user.id, existing.id))
|
|
13536
|
+
} else {
|
|
13537
|
+
console.log('\\n Creating admin user...')
|
|
13538
|
+
}
|
|
13539
|
+
|
|
13516
13540
|
console.log(\` Email: \${EMAIL}\\n\`)
|
|
13517
13541
|
|
|
13518
13542
|
const result = await auth.api.signUpEmail({
|
|
@@ -13529,7 +13553,7 @@ async function main() {
|
|
|
13529
13553
|
.set({ role: 'admin' })
|
|
13530
13554
|
.where(eq(schema.user.id, result.user.id))
|
|
13531
13555
|
|
|
13532
|
-
console.log(\` Admin user created: \${EMAIL}\`)
|
|
13556
|
+
console.log(\` Admin user \${existing ? 'replaced' : 'created'}: \${EMAIL}\`)
|
|
13533
13557
|
console.log(' Role: admin\\n')
|
|
13534
13558
|
process.exit(0)
|
|
13535
13559
|
}
|
|
@@ -13587,22 +13611,52 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13587
13611
|
fs31.mkdirSync(scriptsDir, { recursive: true });
|
|
13588
13612
|
}
|
|
13589
13613
|
fs31.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
13590
|
-
const
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
const tsxBin = path36.join(cwd, "node_modules", ".bin", "tsx");
|
|
13595
|
-
execFileSync5(tsxBin, [seedPath], {
|
|
13614
|
+
const { execFile } = await import("child_process");
|
|
13615
|
+
const tsxBin = path36.join(cwd, "node_modules", ".bin", "tsx");
|
|
13616
|
+
const runSeed2 = (overwrite) => new Promise((resolve, reject) => {
|
|
13617
|
+
execFile(tsxBin, [seedPath], {
|
|
13596
13618
|
cwd,
|
|
13597
|
-
stdio: "pipe",
|
|
13598
13619
|
env: {
|
|
13599
13620
|
...process.env,
|
|
13600
13621
|
SEED_EMAIL: email,
|
|
13601
13622
|
SEED_PASSWORD: password3,
|
|
13602
|
-
SEED_NAME: name || "Admin"
|
|
13623
|
+
SEED_NAME: name || "Admin",
|
|
13624
|
+
...overwrite ? { SEED_OVERWRITE: "true" } : {}
|
|
13625
|
+
}
|
|
13626
|
+
}, (err, stdout, stderr) => {
|
|
13627
|
+
if (err && "code" in err && err.code === 2) {
|
|
13628
|
+
resolve({ code: 2, stdout });
|
|
13629
|
+
} else if (err) {
|
|
13630
|
+
reject(new Error(stderr || err.message));
|
|
13631
|
+
} else {
|
|
13632
|
+
resolve({ code: 0, stdout });
|
|
13603
13633
|
}
|
|
13604
13634
|
});
|
|
13605
|
-
|
|
13635
|
+
});
|
|
13636
|
+
const spinner4 = clack.spinner();
|
|
13637
|
+
spinner4.start("Creating admin user...");
|
|
13638
|
+
try {
|
|
13639
|
+
const result = await runSeed2(false);
|
|
13640
|
+
if (result.code === 2) {
|
|
13641
|
+
const existingName = result.stdout.split("\n").find((l) => l.startsWith("EXISTING_USER:"))?.replace("EXISTING_USER:", "")?.trim() || "unknown";
|
|
13642
|
+
spinner4.stop(`Account already exists for ${email}`);
|
|
13643
|
+
const overwrite = await clack.confirm({
|
|
13644
|
+
message: `An admin account (${existingName}) already exists with this email. Replace it?`
|
|
13645
|
+
});
|
|
13646
|
+
if (clack.isCancel(overwrite) || !overwrite) {
|
|
13647
|
+
clack.cancel("Seed cancelled.");
|
|
13648
|
+
try {
|
|
13649
|
+
fs31.unlinkSync(seedPath);
|
|
13650
|
+
} catch {
|
|
13651
|
+
}
|
|
13652
|
+
process.exit(0);
|
|
13653
|
+
}
|
|
13654
|
+
spinner4.start("Replacing admin user...");
|
|
13655
|
+
await runSeed2(true);
|
|
13656
|
+
spinner4.stop("Admin user replaced");
|
|
13657
|
+
} else {
|
|
13658
|
+
spinner4.stop("Admin user created");
|
|
13659
|
+
}
|
|
13606
13660
|
} catch (err) {
|
|
13607
13661
|
spinner4.stop("Failed to create admin user");
|
|
13608
13662
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -14097,8 +14151,8 @@ function toPascalCase17(str) {
|
|
|
14097
14151
|
return str.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
14098
14152
|
}
|
|
14099
14153
|
function toCamelCase8(str) {
|
|
14100
|
-
const
|
|
14101
|
-
return
|
|
14154
|
+
const p6 = toPascalCase17(str);
|
|
14155
|
+
return p6.charAt(0).toLowerCase() + p6.slice(1);
|
|
14102
14156
|
}
|
|
14103
14157
|
function singularize13(str) {
|
|
14104
14158
|
if (str.endsWith("ies")) return `${str.slice(0, -3)}y`;
|
|
@@ -14339,33 +14393,345 @@ var updateDepsCommand = new Command5("update-deps").description("Install or upda
|
|
|
14339
14393
|
clack2.outro("Dependencies updated");
|
|
14340
14394
|
});
|
|
14341
14395
|
|
|
14342
|
-
// src/commands/
|
|
14343
|
-
import
|
|
14396
|
+
// src/commands/uninstall.ts
|
|
14397
|
+
import fs35 from "fs";
|
|
14344
14398
|
import path40 from "path";
|
|
14345
|
-
import * as
|
|
14399
|
+
import * as p5 from "@clack/prompts";
|
|
14400
|
+
import pc3 from "picocolors";
|
|
14346
14401
|
import { Command as Command6 } from "commander";
|
|
14347
|
-
|
|
14402
|
+
|
|
14403
|
+
// src/commands/uninstall-cleaners.ts
|
|
14404
|
+
import fs34 from "fs";
|
|
14405
|
+
function stripJsonComments2(input) {
|
|
14406
|
+
let result = "";
|
|
14407
|
+
let i = 0;
|
|
14408
|
+
while (i < input.length) {
|
|
14409
|
+
if (input[i] === '"') {
|
|
14410
|
+
let j = i + 1;
|
|
14411
|
+
while (j < input.length) {
|
|
14412
|
+
if (input[j] === "\\") {
|
|
14413
|
+
j += 2;
|
|
14414
|
+
continue;
|
|
14415
|
+
}
|
|
14416
|
+
if (input[j] === '"') {
|
|
14417
|
+
j++;
|
|
14418
|
+
break;
|
|
14419
|
+
}
|
|
14420
|
+
j++;
|
|
14421
|
+
}
|
|
14422
|
+
result += input.slice(i, j);
|
|
14423
|
+
i = j;
|
|
14424
|
+
} else if (input[i] === "/" && input[i + 1] === "/") {
|
|
14425
|
+
const nl = input.indexOf("\n", i);
|
|
14426
|
+
i = nl === -1 ? input.length : nl;
|
|
14427
|
+
} else if (input[i] === "/" && input[i + 1] === "*") {
|
|
14428
|
+
const end = input.indexOf("*/", i + 2);
|
|
14429
|
+
i = end === -1 ? input.length : end + 2;
|
|
14430
|
+
} else {
|
|
14431
|
+
result += input[i];
|
|
14432
|
+
i++;
|
|
14433
|
+
}
|
|
14434
|
+
}
|
|
14435
|
+
return result;
|
|
14436
|
+
}
|
|
14437
|
+
function cleanTsconfig(tsconfigPath) {
|
|
14438
|
+
if (!fs34.existsSync(tsconfigPath)) return [];
|
|
14439
|
+
const raw = fs34.readFileSync(tsconfigPath, "utf-8");
|
|
14440
|
+
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
14441
|
+
let tsconfig;
|
|
14442
|
+
try {
|
|
14443
|
+
tsconfig = JSON.parse(stripped);
|
|
14444
|
+
} catch {
|
|
14445
|
+
return [];
|
|
14446
|
+
}
|
|
14447
|
+
const compilerOptions = tsconfig.compilerOptions ?? {};
|
|
14448
|
+
const paths = compilerOptions.paths ?? {};
|
|
14449
|
+
const removed = [];
|
|
14450
|
+
for (const key of Object.keys(paths)) {
|
|
14451
|
+
if (key.startsWith("@cms/") || key === "@cms/*") {
|
|
14452
|
+
removed.push(key);
|
|
14453
|
+
delete paths[key];
|
|
14454
|
+
}
|
|
14455
|
+
}
|
|
14456
|
+
if (removed.length === 0) return [];
|
|
14457
|
+
if (Object.keys(paths).length === 0) {
|
|
14458
|
+
delete compilerOptions.paths;
|
|
14459
|
+
} else {
|
|
14460
|
+
compilerOptions.paths = paths;
|
|
14461
|
+
}
|
|
14462
|
+
tsconfig.compilerOptions = compilerOptions;
|
|
14463
|
+
fs34.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
14464
|
+
`, "utf-8");
|
|
14465
|
+
return removed;
|
|
14466
|
+
}
|
|
14467
|
+
function cleanCss(cssPath) {
|
|
14468
|
+
if (!fs34.existsSync(cssPath)) return [];
|
|
14469
|
+
const content = fs34.readFileSync(cssPath, "utf-8");
|
|
14470
|
+
const lines = content.split("\n");
|
|
14471
|
+
const sourcePattern = /^@source\s+"[^"]*cms[^"]*";\s*$/;
|
|
14472
|
+
const removed = [];
|
|
14473
|
+
const kept = [];
|
|
14474
|
+
for (const line of lines) {
|
|
14475
|
+
if (sourcePattern.test(line)) {
|
|
14476
|
+
removed.push(line.trim());
|
|
14477
|
+
} else {
|
|
14478
|
+
kept.push(line);
|
|
14479
|
+
}
|
|
14480
|
+
}
|
|
14481
|
+
if (removed.length === 0) return [];
|
|
14482
|
+
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
14483
|
+
fs34.writeFileSync(cssPath, cleaned, "utf-8");
|
|
14484
|
+
return removed;
|
|
14485
|
+
}
|
|
14486
|
+
function cleanEnvFile(envPath) {
|
|
14487
|
+
if (!fs34.existsSync(envPath)) return [];
|
|
14488
|
+
const content = fs34.readFileSync(envPath, "utf-8");
|
|
14489
|
+
const lines = content.split("\n");
|
|
14490
|
+
const removed = [];
|
|
14491
|
+
const kept = [];
|
|
14492
|
+
const headerPattern = /^# =+$/;
|
|
14493
|
+
const headerTextPattern = /^# BetterStart CMS$/;
|
|
14494
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14495
|
+
const line = lines[i];
|
|
14496
|
+
const trimmed = line.trim();
|
|
14497
|
+
if (trimmed.match(/^BETTERSTART_\w+=/)) {
|
|
14498
|
+
const key = trimmed.split("=")[0];
|
|
14499
|
+
removed.push(key);
|
|
14500
|
+
continue;
|
|
14501
|
+
}
|
|
14502
|
+
if (headerPattern.test(trimmed)) {
|
|
14503
|
+
const next = lines[i + 1]?.trim();
|
|
14504
|
+
const afterNext = lines[i + 2]?.trim();
|
|
14505
|
+
if (next && headerTextPattern.test(next) && afterNext && headerPattern.test(afterNext)) {
|
|
14506
|
+
i += 2;
|
|
14507
|
+
continue;
|
|
14508
|
+
}
|
|
14509
|
+
}
|
|
14510
|
+
if (trimmed.startsWith("#") && !headerPattern.test(trimmed)) {
|
|
14511
|
+
const nextNonEmpty = findNextNonEmptyLine(lines, i + 1);
|
|
14512
|
+
if (nextNonEmpty !== null && nextNonEmpty.match(/^BETTERSTART_\w+=/)) {
|
|
14513
|
+
continue;
|
|
14514
|
+
}
|
|
14515
|
+
}
|
|
14516
|
+
kept.push(line);
|
|
14517
|
+
}
|
|
14518
|
+
if (removed.length === 0) return [];
|
|
14519
|
+
let result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
14520
|
+
if (result === "") {
|
|
14521
|
+
fs34.unlinkSync(envPath);
|
|
14522
|
+
} else {
|
|
14523
|
+
fs34.writeFileSync(envPath, `${result}
|
|
14524
|
+
`, "utf-8");
|
|
14525
|
+
}
|
|
14526
|
+
return removed;
|
|
14527
|
+
}
|
|
14528
|
+
function findNextNonEmptyLine(lines, startIndex) {
|
|
14529
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
14530
|
+
const trimmed = lines[i].trim();
|
|
14531
|
+
if (trimmed !== "") return trimmed;
|
|
14532
|
+
}
|
|
14533
|
+
return null;
|
|
14534
|
+
}
|
|
14535
|
+
|
|
14536
|
+
// src/commands/uninstall.ts
|
|
14537
|
+
function findMainCss2(cwd) {
|
|
14538
|
+
const candidates = [
|
|
14539
|
+
"src/app/globals.css",
|
|
14540
|
+
"app/globals.css",
|
|
14541
|
+
"src/app/global.css",
|
|
14542
|
+
"app/global.css",
|
|
14543
|
+
"src/app/app.css",
|
|
14544
|
+
"app/app.css",
|
|
14545
|
+
"src/globals.css",
|
|
14546
|
+
"globals.css"
|
|
14547
|
+
];
|
|
14548
|
+
for (const candidate of candidates) {
|
|
14549
|
+
const filePath = path40.join(cwd, candidate);
|
|
14550
|
+
if (fs35.existsSync(filePath)) return filePath;
|
|
14551
|
+
}
|
|
14552
|
+
return void 0;
|
|
14553
|
+
}
|
|
14554
|
+
function isCLICreatedBiome(biomePath) {
|
|
14555
|
+
if (!fs35.existsSync(biomePath)) return false;
|
|
14556
|
+
try {
|
|
14557
|
+
const content = JSON.parse(fs35.readFileSync(biomePath, "utf-8"));
|
|
14558
|
+
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");
|
|
14559
|
+
} catch {
|
|
14560
|
+
return false;
|
|
14561
|
+
}
|
|
14562
|
+
}
|
|
14563
|
+
function buildUninstallPlan(cwd) {
|
|
14564
|
+
const steps = [];
|
|
14565
|
+
const hasSrc = fs35.existsSync(path40.join(cwd, "src"));
|
|
14566
|
+
const appBase = hasSrc ? "src/app" : "app";
|
|
14567
|
+
const dirs = [];
|
|
14568
|
+
const cmsDir = path40.join(cwd, "cms");
|
|
14569
|
+
const cmsRouteGroup = path40.join(cwd, appBase, "(cms)");
|
|
14570
|
+
if (fs35.existsSync(cmsDir)) dirs.push("cms/");
|
|
14571
|
+
if (fs35.existsSync(cmsRouteGroup)) dirs.push(`${appBase}/(cms)/`);
|
|
14572
|
+
if (dirs.length > 0) {
|
|
14573
|
+
steps.push({
|
|
14574
|
+
label: "CMS directories",
|
|
14575
|
+
items: dirs,
|
|
14576
|
+
execute() {
|
|
14577
|
+
if (fs35.existsSync(cmsDir)) fs35.rmSync(cmsDir, { recursive: true, force: true });
|
|
14578
|
+
if (fs35.existsSync(cmsRouteGroup)) fs35.rmSync(cmsRouteGroup, { recursive: true, force: true });
|
|
14579
|
+
}
|
|
14580
|
+
});
|
|
14581
|
+
}
|
|
14582
|
+
const configFiles = [];
|
|
14583
|
+
const configPaths = [];
|
|
14584
|
+
const candidates = [
|
|
14585
|
+
["cms.config.ts", path40.join(cwd, "cms.config.ts")],
|
|
14586
|
+
["drizzle.config.ts", path40.join(cwd, "drizzle.config.ts")],
|
|
14587
|
+
["CMS.md", path40.join(cwd, "CMS.md")]
|
|
14588
|
+
];
|
|
14589
|
+
for (const [label, fullPath] of candidates) {
|
|
14590
|
+
if (fs35.existsSync(fullPath)) {
|
|
14591
|
+
configFiles.push(label);
|
|
14592
|
+
configPaths.push(fullPath);
|
|
14593
|
+
}
|
|
14594
|
+
}
|
|
14595
|
+
const biomePath = path40.join(cwd, "biome.json");
|
|
14596
|
+
if (isCLICreatedBiome(biomePath)) {
|
|
14597
|
+
configFiles.push("biome.json (CLI-created)");
|
|
14598
|
+
configPaths.push(biomePath);
|
|
14599
|
+
}
|
|
14600
|
+
if (configFiles.length > 0) {
|
|
14601
|
+
steps.push({
|
|
14602
|
+
label: "Config files",
|
|
14603
|
+
items: configFiles,
|
|
14604
|
+
execute() {
|
|
14605
|
+
for (const p6 of configPaths) {
|
|
14606
|
+
if (fs35.existsSync(p6)) fs35.unlinkSync(p6);
|
|
14607
|
+
}
|
|
14608
|
+
}
|
|
14609
|
+
});
|
|
14610
|
+
}
|
|
14611
|
+
const tsconfigPath = path40.join(cwd, "tsconfig.json");
|
|
14612
|
+
if (fs35.existsSync(tsconfigPath)) {
|
|
14613
|
+
const content = fs35.readFileSync(tsconfigPath, "utf-8");
|
|
14614
|
+
if (content.includes("@cms/")) {
|
|
14615
|
+
steps.push({
|
|
14616
|
+
label: "tsconfig.json path aliases",
|
|
14617
|
+
items: ["Remove all @cms/* paths from compilerOptions.paths"],
|
|
14618
|
+
execute() {
|
|
14619
|
+
cleanTsconfig(tsconfigPath);
|
|
14620
|
+
}
|
|
14621
|
+
});
|
|
14622
|
+
}
|
|
14623
|
+
}
|
|
14624
|
+
const cssFile = findMainCss2(cwd);
|
|
14625
|
+
if (cssFile) {
|
|
14626
|
+
const cssContent = fs35.readFileSync(cssFile, "utf-8");
|
|
14627
|
+
const sourcePattern = /^@source\s+"[^"]*cms[^"]*";\s*$/m;
|
|
14628
|
+
if (sourcePattern.test(cssContent)) {
|
|
14629
|
+
const relCss = path40.relative(cwd, cssFile);
|
|
14630
|
+
steps.push({
|
|
14631
|
+
label: `CSS @source lines (${relCss})`,
|
|
14632
|
+
items: ["Remove @source lines referencing cms/"],
|
|
14633
|
+
execute() {
|
|
14634
|
+
cleanCss(cssFile);
|
|
14635
|
+
}
|
|
14636
|
+
});
|
|
14637
|
+
}
|
|
14638
|
+
}
|
|
14639
|
+
const envPath = path40.join(cwd, ".env.local");
|
|
14640
|
+
if (fs35.existsSync(envPath)) {
|
|
14641
|
+
const envContent = fs35.readFileSync(envPath, "utf-8");
|
|
14642
|
+
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
14643
|
+
if (bsVars.length > 0) {
|
|
14644
|
+
steps.push({
|
|
14645
|
+
label: ".env.local variables",
|
|
14646
|
+
items: bsVars,
|
|
14647
|
+
execute() {
|
|
14648
|
+
cleanEnvFile(envPath);
|
|
14649
|
+
}
|
|
14650
|
+
});
|
|
14651
|
+
}
|
|
14652
|
+
}
|
|
14653
|
+
return steps;
|
|
14654
|
+
}
|
|
14655
|
+
var uninstallCommand = new Command6("uninstall").description("Remove all CMS files and undo modifications made by betterstart init").option("-f, --force", "Skip all confirmation prompts", false).option("--cwd <path>", "Project root path").action(async (options) => {
|
|
14348
14656
|
const cwd = options.cwd ? path40.resolve(options.cwd) : process.cwd();
|
|
14657
|
+
p5.intro(pc3.bgRed(pc3.white(" BetterStart Uninstall ")));
|
|
14658
|
+
const steps = buildUninstallPlan(cwd);
|
|
14659
|
+
if (steps.length === 0) {
|
|
14660
|
+
p5.log.info("Nothing to remove \u2014 project is already clean.");
|
|
14661
|
+
p5.outro("Done");
|
|
14662
|
+
return;
|
|
14663
|
+
}
|
|
14664
|
+
p5.log.warn("The following will be removed/modified:\n");
|
|
14665
|
+
for (const step of steps) {
|
|
14666
|
+
p5.log.message(` ${pc3.bold(step.label)}`);
|
|
14667
|
+
for (const item of step.items) {
|
|
14668
|
+
p5.log.message(` ${pc3.red("\xD7")} ${pc3.dim(item)}`);
|
|
14669
|
+
}
|
|
14670
|
+
p5.log.message("");
|
|
14671
|
+
}
|
|
14672
|
+
if (!options.force) {
|
|
14673
|
+
const confirmed = await p5.confirm({
|
|
14674
|
+
message: `Proceed with uninstall? (${steps.length} ${steps.length === 1 ? "area" : "areas"})`,
|
|
14675
|
+
initialValue: false
|
|
14676
|
+
});
|
|
14677
|
+
if (p5.isCancel(confirmed) || !confirmed) {
|
|
14678
|
+
p5.cancel("Uninstall cancelled.");
|
|
14679
|
+
process.exit(0);
|
|
14680
|
+
}
|
|
14681
|
+
}
|
|
14682
|
+
let completedCount = 0;
|
|
14683
|
+
for (const step of steps) {
|
|
14684
|
+
step.execute();
|
|
14685
|
+
completedCount++;
|
|
14686
|
+
p5.log.success(`Removed: ${step.label}`);
|
|
14687
|
+
}
|
|
14688
|
+
p5.log.message("");
|
|
14689
|
+
if (completedCount === 0) {
|
|
14690
|
+
p5.log.info("No changes were made.");
|
|
14691
|
+
} else {
|
|
14692
|
+
p5.note(
|
|
14693
|
+
pc3.dim("Database tables were NOT dropped \u2014 drop them manually if needed."),
|
|
14694
|
+
"Next steps"
|
|
14695
|
+
);
|
|
14696
|
+
}
|
|
14697
|
+
if (findMainCss2(cwd)) {
|
|
14698
|
+
p5.log.info(
|
|
14699
|
+
pc3.dim(
|
|
14700
|
+
"Note: @theme tokens were left in your CSS \u2014 they're harmless and may be shared with your own styles."
|
|
14701
|
+
)
|
|
14702
|
+
);
|
|
14703
|
+
}
|
|
14704
|
+
p5.outro(completedCount > 0 ? "Uninstall complete" : "Done");
|
|
14705
|
+
});
|
|
14706
|
+
|
|
14707
|
+
// src/commands/update-styles.ts
|
|
14708
|
+
import fs36 from "fs";
|
|
14709
|
+
import path41 from "path";
|
|
14710
|
+
import * as clack3 from "@clack/prompts";
|
|
14711
|
+
import { Command as Command7 } from "commander";
|
|
14712
|
+
var updateStylesCommand = new Command7("update-styles").description("Replace cms-globals.css with the latest version from the CLI").option("--cwd <path>", "Project root path").action(async (options) => {
|
|
14713
|
+
const cwd = options.cwd ? path41.resolve(options.cwd) : process.cwd();
|
|
14349
14714
|
clack3.intro("BetterStart Update Styles");
|
|
14350
14715
|
const config = await resolveConfig(cwd);
|
|
14351
14716
|
const cmsDir = config.paths?.cms ?? "./cms";
|
|
14352
|
-
const targetPath =
|
|
14353
|
-
if (!
|
|
14354
|
-
clack3.cancel(`cms-globals.css not found at ${
|
|
14717
|
+
const targetPath = path41.join(cwd, cmsDir, "cms-globals.css");
|
|
14718
|
+
if (!fs36.existsSync(targetPath)) {
|
|
14719
|
+
clack3.cancel(`cms-globals.css not found at ${path41.relative(cwd, targetPath)}`);
|
|
14355
14720
|
process.exit(1);
|
|
14356
14721
|
}
|
|
14357
|
-
|
|
14358
|
-
clack3.log.success(`Updated ${
|
|
14722
|
+
fs36.writeFileSync(targetPath, cmsGlobalsCssTemplate(), "utf-8");
|
|
14723
|
+
clack3.log.success(`Updated ${path41.relative(cwd, targetPath)}`);
|
|
14359
14724
|
clack3.outro("Styles updated");
|
|
14360
14725
|
});
|
|
14361
14726
|
|
|
14362
14727
|
// src/cli.ts
|
|
14363
|
-
var program = new
|
|
14728
|
+
var program = new Command8();
|
|
14364
14729
|
program.name("betterstart").description("Scaffold a full-featured CMS into any Next.js 16 application").version("0.1.0");
|
|
14365
14730
|
program.addCommand(initCommand);
|
|
14366
14731
|
program.addCommand(generateCommand);
|
|
14367
14732
|
program.addCommand(removeCommand);
|
|
14368
14733
|
program.addCommand(seedCommand);
|
|
14734
|
+
program.addCommand(uninstallCommand);
|
|
14369
14735
|
program.addCommand(updateDepsCommand);
|
|
14370
14736
|
program.addCommand(updateStylesCommand);
|
|
14371
14737
|
program.parse();
|