@betterstart/cli 0.1.81 → 0.1.83
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 +176 -305
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/init/components/data-table/data-table.tsx +1 -1
- package/templates/init/components/layout/cms-search.tsx +6 -5
- package/templates/init/components/layout/cms-sidebar.tsx +3 -7
- package/templates/init/components/shared/page-header.tsx +5 -5
- package/templates/init/data/navigation.ts +6 -6
- package/templates/init/lib/actions/profile.ts +4 -0
- package/templates/init/lib/auth/auth.ts +17 -0
- package/templates/init/pages/dashboard-page.tsx +1 -1
- package/templates/init/pages/forgot-password-form.tsx +130 -0
- package/templates/init/pages/forgot-password-page.tsx +20 -0
- package/templates/init/pages/login-form.tsx +17 -1
- package/templates/init/pages/login-page.tsx +12 -2
- package/templates/init/pages/profile/profile-form.tsx +65 -137
- package/templates/init/pages/reset-password-form.tsx +159 -0
- package/templates/init/pages/reset-password-page.tsx +20 -0
- package/templates/init/pages/users/columns.tsx +13 -10
- package/templates/init/pages/users/users-page-content.tsx +9 -2
- package/templates/init/pages/users/users-page.tsx +1 -1
- package/templates/init/pages/users/users-table.tsx +2 -2
- package/templates/ui/badge.tsx +2 -2
- package/templates/ui/button.tsx +2 -2
- package/templates/ui/card.tsx +2 -2
- package/templates/ui/curriculum-editor.tsx +2 -2
- package/templates/ui/image-upload-field.tsx +2 -2
- package/templates/ui/input.tsx +5 -4
- package/templates/ui/media-upload-field.tsx +2 -2
- package/templates/ui/placeholder.tsx +1 -1
- package/templates/ui/sidebar.tsx +3 -3
- package/templates/ui/table.tsx +10 -4
- package/templates/ui/video-upload-field.tsx +2 -2
package/dist/cli.js
CHANGED
|
@@ -717,7 +717,7 @@ function generateColumnDef(col) {
|
|
|
717
717
|
className="hover:bg-muted/50 px-0!"
|
|
718
718
|
>
|
|
719
719
|
${col.header}
|
|
720
|
-
<ArrowUpDown className="size-
|
|
720
|
+
<ArrowUpDown className="size-3" />
|
|
721
721
|
</Button>
|
|
722
722
|
)` : `header: '${col.header}'`;
|
|
723
723
|
let cellDef;
|
|
@@ -771,7 +771,7 @@ function generateColumns(fields, pascal, kebab, camel, customColumns) {
|
|
|
771
771
|
className="hover:bg-muted/50 px-0!"
|
|
772
772
|
>
|
|
773
773
|
${f.label}
|
|
774
|
-
<ArrowUpDown className="size-
|
|
774
|
+
<ArrowUpDown className="size-3" />
|
|
775
775
|
</Button>
|
|
776
776
|
),
|
|
777
777
|
cell: ({ row }) => {
|
|
@@ -4694,7 +4694,7 @@ function generateColumnDef2(column, fields) {
|
|
|
4694
4694
|
aria-sort={sortDirection === 'asc' ? 'ascending' : sortDirection === 'desc' ? 'descending' : 'none'}
|
|
4695
4695
|
>
|
|
4696
4696
|
${column.header}
|
|
4697
|
-
<ArrowUpDown className="size-
|
|
4697
|
+
<ArrowUpDown className="size-3" />
|
|
4698
4698
|
</Button>
|
|
4699
4699
|
)
|
|
4700
4700
|
}` : `header: '${column.header}'`;
|
|
@@ -4842,7 +4842,7 @@ function generateFirstColumnDef(column, fields, schemaName) {
|
|
|
4842
4842
|
aria-sort={sortDirection === 'asc' ? 'ascending' : sortDirection === 'desc' ? 'descending' : 'none'}
|
|
4843
4843
|
>
|
|
4844
4844
|
${column.header}
|
|
4845
|
-
<ArrowUpDown className="size-
|
|
4845
|
+
<ArrowUpDown className="size-3" />
|
|
4846
4846
|
</Button>
|
|
4847
4847
|
</div>
|
|
4848
4848
|
)
|
|
@@ -5483,12 +5483,12 @@ export default async function Create${Singular}Page() {
|
|
|
5483
5483
|
await connection()
|
|
5484
5484
|
|
|
5485
5485
|
return (
|
|
5486
|
-
|
|
5486
|
+
<div className="flex flex-col pt-14">
|
|
5487
5487
|
<PageHeader title="Create ${singLabel}" back={<Button variant="ghost" size="icon" asChild><Link href="/cms/${schema.name}"><ChevronLeft /></Link></Button>} />
|
|
5488
5488
|
<main className="container mx-auto max-w-5xl pt-10 pb-20 w-full">
|
|
5489
5489
|
<${Singular}Form key={Date.now()} />
|
|
5490
5490
|
</main>
|
|
5491
|
-
|
|
5491
|
+
</div>
|
|
5492
5492
|
)
|
|
5493
5493
|
}
|
|
5494
5494
|
`;
|
|
@@ -5779,12 +5779,12 @@ export default async function Edit${Singular}Page({ params }: PageProps) {
|
|
|
5779
5779
|
}
|
|
5780
5780
|
|
|
5781
5781
|
return (
|
|
5782
|
-
|
|
5782
|
+
<div className="flex flex-col pt-14">
|
|
5783
5783
|
<PageHeader title="Edit ${Singular}" back={<Button variant="ghost" size="icon" asChild><Link href="/cms/${schema.name}"><ChevronLeft /></Link></Button>} />
|
|
5784
5784
|
<main className="container mx-auto max-w-5xl pt-10 pb-20 w-full">
|
|
5785
5785
|
<${Singular}Form key={${camelSingular}.id} initialData={${camelSingular}} />
|
|
5786
5786
|
</main>
|
|
5787
|
-
|
|
5787
|
+
</div>
|
|
5788
5788
|
)
|
|
5789
5789
|
}
|
|
5790
5790
|
`;
|
|
@@ -6064,7 +6064,7 @@ ${indent} key={item.id}
|
|
|
6064
6064
|
${indent} value={\`item-\${index + 1}\`}
|
|
6065
6065
|
${indent} className="p-0 border-none"
|
|
6066
6066
|
${indent} >
|
|
6067
|
-
${indent} <div className="space-y-5 rounded-lg border p-4 bg-
|
|
6067
|
+
${indent} <div className="space-y-5 rounded-lg border p-4 bg-background [&_h3]:m-0 w-full">
|
|
6068
6068
|
${indent} <AccordionTrigger className="flex items-center p-0 justify-between w-full">
|
|
6069
6069
|
${indent} <h4 className="text-sm font-medium w-full">
|
|
6070
6070
|
${indent} ${accordionTitle}
|
|
@@ -7591,7 +7591,7 @@ export default function ${Plural}Page() {
|
|
|
7591
7591
|
</div>
|
|
7592
7592
|
}
|
|
7593
7593
|
>
|
|
7594
|
-
<div className="flex flex-col">
|
|
7594
|
+
<div className="flex flex-col pt-14">
|
|
7595
7595
|
<${Plural}PageContent columns={columns} />
|
|
7596
7596
|
</div>
|
|
7597
7597
|
</React.Suspense>
|
|
@@ -7804,7 +7804,7 @@ ${filterLogic}` : ""}`;
|
|
|
7804
7804
|
name="search"
|
|
7805
7805
|
placeholder="Search ${schema.label.toLowerCase()}..."
|
|
7806
7806
|
defaultValue={search}
|
|
7807
|
-
className="w-64 pl-9 bg-white
|
|
7807
|
+
className="w-64 pl-9 bg-white"
|
|
7808
7808
|
/>
|
|
7809
7809
|
<CornerDownLeft className="text-muted-foreground/70 pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2" />
|
|
7810
7810
|
</form>`;
|
|
@@ -7862,12 +7862,12 @@ export function ${Plural}PageContent<TValue>({
|
|
|
7862
7862
|
const queryClient = useQueryClient()
|
|
7863
7863
|
${searchLogic}${deleteLogic}
|
|
7864
7864
|
return (
|
|
7865
|
-
|
|
7865
|
+
<div className="flex flex-col pt-14">
|
|
7866
7866
|
<PageHeader title="${schema.label}" back={<Button variant="ghost" size="icon" onClick={() => router.back()}><ChevronLeft /></Button>} search={${searchInput}} actions={<div className="flex items-center gap-2">${deleteButton} ${createButton}</div>} />
|
|
7867
7867
|
<main className="space-y-6 p-6">
|
|
7868
7868
|
<${Plural}Table ${tableProps} />
|
|
7869
7869
|
</main>
|
|
7870
|
-
|
|
7870
|
+
</div>
|
|
7871
7871
|
)
|
|
7872
7872
|
}
|
|
7873
7873
|
`;
|
|
@@ -7902,12 +7902,12 @@ export default async function ${PageName}Page() {
|
|
|
7902
7902
|
const data = await get${Singular}()
|
|
7903
7903
|
|
|
7904
7904
|
return (
|
|
7905
|
-
|
|
7905
|
+
<div className="flex flex-col pt-14">
|
|
7906
7906
|
<PageHeader title="${schema.label}" />
|
|
7907
7907
|
<main className="container mx-auto max-w-5xl pt-10 pb-20 w-full">
|
|
7908
7908
|
<${Singular}Form initialData={data} />
|
|
7909
7909
|
</main>
|
|
7910
|
-
|
|
7910
|
+
</div>
|
|
7911
7911
|
)
|
|
7912
7912
|
}
|
|
7913
7913
|
`;
|
|
@@ -8203,7 +8203,7 @@ export function ${Plural}Table<TValue>({ columns, selectedIds, setSelectedIds, $
|
|
|
8203
8203
|
|
|
8204
8204
|
<div className="bg-card border overflow-hidden rounded-lg">
|
|
8205
8205
|
<Table>
|
|
8206
|
-
<TableHeader
|
|
8206
|
+
<TableHeader>
|
|
8207
8207
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
8208
8208
|
<TableRow key={headerGroup.id}>
|
|
8209
8209
|
{headerGroup.headers.map((header) => {
|
|
@@ -8831,7 +8831,7 @@ async function generateAll(cwd, options) {
|
|
|
8831
8831
|
|
|
8832
8832
|
// src/commands/init.ts
|
|
8833
8833
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
8834
|
-
import
|
|
8834
|
+
import fs39 from "fs";
|
|
8835
8835
|
import path45 from "path";
|
|
8836
8836
|
import * as p4 from "@clack/prompts";
|
|
8837
8837
|
import { Command as Command3 } from "commander";
|
|
@@ -9027,7 +9027,6 @@ function scaffoldAuth({ cwd, config }) {
|
|
|
9027
9027
|
}
|
|
9028
9028
|
|
|
9029
9029
|
// src/init/scaffolders/base.ts
|
|
9030
|
-
import fs31 from "fs";
|
|
9031
9030
|
import path34 from "path";
|
|
9032
9031
|
function scaffoldBase({ cwd, config }) {
|
|
9033
9032
|
const created = [];
|
|
@@ -9069,16 +9068,8 @@ function scaffoldBase({ cwd, config }) {
|
|
|
9069
9068
|
if (safeWriteFile(path34.resolve(cwd, "cms.config.ts"), configContent)) {
|
|
9070
9069
|
created.push("cms.config.ts");
|
|
9071
9070
|
}
|
|
9072
|
-
const cmsDoc = generateCmsDoc(config, {});
|
|
9073
|
-
if (safeWriteFile(path34.resolve(cwd, "CMS.md"), cmsDoc)) {
|
|
9074
|
-
created.push("CMS.md");
|
|
9075
|
-
}
|
|
9076
9071
|
return created;
|
|
9077
9072
|
}
|
|
9078
|
-
function regenerateCmsDoc(cwd, config, options) {
|
|
9079
|
-
const content = generateCmsDoc(config, options);
|
|
9080
|
-
fs31.writeFileSync(path34.resolve(cwd, "CMS.md"), content, "utf-8");
|
|
9081
|
-
}
|
|
9082
9073
|
function generateConfigFile(config) {
|
|
9083
9074
|
return `import { defineConfig } from '@betterstart/cli'
|
|
9084
9075
|
|
|
@@ -9112,126 +9103,9 @@ export default defineConfig({
|
|
|
9112
9103
|
})
|
|
9113
9104
|
`;
|
|
9114
9105
|
}
|
|
9115
|
-
function generateCmsDoc(config, options) {
|
|
9116
|
-
const sections = [];
|
|
9117
|
-
sections.push(`# CMS
|
|
9118
|
-
|
|
9119
|
-
> Auto-generated by [BetterStart CLI](https://github.com/betterstart/cli).
|
|
9120
|
-
> Regenerated on each \`betterstart init\`.`);
|
|
9121
|
-
if (options.preset) {
|
|
9122
|
-
sections.push(`## Preset
|
|
9123
|
-
|
|
9124
|
-
This project was initialized with the **${options.preset}** preset.`);
|
|
9125
|
-
}
|
|
9126
|
-
sections.push(`## Directory Structure
|
|
9127
|
-
|
|
9128
|
-
| Path | Description |
|
|
9129
|
-
|------|-------------|
|
|
9130
|
-
| \`cms/\` | All CMS source code (you own this) |
|
|
9131
|
-
| \`cms/schemas/\` | Entity JSON schemas |
|
|
9132
|
-
| \`cms/schemas/forms/\` | Form JSON schemas |
|
|
9133
|
-
| \`cms/db/\` | Database client, schema, drizzle config |
|
|
9134
|
-
| \`cms/lib/actions/\` | Server actions (CRUD) |
|
|
9135
|
-
| \`cms/lib/auth/\` | Better Auth configuration |
|
|
9136
|
-
| \`cms/lib/cache/\` | Cache tags, queries, revalidation |
|
|
9137
|
-
| \`cms/lib/emails/\` | React Email templates |
|
|
9138
|
-
| \`cms/hooks/\` | React Query hooks |
|
|
9139
|
-
| \`cms/components/\` | UI components (shadcn-style) |
|
|
9140
|
-
| \`cms/utils/\` | Utility functions |
|
|
9141
|
-
| \`${config.paths.pages}/\` | Admin pages (auth-gated) |
|
|
9142
|
-
| \`${config.paths.login}/\` | Login page |
|
|
9143
|
-
| \`${config.paths.api}/\` | CMS API routes |`);
|
|
9144
|
-
if (options.schemas && options.schemas.length > 0 || options.forms && options.forms.length > 0) {
|
|
9145
|
-
const lines = ["## Generated Content", ""];
|
|
9146
|
-
if (options.schemas && options.schemas.length > 0) {
|
|
9147
|
-
lines.push("### Entities");
|
|
9148
|
-
for (const s of options.schemas) {
|
|
9149
|
-
lines.push(`- **${s}** \u2014 \`cms/schemas/${s}.json\` \u2192 admin at \`/cms/${s}\``);
|
|
9150
|
-
}
|
|
9151
|
-
}
|
|
9152
|
-
if (options.forms && options.forms.length > 0) {
|
|
9153
|
-
lines.push("");
|
|
9154
|
-
lines.push("### Forms");
|
|
9155
|
-
for (const f of options.forms) {
|
|
9156
|
-
lines.push(`- **${f}** \u2014 \`cms/schemas/forms/${f}.json\` \u2192 admin at \`/cms/forms/${f}\``);
|
|
9157
|
-
}
|
|
9158
|
-
}
|
|
9159
|
-
sections.push(lines.join("\n"));
|
|
9160
|
-
}
|
|
9161
|
-
sections.push(`## Commands
|
|
9162
|
-
|
|
9163
|
-
\`\`\`bash
|
|
9164
|
-
# Generate entity CRUD from a schema
|
|
9165
|
-
npx betterstart generate <schema-name>
|
|
9166
|
-
|
|
9167
|
-
# Generate with force overwrite
|
|
9168
|
-
npx betterstart generate <schema-name> --force
|
|
9169
|
-
|
|
9170
|
-
# Remove all generated files for an entity/form
|
|
9171
|
-
npx betterstart remove <schema-name>
|
|
9172
|
-
|
|
9173
|
-
# Create initial admin user
|
|
9174
|
-
npx betterstart seed
|
|
9175
|
-
\`\`\``);
|
|
9176
|
-
sections.push(`## Schema Format
|
|
9177
|
-
|
|
9178
|
-
### Entity Schema (\`cms/schemas/<name>.json\`)
|
|
9179
|
-
|
|
9180
|
-
\`\`\`json
|
|
9181
|
-
{
|
|
9182
|
-
"name": "posts",
|
|
9183
|
-
"label": "Posts",
|
|
9184
|
-
"icon": "FileText",
|
|
9185
|
-
"fields": [
|
|
9186
|
-
{ "name": "title", "type": "string", "label": "Title", "required": true },
|
|
9187
|
-
{ "name": "content", "type": "richtext", "label": "Content" }
|
|
9188
|
-
]
|
|
9189
|
-
}
|
|
9190
|
-
\`\`\`
|
|
9191
|
-
|
|
9192
|
-
Field types: \`string\`, \`text\`, \`richtext\`, \`number\`, \`boolean\`, \`date\`, \`image\`, \`select\`, \`relationship\`.
|
|
9193
|
-
|
|
9194
|
-
### Form Schema (\`cms/schemas/forms/<name>.json\`)
|
|
9195
|
-
|
|
9196
|
-
\`\`\`json
|
|
9197
|
-
{
|
|
9198
|
-
"name": "contact",
|
|
9199
|
-
"label": "Contact Form",
|
|
9200
|
-
"submitButtonText": "Send Message",
|
|
9201
|
-
"fields": [
|
|
9202
|
-
{ "name": "email", "type": "email", "label": "Email", "required": true },
|
|
9203
|
-
{ "name": "message", "type": "textarea", "label": "Message", "required": true }
|
|
9204
|
-
]
|
|
9205
|
-
}
|
|
9206
|
-
\`\`\`
|
|
9207
|
-
|
|
9208
|
-
Field types: \`text\`, \`textarea\`, \`email\`, \`phone\`, \`number\`, \`url\`, \`date\`, \`select\`, \`radio\`, \`checkbox\`, \`multiselect\`, \`file\`.`);
|
|
9209
|
-
sections.push(`## Path Aliases
|
|
9210
|
-
|
|
9211
|
-
| Alias | Resolves to |
|
|
9212
|
-
|-------|-------------|
|
|
9213
|
-
| \`@cms/*\` | \`${config.paths.cms}/*\` |`);
|
|
9214
|
-
sections.push(`## Environment Variables
|
|
9215
|
-
|
|
9216
|
-
Required variables in \`.env.local\`:
|
|
9217
|
-
|
|
9218
|
-
| Variable | Description |
|
|
9219
|
-
|----------|-------------|
|
|
9220
|
-
| \`BETTERSTART_DATABASE_URL\` | PostgreSQL connection string |
|
|
9221
|
-
| \`BETTERSTART_AUTH_SECRET\` | Auth secret (\`openssl rand -base64 32\`) |
|
|
9222
|
-
| \`BETTERSTART_AUTH_URL\` | App URL (e.g. \`http://localhost:3000\`) |
|
|
9223
|
-
| \`BETTERSTART_R2_*\` | Cloudflare R2 storage credentials |
|
|
9224
|
-
| \`BETTERSTART_RESEND_API_KEY\` | Resend API key (if email enabled) |
|
|
9225
|
-
| \`NOTIFICATION_EMAIL\` | Fallback notification email address |`);
|
|
9226
|
-
sections.push(`## Configuration
|
|
9227
|
-
|
|
9228
|
-
Edit \`cms.config.ts\` to customize paths, database provider, and features.`);
|
|
9229
|
-
return `${sections.join("\n\n")}
|
|
9230
|
-
`;
|
|
9231
|
-
}
|
|
9232
9106
|
|
|
9233
9107
|
// src/init/scaffolders/biome.ts
|
|
9234
|
-
import
|
|
9108
|
+
import fs31 from "fs";
|
|
9235
9109
|
import path35 from "path";
|
|
9236
9110
|
function scaffoldBiome(cwd, linter) {
|
|
9237
9111
|
if (linter.type !== "none") {
|
|
@@ -9241,7 +9115,7 @@ function scaffoldBiome(cwd, linter) {
|
|
|
9241
9115
|
};
|
|
9242
9116
|
}
|
|
9243
9117
|
const configPath = path35.join(cwd, "biome.json");
|
|
9244
|
-
if (
|
|
9118
|
+
if (fs31.existsSync(configPath)) {
|
|
9245
9119
|
return { installed: false, skippedReason: "biome.json already exists" };
|
|
9246
9120
|
}
|
|
9247
9121
|
const config = {
|
|
@@ -9292,24 +9166,24 @@ function scaffoldBiome(cwd, linter) {
|
|
|
9292
9166
|
]
|
|
9293
9167
|
}
|
|
9294
9168
|
};
|
|
9295
|
-
|
|
9169
|
+
fs31.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
9296
9170
|
`, "utf-8");
|
|
9297
9171
|
return { installed: true, skippedReason: null };
|
|
9298
9172
|
}
|
|
9299
9173
|
|
|
9300
9174
|
// src/init/scaffolders/components.ts
|
|
9301
9175
|
import path37 from "path";
|
|
9302
|
-
import
|
|
9176
|
+
import fs33 from "fs-extra";
|
|
9303
9177
|
|
|
9304
9178
|
// src/utils/detect.ts
|
|
9305
|
-
import
|
|
9179
|
+
import fs32 from "fs";
|
|
9306
9180
|
import path36 from "path";
|
|
9307
9181
|
var NEXT_CONFIG_FILES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
9308
9182
|
function detectProjectName(cwd) {
|
|
9309
9183
|
const pkgPath = path36.join(cwd, "package.json");
|
|
9310
|
-
if (
|
|
9184
|
+
if (fs32.existsSync(pkgPath)) {
|
|
9311
9185
|
try {
|
|
9312
|
-
const pkg = JSON.parse(
|
|
9186
|
+
const pkg = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
|
|
9313
9187
|
if (typeof pkg.name === "string" && pkg.name.length > 0) {
|
|
9314
9188
|
return formatProjectName(pkg.name);
|
|
9315
9189
|
}
|
|
@@ -9323,21 +9197,21 @@ function formatProjectName(name) {
|
|
|
9323
9197
|
return base.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
9324
9198
|
}
|
|
9325
9199
|
function detectProject(cwd) {
|
|
9326
|
-
const isExisting = NEXT_CONFIG_FILES.some((f) =>
|
|
9327
|
-
const hasSrcDir =
|
|
9328
|
-
const hasTypeScript =
|
|
9200
|
+
const isExisting = NEXT_CONFIG_FILES.some((f) => fs32.existsSync(path36.join(cwd, f)));
|
|
9201
|
+
const hasSrcDir = fs32.existsSync(path36.join(cwd, "src"));
|
|
9202
|
+
const hasTypeScript = fs32.existsSync(path36.join(cwd, "tsconfig.json")) || fs32.existsSync(path36.join(cwd, "tsconfig.app.json"));
|
|
9329
9203
|
const hasTailwind = detectTailwind(cwd);
|
|
9330
9204
|
const linter = detectLinter(cwd);
|
|
9331
9205
|
const conflicts = [];
|
|
9332
9206
|
if (isExisting) {
|
|
9333
|
-
if (
|
|
9207
|
+
if (fs32.existsSync(path36.join(cwd, "cms"))) {
|
|
9334
9208
|
conflicts.push("cms/ directory already exists");
|
|
9335
9209
|
}
|
|
9336
|
-
if (
|
|
9210
|
+
if (fs32.existsSync(path36.join(cwd, "cms.config.ts"))) {
|
|
9337
9211
|
conflicts.push("cms.config.ts already exists");
|
|
9338
9212
|
}
|
|
9339
9213
|
const appBase = hasSrcDir ? "src/app" : "app";
|
|
9340
|
-
if (
|
|
9214
|
+
if (fs32.existsSync(path36.join(cwd, appBase, "(cms)"))) {
|
|
9341
9215
|
conflicts.push(`${appBase}/(cms)/ route group already exists`);
|
|
9342
9216
|
}
|
|
9343
9217
|
if (hasTsconfigCmsAliases(cwd)) {
|
|
@@ -9364,19 +9238,19 @@ var ESLINT_CONFIG_FILES = [
|
|
|
9364
9238
|
];
|
|
9365
9239
|
function detectLinter(cwd) {
|
|
9366
9240
|
for (const f of BIOME_CONFIG_FILES) {
|
|
9367
|
-
if (
|
|
9241
|
+
if (fs32.existsSync(path36.join(cwd, f))) {
|
|
9368
9242
|
return { type: "biome", configFile: f };
|
|
9369
9243
|
}
|
|
9370
9244
|
}
|
|
9371
9245
|
for (const f of ESLINT_CONFIG_FILES) {
|
|
9372
|
-
if (
|
|
9246
|
+
if (fs32.existsSync(path36.join(cwd, f))) {
|
|
9373
9247
|
return { type: "eslint", configFile: f };
|
|
9374
9248
|
}
|
|
9375
9249
|
}
|
|
9376
9250
|
const pkgPath = path36.join(cwd, "package.json");
|
|
9377
|
-
if (
|
|
9251
|
+
if (fs32.existsSync(pkgPath)) {
|
|
9378
9252
|
try {
|
|
9379
|
-
const pkg = JSON.parse(
|
|
9253
|
+
const pkg = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
|
|
9380
9254
|
if (pkg.eslintConfig) {
|
|
9381
9255
|
return { type: "eslint", configFile: "package.json (eslintConfig)" };
|
|
9382
9256
|
}
|
|
@@ -9393,8 +9267,8 @@ function detectTailwind(cwd) {
|
|
|
9393
9267
|
path36.join(cwd, f)
|
|
9394
9268
|
]);
|
|
9395
9269
|
for (const cssFile of cssFiles) {
|
|
9396
|
-
if (
|
|
9397
|
-
const content =
|
|
9270
|
+
if (fs32.existsSync(cssFile)) {
|
|
9271
|
+
const content = fs32.readFileSync(cssFile, "utf-8");
|
|
9398
9272
|
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'") || content.includes("@theme")) {
|
|
9399
9273
|
return true;
|
|
9400
9274
|
}
|
|
@@ -9402,8 +9276,8 @@ function detectTailwind(cwd) {
|
|
|
9402
9276
|
}
|
|
9403
9277
|
const postcssFiles = ["postcss.config.js", "postcss.config.mjs", "postcss.config.cjs"];
|
|
9404
9278
|
for (const f of postcssFiles) {
|
|
9405
|
-
if (
|
|
9406
|
-
const content =
|
|
9279
|
+
if (fs32.existsSync(path36.join(cwd, f))) {
|
|
9280
|
+
const content = fs32.readFileSync(path36.join(cwd, f), "utf-8");
|
|
9407
9281
|
if (content.includes("tailwindcss") || content.includes("@tailwindcss")) {
|
|
9408
9282
|
return true;
|
|
9409
9283
|
}
|
|
@@ -9413,9 +9287,9 @@ function detectTailwind(cwd) {
|
|
|
9413
9287
|
}
|
|
9414
9288
|
function hasTsconfigCmsAliases(cwd) {
|
|
9415
9289
|
const tsconfigPath = path36.join(cwd, "tsconfig.json");
|
|
9416
|
-
if (!
|
|
9290
|
+
if (!fs32.existsSync(tsconfigPath)) return false;
|
|
9417
9291
|
try {
|
|
9418
|
-
const content =
|
|
9292
|
+
const content = fs32.readFileSync(tsconfigPath, "utf-8");
|
|
9419
9293
|
return content.includes("@cms/");
|
|
9420
9294
|
} catch {
|
|
9421
9295
|
return false;
|
|
@@ -9423,9 +9297,9 @@ function hasTsconfigCmsAliases(cwd) {
|
|
|
9423
9297
|
}
|
|
9424
9298
|
function hasEnvBetterstartVars(cwd) {
|
|
9425
9299
|
const envPath = path36.join(cwd, ".env.local");
|
|
9426
|
-
if (!
|
|
9300
|
+
if (!fs32.existsSync(envPath)) return false;
|
|
9427
9301
|
try {
|
|
9428
|
-
const content =
|
|
9302
|
+
const content = fs32.readFileSync(envPath, "utf-8");
|
|
9429
9303
|
return content.includes("BETTERSTART_");
|
|
9430
9304
|
} catch {
|
|
9431
9305
|
return false;
|
|
@@ -9499,14 +9373,14 @@ function copyUiTemplates(cwd, config) {
|
|
|
9499
9373
|
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui");
|
|
9500
9374
|
const cliRoot = findCliRoot();
|
|
9501
9375
|
const srcDir = path37.join(cliRoot, "templates", "ui");
|
|
9502
|
-
if (!
|
|
9376
|
+
if (!fs33.existsSync(srcDir)) {
|
|
9503
9377
|
return created;
|
|
9504
9378
|
}
|
|
9505
|
-
const files =
|
|
9379
|
+
const files = fs33.readdirSync(srcDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
|
|
9506
9380
|
for (const file of files) {
|
|
9507
9381
|
const destPath = path37.join(destDir, file);
|
|
9508
|
-
if (!
|
|
9509
|
-
|
|
9382
|
+
if (!fs33.existsSync(destPath)) {
|
|
9383
|
+
fs33.copyFileSync(path37.join(srcDir, file), destPath);
|
|
9510
9384
|
created.push(path37.join(config.paths.cms, "components", "ui", file));
|
|
9511
9385
|
}
|
|
9512
9386
|
}
|
|
@@ -9517,22 +9391,22 @@ function copyTiptapTemplates(cwd, config) {
|
|
|
9517
9391
|
const cliRoot = findCliRoot();
|
|
9518
9392
|
const srcDir = path37.join(cliRoot, "templates", "tiptap");
|
|
9519
9393
|
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui", "tiptap");
|
|
9520
|
-
if (!
|
|
9394
|
+
if (!fs33.existsSync(srcDir)) {
|
|
9521
9395
|
return created;
|
|
9522
9396
|
}
|
|
9523
9397
|
copyDirRecursive(srcDir, destDir, config.paths.cms, created);
|
|
9524
9398
|
return created;
|
|
9525
9399
|
}
|
|
9526
9400
|
function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
9527
|
-
|
|
9528
|
-
const entries =
|
|
9401
|
+
fs33.ensureDirSync(dest);
|
|
9402
|
+
const entries = fs33.readdirSync(src, { withFileTypes: true });
|
|
9529
9403
|
for (const entry of entries) {
|
|
9530
9404
|
const srcPath = path37.join(src, entry.name);
|
|
9531
9405
|
const destPath = path37.join(dest, entry.name);
|
|
9532
9406
|
if (entry.isDirectory()) {
|
|
9533
9407
|
copyDirRecursive(srcPath, destPath, cmsPrefix, created);
|
|
9534
|
-
} else if (!
|
|
9535
|
-
|
|
9408
|
+
} else if (!fs33.existsSync(destPath)) {
|
|
9409
|
+
fs33.copyFileSync(srcPath, destPath);
|
|
9536
9410
|
const relFromCms = path37.relative(path37.resolve(dest, "..", "..", "..", ".."), destPath);
|
|
9537
9411
|
created.push(relFromCms);
|
|
9538
9412
|
}
|
|
@@ -9543,9 +9417,9 @@ function copySchemaMetaschema(cwd, config) {
|
|
|
9543
9417
|
const cliRoot = findCliRoot();
|
|
9544
9418
|
const srcPath = path37.join(cliRoot, "templates", "schema.json");
|
|
9545
9419
|
const destPath = path37.resolve(cwd, config.paths.schemas, "schema.json");
|
|
9546
|
-
if (
|
|
9547
|
-
|
|
9548
|
-
|
|
9420
|
+
if (fs33.existsSync(srcPath) && !fs33.existsSync(destPath)) {
|
|
9421
|
+
fs33.ensureDirSync(path37.dirname(destPath));
|
|
9422
|
+
fs33.copyFileSync(srcPath, destPath);
|
|
9549
9423
|
created.push(path37.join(config.paths.schemas, "schema.json"));
|
|
9550
9424
|
}
|
|
9551
9425
|
return created;
|
|
@@ -9596,7 +9470,7 @@ var CORE_DEPS = [
|
|
|
9596
9470
|
"clsx",
|
|
9597
9471
|
"tailwind-merge",
|
|
9598
9472
|
// Icons
|
|
9599
|
-
"lucide-react",
|
|
9473
|
+
"lucide-react@1.7.0",
|
|
9600
9474
|
// Storage (R2)
|
|
9601
9475
|
"@aws-sdk/client-s3",
|
|
9602
9476
|
// Radix UI (unified package + legacy scoped packages)
|
|
@@ -9738,11 +9612,11 @@ import { existsSync, readFileSync } from "fs";
|
|
|
9738
9612
|
import { join } from "path";
|
|
9739
9613
|
|
|
9740
9614
|
// src/utils/env.ts
|
|
9741
|
-
import
|
|
9615
|
+
import fs34 from "fs";
|
|
9742
9616
|
import path39 from "path";
|
|
9743
9617
|
function appendEnvVars(cwd, sections, overwrite) {
|
|
9744
9618
|
const envPath = path39.join(cwd, ".env.local");
|
|
9745
|
-
let existing =
|
|
9619
|
+
let existing = fs34.existsSync(envPath) ? fs34.readFileSync(envPath, "utf-8") : "";
|
|
9746
9620
|
const existingKeys = new Set(
|
|
9747
9621
|
existing.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#")).map((line) => line.split("=")[0]?.trim()).filter(Boolean)
|
|
9748
9622
|
);
|
|
@@ -9786,7 +9660,7 @@ function appendEnvVars(cwd, sections, overwrite) {
|
|
|
9786
9660
|
const header = existing.trim() ? "" : "# ============================================\n# BetterStart CMS\n# ============================================\n";
|
|
9787
9661
|
const content = existing.trim() ? `${existing.trimEnd()}
|
|
9788
9662
|
${lines.join("\n")}` : header + lines.join("\n");
|
|
9789
|
-
|
|
9663
|
+
fs34.writeFileSync(envPath, content);
|
|
9790
9664
|
}
|
|
9791
9665
|
return { added, skipped, updated };
|
|
9792
9666
|
}
|
|
@@ -9880,6 +9754,12 @@ function scaffoldLayout({ cwd, config }) {
|
|
|
9880
9754
|
write(path40.join(config.paths.pages, "layout.tsx"), readTemplate("pages/authenticated-layout.tsx"));
|
|
9881
9755
|
write(path40.join(config.paths.login, "page.tsx"), readTemplate("pages/login-page.tsx"));
|
|
9882
9756
|
write(path40.join(config.paths.login, "login-form.tsx"), readTemplate("pages/login-form.tsx"));
|
|
9757
|
+
const forgotPasswordDir = path40.join(cmsDir, "forgot-password");
|
|
9758
|
+
write(path40.join(forgotPasswordDir, "page.tsx"), readTemplate("pages/forgot-password-page.tsx"));
|
|
9759
|
+
write(path40.join(forgotPasswordDir, "forgot-password-form.tsx"), readTemplate("pages/forgot-password-form.tsx"));
|
|
9760
|
+
const resetPasswordDir = path40.join(cmsDir, "reset-password");
|
|
9761
|
+
write(path40.join(resetPasswordDir, "page.tsx"), readTemplate("pages/reset-password-page.tsx"));
|
|
9762
|
+
write(path40.join(resetPasswordDir, "reset-password-form.tsx"), readTemplate("pages/reset-password-form.tsx"));
|
|
9883
9763
|
write(path40.join(config.paths.pages, "page.tsx"), readTemplate("pages/dashboard-page.tsx"));
|
|
9884
9764
|
const usersDir = path40.join(config.paths.pages, "users");
|
|
9885
9765
|
write(path40.join(usersDir, "page.tsx"), readTemplate("pages/users/users-page.tsx"));
|
|
@@ -9900,7 +9780,7 @@ function scaffoldLayout({ cwd, config }) {
|
|
|
9900
9780
|
}
|
|
9901
9781
|
|
|
9902
9782
|
// src/init/scaffolders/preset.ts
|
|
9903
|
-
import
|
|
9783
|
+
import fs35 from "fs";
|
|
9904
9784
|
import path41 from "path";
|
|
9905
9785
|
|
|
9906
9786
|
// src/init/templates/presets/blog-categories.ts
|
|
@@ -10325,10 +10205,10 @@ function scaffoldPreset({
|
|
|
10325
10205
|
for (const ps of presetSchemas) {
|
|
10326
10206
|
const filePath = path41.join(schemasDir, ps.filename);
|
|
10327
10207
|
const dir = path41.dirname(filePath);
|
|
10328
|
-
if (!
|
|
10329
|
-
|
|
10208
|
+
if (!fs35.existsSync(dir)) {
|
|
10209
|
+
fs35.mkdirSync(dir, { recursive: true });
|
|
10330
10210
|
}
|
|
10331
|
-
|
|
10211
|
+
fs35.writeFileSync(filePath, ps.content, "utf-8");
|
|
10332
10212
|
result.schemas.push(ps.filename);
|
|
10333
10213
|
}
|
|
10334
10214
|
for (const ps of presetSchemas) {
|
|
@@ -10369,7 +10249,7 @@ function scaffoldPreset({
|
|
|
10369
10249
|
}
|
|
10370
10250
|
|
|
10371
10251
|
// src/init/scaffolders/tailwind.ts
|
|
10372
|
-
import
|
|
10252
|
+
import fs36 from "fs";
|
|
10373
10253
|
import path42 from "path";
|
|
10374
10254
|
var SOURCE_LINES = ['@source "../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
10375
10255
|
var SOURCE_LINES_SRC = ['@source "../../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
@@ -10426,7 +10306,7 @@ function findMainCss(cwd) {
|
|
|
10426
10306
|
];
|
|
10427
10307
|
for (const candidate of candidates) {
|
|
10428
10308
|
const filePath = path42.join(cwd, candidate);
|
|
10429
|
-
if (
|
|
10309
|
+
if (fs36.existsSync(filePath)) {
|
|
10430
10310
|
return filePath;
|
|
10431
10311
|
}
|
|
10432
10312
|
}
|
|
@@ -10437,7 +10317,7 @@ function scaffoldTailwind(cwd, hasSrcDir) {
|
|
|
10437
10317
|
if (!cssFile) {
|
|
10438
10318
|
return { file: null, appended: false };
|
|
10439
10319
|
}
|
|
10440
|
-
let content =
|
|
10320
|
+
let content = fs36.readFileSync(cssFile, "utf-8");
|
|
10441
10321
|
let changed = false;
|
|
10442
10322
|
const sourceLines = hasSrcDir ? SOURCE_LINES_SRC : SOURCE_LINES;
|
|
10443
10323
|
const missingLines = sourceLines.filter((sl) => !content.includes(sl));
|
|
@@ -10489,13 +10369,13 @@ ${CMS_THEME_BLOCK}
|
|
|
10489
10369
|
}
|
|
10490
10370
|
}
|
|
10491
10371
|
if (changed) {
|
|
10492
|
-
|
|
10372
|
+
fs36.writeFileSync(cssFile, content, "utf-8");
|
|
10493
10373
|
}
|
|
10494
10374
|
return { file: cssFile, appended: changed };
|
|
10495
10375
|
}
|
|
10496
10376
|
|
|
10497
10377
|
// src/init/scaffolders/tsconfig.ts
|
|
10498
|
-
import
|
|
10378
|
+
import fs37 from "fs";
|
|
10499
10379
|
import path43 from "path";
|
|
10500
10380
|
function stripJsonComments(input) {
|
|
10501
10381
|
let result = "";
|
|
@@ -10549,11 +10429,11 @@ function scaffoldTsconfig(cwd) {
|
|
|
10549
10429
|
const tsconfigPath = path43.join(cwd, "tsconfig.json");
|
|
10550
10430
|
const added = [];
|
|
10551
10431
|
const skipped = [];
|
|
10552
|
-
if (!
|
|
10432
|
+
if (!fs37.existsSync(tsconfigPath)) {
|
|
10553
10433
|
skipped.push("tsconfig.json not found");
|
|
10554
10434
|
return { added, skipped };
|
|
10555
10435
|
}
|
|
10556
|
-
const raw =
|
|
10436
|
+
const raw = fs37.readFileSync(tsconfigPath, "utf-8");
|
|
10557
10437
|
const stripped = stripJsonComments(raw).replace(/,\s*([\]}])/g, "$1");
|
|
10558
10438
|
let tsconfig;
|
|
10559
10439
|
try {
|
|
@@ -10574,13 +10454,13 @@ function scaffoldTsconfig(cwd) {
|
|
|
10574
10454
|
}
|
|
10575
10455
|
compilerOptions.paths = paths;
|
|
10576
10456
|
tsconfig.compilerOptions = compilerOptions;
|
|
10577
|
-
|
|
10457
|
+
fs37.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
10578
10458
|
`, "utf-8");
|
|
10579
10459
|
return { added, skipped };
|
|
10580
10460
|
}
|
|
10581
10461
|
|
|
10582
10462
|
// src/commands/seed.ts
|
|
10583
|
-
import
|
|
10463
|
+
import fs38 from "fs";
|
|
10584
10464
|
import path44 from "path";
|
|
10585
10465
|
import * as clack from "@clack/prompts";
|
|
10586
10466
|
import { Command as Command2 } from "commander";
|
|
@@ -10742,10 +10622,10 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
10742
10622
|
}
|
|
10743
10623
|
const scriptsDir = path44.join(cwd, cmsDir, "scripts");
|
|
10744
10624
|
const seedPath = path44.join(scriptsDir, "seed.ts");
|
|
10745
|
-
if (!
|
|
10746
|
-
|
|
10625
|
+
if (!fs38.existsSync(scriptsDir)) {
|
|
10626
|
+
fs38.mkdirSync(scriptsDir, { recursive: true });
|
|
10747
10627
|
}
|
|
10748
|
-
|
|
10628
|
+
fs38.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
10749
10629
|
const { execFile } = await import("child_process");
|
|
10750
10630
|
const tsxBin = path44.join(cwd, "node_modules", ".bin", "tsx");
|
|
10751
10631
|
const runSeed2 = (overwrite) => new Promise((resolve, reject) => {
|
|
@@ -10786,7 +10666,7 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
10786
10666
|
if (clack.isCancel(overwrite) || !overwrite) {
|
|
10787
10667
|
clack.cancel("Seed cancelled.");
|
|
10788
10668
|
try {
|
|
10789
|
-
|
|
10669
|
+
fs38.unlinkSync(seedPath);
|
|
10790
10670
|
} catch {
|
|
10791
10671
|
}
|
|
10792
10672
|
process.exit(0);
|
|
@@ -10809,9 +10689,9 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
10809
10689
|
process.exit(1);
|
|
10810
10690
|
}
|
|
10811
10691
|
try {
|
|
10812
|
-
|
|
10813
|
-
if (
|
|
10814
|
-
|
|
10692
|
+
fs38.unlinkSync(seedPath);
|
|
10693
|
+
if (fs38.existsSync(scriptsDir) && fs38.readdirSync(scriptsDir).length === 0) {
|
|
10694
|
+
fs38.rmdirSync(scriptsDir);
|
|
10815
10695
|
}
|
|
10816
10696
|
} catch {
|
|
10817
10697
|
}
|
|
@@ -10819,7 +10699,7 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
10819
10699
|
});
|
|
10820
10700
|
|
|
10821
10701
|
// src/commands/init.ts
|
|
10822
|
-
var initCommand = new Command3("init").description("Scaffold CMS into a new or existing Next.js project").argument("[name]", "Project name (creates new directory if fresh project)").option("--preset <preset>", "Starter preset: blank, blog, or full", "blog").option("-y, --yes", "Skip all prompts (accept defaults)").option(
|
|
10702
|
+
var initCommand = new Command3("init").description("Scaffold CMS into a new or existing Next.js project").argument("[name]", "Project name (creates new directory if fresh project)").option("--preset <preset>", "Starter preset: blank, blog, or full", "blog").option("-y, --yes", "Skip all prompts (accept defaults)").option("--skip-migration", "Skip running drizzle-kit push after scaffolding").option("--skip-admin-creation", "Skip prompting to create the initial admin user").option("--skip-dev-server-start", "Skip prompting to start the development server").option(
|
|
10823
10703
|
"--database-url <url>",
|
|
10824
10704
|
"PostgreSQL database connection string (postgres:// or postgresql://)"
|
|
10825
10705
|
).option("--force", "Overwrite all existing CMS files (nuclear option)").action(
|
|
@@ -10843,15 +10723,15 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
10843
10723
|
let nuked = 0;
|
|
10844
10724
|
for (const dir of nukeDirs) {
|
|
10845
10725
|
const fullPath = path45.resolve(cwd, dir);
|
|
10846
|
-
if (
|
|
10847
|
-
|
|
10726
|
+
if (fs39.existsSync(fullPath)) {
|
|
10727
|
+
fs39.rmSync(fullPath, { recursive: true, force: true });
|
|
10848
10728
|
nuked++;
|
|
10849
10729
|
}
|
|
10850
10730
|
}
|
|
10851
10731
|
for (const file of nukeFiles) {
|
|
10852
10732
|
const fullPath = path45.resolve(cwd, file);
|
|
10853
|
-
if (
|
|
10854
|
-
|
|
10733
|
+
if (fs39.existsSync(fullPath)) {
|
|
10734
|
+
fs39.unlinkSync(fullPath);
|
|
10855
10735
|
nuked++;
|
|
10856
10736
|
}
|
|
10857
10737
|
}
|
|
@@ -10931,9 +10811,9 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
10931
10811
|
process.exit(1);
|
|
10932
10812
|
}
|
|
10933
10813
|
cwd = path45.resolve(cwd, projectPrompt.projectName);
|
|
10934
|
-
const hasPackageJson =
|
|
10814
|
+
const hasPackageJson = fs39.existsSync(path45.join(cwd, "package.json"));
|
|
10935
10815
|
const hasNextConfig = ["next.config.ts", "next.config.js", "next.config.mjs"].some(
|
|
10936
|
-
(f) =>
|
|
10816
|
+
(f) => fs39.existsSync(path45.join(cwd, f))
|
|
10937
10817
|
);
|
|
10938
10818
|
if (!hasPackageJson || !hasNextConfig) {
|
|
10939
10819
|
p4.log.error(
|
|
@@ -11035,10 +10915,10 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
11035
10915
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
11036
10916
|
p4.note(noteLines.join("\n"), "Scaffolded CMS");
|
|
11037
10917
|
const drizzleConfigPath = path45.join(cwd, "drizzle.config.ts");
|
|
11038
|
-
if (!dbFiles.includes("drizzle.config.ts") &&
|
|
10918
|
+
if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
|
|
11039
10919
|
if (options.force) {
|
|
11040
10920
|
const { readTemplate: readTemplate2 } = await import("./reader-2T45D7JZ.js");
|
|
11041
|
-
|
|
10921
|
+
fs39.writeFileSync(drizzleConfigPath, readTemplate2("drizzle.config.ts"), "utf-8");
|
|
11042
10922
|
p4.log.success("Updated drizzle.config.ts");
|
|
11043
10923
|
} else if (!options.yes) {
|
|
11044
10924
|
const overwrite = await p4.confirm({
|
|
@@ -11047,7 +10927,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
11047
10927
|
});
|
|
11048
10928
|
if (!p4.isCancel(overwrite) && overwrite) {
|
|
11049
10929
|
const { readTemplate: readTemplate2 } = await import("./reader-2T45D7JZ.js");
|
|
11050
|
-
|
|
10930
|
+
fs39.writeFileSync(drizzleConfigPath, readTemplate2("drizzle.config.ts"), "utf-8");
|
|
11051
10931
|
p4.log.success("Updated drizzle.config.ts");
|
|
11052
10932
|
}
|
|
11053
10933
|
}
|
|
@@ -11077,27 +10957,6 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
11077
10957
|
}
|
|
11078
10958
|
s.start(`Applying ${features.preset} preset`);
|
|
11079
10959
|
const presetResult = scaffoldPreset({ cwd, config, preset: features.preset });
|
|
11080
|
-
{
|
|
11081
|
-
const entityNames = [];
|
|
11082
|
-
const formNames = [];
|
|
11083
|
-
const schemasDir = path45.join(cwd, config.paths.schemas);
|
|
11084
|
-
const formsDir = path45.join(schemasDir, "forms");
|
|
11085
|
-
if (fs40.existsSync(schemasDir)) {
|
|
11086
|
-
for (const f of fs40.readdirSync(schemasDir)) {
|
|
11087
|
-
if (f.endsWith(".json")) entityNames.push(f.replace(".json", ""));
|
|
11088
|
-
}
|
|
11089
|
-
}
|
|
11090
|
-
if (fs40.existsSync(formsDir)) {
|
|
11091
|
-
for (const f of fs40.readdirSync(formsDir)) {
|
|
11092
|
-
if (f.endsWith(".json")) formNames.push(f.replace(".json", ""));
|
|
11093
|
-
}
|
|
11094
|
-
}
|
|
11095
|
-
regenerateCmsDoc(cwd, config, {
|
|
11096
|
-
preset: features.preset,
|
|
11097
|
-
schemas: entityNames,
|
|
11098
|
-
forms: formNames
|
|
11099
|
-
});
|
|
11100
|
-
}
|
|
11101
10960
|
s.stop("");
|
|
11102
10961
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
11103
10962
|
const installLines = [];
|
|
@@ -11120,7 +10979,9 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
11120
10979
|
}
|
|
11121
10980
|
p4.note(installLines.join("\n"), "Installed");
|
|
11122
10981
|
let dbPushed = false;
|
|
11123
|
-
if (depsResult.success && hasDbUrl(cwd)) {
|
|
10982
|
+
if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
|
|
10983
|
+
p4.log.info(`Skipping database schema push ${pc2.dim("(--skip-migration)")}`);
|
|
10984
|
+
} else if (depsResult.success && hasDbUrl(cwd)) {
|
|
11124
10985
|
s.start("Pushing database schema (drizzle-kit push)");
|
|
11125
10986
|
const pushResult = await runDrizzlePush(cwd);
|
|
11126
10987
|
if (pushResult.success) {
|
|
@@ -11135,7 +10996,10 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
11135
10996
|
let seedEmail;
|
|
11136
10997
|
let seedPassword;
|
|
11137
10998
|
let seedSuccess = false;
|
|
11138
|
-
if (dbPushed &&
|
|
10999
|
+
if (dbPushed && options.skipAdminCreation) {
|
|
11000
|
+
p4.log.info(`Skipping admin user creation ${pc2.dim("(use npx betterstart seed later)")}`);
|
|
11001
|
+
}
|
|
11002
|
+
if (dbPushed && !options.yes && !options.skipAdminCreation) {
|
|
11139
11003
|
p4.note(pc2.dim("Create your first admin user to access the CMS."), "Admin account");
|
|
11140
11004
|
const credentials = await p4.group(
|
|
11141
11005
|
{
|
|
@@ -11247,7 +11111,7 @@ Run manually: ${pc2.cyan("npx betterstart seed")}`,
|
|
|
11247
11111
|
);
|
|
11248
11112
|
summaryLines.push("", "Next steps:", ...nextSteps);
|
|
11249
11113
|
p4.note(summaryLines.join("\n"), "CMS scaffolded successfully");
|
|
11250
|
-
if (!options.yes) {
|
|
11114
|
+
if (!options.yes && !options.skipDevServerStart) {
|
|
11251
11115
|
const devCmd = runCommand(pm, "dev");
|
|
11252
11116
|
const startDev = await p4.confirm({
|
|
11253
11117
|
message: "Start the development server?",
|
|
@@ -11268,8 +11132,8 @@ function isValidDbUrl(url) {
|
|
|
11268
11132
|
}
|
|
11269
11133
|
function readExistingDbUrl(cwd) {
|
|
11270
11134
|
const envPath = path45.join(cwd, ".env.local");
|
|
11271
|
-
if (!
|
|
11272
|
-
const content =
|
|
11135
|
+
if (!fs39.existsSync(envPath)) return void 0;
|
|
11136
|
+
const content = fs39.readFileSync(envPath, "utf-8");
|
|
11273
11137
|
for (const line of content.split("\n")) {
|
|
11274
11138
|
const trimmed = line.trim();
|
|
11275
11139
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -11293,8 +11157,8 @@ function maskDbUrl(url) {
|
|
|
11293
11157
|
}
|
|
11294
11158
|
function hasDbUrl(cwd) {
|
|
11295
11159
|
const envPath = path45.join(cwd, ".env.local");
|
|
11296
|
-
if (!
|
|
11297
|
-
const content =
|
|
11160
|
+
if (!fs39.existsSync(envPath)) return false;
|
|
11161
|
+
const content = fs39.readFileSync(envPath, "utf-8");
|
|
11298
11162
|
for (const line of content.split("\n")) {
|
|
11299
11163
|
const trimmed = line.trim();
|
|
11300
11164
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -11310,15 +11174,15 @@ function hasDbUrl(cwd) {
|
|
|
11310
11174
|
function runSeed(cwd, cmsDir, email, password4, overwrite = false) {
|
|
11311
11175
|
const scriptsDir = path45.join(cwd, cmsDir, "scripts");
|
|
11312
11176
|
const seedPath = path45.join(scriptsDir, "seed.ts");
|
|
11313
|
-
if (!
|
|
11314
|
-
|
|
11177
|
+
if (!fs39.existsSync(scriptsDir)) {
|
|
11178
|
+
fs39.mkdirSync(scriptsDir, { recursive: true });
|
|
11315
11179
|
}
|
|
11316
|
-
|
|
11180
|
+
fs39.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
11317
11181
|
const cleanup = () => {
|
|
11318
11182
|
try {
|
|
11319
|
-
|
|
11320
|
-
if (
|
|
11321
|
-
|
|
11183
|
+
fs39.unlinkSync(seedPath);
|
|
11184
|
+
if (fs39.existsSync(scriptsDir) && fs39.readdirSync(scriptsDir).length === 0) {
|
|
11185
|
+
fs39.rmdirSync(scriptsDir);
|
|
11322
11186
|
}
|
|
11323
11187
|
} catch {
|
|
11324
11188
|
}
|
|
@@ -11410,7 +11274,7 @@ function runDrizzlePush(cwd) {
|
|
|
11410
11274
|
}
|
|
11411
11275
|
|
|
11412
11276
|
// src/commands/remove.ts
|
|
11413
|
-
import
|
|
11277
|
+
import fs40 from "fs";
|
|
11414
11278
|
import path46 from "path";
|
|
11415
11279
|
import readline from "readline";
|
|
11416
11280
|
import { Command as Command4 } from "commander";
|
|
@@ -11442,8 +11306,8 @@ function findTableEnd3(content, startIndex) {
|
|
|
11442
11306
|
return content.length;
|
|
11443
11307
|
}
|
|
11444
11308
|
function removeTableFromSchema(schemaFilePath, name) {
|
|
11445
|
-
if (!
|
|
11446
|
-
let content =
|
|
11309
|
+
if (!fs40.existsSync(schemaFilePath)) return false;
|
|
11310
|
+
let content = fs40.readFileSync(schemaFilePath, "utf-8");
|
|
11447
11311
|
const variableName = toCamelCase(name);
|
|
11448
11312
|
let changed = false;
|
|
11449
11313
|
if (content.includes(`export const ${variableName} =`)) {
|
|
@@ -11471,13 +11335,13 @@ function removeTableFromSchema(schemaFilePath, name) {
|
|
|
11471
11335
|
}
|
|
11472
11336
|
if (changed) {
|
|
11473
11337
|
content = content.replace(/\n{3,}/g, "\n\n");
|
|
11474
|
-
|
|
11338
|
+
fs40.writeFileSync(schemaFilePath, content, "utf-8");
|
|
11475
11339
|
}
|
|
11476
11340
|
return changed;
|
|
11477
11341
|
}
|
|
11478
11342
|
function removeFromNavigation(navFilePath, name) {
|
|
11479
|
-
if (!
|
|
11480
|
-
const content =
|
|
11343
|
+
if (!fs40.existsSync(navFilePath)) return false;
|
|
11344
|
+
const content = fs40.readFileSync(navFilePath, "utf-8");
|
|
11481
11345
|
const href = `/cms/${name}`;
|
|
11482
11346
|
if (!content.includes(`'${href}'`)) return false;
|
|
11483
11347
|
const lines = content.split("\n");
|
|
@@ -11508,7 +11372,7 @@ function removeFromNavigation(navFilePath, name) {
|
|
|
11508
11372
|
if (startLine === -1 || endLine === -1) return false;
|
|
11509
11373
|
lines.splice(startLine, endLine - startLine + 1);
|
|
11510
11374
|
const updated = lines.join("\n").replace(/,\s*,/g, ",").replace(/\[\s*,/, "[");
|
|
11511
|
-
|
|
11375
|
+
fs40.writeFileSync(navFilePath, updated, "utf-8");
|
|
11512
11376
|
return true;
|
|
11513
11377
|
}
|
|
11514
11378
|
async function promptConfirm(message) {
|
|
@@ -11538,7 +11402,7 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
11538
11402
|
const kebabName = toKebabCase(schemaName);
|
|
11539
11403
|
const targets = [];
|
|
11540
11404
|
const entityPagesDir = path46.join(cwd, pagesDir, schemaName);
|
|
11541
|
-
if (
|
|
11405
|
+
if (fs40.existsSync(entityPagesDir)) {
|
|
11542
11406
|
targets.push({
|
|
11543
11407
|
path: entityPagesDir,
|
|
11544
11408
|
label: `${path46.join(pagesDir, schemaName)}/`,
|
|
@@ -11547,13 +11411,13 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
11547
11411
|
}
|
|
11548
11412
|
const actionsDir = path46.join(cwd, cmsDir, "lib", "actions", kebabName);
|
|
11549
11413
|
const actionsFile = path46.join(cwd, cmsDir, "lib", "actions", `${kebabName}.ts`);
|
|
11550
|
-
if (
|
|
11414
|
+
if (fs40.existsSync(actionsDir)) {
|
|
11551
11415
|
targets.push({
|
|
11552
11416
|
path: actionsDir,
|
|
11553
11417
|
label: `${path46.join(cmsDir, "lib", "actions", kebabName)}/`,
|
|
11554
11418
|
isDir: true
|
|
11555
11419
|
});
|
|
11556
|
-
} else if (
|
|
11420
|
+
} else if (fs40.existsSync(actionsFile)) {
|
|
11557
11421
|
targets.push({
|
|
11558
11422
|
path: actionsFile,
|
|
11559
11423
|
label: path46.join(cmsDir, "lib", "actions", `${kebabName}.ts`),
|
|
@@ -11561,7 +11425,7 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
11561
11425
|
});
|
|
11562
11426
|
}
|
|
11563
11427
|
const hookFile = path46.join(cwd, cmsDir, "hooks", `use-${kebabName}.ts`);
|
|
11564
|
-
if (
|
|
11428
|
+
if (fs40.existsSync(hookFile)) {
|
|
11565
11429
|
targets.push({
|
|
11566
11430
|
path: hookFile,
|
|
11567
11431
|
label: path46.join(cmsDir, "hooks", `use-${kebabName}.ts`),
|
|
@@ -11569,9 +11433,9 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
11569
11433
|
});
|
|
11570
11434
|
}
|
|
11571
11435
|
const schemaFilePath = path46.join(cwd, cmsDir, "db", "schema.ts");
|
|
11572
|
-
const hasTable =
|
|
11436
|
+
const hasTable = fs40.existsSync(schemaFilePath) && fs40.readFileSync(schemaFilePath, "utf-8").includes(`export const ${toCamelCase(schemaName)} =`);
|
|
11573
11437
|
const navFilePath = path46.join(cwd, cmsDir, "data", "navigation.ts");
|
|
11574
|
-
const hasNavEntry =
|
|
11438
|
+
const hasNavEntry = fs40.existsSync(navFilePath) && fs40.readFileSync(navFilePath, "utf-8").includes(`'/cms/${schemaName}'`);
|
|
11575
11439
|
if (targets.length === 0 && !hasTable && !hasNavEntry) {
|
|
11576
11440
|
console.log(` No generated files found for: ${schemaName}`);
|
|
11577
11441
|
return;
|
|
@@ -11597,9 +11461,9 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
11597
11461
|
console.log("");
|
|
11598
11462
|
for (const t of targets) {
|
|
11599
11463
|
if (t.isDir) {
|
|
11600
|
-
|
|
11464
|
+
fs40.rmSync(t.path, { recursive: true, force: true });
|
|
11601
11465
|
} else {
|
|
11602
|
-
|
|
11466
|
+
fs40.unlinkSync(t.path);
|
|
11603
11467
|
}
|
|
11604
11468
|
console.log(` Removed: ${t.label}`);
|
|
11605
11469
|
}
|
|
@@ -11621,7 +11485,7 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
11621
11485
|
|
|
11622
11486
|
// src/commands/setup-r2.ts
|
|
11623
11487
|
import { execFileSync as execFileSync5, spawnSync } from "child_process";
|
|
11624
|
-
import
|
|
11488
|
+
import fs41 from "fs";
|
|
11625
11489
|
import os from "os";
|
|
11626
11490
|
import path47 from "path";
|
|
11627
11491
|
import * as p5 from "@clack/prompts";
|
|
@@ -11821,7 +11685,7 @@ var setupR2Command = new Command5("setup-r2").description("Create a Cloudflare R
|
|
|
11821
11685
|
});
|
|
11822
11686
|
function findWrangler(cwd) {
|
|
11823
11687
|
const localBin = path47.join(cwd, "node_modules", ".bin", "wrangler");
|
|
11824
|
-
if (
|
|
11688
|
+
if (fs41.existsSync(localBin)) return { bin: localBin, prefix: [] };
|
|
11825
11689
|
const result = spawnSync("which", ["wrangler"], { stdio: "pipe", timeout: 5e3 });
|
|
11826
11690
|
if (result.status === 0) {
|
|
11827
11691
|
const found = result.stdout?.toString().trim();
|
|
@@ -11869,9 +11733,9 @@ function readWranglerToken() {
|
|
|
11869
11733
|
);
|
|
11870
11734
|
}
|
|
11871
11735
|
for (const configPath of candidates) {
|
|
11872
|
-
if (!
|
|
11736
|
+
if (!fs41.existsSync(configPath)) continue;
|
|
11873
11737
|
try {
|
|
11874
|
-
const content =
|
|
11738
|
+
const content = fs41.readFileSync(configPath, "utf-8");
|
|
11875
11739
|
const match = content.match(/^oauth_token\s*=\s*"([^"]+)"/m);
|
|
11876
11740
|
if (match) return match[1];
|
|
11877
11741
|
} catch {
|
|
@@ -11903,14 +11767,14 @@ async function enablePublicDomain(accountId, bucketName, token) {
|
|
|
11903
11767
|
}
|
|
11904
11768
|
|
|
11905
11769
|
// src/commands/uninstall.ts
|
|
11906
|
-
import
|
|
11770
|
+
import fs43 from "fs";
|
|
11907
11771
|
import path48 from "path";
|
|
11908
11772
|
import * as p6 from "@clack/prompts";
|
|
11909
11773
|
import { Command as Command6 } from "commander";
|
|
11910
11774
|
import pc4 from "picocolors";
|
|
11911
11775
|
|
|
11912
11776
|
// src/commands/uninstall-cleaners.ts
|
|
11913
|
-
import
|
|
11777
|
+
import fs42 from "fs";
|
|
11914
11778
|
function stripJsonComments2(input) {
|
|
11915
11779
|
let result = "";
|
|
11916
11780
|
let i = 0;
|
|
@@ -11944,8 +11808,8 @@ function stripJsonComments2(input) {
|
|
|
11944
11808
|
return result;
|
|
11945
11809
|
}
|
|
11946
11810
|
function cleanTsconfig(tsconfigPath) {
|
|
11947
|
-
if (!
|
|
11948
|
-
const raw =
|
|
11811
|
+
if (!fs42.existsSync(tsconfigPath)) return [];
|
|
11812
|
+
const raw = fs42.readFileSync(tsconfigPath, "utf-8");
|
|
11949
11813
|
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
11950
11814
|
let tsconfig;
|
|
11951
11815
|
try {
|
|
@@ -11969,13 +11833,13 @@ function cleanTsconfig(tsconfigPath) {
|
|
|
11969
11833
|
compilerOptions.paths = paths;
|
|
11970
11834
|
}
|
|
11971
11835
|
tsconfig.compilerOptions = compilerOptions;
|
|
11972
|
-
|
|
11836
|
+
fs42.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
11973
11837
|
`, "utf-8");
|
|
11974
11838
|
return removed;
|
|
11975
11839
|
}
|
|
11976
11840
|
function cleanCss(cssPath) {
|
|
11977
|
-
if (!
|
|
11978
|
-
const content =
|
|
11841
|
+
if (!fs42.existsSync(cssPath)) return [];
|
|
11842
|
+
const content = fs42.readFileSync(cssPath, "utf-8");
|
|
11979
11843
|
const lines = content.split("\n");
|
|
11980
11844
|
const sourcePattern = /^@source\s+"[^"]*cms[^"]*";\s*$/;
|
|
11981
11845
|
const removed = [];
|
|
@@ -11989,12 +11853,12 @@ function cleanCss(cssPath) {
|
|
|
11989
11853
|
}
|
|
11990
11854
|
if (removed.length === 0) return [];
|
|
11991
11855
|
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
11992
|
-
|
|
11856
|
+
fs42.writeFileSync(cssPath, cleaned, "utf-8");
|
|
11993
11857
|
return removed;
|
|
11994
11858
|
}
|
|
11995
11859
|
function cleanEnvFile(envPath) {
|
|
11996
|
-
if (!
|
|
11997
|
-
const content =
|
|
11860
|
+
if (!fs42.existsSync(envPath)) return [];
|
|
11861
|
+
const content = fs42.readFileSync(envPath, "utf-8");
|
|
11998
11862
|
const lines = content.split("\n");
|
|
11999
11863
|
const removed = [];
|
|
12000
11864
|
const kept = [];
|
|
@@ -12027,9 +11891,9 @@ function cleanEnvFile(envPath) {
|
|
|
12027
11891
|
if (removed.length === 0) return [];
|
|
12028
11892
|
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
12029
11893
|
if (result === "") {
|
|
12030
|
-
|
|
11894
|
+
fs42.unlinkSync(envPath);
|
|
12031
11895
|
} else {
|
|
12032
|
-
|
|
11896
|
+
fs42.writeFileSync(envPath, `${result}
|
|
12033
11897
|
`, "utf-8");
|
|
12034
11898
|
}
|
|
12035
11899
|
return removed;
|
|
@@ -12056,14 +11920,14 @@ function findMainCss2(cwd) {
|
|
|
12056
11920
|
];
|
|
12057
11921
|
for (const candidate of candidates) {
|
|
12058
11922
|
const filePath = path48.join(cwd, candidate);
|
|
12059
|
-
if (
|
|
11923
|
+
if (fs43.existsSync(filePath)) return filePath;
|
|
12060
11924
|
}
|
|
12061
11925
|
return void 0;
|
|
12062
11926
|
}
|
|
12063
11927
|
function isCLICreatedBiome(biomePath) {
|
|
12064
|
-
if (!
|
|
11928
|
+
if (!fs43.existsSync(biomePath)) return false;
|
|
12065
11929
|
try {
|
|
12066
|
-
const content = JSON.parse(
|
|
11930
|
+
const content = JSON.parse(fs43.readFileSync(biomePath, "utf-8"));
|
|
12067
11931
|
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");
|
|
12068
11932
|
} catch {
|
|
12069
11933
|
return false;
|
|
@@ -12071,13 +11935,13 @@ function isCLICreatedBiome(biomePath) {
|
|
|
12071
11935
|
}
|
|
12072
11936
|
function buildUninstallPlan(cwd) {
|
|
12073
11937
|
const steps = [];
|
|
12074
|
-
const hasSrc =
|
|
11938
|
+
const hasSrc = fs43.existsSync(path48.join(cwd, "src"));
|
|
12075
11939
|
const appBase = hasSrc ? "src/app" : "app";
|
|
12076
11940
|
const dirs = [];
|
|
12077
11941
|
const cmsDir = path48.join(cwd, "cms");
|
|
12078
11942
|
const cmsRouteGroup = path48.join(cwd, appBase, "(cms)");
|
|
12079
|
-
if (
|
|
12080
|
-
if (
|
|
11943
|
+
if (fs43.existsSync(cmsDir)) dirs.push("cms/");
|
|
11944
|
+
if (fs43.existsSync(cmsRouteGroup)) dirs.push(`${appBase}/(cms)/`);
|
|
12081
11945
|
if (dirs.length > 0) {
|
|
12082
11946
|
steps.push({
|
|
12083
11947
|
label: "CMS directories",
|
|
@@ -12085,8 +11949,8 @@ function buildUninstallPlan(cwd) {
|
|
|
12085
11949
|
count: dirs.length,
|
|
12086
11950
|
unit: dirs.length === 1 ? "directory" : "directories",
|
|
12087
11951
|
execute() {
|
|
12088
|
-
if (
|
|
12089
|
-
if (
|
|
11952
|
+
if (fs43.existsSync(cmsDir)) fs43.rmSync(cmsDir, { recursive: true, force: true });
|
|
11953
|
+
if (fs43.existsSync(cmsRouteGroup)) fs43.rmSync(cmsRouteGroup, { recursive: true, force: true });
|
|
12090
11954
|
}
|
|
12091
11955
|
});
|
|
12092
11956
|
}
|
|
@@ -12098,7 +11962,7 @@ function buildUninstallPlan(cwd) {
|
|
|
12098
11962
|
["CMS.md", path48.join(cwd, "CMS.md")]
|
|
12099
11963
|
];
|
|
12100
11964
|
for (const [label, fullPath] of candidates) {
|
|
12101
|
-
if (
|
|
11965
|
+
if (fs43.existsSync(fullPath)) {
|
|
12102
11966
|
configFiles.push(label);
|
|
12103
11967
|
configPaths.push(fullPath);
|
|
12104
11968
|
}
|
|
@@ -12116,14 +11980,14 @@ function buildUninstallPlan(cwd) {
|
|
|
12116
11980
|
unit: configFiles.length === 1 ? "file" : "files",
|
|
12117
11981
|
execute() {
|
|
12118
11982
|
for (const p7 of configPaths) {
|
|
12119
|
-
if (
|
|
11983
|
+
if (fs43.existsSync(p7)) fs43.unlinkSync(p7);
|
|
12120
11984
|
}
|
|
12121
11985
|
}
|
|
12122
11986
|
});
|
|
12123
11987
|
}
|
|
12124
11988
|
const tsconfigPath = path48.join(cwd, "tsconfig.json");
|
|
12125
|
-
if (
|
|
12126
|
-
const content =
|
|
11989
|
+
if (fs43.existsSync(tsconfigPath)) {
|
|
11990
|
+
const content = fs43.readFileSync(tsconfigPath, "utf-8");
|
|
12127
11991
|
const aliasMatches = content.match(/"@cms\//g);
|
|
12128
11992
|
if (aliasMatches && aliasMatches.length > 0) {
|
|
12129
11993
|
const aliasCount = aliasMatches.length;
|
|
@@ -12140,7 +12004,7 @@ function buildUninstallPlan(cwd) {
|
|
|
12140
12004
|
}
|
|
12141
12005
|
const cssFile = findMainCss2(cwd);
|
|
12142
12006
|
if (cssFile) {
|
|
12143
|
-
const cssContent =
|
|
12007
|
+
const cssContent = fs43.readFileSync(cssFile, "utf-8");
|
|
12144
12008
|
const sourceLines = cssContent.split("\n").filter((l) => /^@source\s+"[^"]*cms[^"]*";\s*$/.test(l));
|
|
12145
12009
|
if (sourceLines.length > 0) {
|
|
12146
12010
|
const relCss = path48.relative(cwd, cssFile);
|
|
@@ -12156,8 +12020,8 @@ function buildUninstallPlan(cwd) {
|
|
|
12156
12020
|
}
|
|
12157
12021
|
}
|
|
12158
12022
|
const envPath = path48.join(cwd, ".env.local");
|
|
12159
|
-
if (
|
|
12160
|
-
const envContent =
|
|
12023
|
+
if (fs43.existsSync(envPath)) {
|
|
12024
|
+
const envContent = fs43.readFileSync(envPath, "utf-8");
|
|
12161
12025
|
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
12162
12026
|
if (bsVars.length > 0) {
|
|
12163
12027
|
steps.push({
|
|
@@ -12211,7 +12075,7 @@ var uninstallCommand = new Command6("uninstall").description("Remove all CMS fil
|
|
|
12211
12075
|
});
|
|
12212
12076
|
|
|
12213
12077
|
// src/commands/update-component.ts
|
|
12214
|
-
import
|
|
12078
|
+
import fs44 from "fs";
|
|
12215
12079
|
import path49 from "path";
|
|
12216
12080
|
import * as clack2 from "@clack/prompts";
|
|
12217
12081
|
import { Command as Command7 } from "commander";
|
|
@@ -12257,6 +12121,13 @@ var TEMPLATE_REGISTRY = {
|
|
|
12257
12121
|
"account-layout": { relPath: "app/(cms)/cms/(account)/layout.tsx", content: () => readTemplate("pages/account-layout.tsx"), base: "cwd" },
|
|
12258
12122
|
"profile-page": { relPath: "app/(cms)/cms/(account)/profile/page.tsx", content: () => readTemplate("pages/profile/profile-page.tsx"), base: "cwd" },
|
|
12259
12123
|
"profile-form": { relPath: "app/(cms)/cms/(account)/profile/profile-form.tsx", content: () => readTemplate("pages/profile/profile-form.tsx"), base: "cwd" },
|
|
12124
|
+
// Password reset
|
|
12125
|
+
"login-page": { relPath: "app/(cms)/cms/login/page.tsx", content: () => readTemplate("pages/login-page.tsx"), base: "cwd" },
|
|
12126
|
+
"login-form": { relPath: "app/(cms)/cms/login/login-form.tsx", content: () => readTemplate("pages/login-form.tsx"), base: "cwd" },
|
|
12127
|
+
"forgot-password-page": { relPath: "app/(cms)/cms/forgot-password/page.tsx", content: () => readTemplate("pages/forgot-password-page.tsx"), base: "cwd" },
|
|
12128
|
+
"forgot-password-form": { relPath: "app/(cms)/cms/forgot-password/forgot-password-form.tsx", content: () => readTemplate("pages/forgot-password-form.tsx"), base: "cwd" },
|
|
12129
|
+
"reset-password-page": { relPath: "app/(cms)/cms/reset-password/page.tsx", content: () => readTemplate("pages/reset-password-page.tsx"), base: "cwd" },
|
|
12130
|
+
"reset-password-form": { relPath: "app/(cms)/cms/reset-password/reset-password-form.tsx", content: () => readTemplate("pages/reset-password-form.tsx"), base: "cwd" },
|
|
12260
12131
|
// Lib
|
|
12261
12132
|
r2: { relPath: "lib/r2.ts", content: () => readTemplate("lib/r2.ts") },
|
|
12262
12133
|
"form-settings-action": { relPath: "lib/actions/form-settings.ts", content: () => readTemplate("lib/actions/form-settings.ts") },
|
|
@@ -12271,8 +12142,8 @@ var TEMPLATE_REGISTRY = {
|
|
|
12271
12142
|
function getStaticUiComponents() {
|
|
12272
12143
|
const cliRoot = findCliRoot();
|
|
12273
12144
|
const uiDir = path49.join(cliRoot, "templates", "ui");
|
|
12274
|
-
if (!
|
|
12275
|
-
return
|
|
12145
|
+
if (!fs44.existsSync(uiDir)) return [];
|
|
12146
|
+
return fs44.readdirSync(uiDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => f.replace(/\.(tsx|ts)$/, ""));
|
|
12276
12147
|
}
|
|
12277
12148
|
function getAllComponentNames() {
|
|
12278
12149
|
const staticUi = getStaticUiComponents();
|
|
@@ -12316,7 +12187,7 @@ var updateComponentCommand = new Command7("update").alias("update-component").de
|
|
|
12316
12187
|
}
|
|
12317
12188
|
const config = await resolveConfig(cwd);
|
|
12318
12189
|
const cms = path49.resolve(cwd, config.paths.cms);
|
|
12319
|
-
if (!
|
|
12190
|
+
if (!fs44.existsSync(cms)) {
|
|
12320
12191
|
clack2.cancel(`CMS directory not found at ${config.paths.cms}. Run 'betterstart init' first.`);
|
|
12321
12192
|
process.exit(1);
|
|
12322
12193
|
}
|
|
@@ -12332,18 +12203,18 @@ var updateComponentCommand = new Command7("update").alias("update-component").de
|
|
|
12332
12203
|
const baseDir = entry.base === "cwd" ? cwd : cms;
|
|
12333
12204
|
const destPath = path49.join(baseDir, entry.relPath);
|
|
12334
12205
|
fsExtra.ensureDirSync(path49.dirname(destPath));
|
|
12335
|
-
|
|
12206
|
+
fs44.writeFileSync(destPath, entry.content(), "utf-8");
|
|
12336
12207
|
clack2.log.success(`Updated ${entry.relPath}`);
|
|
12337
12208
|
updated++;
|
|
12338
12209
|
continue;
|
|
12339
12210
|
}
|
|
12340
|
-
const uiFile =
|
|
12211
|
+
const uiFile = fs44.readdirSync(uiDir).find(
|
|
12341
12212
|
(f) => f.replace(/\.(tsx|ts)$/, "") === name
|
|
12342
12213
|
);
|
|
12343
12214
|
if (uiFile) {
|
|
12344
12215
|
const destPath = path49.join(cms, "components", "ui", uiFile);
|
|
12345
12216
|
fsExtra.ensureDirSync(path49.dirname(destPath));
|
|
12346
|
-
|
|
12217
|
+
fs44.copyFileSync(path49.join(uiDir, uiFile), destPath);
|
|
12347
12218
|
clack2.log.success(`Updated components/ui/${uiFile}`);
|
|
12348
12219
|
updated++;
|
|
12349
12220
|
continue;
|
|
@@ -12351,7 +12222,7 @@ var updateComponentCommand = new Command7("update").alias("update-component").de
|
|
|
12351
12222
|
if (name === "tiptap") {
|
|
12352
12223
|
const srcDir = path49.join(cliRoot, "templates", "tiptap");
|
|
12353
12224
|
const destDir = path49.join(cms, "components", "ui", "tiptap");
|
|
12354
|
-
if (
|
|
12225
|
+
if (fs44.existsSync(srcDir)) {
|
|
12355
12226
|
fsExtra.copySync(srcDir, destDir, { overwrite: true });
|
|
12356
12227
|
clack2.log.success("Updated components/ui/tiptap/ (all files)");
|
|
12357
12228
|
updated++;
|
|
@@ -12397,7 +12268,7 @@ var updateDepsCommand = new Command8("update-deps").description("Install or upda
|
|
|
12397
12268
|
});
|
|
12398
12269
|
|
|
12399
12270
|
// src/commands/update-styles.ts
|
|
12400
|
-
import
|
|
12271
|
+
import fs45 from "fs";
|
|
12401
12272
|
import path51 from "path";
|
|
12402
12273
|
import * as clack4 from "@clack/prompts";
|
|
12403
12274
|
import { Command as Command9 } from "commander";
|
|
@@ -12407,11 +12278,11 @@ var updateStylesCommand = new Command9("update-styles").description("Replace cms
|
|
|
12407
12278
|
const config = await resolveConfig(cwd);
|
|
12408
12279
|
const cmsDir = config.paths?.cms ?? "./cms";
|
|
12409
12280
|
const targetPath = path51.join(cwd, cmsDir, "cms-globals.css");
|
|
12410
|
-
if (!
|
|
12281
|
+
if (!fs45.existsSync(targetPath)) {
|
|
12411
12282
|
clack4.cancel(`cms-globals.css not found at ${path51.relative(cwd, targetPath)}`);
|
|
12412
12283
|
process.exit(1);
|
|
12413
12284
|
}
|
|
12414
|
-
|
|
12285
|
+
fs45.writeFileSync(targetPath, readTemplate("cms-globals.css"), "utf-8");
|
|
12415
12286
|
clack4.log.success(`Updated ${path51.relative(cwd, targetPath)}`);
|
|
12416
12287
|
clack4.outro("Styles updated");
|
|
12417
12288
|
});
|