@betterstart/cli 0.1.41 → 0.1.43
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/README.md +1 -258
- package/dist/cli.js +1000 -665
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { Command as Command9 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/commands/generate.ts
|
|
10
|
-
import
|
|
10
|
+
import path30 from "path";
|
|
11
11
|
import { Command } from "commander";
|
|
12
12
|
|
|
13
13
|
// src/config/resolver.ts
|
|
@@ -159,6 +159,9 @@ function getManyToManyFields(fields) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// src/core/field-helpers/form-fields.ts
|
|
162
|
+
function isMultiStepForm(schema) {
|
|
163
|
+
return Array.isArray(schema.steps) && schema.steps.length > 0;
|
|
164
|
+
}
|
|
162
165
|
function getAllFormSchemaFields(schema) {
|
|
163
166
|
const flatten = (fields) => {
|
|
164
167
|
return fields.flatMap((field) => {
|
|
@@ -171,6 +174,22 @@ function getAllFormSchemaFields(schema) {
|
|
|
171
174
|
if (schema.steps) return schema.steps.flatMap((step) => flatten(step.fields));
|
|
172
175
|
return [];
|
|
173
176
|
}
|
|
177
|
+
function getStepFieldNames(step) {
|
|
178
|
+
const names = [];
|
|
179
|
+
const collect = (fields) => {
|
|
180
|
+
for (const field of fields) {
|
|
181
|
+
if (field.hidden) continue;
|
|
182
|
+
if (field.type === "dynamicFields") continue;
|
|
183
|
+
if (field.type === "group" && field.fields) {
|
|
184
|
+
collect(field.fields);
|
|
185
|
+
} else if (field.name) {
|
|
186
|
+
names.push(field.name);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
collect(step.fields);
|
|
191
|
+
return names;
|
|
192
|
+
}
|
|
174
193
|
function hasDynamicFields(schema) {
|
|
175
194
|
const check = (fields) => {
|
|
176
195
|
for (const f of fields) {
|
|
@@ -1927,7 +1946,11 @@ export async function export${pascal}SubmissionsJSON(): Promise<string> {
|
|
|
1927
1946
|
return { files: [path6.relative(cwd, filePath)] };
|
|
1928
1947
|
}
|
|
1929
1948
|
|
|
1930
|
-
// src/generators/form-pipeline/form-component.ts
|
|
1949
|
+
// src/generators/form-pipeline/form-component-multistep.ts
|
|
1950
|
+
import fs8 from "fs";
|
|
1951
|
+
import path8 from "path";
|
|
1952
|
+
|
|
1953
|
+
// src/generators/form-pipeline/form-component-shared.ts
|
|
1931
1954
|
import fs7 from "fs";
|
|
1932
1955
|
import path7 from "path";
|
|
1933
1956
|
|
|
@@ -2225,7 +2248,7 @@ function toTypeScriptType(field, mode = "output") {
|
|
|
2225
2248
|
}
|
|
2226
2249
|
}
|
|
2227
2250
|
|
|
2228
|
-
// src/generators/form-pipeline/form-component.ts
|
|
2251
|
+
// src/generators/form-pipeline/form-component-shared.ts
|
|
2229
2252
|
function resolveUiImport(cwd, componentName) {
|
|
2230
2253
|
const locations = ["components/ui", "src/components/ui"];
|
|
2231
2254
|
for (const loc of locations) {
|
|
@@ -2238,160 +2261,63 @@ function resolveUiImport(cwd, componentName) {
|
|
|
2238
2261
|
function escapeJsx(str) {
|
|
2239
2262
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2240
2263
|
}
|
|
2241
|
-
function
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
const defaults = fields.filter((f) => f.name).map((f) => {
|
|
2264
|
+
function flattenFormFields(fields) {
|
|
2265
|
+
return fields.flatMap((field) => {
|
|
2266
|
+
if (field.type === "dynamicFields") return [];
|
|
2267
|
+
if (field.type === "group" && field.fields) return flattenFormFields(field.fields);
|
|
2268
|
+
return field.type === "group" ? [] : [field];
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
function buildZodFields(fields) {
|
|
2272
|
+
return fields.filter((f) => f.name).map((f) => ` ${f.name}: ${formFieldToZodType(f)}`).join(",\n");
|
|
2273
|
+
}
|
|
2274
|
+
function buildDefaultValues(fields) {
|
|
2275
|
+
return fields.filter((f) => f.name).map((f) => {
|
|
2254
2276
|
if (f.type === "checkbox") return ` ${f.name}: false`;
|
|
2255
2277
|
if (f.type === "number") return ` ${f.name}: undefined`;
|
|
2256
2278
|
if (f.type === "multiselect" || f.type === "list") return ` ${f.name}: []`;
|
|
2279
|
+
if (f.type === "radio" && f.defaultValue !== void 0) return ` ${f.name}: '${f.defaultValue}'`;
|
|
2257
2280
|
if (f.type === "select" || f.type === "radio") return ` ${f.name}: undefined`;
|
|
2258
2281
|
if (f.defaultValue !== void 0) return ` ${f.name}: '${f.defaultValue}'`;
|
|
2259
2282
|
return ` ${f.name}: ''`;
|
|
2260
2283
|
}).join(",\n");
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
const hasListFields = listFields.length > 0;
|
|
2265
|
-
const fieldArrayDecls = listFields.map(
|
|
2284
|
+
}
|
|
2285
|
+
function buildFieldArrayDecls(listFields) {
|
|
2286
|
+
return listFields.map(
|
|
2266
2287
|
(f) => ` const ${f.name}FieldArray = useFieldArray({ control: form.control, name: '${f.name}' })`
|
|
2267
2288
|
).join("\n");
|
|
2289
|
+
}
|
|
2290
|
+
function buildWatchDecls(fields) {
|
|
2268
2291
|
const watchFields = /* @__PURE__ */ new Set();
|
|
2269
2292
|
for (const f of fields) {
|
|
2270
2293
|
if (f.showWhen) {
|
|
2271
2294
|
watchFields.add(f.showWhen.field);
|
|
2272
2295
|
}
|
|
2273
2296
|
}
|
|
2274
|
-
|
|
2275
|
-
const
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2297
|
+
if (watchFields.size === 0) return { setup: "", hasWatch: false };
|
|
2298
|
+
const decls = Array.from(watchFields).map((wf) => ` const ${wf}Value = form.watch('${wf}')`).join("\n");
|
|
2299
|
+
return { setup: `
|
|
2300
|
+
${decls}
|
|
2301
|
+
`, hasWatch: true };
|
|
2302
|
+
}
|
|
2303
|
+
function getListFields(fields) {
|
|
2304
|
+
return fields.filter(
|
|
2305
|
+
(f) => f.name && f.type === "list" && f.fields && f.fields.length > 0
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
function wrapShowWhen(field, jsx) {
|
|
2309
|
+
if (!field.showWhen) return jsx;
|
|
2310
|
+
const watchVar = `${field.showWhen.field}Value`;
|
|
2311
|
+
const { value } = field.showWhen;
|
|
2312
|
+
if (Array.isArray(value)) {
|
|
2313
|
+
const vals = value.map((v) => `'${v}'`).join(", ");
|
|
2314
|
+
return ` {[${vals}].includes(${watchVar} as string) && (
|
|
2286
2315
|
${jsx}
|
|
2287
2316
|
)}`;
|
|
2288
|
-
|
|
2289
|
-
|
|
2317
|
+
}
|
|
2318
|
+
return ` {${watchVar} === '${value}' && (
|
|
2290
2319
|
${jsx}
|
|
2291
2320
|
)}`;
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
|
-
return jsx;
|
|
2295
|
-
}).join("\n\n");
|
|
2296
|
-
const submitText = schema.submitButtonText || "Submit";
|
|
2297
|
-
const successMessage = escapeJsx(schema.successMessage || "Form submitted successfully!");
|
|
2298
|
-
const rhfImport = hasListFields ? `import { useFieldArray, useForm } from 'react-hook-form'` : `import { useForm } from 'react-hook-form'`;
|
|
2299
|
-
const fieldArraySetup = hasListFields ? `
|
|
2300
|
-
${fieldArrayDecls}
|
|
2301
|
-
` : "";
|
|
2302
|
-
const buttonImport = resolveUiImport(cwd, "button");
|
|
2303
|
-
const formImport = resolveUiImport(cwd, "form");
|
|
2304
|
-
const inputImport = resolveUiImport(cwd, "input");
|
|
2305
|
-
const textareaImport = resolveUiImport(cwd, "textarea");
|
|
2306
|
-
const selectImport = resolveUiImport(cwd, "select");
|
|
2307
|
-
const content = `'use client'
|
|
2308
|
-
|
|
2309
|
-
import { zodResolver } from '@hookform/resolvers/zod'
|
|
2310
|
-
import { useState } from 'react'
|
|
2311
|
-
${rhfImport}
|
|
2312
|
-
import { z } from 'zod/v3'
|
|
2313
|
-
import { create${pascal}Submission } from '@cms/actions/${kebab}-form'
|
|
2314
|
-
import { Button } from '${buttonImport}'
|
|
2315
|
-
import {
|
|
2316
|
-
Form,
|
|
2317
|
-
FormControl,
|
|
2318
|
-
FormDescription,
|
|
2319
|
-
FormField,
|
|
2320
|
-
FormItem,
|
|
2321
|
-
FormLabel,
|
|
2322
|
-
FormMessage,
|
|
2323
|
-
} from '${formImport}'
|
|
2324
|
-
import { Input } from '${inputImport}'
|
|
2325
|
-
import { Textarea } from '${textareaImport}'
|
|
2326
|
-
import {
|
|
2327
|
-
Select,
|
|
2328
|
-
SelectContent,
|
|
2329
|
-
SelectItem,
|
|
2330
|
-
SelectTrigger,
|
|
2331
|
-
SelectValue,
|
|
2332
|
-
} from '${selectImport}'
|
|
2333
|
-
|
|
2334
|
-
const formSchema = z.object({
|
|
2335
|
-
${zodFields}
|
|
2336
|
-
})
|
|
2337
|
-
|
|
2338
|
-
type FormValues = z.infer<typeof formSchema>
|
|
2339
|
-
|
|
2340
|
-
export function ${pascal}Form() {
|
|
2341
|
-
const [submitted, setSubmitted] = useState(false)
|
|
2342
|
-
const [submitting, setSubmitting] = useState(false)
|
|
2343
|
-
|
|
2344
|
-
const form = useForm<FormValues>({
|
|
2345
|
-
resolver: zodResolver(formSchema),
|
|
2346
|
-
defaultValues: {
|
|
2347
|
-
${defaults}
|
|
2348
|
-
},
|
|
2349
|
-
})
|
|
2350
|
-
${fieldArraySetup}${watchSetup}
|
|
2351
|
-
async function onSubmit(values: FormValues) {
|
|
2352
|
-
setSubmitting(true)
|
|
2353
|
-
try {
|
|
2354
|
-
const result = await create${pascal}Submission(values)
|
|
2355
|
-
if (result.success) {
|
|
2356
|
-
setSubmitted(true)
|
|
2357
|
-
} else {
|
|
2358
|
-
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
2359
|
-
}
|
|
2360
|
-
} catch {
|
|
2361
|
-
form.setError('root', { message: 'Something went wrong. Please try again.' })
|
|
2362
|
-
} finally {
|
|
2363
|
-
setSubmitting(false)
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
if (submitted) {
|
|
2368
|
-
return (
|
|
2369
|
-
<div className="rounded-lg border p-6 text-center">
|
|
2370
|
-
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
2371
|
-
<p className="mt-2 text-muted-foreground">${successMessage}</p>
|
|
2372
|
-
</div>
|
|
2373
|
-
)
|
|
2374
|
-
}
|
|
2375
|
-
|
|
2376
|
-
return (
|
|
2377
|
-
<Form {...form}>
|
|
2378
|
-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
2379
|
-
${fieldJSX}
|
|
2380
|
-
|
|
2381
|
-
{form.formState.errors.root && (
|
|
2382
|
-
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
2383
|
-
)}
|
|
2384
|
-
|
|
2385
|
-
<Button type="submit" disabled={submitting}>
|
|
2386
|
-
{submitting ? 'Submitting...' : '${submitText}'}
|
|
2387
|
-
</Button>
|
|
2388
|
-
</form>
|
|
2389
|
-
</Form>
|
|
2390
|
-
)
|
|
2391
|
-
}
|
|
2392
|
-
`;
|
|
2393
|
-
fs7.writeFileSync(filePath, content, "utf-8");
|
|
2394
|
-
return { files: [path7.relative(cwd, filePath)] };
|
|
2395
2321
|
}
|
|
2396
2322
|
function generateFieldJSX(field) {
|
|
2397
2323
|
const name = field.name || "";
|
|
@@ -2418,8 +2344,32 @@ function generateFieldJSX(field) {
|
|
|
2418
2344
|
</FormItem>
|
|
2419
2345
|
)}
|
|
2420
2346
|
/>`;
|
|
2421
|
-
case "select":
|
|
2422
2347
|
case "radio":
|
|
2348
|
+
if (field.options && field.options.length > 0) {
|
|
2349
|
+
const radioItems = field.options.map(
|
|
2350
|
+
(opt) => ` <div className="flex items-center space-x-2">
|
|
2351
|
+
<RadioGroupItem value="${opt.value}" id="${name}-${opt.value}" />
|
|
2352
|
+
<Label htmlFor="${name}-${opt.value}">${escapeJsx(opt.label)}</Label>
|
|
2353
|
+
</div>`
|
|
2354
|
+
).join("\n");
|
|
2355
|
+
return ` <FormField
|
|
2356
|
+
control={form.control}
|
|
2357
|
+
name="${name}"
|
|
2358
|
+
render={({ field }) => (
|
|
2359
|
+
<FormItem className="space-y-3">
|
|
2360
|
+
<FormLabel>${label}${requiredStar}</FormLabel>
|
|
2361
|
+
<FormControl>
|
|
2362
|
+
<RadioGroup onValueChange={field.onChange} defaultValue={field.value} className="flex flex-col space-y-1">
|
|
2363
|
+
${radioItems}
|
|
2364
|
+
</RadioGroup>
|
|
2365
|
+
</FormControl>${hintJSX}
|
|
2366
|
+
<FormMessage />
|
|
2367
|
+
</FormItem>
|
|
2368
|
+
)}
|
|
2369
|
+
/>`;
|
|
2370
|
+
}
|
|
2371
|
+
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "text");
|
|
2372
|
+
case "select":
|
|
2423
2373
|
if (field.options && field.options.length > 0) {
|
|
2424
2374
|
const optionItems = field.options.map(
|
|
2425
2375
|
(opt) => ` <SelectItem value="${opt.value}">${escapeJsx(opt.label)}</SelectItem>`
|
|
@@ -2468,36 +2418,15 @@ ${optionItems}
|
|
|
2468
2418
|
)}
|
|
2469
2419
|
/>`;
|
|
2470
2420
|
case "email":
|
|
2471
|
-
return generateTextFieldJSX(
|
|
2472
|
-
name,
|
|
2473
|
-
label,
|
|
2474
|
-
placeholder || "email@example.com",
|
|
2475
|
-
hintJSX,
|
|
2476
|
-
requiredStar,
|
|
2477
|
-
"email"
|
|
2478
|
-
);
|
|
2421
|
+
return generateTextFieldJSX(name, label, placeholder || "email@example.com", hintJSX, requiredStar, "email");
|
|
2479
2422
|
case "number":
|
|
2480
2423
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "number");
|
|
2481
2424
|
case "date":
|
|
2482
2425
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "date");
|
|
2483
2426
|
case "url":
|
|
2484
|
-
return generateTextFieldJSX(
|
|
2485
|
-
name,
|
|
2486
|
-
label,
|
|
2487
|
-
placeholder || "https://",
|
|
2488
|
-
hintJSX,
|
|
2489
|
-
requiredStar,
|
|
2490
|
-
"url"
|
|
2491
|
-
);
|
|
2427
|
+
return generateTextFieldJSX(name, label, placeholder || "https://", hintJSX, requiredStar, "url");
|
|
2492
2428
|
case "phone":
|
|
2493
|
-
return generateTextFieldJSX(
|
|
2494
|
-
name,
|
|
2495
|
-
label,
|
|
2496
|
-
placeholder || "+1 (555) 000-0000",
|
|
2497
|
-
hintJSX,
|
|
2498
|
-
requiredStar,
|
|
2499
|
-
"tel"
|
|
2500
|
-
);
|
|
2429
|
+
return generateTextFieldJSX(name, label, placeholder || "+1 (555) 000-0000", hintJSX, requiredStar, "tel");
|
|
2501
2430
|
case "file":
|
|
2502
2431
|
case "upload":
|
|
2503
2432
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "file");
|
|
@@ -2598,9 +2527,415 @@ ${nestedFieldsJSX}
|
|
|
2598
2527
|
</div>`;
|
|
2599
2528
|
}
|
|
2600
2529
|
|
|
2530
|
+
// src/generators/form-pipeline/form-component-multistep.ts
|
|
2531
|
+
function generateMultiStepForm(schema, cwd, cmsDir, options) {
|
|
2532
|
+
const formName = schema.name;
|
|
2533
|
+
const pascal = toPascalCase(formName);
|
|
2534
|
+
const kebab = toKebabCase(formName);
|
|
2535
|
+
const steps = schema.steps;
|
|
2536
|
+
const filePath = path8.join(cwd, cmsDir, "components", "forms", `${kebab}-form.tsx`);
|
|
2537
|
+
const dir = path8.dirname(filePath);
|
|
2538
|
+
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
2539
|
+
if (fs8.existsSync(filePath) && !options.force) {
|
|
2540
|
+
return { files: [path8.relative(cwd, filePath)] };
|
|
2541
|
+
}
|
|
2542
|
+
const allFields = getAllFormSchemaFields(schema);
|
|
2543
|
+
const zodFields = buildZodFields(allFields);
|
|
2544
|
+
const defaults = buildDefaultValues(allFields);
|
|
2545
|
+
const listFields = getListFields(allFields);
|
|
2546
|
+
const hasListFields = listFields.length > 0;
|
|
2547
|
+
const { setup: watchSetup } = buildWatchDecls(allFields);
|
|
2548
|
+
const rhfImport = hasListFields ? `import { useFieldArray, useForm } from 'react-hook-form'` : `import { useForm } from 'react-hook-form'`;
|
|
2549
|
+
const fieldArraySetup = hasListFields ? `
|
|
2550
|
+
${buildFieldArrayDecls(listFields)}
|
|
2551
|
+
` : "";
|
|
2552
|
+
const stepsConst = buildStepsConstant(steps, schema);
|
|
2553
|
+
const stepContentBlocks = steps.map((step, index) => {
|
|
2554
|
+
const stepFields = flattenFormFields(step.fields);
|
|
2555
|
+
const fieldsJSX = stepFields.filter((f) => f.name && !f.hidden).map((f) => wrapShowWhen(f, generateFieldJSX(f))).join("\n\n");
|
|
2556
|
+
return ` {currentStep === ${index} && (
|
|
2557
|
+
<>
|
|
2558
|
+
${fieldsJSX}
|
|
2559
|
+
</>
|
|
2560
|
+
)}`;
|
|
2561
|
+
}).join("\n");
|
|
2562
|
+
const submitText = schema.submitButtonText || "Submit";
|
|
2563
|
+
const successMessage = escapeJsx(schema.successMessage || "Form submitted successfully!");
|
|
2564
|
+
const hasRadio = allFields.some((f) => f.type === "radio");
|
|
2565
|
+
const buttonImport = resolveUiImport(cwd, "button");
|
|
2566
|
+
const formImport = resolveUiImport(cwd, "form");
|
|
2567
|
+
const inputImport = resolveUiImport(cwd, "input");
|
|
2568
|
+
const labelImport = resolveUiImport(cwd, "label");
|
|
2569
|
+
const textareaImport = resolveUiImport(cwd, "textarea");
|
|
2570
|
+
const selectImport = resolveUiImport(cwd, "select");
|
|
2571
|
+
const radioGroupImport = resolveUiImport(cwd, "radio-group");
|
|
2572
|
+
const progressImport = resolveUiImport(cwd, "progress");
|
|
2573
|
+
const content = buildComponentSource({
|
|
2574
|
+
pascal,
|
|
2575
|
+
kebab,
|
|
2576
|
+
rhfImport,
|
|
2577
|
+
hasRadio,
|
|
2578
|
+
buttonImport,
|
|
2579
|
+
formImport,
|
|
2580
|
+
inputImport,
|
|
2581
|
+
labelImport,
|
|
2582
|
+
textareaImport,
|
|
2583
|
+
selectImport,
|
|
2584
|
+
radioGroupImport,
|
|
2585
|
+
progressImport,
|
|
2586
|
+
zodFields,
|
|
2587
|
+
defaults,
|
|
2588
|
+
stepsConst,
|
|
2589
|
+
fieldArraySetup,
|
|
2590
|
+
watchSetup,
|
|
2591
|
+
stepContentBlocks,
|
|
2592
|
+
submitText,
|
|
2593
|
+
successMessage
|
|
2594
|
+
});
|
|
2595
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
2596
|
+
return { files: [path8.relative(cwd, filePath)] };
|
|
2597
|
+
}
|
|
2598
|
+
function buildStepsConstant(steps, schema) {
|
|
2599
|
+
const entries = steps.map((step) => {
|
|
2600
|
+
const fieldNames = getStepFieldNames(step);
|
|
2601
|
+
const fieldsStr = fieldNames.map((n) => `'${n}'`).join(", ");
|
|
2602
|
+
const desc = step.description ? `, description: '${escapeQuotes(step.description)}'` : "";
|
|
2603
|
+
return ` { name: '${step.name}', label: '${escapeQuotes(step.label)}'${desc}, fields: [${fieldsStr}] }`;
|
|
2604
|
+
});
|
|
2605
|
+
return `const STEPS = [
|
|
2606
|
+
${entries.join(",\n")}
|
|
2607
|
+
]`;
|
|
2608
|
+
}
|
|
2609
|
+
function escapeQuotes(str) {
|
|
2610
|
+
return str.replace(/'/g, "\\'");
|
|
2611
|
+
}
|
|
2612
|
+
function buildComponentSource(p7) {
|
|
2613
|
+
return `'use client'
|
|
2614
|
+
|
|
2615
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
2616
|
+
import { Check, ChevronLeft, ChevronRight } from 'lucide-react'
|
|
2617
|
+
import { useState } from 'react'
|
|
2618
|
+
${p7.rhfImport}
|
|
2619
|
+
import { z } from 'zod/v3'
|
|
2620
|
+
import { create${p7.pascal}Submission } from '@cms/actions/${p7.kebab}-form'
|
|
2621
|
+
import { Button } from '${p7.buttonImport}'
|
|
2622
|
+
import {
|
|
2623
|
+
Form,
|
|
2624
|
+
FormControl,
|
|
2625
|
+
FormDescription,
|
|
2626
|
+
FormField,
|
|
2627
|
+
FormItem,
|
|
2628
|
+
FormLabel,
|
|
2629
|
+
FormMessage,
|
|
2630
|
+
} from '${p7.formImport}'
|
|
2631
|
+
import { Input } from '${p7.inputImport}'
|
|
2632
|
+
import { Label } from '${p7.labelImport}'
|
|
2633
|
+
import { Progress } from '${p7.progressImport}'${p7.hasRadio ? `
|
|
2634
|
+
import { RadioGroup, RadioGroupItem } from '${p7.radioGroupImport}'` : ""}
|
|
2635
|
+
import { Textarea } from '${p7.textareaImport}'
|
|
2636
|
+
import {
|
|
2637
|
+
Select,
|
|
2638
|
+
SelectContent,
|
|
2639
|
+
SelectItem,
|
|
2640
|
+
SelectTrigger,
|
|
2641
|
+
SelectValue,
|
|
2642
|
+
} from '${p7.selectImport}'
|
|
2643
|
+
|
|
2644
|
+
const formSchema = z.object({
|
|
2645
|
+
${p7.zodFields}
|
|
2646
|
+
})
|
|
2647
|
+
|
|
2648
|
+
type FormValues = z.infer<typeof formSchema>
|
|
2649
|
+
|
|
2650
|
+
${p7.stepsConst}
|
|
2651
|
+
|
|
2652
|
+
export function ${p7.pascal}Form() {
|
|
2653
|
+
const [currentStep, setCurrentStep] = useState(0)
|
|
2654
|
+
const [completedSteps, setCompletedSteps] = useState<Set<number>>(new Set())
|
|
2655
|
+
const [submitted, setSubmitted] = useState(false)
|
|
2656
|
+
const [submitting, setSubmitting] = useState(false)
|
|
2657
|
+
|
|
2658
|
+
const form = useForm<FormValues>({
|
|
2659
|
+
resolver: zodResolver(formSchema),
|
|
2660
|
+
mode: 'onTouched',
|
|
2661
|
+
defaultValues: {
|
|
2662
|
+
${p7.defaults}
|
|
2663
|
+
},
|
|
2664
|
+
})
|
|
2665
|
+
${p7.fieldArraySetup}${p7.watchSetup}
|
|
2666
|
+
async function handleNext() {
|
|
2667
|
+
const isValid = await form.trigger(
|
|
2668
|
+
STEPS[currentStep].fields as (keyof FormValues)[]
|
|
2669
|
+
)
|
|
2670
|
+
if (isValid) {
|
|
2671
|
+
setCompletedSteps((prev) => new Set(prev).add(currentStep))
|
|
2672
|
+
setCurrentStep((prev) => prev + 1)
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
function handleBack() {
|
|
2677
|
+
setCurrentStep((prev) => prev - 1)
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
function handleStepClick(index: number) {
|
|
2681
|
+
if (index < currentStep || completedSteps.has(index)) {
|
|
2682
|
+
setCurrentStep(index)
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
async function onSubmit(values: FormValues) {
|
|
2687
|
+
setSubmitting(true)
|
|
2688
|
+
try {
|
|
2689
|
+
const result = await create${p7.pascal}Submission(values)
|
|
2690
|
+
if (result.success) {
|
|
2691
|
+
setSubmitted(true)
|
|
2692
|
+
} else {
|
|
2693
|
+
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
2694
|
+
}
|
|
2695
|
+
} catch {
|
|
2696
|
+
form.setError('root', { message: 'Something went wrong. Please try again.' })
|
|
2697
|
+
} finally {
|
|
2698
|
+
setSubmitting(false)
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
if (submitted) {
|
|
2703
|
+
return (
|
|
2704
|
+
<div className="rounded-lg border p-6 text-center">
|
|
2705
|
+
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
2706
|
+
<p className="mt-2 text-muted-foreground">${p7.successMessage}</p>
|
|
2707
|
+
</div>
|
|
2708
|
+
)
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
return (
|
|
2712
|
+
<Form {...form}>
|
|
2713
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
2714
|
+
{/* Mobile step indicator */}
|
|
2715
|
+
<div className="md:hidden space-y-2">
|
|
2716
|
+
<p className="text-sm text-muted-foreground">
|
|
2717
|
+
Step {currentStep + 1} of {STEPS.length} — {STEPS[currentStep].label}
|
|
2718
|
+
</p>
|
|
2719
|
+
<Progress value={((currentStep + 1) / STEPS.length) * 100} />
|
|
2720
|
+
</div>
|
|
2721
|
+
|
|
2722
|
+
{/* Desktop step indicator */}
|
|
2723
|
+
<nav className="hidden md:flex items-center justify-center" aria-label="Form progress">
|
|
2724
|
+
{STEPS.map((step, index) => (
|
|
2725
|
+
<div key={step.name} className="flex items-center">
|
|
2726
|
+
<button
|
|
2727
|
+
type="button"
|
|
2728
|
+
onClick={() => handleStepClick(index)}
|
|
2729
|
+
disabled={index > currentStep && !completedSteps.has(index)}
|
|
2730
|
+
aria-current={index === currentStep ? 'step' : undefined}
|
|
2731
|
+
className={\`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium transition-colors \${
|
|
2732
|
+
completedSteps.has(index)
|
|
2733
|
+
? 'bg-primary text-primary-foreground cursor-pointer'
|
|
2734
|
+
: index === currentStep
|
|
2735
|
+
? 'border-2 border-primary text-primary'
|
|
2736
|
+
: 'border-2 border-muted text-muted-foreground'
|
|
2737
|
+
}\`}
|
|
2738
|
+
>
|
|
2739
|
+
{completedSteps.has(index) ? (
|
|
2740
|
+
<Check className="h-4 w-4" />
|
|
2741
|
+
) : (
|
|
2742
|
+
index + 1
|
|
2743
|
+
)}
|
|
2744
|
+
</button>
|
|
2745
|
+
{index < STEPS.length - 1 && (
|
|
2746
|
+
<div
|
|
2747
|
+
className={\`h-0.5 w-8 mx-1 \${
|
|
2748
|
+
completedSteps.has(index) ? 'bg-primary' : 'bg-muted'
|
|
2749
|
+
}\`}
|
|
2750
|
+
/>
|
|
2751
|
+
)}
|
|
2752
|
+
</div>
|
|
2753
|
+
))}
|
|
2754
|
+
</nav>
|
|
2755
|
+
|
|
2756
|
+
{/* Step header */}
|
|
2757
|
+
<div>
|
|
2758
|
+
<h3 className="text-lg font-semibold">{STEPS[currentStep].label}</h3>
|
|
2759
|
+
{'description' in STEPS[currentStep] && STEPS[currentStep].description && (
|
|
2760
|
+
<p className="text-sm text-muted-foreground mt-1">{STEPS[currentStep].description}</p>
|
|
2761
|
+
)}
|
|
2762
|
+
</div>
|
|
2763
|
+
|
|
2764
|
+
{/* Step content */}
|
|
2765
|
+
<div key={currentStep} className="animate-in fade-in duration-300 space-y-6">
|
|
2766
|
+
${p7.stepContentBlocks}
|
|
2767
|
+
</div>
|
|
2768
|
+
|
|
2769
|
+
{form.formState.errors.root && (
|
|
2770
|
+
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
2771
|
+
)}
|
|
2772
|
+
|
|
2773
|
+
{/* Navigation */}
|
|
2774
|
+
<div className="flex justify-between">
|
|
2775
|
+
<Button
|
|
2776
|
+
type="button"
|
|
2777
|
+
variant="outline"
|
|
2778
|
+
onClick={handleBack}
|
|
2779
|
+
disabled={currentStep === 0}
|
|
2780
|
+
>
|
|
2781
|
+
<ChevronLeft className="mr-2 h-4 w-4" />
|
|
2782
|
+
Back
|
|
2783
|
+
</Button>
|
|
2784
|
+
{currentStep < STEPS.length - 1 ? (
|
|
2785
|
+
<Button type="button" onClick={handleNext}>
|
|
2786
|
+
Next
|
|
2787
|
+
<ChevronRight className="ml-2 h-4 w-4" />
|
|
2788
|
+
</Button>
|
|
2789
|
+
) : (
|
|
2790
|
+
<Button type="submit" disabled={submitting}>
|
|
2791
|
+
{submitting ? 'Submitting...' : '${p7.submitText}'}
|
|
2792
|
+
</Button>
|
|
2793
|
+
)}
|
|
2794
|
+
</div>
|
|
2795
|
+
</form>
|
|
2796
|
+
</Form>
|
|
2797
|
+
)
|
|
2798
|
+
}
|
|
2799
|
+
`;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
// src/generators/form-pipeline/form-component-single.ts
|
|
2803
|
+
import fs9 from "fs";
|
|
2804
|
+
import path9 from "path";
|
|
2805
|
+
function generateSingleStepForm(schema, cwd, cmsDir, options) {
|
|
2806
|
+
const formName = schema.name;
|
|
2807
|
+
const pascal = toPascalCase(formName);
|
|
2808
|
+
const fields = getAllFormSchemaFields(schema);
|
|
2809
|
+
const kebab = toKebabCase(formName);
|
|
2810
|
+
const filePath = path9.join(cwd, cmsDir, "components", "forms", `${kebab}-form.tsx`);
|
|
2811
|
+
const dir = path9.dirname(filePath);
|
|
2812
|
+
if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
|
|
2813
|
+
if (fs9.existsSync(filePath) && !options.force) {
|
|
2814
|
+
return { files: [path9.relative(cwd, filePath)] };
|
|
2815
|
+
}
|
|
2816
|
+
const zodFields = buildZodFields(fields);
|
|
2817
|
+
const defaults = buildDefaultValues(fields);
|
|
2818
|
+
const listFields = getListFields(fields);
|
|
2819
|
+
const hasListFields = listFields.length > 0;
|
|
2820
|
+
const { setup: watchSetup } = buildWatchDecls(fields);
|
|
2821
|
+
const fieldJSX = fields.filter((f) => f.name && !f.hidden).map((f) => wrapShowWhen(f, generateFieldJSX(f))).join("\n\n");
|
|
2822
|
+
const submitText = schema.submitButtonText || "Submit";
|
|
2823
|
+
const successMessage = escapeJsx(schema.successMessage || "Form submitted successfully!");
|
|
2824
|
+
const rhfImport = hasListFields ? `import { useFieldArray, useForm } from 'react-hook-form'` : `import { useForm } from 'react-hook-form'`;
|
|
2825
|
+
const fieldArraySetup = hasListFields ? `
|
|
2826
|
+
${buildFieldArrayDecls(listFields)}
|
|
2827
|
+
` : "";
|
|
2828
|
+
const hasRadio = fields.some((f) => f.type === "radio");
|
|
2829
|
+
const buttonImport = resolveUiImport(cwd, "button");
|
|
2830
|
+
const formImport = resolveUiImport(cwd, "form");
|
|
2831
|
+
const inputImport = resolveUiImport(cwd, "input");
|
|
2832
|
+
const labelImport = resolveUiImport(cwd, "label");
|
|
2833
|
+
const textareaImport = resolveUiImport(cwd, "textarea");
|
|
2834
|
+
const selectImport = resolveUiImport(cwd, "select");
|
|
2835
|
+
const radioGroupImport = resolveUiImport(cwd, "radio-group");
|
|
2836
|
+
const content = `'use client'
|
|
2837
|
+
|
|
2838
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
2839
|
+
import { useState } from 'react'
|
|
2840
|
+
${rhfImport}
|
|
2841
|
+
import { z } from 'zod/v3'
|
|
2842
|
+
import { create${pascal}Submission } from '@cms/actions/${kebab}-form'
|
|
2843
|
+
import { Button } from '${buttonImport}'
|
|
2844
|
+
import {
|
|
2845
|
+
Form,
|
|
2846
|
+
FormControl,
|
|
2847
|
+
FormDescription,
|
|
2848
|
+
FormField,
|
|
2849
|
+
FormItem,
|
|
2850
|
+
FormLabel,
|
|
2851
|
+
FormMessage,
|
|
2852
|
+
} from '${formImport}'
|
|
2853
|
+
import { Input } from '${inputImport}'
|
|
2854
|
+
import { Label } from '${labelImport}'${hasRadio ? `
|
|
2855
|
+
import { RadioGroup, RadioGroupItem } from '${radioGroupImport}'` : ""}
|
|
2856
|
+
import { Textarea } from '${textareaImport}'
|
|
2857
|
+
import {
|
|
2858
|
+
Select,
|
|
2859
|
+
SelectContent,
|
|
2860
|
+
SelectItem,
|
|
2861
|
+
SelectTrigger,
|
|
2862
|
+
SelectValue,
|
|
2863
|
+
} from '${selectImport}'
|
|
2864
|
+
|
|
2865
|
+
const formSchema = z.object({
|
|
2866
|
+
${zodFields}
|
|
2867
|
+
})
|
|
2868
|
+
|
|
2869
|
+
type FormValues = z.infer<typeof formSchema>
|
|
2870
|
+
|
|
2871
|
+
export function ${pascal}Form() {
|
|
2872
|
+
const [submitted, setSubmitted] = useState(false)
|
|
2873
|
+
const [submitting, setSubmitting] = useState(false)
|
|
2874
|
+
|
|
2875
|
+
const form = useForm<FormValues>({
|
|
2876
|
+
resolver: zodResolver(formSchema),
|
|
2877
|
+
defaultValues: {
|
|
2878
|
+
${defaults}
|
|
2879
|
+
},
|
|
2880
|
+
})
|
|
2881
|
+
${fieldArraySetup}${watchSetup}
|
|
2882
|
+
async function onSubmit(values: FormValues) {
|
|
2883
|
+
setSubmitting(true)
|
|
2884
|
+
try {
|
|
2885
|
+
const result = await create${pascal}Submission(values)
|
|
2886
|
+
if (result.success) {
|
|
2887
|
+
setSubmitted(true)
|
|
2888
|
+
} else {
|
|
2889
|
+
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
2890
|
+
}
|
|
2891
|
+
} catch {
|
|
2892
|
+
form.setError('root', { message: 'Something went wrong. Please try again.' })
|
|
2893
|
+
} finally {
|
|
2894
|
+
setSubmitting(false)
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
if (submitted) {
|
|
2899
|
+
return (
|
|
2900
|
+
<div className="rounded-lg border p-6 text-center">
|
|
2901
|
+
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
2902
|
+
<p className="mt-2 text-muted-foreground">${successMessage}</p>
|
|
2903
|
+
</div>
|
|
2904
|
+
)
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
return (
|
|
2908
|
+
<Form {...form}>
|
|
2909
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
2910
|
+
${fieldJSX}
|
|
2911
|
+
|
|
2912
|
+
{form.formState.errors.root && (
|
|
2913
|
+
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
2914
|
+
)}
|
|
2915
|
+
|
|
2916
|
+
<Button type="submit" disabled={submitting}>
|
|
2917
|
+
{submitting ? 'Submitting...' : '${submitText}'}
|
|
2918
|
+
</Button>
|
|
2919
|
+
</form>
|
|
2920
|
+
</Form>
|
|
2921
|
+
)
|
|
2922
|
+
}
|
|
2923
|
+
`;
|
|
2924
|
+
fs9.writeFileSync(filePath, content, "utf-8");
|
|
2925
|
+
return { files: [path9.relative(cwd, filePath)] };
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
// src/generators/form-pipeline/form-component.ts
|
|
2929
|
+
function generateFormComponent(schema, cwd, cmsDir, options) {
|
|
2930
|
+
if (isMultiStepForm(schema) && schema.steps.length > 1) {
|
|
2931
|
+
return generateMultiStepForm(schema, cwd, cmsDir, options);
|
|
2932
|
+
}
|
|
2933
|
+
return generateSingleStepForm(schema, cwd, cmsDir, options);
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2601
2936
|
// src/generators/form-pipeline/form-database.ts
|
|
2602
|
-
import
|
|
2603
|
-
import
|
|
2937
|
+
import fs10 from "fs";
|
|
2938
|
+
import path10 from "path";
|
|
2604
2939
|
function findTableEnd(content, startIndex) {
|
|
2605
2940
|
let depth = 0;
|
|
2606
2941
|
let inString = false;
|
|
@@ -2655,8 +2990,8 @@ ${fieldDefs}${customFieldsCol}
|
|
|
2655
2990
|
updatedAt: timestamp({ precision: 3, mode: 'string' }).default(sql\`CURRENT_TIMESTAMP\`).notNull()
|
|
2656
2991
|
})
|
|
2657
2992
|
`;
|
|
2658
|
-
const filePath =
|
|
2659
|
-
let content =
|
|
2993
|
+
const filePath = path10.join(cwd, dbSchemaPath);
|
|
2994
|
+
let content = fs10.readFileSync(filePath, "utf-8");
|
|
2660
2995
|
const pgCoreMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]drizzle-orm\/pg-core['"]/);
|
|
2661
2996
|
if (pgCoreMatch) {
|
|
2662
2997
|
const existing = new Set(
|
|
@@ -2692,23 +3027,23 @@ ${match}`
|
|
|
2692
3027
|
}
|
|
2693
3028
|
content += `
|
|
2694
3029
|
${tableSchema}`;
|
|
2695
|
-
|
|
3030
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
2696
3031
|
return { files: [dbSchemaPath] };
|
|
2697
3032
|
}
|
|
2698
3033
|
|
|
2699
3034
|
// src/generators/form-pipeline/form-hook.ts
|
|
2700
|
-
import
|
|
2701
|
-
import
|
|
3035
|
+
import fs11 from "fs";
|
|
3036
|
+
import path11 from "path";
|
|
2702
3037
|
function generateFormHook(schema, cwd, hooksDir, options) {
|
|
2703
3038
|
const formName = schema.name;
|
|
2704
3039
|
const pascal = toPascalCase(formName);
|
|
2705
3040
|
const camel = toCamelCase(formName);
|
|
2706
3041
|
const kebab = toKebabCase(formName);
|
|
2707
|
-
const filePath =
|
|
2708
|
-
const dir =
|
|
2709
|
-
if (!
|
|
2710
|
-
if (
|
|
2711
|
-
return { files: [
|
|
3042
|
+
const filePath = path11.join(cwd, hooksDir, `use-${kebab}-form.ts`);
|
|
3043
|
+
const dir = path11.dirname(filePath);
|
|
3044
|
+
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
3045
|
+
if (fs11.existsSync(filePath) && !options.force) {
|
|
3046
|
+
return { files: [path11.relative(cwd, filePath)] };
|
|
2712
3047
|
}
|
|
2713
3048
|
const successMsg = (schema.successMessage || "Form submitted successfully").replace(/'/g, "\\'");
|
|
2714
3049
|
const content = `import {
|
|
@@ -2814,8 +3149,8 @@ export function useExport${pascal}SubmissionsJSON() {
|
|
|
2814
3149
|
})
|
|
2815
3150
|
}
|
|
2816
3151
|
`;
|
|
2817
|
-
|
|
2818
|
-
return { files: [
|
|
3152
|
+
fs11.writeFileSync(filePath, content, "utf-8");
|
|
3153
|
+
return { files: [path11.relative(cwd, filePath)] };
|
|
2819
3154
|
}
|
|
2820
3155
|
|
|
2821
3156
|
// src/generators/form-pipeline/pipeline.ts
|
|
@@ -2881,8 +3216,8 @@ function runFormPipeline(schema, cwd, config, options = {}) {
|
|
|
2881
3216
|
}
|
|
2882
3217
|
|
|
2883
3218
|
// src/generators/actions/entity-actions.ts
|
|
2884
|
-
import
|
|
2885
|
-
import
|
|
3219
|
+
import fs12 from "fs";
|
|
3220
|
+
import path12 from "path";
|
|
2886
3221
|
|
|
2887
3222
|
// src/generators/actions/action-helpers.ts
|
|
2888
3223
|
function generateFieldMapping(field, source = "input") {
|
|
@@ -3021,9 +3356,9 @@ ${blocks.join("\n\n")}
|
|
|
3021
3356
|
|
|
3022
3357
|
// src/generators/actions/entity-actions.ts
|
|
3023
3358
|
function generateActions(schema, cwd, actionsDir, options = {}) {
|
|
3024
|
-
const absActionsDir =
|
|
3025
|
-
const filePath =
|
|
3026
|
-
if (
|
|
3359
|
+
const absActionsDir = path12.join(cwd, actionsDir);
|
|
3360
|
+
const filePath = path12.join(absActionsDir, `${schema.name}.ts`);
|
|
3361
|
+
if (fs12.existsSync(filePath) && !options.force) {
|
|
3027
3362
|
return { files: [] };
|
|
3028
3363
|
}
|
|
3029
3364
|
const singular = singularize(schema.name);
|
|
@@ -3509,20 +3844,20 @@ export async function bulkUpdate${Plural}SortOrder(
|
|
|
3509
3844
|
}
|
|
3510
3845
|
}${m2mHelpers}
|
|
3511
3846
|
`;
|
|
3512
|
-
if (!
|
|
3513
|
-
|
|
3847
|
+
if (!fs12.existsSync(absActionsDir)) {
|
|
3848
|
+
fs12.mkdirSync(absActionsDir, { recursive: true });
|
|
3514
3849
|
}
|
|
3515
|
-
|
|
3516
|
-
return { files: [
|
|
3850
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
3851
|
+
return { files: [path12.join(actionsDir, `${schema.name}.ts`)] };
|
|
3517
3852
|
}
|
|
3518
3853
|
|
|
3519
3854
|
// src/generators/actions/single-actions.ts
|
|
3520
|
-
import
|
|
3521
|
-
import
|
|
3855
|
+
import fs13 from "fs";
|
|
3856
|
+
import path13 from "path";
|
|
3522
3857
|
function generateSingleActions(schema, cwd, actionsDir, options = {}) {
|
|
3523
|
-
const absActionsDir =
|
|
3524
|
-
const filePath =
|
|
3525
|
-
if (
|
|
3858
|
+
const absActionsDir = path13.join(cwd, actionsDir);
|
|
3859
|
+
const filePath = path13.join(absActionsDir, `${schema.name}.ts`);
|
|
3860
|
+
if (fs13.existsSync(filePath) && !options.force) {
|
|
3526
3861
|
return { files: [] };
|
|
3527
3862
|
}
|
|
3528
3863
|
const singular = singularize(schema.name);
|
|
@@ -3645,24 +3980,24 @@ ${upsertMappings},${singleHasHtmlOutput ? "\n" + singleHtmlOutputFields.map((f)
|
|
|
3645
3980
|
}
|
|
3646
3981
|
}
|
|
3647
3982
|
`;
|
|
3648
|
-
if (!
|
|
3649
|
-
|
|
3983
|
+
if (!fs13.existsSync(absActionsDir)) {
|
|
3984
|
+
fs13.mkdirSync(absActionsDir, { recursive: true });
|
|
3650
3985
|
}
|
|
3651
|
-
|
|
3652
|
-
return { files: [
|
|
3986
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
3987
|
+
return { files: [path13.join(actionsDir, `${schema.name}.ts`)] };
|
|
3653
3988
|
}
|
|
3654
3989
|
|
|
3655
3990
|
// src/generators/cache.ts
|
|
3656
|
-
import
|
|
3657
|
-
import
|
|
3991
|
+
import fs14 from "fs";
|
|
3992
|
+
import path14 from "path";
|
|
3658
3993
|
function loadAllSchemas(cwd, schemasDir) {
|
|
3659
|
-
const dir =
|
|
3660
|
-
if (!
|
|
3661
|
-
const files =
|
|
3994
|
+
const dir = path14.join(cwd, schemasDir);
|
|
3995
|
+
if (!fs14.existsSync(dir)) return [];
|
|
3996
|
+
const files = fs14.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
3662
3997
|
const seen = /* @__PURE__ */ new Map();
|
|
3663
3998
|
for (const file of files) {
|
|
3664
3999
|
if (file === "schema.json") continue;
|
|
3665
|
-
const content =
|
|
4000
|
+
const content = fs14.readFileSync(path14.join(dir, file), "utf-8");
|
|
3666
4001
|
const schema = JSON.parse(content);
|
|
3667
4002
|
if (!schema.name) continue;
|
|
3668
4003
|
seen.set(schema.name, schema);
|
|
@@ -3852,27 +4187,27 @@ export * from './revalidate'
|
|
|
3852
4187
|
`;
|
|
3853
4188
|
}
|
|
3854
4189
|
function generateCache(_schema, cwd, cmsDir, _options = {}) {
|
|
3855
|
-
const cacheDir =
|
|
3856
|
-
const schemasDir =
|
|
4190
|
+
const cacheDir = path14.join(cwd, cmsDir, "lib", "cache");
|
|
4191
|
+
const schemasDir = path14.join(cmsDir, "schemas");
|
|
3857
4192
|
const schemas = loadAllSchemas(cwd, schemasDir);
|
|
3858
4193
|
const configs = schemas.map(buildCacheConfig).sort((a, b) => a.name.localeCompare(b.name));
|
|
3859
|
-
if (!
|
|
3860
|
-
|
|
4194
|
+
if (!fs14.existsSync(cacheDir)) {
|
|
4195
|
+
fs14.mkdirSync(cacheDir, { recursive: true });
|
|
3861
4196
|
}
|
|
3862
4197
|
const files = [];
|
|
3863
|
-
|
|
3864
|
-
files.push(
|
|
3865
|
-
|
|
3866
|
-
|
|
4198
|
+
fs14.writeFileSync(path14.join(cacheDir, "tags.ts"), generateTags(configs), "utf-8");
|
|
4199
|
+
files.push(path14.join(cmsDir, "lib", "cache", "tags.ts"));
|
|
4200
|
+
fs14.writeFileSync(
|
|
4201
|
+
path14.join(cacheDir, "cached-queries.ts"),
|
|
3867
4202
|
generateCachedQueries(configs),
|
|
3868
4203
|
"utf-8"
|
|
3869
4204
|
);
|
|
3870
|
-
files.push(
|
|
3871
|
-
|
|
3872
|
-
files.push(
|
|
3873
|
-
const customQueriesPath =
|
|
3874
|
-
if (!
|
|
3875
|
-
|
|
4205
|
+
files.push(path14.join(cmsDir, "lib", "cache", "cached-queries.ts"));
|
|
4206
|
+
fs14.writeFileSync(path14.join(cacheDir, "revalidate.ts"), generateRevalidate(configs), "utf-8");
|
|
4207
|
+
files.push(path14.join(cmsDir, "lib", "cache", "revalidate.ts"));
|
|
4208
|
+
const customQueriesPath = path14.join(cacheDir, "cached-queries-custom.ts");
|
|
4209
|
+
if (!fs14.existsSync(customQueriesPath)) {
|
|
4210
|
+
fs14.writeFileSync(
|
|
3876
4211
|
customQueriesPath,
|
|
3877
4212
|
`/**
|
|
3878
4213
|
* Custom cached query functions (not auto-generated)
|
|
@@ -3883,16 +4218,16 @@ export {}
|
|
|
3883
4218
|
`,
|
|
3884
4219
|
"utf-8"
|
|
3885
4220
|
);
|
|
3886
|
-
files.push(
|
|
4221
|
+
files.push(path14.join(cmsDir, "lib", "cache", "cached-queries-custom.ts"));
|
|
3887
4222
|
}
|
|
3888
|
-
|
|
3889
|
-
files.push(
|
|
4223
|
+
fs14.writeFileSync(path14.join(cacheDir, "index.ts"), generateIndex(), "utf-8");
|
|
4224
|
+
files.push(path14.join(cmsDir, "lib", "cache", "index.ts"));
|
|
3890
4225
|
return { files };
|
|
3891
4226
|
}
|
|
3892
4227
|
|
|
3893
4228
|
// src/generators/columns/generate-columns.ts
|
|
3894
|
-
import
|
|
3895
|
-
import
|
|
4229
|
+
import fs16 from "fs";
|
|
4230
|
+
import path16 from "path";
|
|
3896
4231
|
|
|
3897
4232
|
// src/generators/columns/column-defs.ts
|
|
3898
4233
|
function isSortableColumn(column) {
|
|
@@ -4510,12 +4845,12 @@ function truncateStr(str: string, maxLength: number): string {
|
|
|
4510
4845
|
}
|
|
4511
4846
|
|
|
4512
4847
|
// src/generators/columns/custom-cell.ts
|
|
4513
|
-
import
|
|
4514
|
-
import
|
|
4848
|
+
import fs15 from "fs";
|
|
4849
|
+
import path15 from "path";
|
|
4515
4850
|
function createCustomCellComponent(schema, componentName, cwd, pagesDir, options) {
|
|
4516
|
-
const cellsDir =
|
|
4517
|
-
const componentFilePath =
|
|
4518
|
-
if (
|
|
4851
|
+
const cellsDir = path15.join(cwd, pagesDir, schema.name, "cells");
|
|
4852
|
+
const componentFilePath = path15.join(cellsDir, `${componentName}.tsx`);
|
|
4853
|
+
if (fs15.existsSync(componentFilePath) && !options.force) return;
|
|
4519
4854
|
const singular = singularize(schema.name);
|
|
4520
4855
|
const Singular = toPascalCase(singular);
|
|
4521
4856
|
const content = `import type { ${Singular}Data } from '@cms/actions/${schema.name}'
|
|
@@ -4532,17 +4867,17 @@ export function ${componentName}({ data }: ${componentName}Props) {
|
|
|
4532
4867
|
)
|
|
4533
4868
|
}
|
|
4534
4869
|
`;
|
|
4535
|
-
if (!
|
|
4536
|
-
|
|
4870
|
+
if (!fs15.existsSync(cellsDir)) {
|
|
4871
|
+
fs15.mkdirSync(cellsDir, { recursive: true });
|
|
4537
4872
|
}
|
|
4538
|
-
|
|
4873
|
+
fs15.writeFileSync(componentFilePath, content, "utf-8");
|
|
4539
4874
|
}
|
|
4540
4875
|
|
|
4541
4876
|
// src/generators/columns/generate-columns.ts
|
|
4542
4877
|
function generateColumns2(schema, cwd, pagesDir, options = {}) {
|
|
4543
|
-
const entityDir =
|
|
4544
|
-
const columnsFilePath =
|
|
4545
|
-
if (
|
|
4878
|
+
const entityDir = path16.join(cwd, pagesDir, schema.name);
|
|
4879
|
+
const columnsFilePath = path16.join(entityDir, "columns.tsx");
|
|
4880
|
+
if (fs16.existsSync(columnsFilePath) && !options.force) {
|
|
4546
4881
|
return { files: [] };
|
|
4547
4882
|
}
|
|
4548
4883
|
const singular = singularize(schema.name);
|
|
@@ -4684,22 +5019,22 @@ ${restColDefs}` : ""},
|
|
|
4684
5019
|
${actionsColumn}
|
|
4685
5020
|
]
|
|
4686
5021
|
`;
|
|
4687
|
-
if (!
|
|
4688
|
-
|
|
5022
|
+
if (!fs16.existsSync(entityDir)) {
|
|
5023
|
+
fs16.mkdirSync(entityDir, { recursive: true });
|
|
4689
5024
|
}
|
|
4690
|
-
|
|
5025
|
+
fs16.writeFileSync(columnsFilePath, content, "utf-8");
|
|
4691
5026
|
return {
|
|
4692
|
-
files: [
|
|
5027
|
+
files: [path16.join(pagesDir, schema.name, "columns.tsx")]
|
|
4693
5028
|
};
|
|
4694
5029
|
}
|
|
4695
5030
|
|
|
4696
5031
|
// src/generators/create-page.ts
|
|
4697
|
-
import
|
|
4698
|
-
import
|
|
5032
|
+
import fs17 from "fs";
|
|
5033
|
+
import path17 from "path";
|
|
4699
5034
|
function generateCreatePage(schema, cwd, pagesDir, options = {}) {
|
|
4700
|
-
const newDir =
|
|
4701
|
-
const pageFilePath =
|
|
4702
|
-
if (
|
|
5035
|
+
const newDir = path17.join(cwd, pagesDir, schema.name, "new");
|
|
5036
|
+
const pageFilePath = path17.join(newDir, "page.tsx");
|
|
5037
|
+
if (fs17.existsSync(pageFilePath) && !options.force) {
|
|
4703
5038
|
return { files: [] };
|
|
4704
5039
|
}
|
|
4705
5040
|
const singular = singularize(schema.name);
|
|
@@ -4726,18 +5061,18 @@ export default async function Create${Singular}Page() {
|
|
|
4726
5061
|
)
|
|
4727
5062
|
}
|
|
4728
5063
|
`;
|
|
4729
|
-
if (!
|
|
4730
|
-
|
|
5064
|
+
if (!fs17.existsSync(newDir)) {
|
|
5065
|
+
fs17.mkdirSync(newDir, { recursive: true });
|
|
4731
5066
|
}
|
|
4732
|
-
|
|
5067
|
+
fs17.writeFileSync(pageFilePath, content, "utf-8");
|
|
4733
5068
|
return {
|
|
4734
|
-
files: [
|
|
5069
|
+
files: [path17.join(pagesDir, schema.name, "new", "page.tsx")]
|
|
4735
5070
|
};
|
|
4736
5071
|
}
|
|
4737
5072
|
|
|
4738
5073
|
// src/generators/database.ts
|
|
4739
|
-
import
|
|
4740
|
-
import
|
|
5074
|
+
import fs18 from "fs";
|
|
5075
|
+
import path18 from "path";
|
|
4741
5076
|
function getFieldModifiers(field, needsSql) {
|
|
4742
5077
|
const modifiers = [];
|
|
4743
5078
|
if (field.primaryKey) {
|
|
@@ -4918,11 +5253,11 @@ function findTableEnd2(content, startIndex) {
|
|
|
4918
5253
|
return content.length;
|
|
4919
5254
|
}
|
|
4920
5255
|
function generateDatabase(schema, cwd, schemaDir, options = {}) {
|
|
4921
|
-
const schemaFilePath =
|
|
5256
|
+
const schemaFilePath = path18.join(cwd, schemaDir);
|
|
4922
5257
|
const files = [];
|
|
4923
5258
|
let content = "";
|
|
4924
|
-
if (
|
|
4925
|
-
content =
|
|
5259
|
+
if (fs18.existsSync(schemaFilePath)) {
|
|
5260
|
+
content = fs18.readFileSync(schemaFilePath, "utf-8");
|
|
4926
5261
|
}
|
|
4927
5262
|
const variableName = toCamelCase(schema.name);
|
|
4928
5263
|
if (content.includes(`export const ${variableName} =`) && !options.force) {
|
|
@@ -4962,11 +5297,11 @@ ${junctionDefs[i]}`;
|
|
|
4962
5297
|
}
|
|
4963
5298
|
}
|
|
4964
5299
|
updated = mergeImports(updated, requiredImports, needsSql.value);
|
|
4965
|
-
const dir =
|
|
4966
|
-
if (!
|
|
4967
|
-
|
|
5300
|
+
const dir = path18.dirname(schemaFilePath);
|
|
5301
|
+
if (!fs18.existsSync(dir)) {
|
|
5302
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
4968
5303
|
}
|
|
4969
|
-
|
|
5304
|
+
fs18.writeFileSync(schemaFilePath, updated, "utf-8");
|
|
4970
5305
|
files.push(schemaDir);
|
|
4971
5306
|
return {
|
|
4972
5307
|
files,
|
|
@@ -4976,12 +5311,12 @@ ${junctionDefs[i]}`;
|
|
|
4976
5311
|
}
|
|
4977
5312
|
|
|
4978
5313
|
// src/generators/edit-page.ts
|
|
4979
|
-
import
|
|
4980
|
-
import
|
|
5314
|
+
import fs19 from "fs";
|
|
5315
|
+
import path19 from "path";
|
|
4981
5316
|
function generateEditPage(schema, cwd, pagesDir, options = {}) {
|
|
4982
|
-
const editDir =
|
|
4983
|
-
const pageFilePath =
|
|
4984
|
-
if (
|
|
5317
|
+
const editDir = path19.join(cwd, pagesDir, schema.name, "[id]", "edit");
|
|
5318
|
+
const pageFilePath = path19.join(editDir, "page.tsx");
|
|
5319
|
+
if (fs19.existsSync(pageFilePath) && !options.force) {
|
|
4985
5320
|
return { files: [] };
|
|
4986
5321
|
}
|
|
4987
5322
|
const singular = singularize(schema.name);
|
|
@@ -5020,18 +5355,18 @@ export default async function Edit${Singular}Page({ params }: PageProps) {
|
|
|
5020
5355
|
)
|
|
5021
5356
|
}
|
|
5022
5357
|
`;
|
|
5023
|
-
if (!
|
|
5024
|
-
|
|
5358
|
+
if (!fs19.existsSync(editDir)) {
|
|
5359
|
+
fs19.mkdirSync(editDir, { recursive: true });
|
|
5025
5360
|
}
|
|
5026
|
-
|
|
5361
|
+
fs19.writeFileSync(pageFilePath, content, "utf-8");
|
|
5027
5362
|
return {
|
|
5028
|
-
files: [
|
|
5363
|
+
files: [path19.join(pagesDir, schema.name, "[id]", "edit", "page.tsx")]
|
|
5029
5364
|
};
|
|
5030
5365
|
}
|
|
5031
5366
|
|
|
5032
5367
|
// src/generators/form/form-entity.ts
|
|
5033
|
-
import
|
|
5034
|
-
import
|
|
5368
|
+
import fs20 from "fs";
|
|
5369
|
+
import path20 from "path";
|
|
5035
5370
|
|
|
5036
5371
|
// src/generators/form/zod-schema.ts
|
|
5037
5372
|
function getFormFieldType(field) {
|
|
@@ -5829,7 +6164,7 @@ function generateDefaultValue(f) {
|
|
|
5829
6164
|
}
|
|
5830
6165
|
return ` ${f.name}: initialData?.${f.name} ?? ''`;
|
|
5831
6166
|
}
|
|
5832
|
-
function
|
|
6167
|
+
function buildZodFields2(flatFields) {
|
|
5833
6168
|
return flatFields.filter((f) => f.type !== "tabs").flatMap((f) => {
|
|
5834
6169
|
const defs = [];
|
|
5835
6170
|
const zodType = getZodType(f);
|
|
@@ -5841,7 +6176,7 @@ function buildZodFields(flatFields) {
|
|
|
5841
6176
|
return defs;
|
|
5842
6177
|
}).join(",\n");
|
|
5843
6178
|
}
|
|
5844
|
-
function
|
|
6179
|
+
function buildDefaultValues2(flatFields) {
|
|
5845
6180
|
return flatFields.filter((f) => f.type !== "tabs").flatMap((f) => {
|
|
5846
6181
|
const defs = [generateDefaultValue(f)];
|
|
5847
6182
|
if (f.hasIcon) defs.push(` ${f.name}Icon: initialData?.${f.name}Icon ?? ''`);
|
|
@@ -5928,9 +6263,9 @@ function buildUiImports(ctx) {
|
|
|
5928
6263
|
|
|
5929
6264
|
// src/generators/form/form-entity.ts
|
|
5930
6265
|
function generateForm(schema, cwd, pagesDir, options = {}) {
|
|
5931
|
-
const entityDir =
|
|
5932
|
-
const formFilePath =
|
|
5933
|
-
if (
|
|
6266
|
+
const entityDir = path20.join(cwd, pagesDir, schema.name);
|
|
6267
|
+
const formFilePath = path20.join(entityDir, `${schema.name}-form.tsx`);
|
|
6268
|
+
if (fs20.existsSync(formFilePath) && !options.force) {
|
|
5934
6269
|
return { files: [] };
|
|
5935
6270
|
}
|
|
5936
6271
|
const singular = singularize(schema.name);
|
|
@@ -5984,8 +6319,8 @@ function generateForm(schema, cwd, pagesDir, options = {}) {
|
|
|
5984
6319
|
const hasList = hasFieldType(schema.fields, "list");
|
|
5985
6320
|
const hasNestedList = listFieldsWithNested.length > 0;
|
|
5986
6321
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
5987
|
-
const zodFields =
|
|
5988
|
-
const defaultValues =
|
|
6322
|
+
const zodFields = buildZodFields2(flatFields);
|
|
6323
|
+
const defaultValues = buildDefaultValues2(flatFields);
|
|
5989
6324
|
const formFieldsJSX = allFormFields.filter((f) => !(hasDraft && f.name === "published")).map((f) => {
|
|
5990
6325
|
if (f.type === "tabs" && f.tabs) {
|
|
5991
6326
|
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
@@ -6186,22 +6521,22 @@ ${hasDraft ? ` <Button
|
|
|
6186
6521
|
)
|
|
6187
6522
|
}
|
|
6188
6523
|
`;
|
|
6189
|
-
if (!
|
|
6190
|
-
|
|
6524
|
+
if (!fs20.existsSync(entityDir)) {
|
|
6525
|
+
fs20.mkdirSync(entityDir, { recursive: true });
|
|
6191
6526
|
}
|
|
6192
|
-
|
|
6527
|
+
fs20.writeFileSync(formFilePath, content, "utf-8");
|
|
6193
6528
|
return {
|
|
6194
|
-
files: [
|
|
6529
|
+
files: [path20.join(pagesDir, schema.name, `${schema.name}-form.tsx`)]
|
|
6195
6530
|
};
|
|
6196
6531
|
}
|
|
6197
6532
|
|
|
6198
6533
|
// src/generators/form/form-single.ts
|
|
6199
|
-
import
|
|
6200
|
-
import
|
|
6534
|
+
import fs21 from "fs";
|
|
6535
|
+
import path21 from "path";
|
|
6201
6536
|
function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
6202
|
-
const entityDir =
|
|
6203
|
-
const formFilePath =
|
|
6204
|
-
if (
|
|
6537
|
+
const entityDir = path21.join(cwd, pagesDir, schema.name);
|
|
6538
|
+
const formFilePath = path21.join(entityDir, `${schema.name}-form.tsx`);
|
|
6539
|
+
if (fs21.existsSync(formFilePath) && !options.force) {
|
|
6205
6540
|
return { files: [] };
|
|
6206
6541
|
}
|
|
6207
6542
|
const singular = singularize(schema.name);
|
|
@@ -6253,8 +6588,8 @@ function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
|
6253
6588
|
const hasList = hasFieldType(schema.fields, "list");
|
|
6254
6589
|
const hasNestedList = listFieldsWithNested.length > 0;
|
|
6255
6590
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
6256
|
-
const zodFields =
|
|
6257
|
-
const defaultValues =
|
|
6591
|
+
const zodFields = buildZodFields2(flatFields);
|
|
6592
|
+
const defaultValues = buildDefaultValues2(flatFields);
|
|
6258
6593
|
const formFieldsJSX = allFormFields.map((f) => {
|
|
6259
6594
|
if (f.type === "tabs" && f.tabs) {
|
|
6260
6595
|
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
@@ -6404,21 +6739,21 @@ ${formFieldsJSX}
|
|
|
6404
6739
|
)
|
|
6405
6740
|
}
|
|
6406
6741
|
`;
|
|
6407
|
-
if (!
|
|
6408
|
-
|
|
6742
|
+
if (!fs21.existsSync(entityDir)) {
|
|
6743
|
+
fs21.mkdirSync(entityDir, { recursive: true });
|
|
6409
6744
|
}
|
|
6410
|
-
|
|
6745
|
+
fs21.writeFileSync(formFilePath, content, "utf-8");
|
|
6411
6746
|
return {
|
|
6412
|
-
files: [
|
|
6747
|
+
files: [path21.join(pagesDir, schema.name, `${schema.name}-form.tsx`)]
|
|
6413
6748
|
};
|
|
6414
6749
|
}
|
|
6415
6750
|
|
|
6416
6751
|
// src/generators/hook.ts
|
|
6417
|
-
import
|
|
6418
|
-
import
|
|
6752
|
+
import fs22 from "fs";
|
|
6753
|
+
import path22 from "path";
|
|
6419
6754
|
function generateHook(schema, cwd, hooksDir, options = {}) {
|
|
6420
6755
|
const hookFileName = `use-${schema.name}.ts`;
|
|
6421
|
-
const hookFilePath =
|
|
6756
|
+
const hookFilePath = path22.join(cwd, hooksDir, hookFileName);
|
|
6422
6757
|
const singular = singularize(schema.name);
|
|
6423
6758
|
const plural = pluralize(schema.name);
|
|
6424
6759
|
const Singular = toPascalCase(singular);
|
|
@@ -6428,7 +6763,7 @@ function generateHook(schema, cwd, hooksDir, options = {}) {
|
|
|
6428
6763
|
const hasHtmlOutput = dbFields.some(
|
|
6429
6764
|
(f) => (f.type === "richtext" || f.type === "markdown") && f.output === "html"
|
|
6430
6765
|
);
|
|
6431
|
-
if (
|
|
6766
|
+
if (fs22.existsSync(hookFilePath) && !options.force) {
|
|
6432
6767
|
return { files: [], hookName: `use${Plural}` };
|
|
6433
6768
|
}
|
|
6434
6769
|
const hasFilters = schema.filters && schema.filters.length > 0;
|
|
@@ -6503,22 +6838,22 @@ export function use${Plural}(
|
|
|
6503
6838
|
}
|
|
6504
6839
|
${singularHook}${slugHook}${distinctHooks}
|
|
6505
6840
|
`;
|
|
6506
|
-
const dir =
|
|
6507
|
-
if (!
|
|
6508
|
-
|
|
6841
|
+
const dir = path22.dirname(hookFilePath);
|
|
6842
|
+
if (!fs22.existsSync(dir)) {
|
|
6843
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
6509
6844
|
}
|
|
6510
|
-
|
|
6845
|
+
fs22.writeFileSync(hookFilePath, content, "utf-8");
|
|
6511
6846
|
return {
|
|
6512
|
-
files: [
|
|
6847
|
+
files: [path22.join(hooksDir, hookFileName)],
|
|
6513
6848
|
hookName: `use${Plural}`
|
|
6514
6849
|
};
|
|
6515
6850
|
}
|
|
6516
6851
|
function generateSingleHook(schema, cwd, hooksDir, options = {}) {
|
|
6517
6852
|
const hookFileName = `use-${schema.name}.ts`;
|
|
6518
|
-
const hookFilePath =
|
|
6853
|
+
const hookFilePath = path22.join(cwd, hooksDir, hookFileName);
|
|
6519
6854
|
const singular = singularize(schema.name);
|
|
6520
6855
|
const Singular = toPascalCase(singular);
|
|
6521
|
-
if (
|
|
6856
|
+
if (fs22.existsSync(hookFilePath) && !options.force) {
|
|
6522
6857
|
return { files: [], hookName: `use${Singular}` };
|
|
6523
6858
|
}
|
|
6524
6859
|
const content = `import {
|
|
@@ -6535,20 +6870,20 @@ export function use${Singular}(): UseQueryResult<${Singular}Data | null, Error>
|
|
|
6535
6870
|
})
|
|
6536
6871
|
}
|
|
6537
6872
|
`;
|
|
6538
|
-
const dir =
|
|
6539
|
-
if (!
|
|
6540
|
-
|
|
6873
|
+
const dir = path22.dirname(hookFilePath);
|
|
6874
|
+
if (!fs22.existsSync(dir)) {
|
|
6875
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
6541
6876
|
}
|
|
6542
|
-
|
|
6877
|
+
fs22.writeFileSync(hookFilePath, content, "utf-8");
|
|
6543
6878
|
return {
|
|
6544
|
-
files: [
|
|
6879
|
+
files: [path22.join(hooksDir, hookFileName)],
|
|
6545
6880
|
hookName: `use${Singular}`
|
|
6546
6881
|
};
|
|
6547
6882
|
}
|
|
6548
6883
|
|
|
6549
6884
|
// src/generators/navigation.ts
|
|
6550
|
-
import
|
|
6551
|
-
import
|
|
6885
|
+
import fs23 from "fs";
|
|
6886
|
+
import path23 from "path";
|
|
6552
6887
|
function parseNavigationFile2(content) {
|
|
6553
6888
|
const iconImportMatch = content.match(/import\s*\{([^}]+)\}\s*from\s*['"]lucide-react['"]/);
|
|
6554
6889
|
const iconImports = iconImportMatch ? iconImportMatch[1].split(",").map((s) => s.trim()).filter((s) => s && s !== "LucideIcon") : [];
|
|
@@ -6651,14 +6986,14 @@ function appendItem2(lines, item, isLast) {
|
|
|
6651
6986
|
lines.push(` }${isLast ? "" : ","}`);
|
|
6652
6987
|
}
|
|
6653
6988
|
function updateNavigation(schema, cwd, cmsDir, options = {}) {
|
|
6654
|
-
const navFilePath =
|
|
6989
|
+
const navFilePath = path23.join(cwd, cmsDir, "data", "navigation.ts");
|
|
6655
6990
|
if (schema.name === "settings") {
|
|
6656
6991
|
return { files: [] };
|
|
6657
6992
|
}
|
|
6658
6993
|
let items = [];
|
|
6659
6994
|
let iconImports = [];
|
|
6660
|
-
if (
|
|
6661
|
-
const content =
|
|
6995
|
+
if (fs23.existsSync(navFilePath)) {
|
|
6996
|
+
const content = fs23.readFileSync(navFilePath, "utf-8");
|
|
6662
6997
|
const parsed = parseNavigationFile2(content);
|
|
6663
6998
|
items = parsed.items;
|
|
6664
6999
|
iconImports = parsed.iconImports;
|
|
@@ -6695,24 +7030,24 @@ function updateNavigation(schema, cwd, cmsDir, options = {}) {
|
|
|
6695
7030
|
iconImports.push(schema.icon);
|
|
6696
7031
|
}
|
|
6697
7032
|
iconImports.sort();
|
|
6698
|
-
const dir =
|
|
6699
|
-
if (!
|
|
6700
|
-
|
|
7033
|
+
const dir = path23.dirname(navFilePath);
|
|
7034
|
+
if (!fs23.existsSync(dir)) {
|
|
7035
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
6701
7036
|
}
|
|
6702
7037
|
const code = generateNavigationCode2(items, iconImports);
|
|
6703
|
-
|
|
7038
|
+
fs23.writeFileSync(navFilePath, code, "utf-8");
|
|
6704
7039
|
return {
|
|
6705
|
-
files: [
|
|
7040
|
+
files: [path23.join(cmsDir, "data", "navigation.ts")]
|
|
6706
7041
|
};
|
|
6707
7042
|
}
|
|
6708
7043
|
|
|
6709
7044
|
// src/generators/page.ts
|
|
6710
|
-
import
|
|
6711
|
-
import
|
|
7045
|
+
import fs24 from "fs";
|
|
7046
|
+
import path24 from "path";
|
|
6712
7047
|
function generatePage2(schema, cwd, pagesDir, options = {}) {
|
|
6713
|
-
const entityDir =
|
|
6714
|
-
const pageFilePath =
|
|
6715
|
-
if (
|
|
7048
|
+
const entityDir = path24.join(cwd, pagesDir, schema.name);
|
|
7049
|
+
const pageFilePath = path24.join(entityDir, "page.tsx");
|
|
7050
|
+
if (fs24.existsSync(pageFilePath) && !options.force) {
|
|
6716
7051
|
return { files: [] };
|
|
6717
7052
|
}
|
|
6718
7053
|
const plural = pluralize(schema.name);
|
|
@@ -6738,27 +7073,27 @@ export default function ${Plural}Page() {
|
|
|
6738
7073
|
)
|
|
6739
7074
|
}
|
|
6740
7075
|
`;
|
|
6741
|
-
if (!
|
|
6742
|
-
|
|
7076
|
+
if (!fs24.existsSync(entityDir)) {
|
|
7077
|
+
fs24.mkdirSync(entityDir, { recursive: true });
|
|
6743
7078
|
}
|
|
6744
|
-
|
|
7079
|
+
fs24.writeFileSync(pageFilePath, content, "utf-8");
|
|
6745
7080
|
return {
|
|
6746
|
-
files: [
|
|
7081
|
+
files: [path24.join(pagesDir, schema.name, "page.tsx")]
|
|
6747
7082
|
};
|
|
6748
7083
|
}
|
|
6749
7084
|
|
|
6750
7085
|
// src/generators/page-content.ts
|
|
6751
|
-
import
|
|
6752
|
-
import
|
|
7086
|
+
import fs25 from "fs";
|
|
7087
|
+
import path25 from "path";
|
|
6753
7088
|
function generatePageContent2(schema, cwd, pagesDir, options = {}) {
|
|
6754
|
-
const entityDir =
|
|
7089
|
+
const entityDir = path25.join(cwd, pagesDir, schema.name);
|
|
6755
7090
|
const singular = singularize(schema.name);
|
|
6756
7091
|
const plural = pluralize(schema.name);
|
|
6757
7092
|
const Singular = toPascalCase(singular);
|
|
6758
7093
|
const Plural = toPascalCase(plural);
|
|
6759
7094
|
const fileName = `${toKebabCase(plural)}-page-content.tsx`;
|
|
6760
|
-
const filePath =
|
|
6761
|
-
if (
|
|
7095
|
+
const filePath = path25.join(entityDir, fileName);
|
|
7096
|
+
if (fs25.existsSync(filePath) && !options.force) {
|
|
6762
7097
|
return { files: [] };
|
|
6763
7098
|
}
|
|
6764
7099
|
const hasCreate = schema.actions?.create ?? false;
|
|
@@ -7011,22 +7346,22 @@ ${searchLogic}${deleteLogic}
|
|
|
7011
7346
|
)
|
|
7012
7347
|
}
|
|
7013
7348
|
`;
|
|
7014
|
-
if (!
|
|
7015
|
-
|
|
7349
|
+
if (!fs25.existsSync(entityDir)) {
|
|
7350
|
+
fs25.mkdirSync(entityDir, { recursive: true });
|
|
7016
7351
|
}
|
|
7017
|
-
|
|
7352
|
+
fs25.writeFileSync(filePath, content, "utf-8");
|
|
7018
7353
|
return {
|
|
7019
|
-
files: [
|
|
7354
|
+
files: [path25.join(pagesDir, schema.name, fileName)]
|
|
7020
7355
|
};
|
|
7021
7356
|
}
|
|
7022
7357
|
|
|
7023
7358
|
// src/generators/single-page.ts
|
|
7024
|
-
import
|
|
7025
|
-
import
|
|
7359
|
+
import fs26 from "fs";
|
|
7360
|
+
import path26 from "path";
|
|
7026
7361
|
function generateSinglePage(schema, cwd, pagesDir, options = {}) {
|
|
7027
|
-
const entityDir =
|
|
7028
|
-
const pageFilePath =
|
|
7029
|
-
if (
|
|
7362
|
+
const entityDir = path26.join(cwd, pagesDir, schema.name);
|
|
7363
|
+
const pageFilePath = path26.join(entityDir, "page.tsx");
|
|
7364
|
+
if (fs26.existsSync(pageFilePath) && !options.force) {
|
|
7030
7365
|
return { files: [] };
|
|
7031
7366
|
}
|
|
7032
7367
|
const singular = singularize(schema.name);
|
|
@@ -7049,20 +7384,20 @@ export default async function ${PageName}Page() {
|
|
|
7049
7384
|
)
|
|
7050
7385
|
}
|
|
7051
7386
|
`;
|
|
7052
|
-
if (!
|
|
7053
|
-
|
|
7387
|
+
if (!fs26.existsSync(entityDir)) {
|
|
7388
|
+
fs26.mkdirSync(entityDir, { recursive: true });
|
|
7054
7389
|
}
|
|
7055
|
-
|
|
7390
|
+
fs26.writeFileSync(pageFilePath, content, "utf-8");
|
|
7056
7391
|
return {
|
|
7057
|
-
files: [
|
|
7392
|
+
files: [path26.join(pagesDir, schema.name, "page.tsx")]
|
|
7058
7393
|
};
|
|
7059
7394
|
}
|
|
7060
7395
|
|
|
7061
7396
|
// src/generators/table.ts
|
|
7062
|
-
import
|
|
7063
|
-
import
|
|
7397
|
+
import fs27 from "fs";
|
|
7398
|
+
import path27 from "path";
|
|
7064
7399
|
function generateTable2(schema, cwd, pagesDir, options = {}) {
|
|
7065
|
-
const entityDir =
|
|
7400
|
+
const entityDir = path27.join(cwd, pagesDir, schema.name);
|
|
7066
7401
|
const singular = singularize(schema.name);
|
|
7067
7402
|
const plural = pluralize(schema.name);
|
|
7068
7403
|
const Singular = toPascalCase(singular);
|
|
@@ -7070,8 +7405,8 @@ function generateTable2(schema, cwd, pagesDir, options = {}) {
|
|
|
7070
7405
|
const camelPlural = toCamelCase(plural);
|
|
7071
7406
|
const camelSingular = toCamelCase(singular);
|
|
7072
7407
|
const tableFileName = `${toKebabCase(plural)}-table.tsx`;
|
|
7073
|
-
const tableFilePath =
|
|
7074
|
-
if (
|
|
7408
|
+
const tableFilePath = path27.join(entityDir, tableFileName);
|
|
7409
|
+
if (fs27.existsSync(tableFilePath) && !options.force) {
|
|
7075
7410
|
return { files: [] };
|
|
7076
7411
|
}
|
|
7077
7412
|
const hasFilters = schema.filters && schema.filters.length > 0;
|
|
@@ -7435,12 +7770,12 @@ export function ${Plural}Table<TValue>({ columns, selectedIds, setSelectedIds, $
|
|
|
7435
7770
|
)
|
|
7436
7771
|
}
|
|
7437
7772
|
`;
|
|
7438
|
-
if (!
|
|
7439
|
-
|
|
7773
|
+
if (!fs27.existsSync(entityDir)) {
|
|
7774
|
+
fs27.mkdirSync(entityDir, { recursive: true });
|
|
7440
7775
|
}
|
|
7441
|
-
|
|
7776
|
+
fs27.writeFileSync(tableFilePath, content, "utf-8");
|
|
7442
7777
|
return {
|
|
7443
|
-
files: [
|
|
7778
|
+
files: [path27.join(pagesDir, schema.name, tableFileName)]
|
|
7444
7779
|
};
|
|
7445
7780
|
}
|
|
7446
7781
|
|
|
@@ -7598,13 +7933,13 @@ function runSinglePipeline(schema, cwd, config, options = {}) {
|
|
|
7598
7933
|
|
|
7599
7934
|
// src/generators/post-generate.ts
|
|
7600
7935
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
7601
|
-
import
|
|
7602
|
-
import
|
|
7936
|
+
import fs29 from "fs";
|
|
7937
|
+
import path29 from "path";
|
|
7603
7938
|
|
|
7604
7939
|
// src/utils/package-manager.ts
|
|
7605
7940
|
import { execFileSync } from "child_process";
|
|
7606
|
-
import
|
|
7607
|
-
import
|
|
7941
|
+
import fs28 from "fs";
|
|
7942
|
+
import path28 from "path";
|
|
7608
7943
|
var LOCKFILE_MAP = {
|
|
7609
7944
|
"pnpm-lock.yaml": "pnpm",
|
|
7610
7945
|
"package-lock.json": "npm",
|
|
@@ -7613,18 +7948,18 @@ var LOCKFILE_MAP = {
|
|
|
7613
7948
|
"bun.lock": "bun"
|
|
7614
7949
|
};
|
|
7615
7950
|
function detectPackageManager(cwd) {
|
|
7616
|
-
let dir =
|
|
7617
|
-
const root =
|
|
7951
|
+
let dir = path28.resolve(cwd);
|
|
7952
|
+
const root = path28.parse(dir).root;
|
|
7618
7953
|
while (dir !== root) {
|
|
7619
7954
|
for (const [lockfile, pm] of Object.entries(LOCKFILE_MAP)) {
|
|
7620
|
-
if (
|
|
7955
|
+
if (fs28.existsSync(path28.join(dir, lockfile))) {
|
|
7621
7956
|
return pm;
|
|
7622
7957
|
}
|
|
7623
7958
|
}
|
|
7624
|
-
const pkgPath =
|
|
7625
|
-
if (
|
|
7959
|
+
const pkgPath = path28.join(dir, "package.json");
|
|
7960
|
+
if (fs28.existsSync(pkgPath)) {
|
|
7626
7961
|
try {
|
|
7627
|
-
const pkg = JSON.parse(
|
|
7962
|
+
const pkg = JSON.parse(fs28.readFileSync(pkgPath, "utf-8"));
|
|
7628
7963
|
if (typeof pkg.packageManager === "string") {
|
|
7629
7964
|
const name = pkg.packageManager.split("@")[0];
|
|
7630
7965
|
if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
|
|
@@ -7634,7 +7969,7 @@ function detectPackageManager(cwd) {
|
|
|
7634
7969
|
} catch {
|
|
7635
7970
|
}
|
|
7636
7971
|
}
|
|
7637
|
-
dir =
|
|
7972
|
+
dir = path28.dirname(dir);
|
|
7638
7973
|
}
|
|
7639
7974
|
return "npm";
|
|
7640
7975
|
}
|
|
@@ -7665,9 +8000,9 @@ function createNextAppCommand(pm) {
|
|
|
7665
8000
|
|
|
7666
8001
|
// src/generators/post-generate.ts
|
|
7667
8002
|
function loadEnvFile(cwd) {
|
|
7668
|
-
const envPath =
|
|
7669
|
-
if (!
|
|
7670
|
-
const content =
|
|
8003
|
+
const envPath = path29.join(cwd, ".env.local");
|
|
8004
|
+
if (!fs29.existsSync(envPath)) return;
|
|
8005
|
+
const content = fs29.readFileSync(envPath, "utf-8");
|
|
7671
8006
|
for (const line of content.split("\n")) {
|
|
7672
8007
|
const trimmed = line.trim();
|
|
7673
8008
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -7697,10 +8032,10 @@ function runPmScript(pm, script, cwd) {
|
|
|
7697
8032
|
}
|
|
7698
8033
|
}
|
|
7699
8034
|
function hasPkgScript(cwd, script) {
|
|
7700
|
-
const pkgPath =
|
|
7701
|
-
if (!
|
|
8035
|
+
const pkgPath = path29.join(cwd, "package.json");
|
|
8036
|
+
if (!fs29.existsSync(pkgPath)) return false;
|
|
7702
8037
|
try {
|
|
7703
|
-
const pkg = JSON.parse(
|
|
8038
|
+
const pkg = JSON.parse(fs29.readFileSync(pkgPath, "utf-8"));
|
|
7704
8039
|
const scripts = pkg.scripts;
|
|
7705
8040
|
return !!scripts?.[script];
|
|
7706
8041
|
} catch {
|
|
@@ -7727,7 +8062,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7727
8062
|
console.log(ok ? " Database schema synced" : " Database push failed (run db:push manually)");
|
|
7728
8063
|
} else {
|
|
7729
8064
|
console.log("\n Running drizzle-kit push...");
|
|
7730
|
-
const drizzleBin =
|
|
8065
|
+
const drizzleBin = path29.join(cwd, "node_modules", ".bin", "drizzle-kit");
|
|
7731
8066
|
try {
|
|
7732
8067
|
execFileSync2(drizzleBin, ["push", "--force"], { cwd, stdio: "inherit" });
|
|
7733
8068
|
result.dbPush = "success";
|
|
@@ -7746,7 +8081,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7746
8081
|
result.lintFix = ok ? "success" : "failed";
|
|
7747
8082
|
console.log(ok ? " Code formatted" : " Lint fix had issues (run lint:fix manually)");
|
|
7748
8083
|
} else {
|
|
7749
|
-
const biomeBin =
|
|
8084
|
+
const biomeBin = path29.join(cwd, "node_modules", ".bin", "biome");
|
|
7750
8085
|
try {
|
|
7751
8086
|
execFileSync2(biomeBin, ["check", "--write", "."], { cwd, stdio: "pipe" });
|
|
7752
8087
|
result.lintFix = "success";
|
|
@@ -7769,7 +8104,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7769
8104
|
// src/commands/generate.ts
|
|
7770
8105
|
var generateCommand = new Command("generate").alias("g").description("Generate entity or form from a JSON schema").argument("<schema>", "Schema name (e.g. posts, categories, contact)").option("-f, --force", "Overwrite existing generated files", false).option("--skip-migration", "Skip running db:push after generation", false).option("--cwd <path>", "Project root path").action(
|
|
7771
8106
|
async (schemaName, options) => {
|
|
7772
|
-
const cwd = options.cwd ?
|
|
8107
|
+
const cwd = options.cwd ? path30.resolve(options.cwd) : process.cwd();
|
|
7773
8108
|
console.log("\n BetterStart Generator\n");
|
|
7774
8109
|
let config;
|
|
7775
8110
|
try {
|
|
@@ -7778,7 +8113,7 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7778
8113
|
console.error(` Error loading config: ${err instanceof Error ? err.message : String(err)}`);
|
|
7779
8114
|
process.exit(1);
|
|
7780
8115
|
}
|
|
7781
|
-
const schemasDir =
|
|
8116
|
+
const schemasDir = path30.join(cwd, config.paths?.schemas ?? "./cms/schemas");
|
|
7782
8117
|
console.log(` Project root: ${cwd}`);
|
|
7783
8118
|
console.log(` Loading schema: ${schemaName}.json
|
|
7784
8119
|
`);
|
|
@@ -7790,7 +8125,7 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7790
8125
|
console.error(` ${err.message}`);
|
|
7791
8126
|
console.error(
|
|
7792
8127
|
`
|
|
7793
|
-
Create a schema file at: ${
|
|
8128
|
+
Create a schema file at: ${path30.join(schemasDir, `${schemaName}.json`)}`
|
|
7794
8129
|
);
|
|
7795
8130
|
} else {
|
|
7796
8131
|
console.error(
|
|
@@ -7883,8 +8218,8 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7883
8218
|
|
|
7884
8219
|
// src/commands/init.ts
|
|
7885
8220
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
7886
|
-
import
|
|
7887
|
-
import
|
|
8221
|
+
import fs40 from "fs";
|
|
8222
|
+
import path45 from "path";
|
|
7888
8223
|
import * as p4 from "@clack/prompts";
|
|
7889
8224
|
import { Command as Command3 } from "commander";
|
|
7890
8225
|
import pc2 from "picocolors";
|
|
@@ -8027,21 +8362,21 @@ async function promptProject(defaultName) {
|
|
|
8027
8362
|
}
|
|
8028
8363
|
|
|
8029
8364
|
// src/init/scaffolders/api-routes.ts
|
|
8030
|
-
import
|
|
8365
|
+
import path32 from "path";
|
|
8031
8366
|
|
|
8032
8367
|
// src/utils/fs.ts
|
|
8033
|
-
import
|
|
8034
|
-
import
|
|
8368
|
+
import fs30 from "fs";
|
|
8369
|
+
import path31 from "path";
|
|
8035
8370
|
import fse from "fs-extra";
|
|
8036
8371
|
function ensureDir(dirPath) {
|
|
8037
8372
|
fse.ensureDirSync(dirPath);
|
|
8038
8373
|
}
|
|
8039
8374
|
function safeWriteFile(filePath, content, force = false) {
|
|
8040
|
-
if (!force &&
|
|
8375
|
+
if (!force && fs30.existsSync(filePath)) {
|
|
8041
8376
|
return false;
|
|
8042
8377
|
}
|
|
8043
|
-
ensureDir(
|
|
8044
|
-
|
|
8378
|
+
ensureDir(path31.dirname(filePath));
|
|
8379
|
+
fs30.writeFileSync(filePath, content, "utf-8");
|
|
8045
8380
|
return true;
|
|
8046
8381
|
}
|
|
8047
8382
|
|
|
@@ -8135,21 +8470,21 @@ export async function POST(request: NextRequest) {
|
|
|
8135
8470
|
// src/init/scaffolders/api-routes.ts
|
|
8136
8471
|
function scaffoldApiRoutes({ cwd, config }) {
|
|
8137
8472
|
const created = [];
|
|
8138
|
-
const apiDir =
|
|
8473
|
+
const apiDir = path32.resolve(cwd, config.paths.api);
|
|
8139
8474
|
function write(relPath, content) {
|
|
8140
|
-
const fullPath =
|
|
8141
|
-
ensureDir(
|
|
8475
|
+
const fullPath = path32.join(apiDir, relPath);
|
|
8476
|
+
ensureDir(path32.dirname(fullPath));
|
|
8142
8477
|
if (safeWriteFile(fullPath, content)) {
|
|
8143
|
-
created.push(
|
|
8478
|
+
created.push(path32.join(config.paths.api, relPath));
|
|
8144
8479
|
}
|
|
8145
8480
|
}
|
|
8146
|
-
write(
|
|
8147
|
-
write(
|
|
8481
|
+
write(path32.join("auth", "[...all]", "route.ts"), authRouteTemplate());
|
|
8482
|
+
write(path32.join("upload", "route.ts"), uploadRouteTemplate());
|
|
8148
8483
|
return created;
|
|
8149
8484
|
}
|
|
8150
8485
|
|
|
8151
8486
|
// src/init/scaffolders/auth.ts
|
|
8152
|
-
import
|
|
8487
|
+
import path33 from "path";
|
|
8153
8488
|
|
|
8154
8489
|
// src/init/templates/lib/auth/auth.ts
|
|
8155
8490
|
function authTemplate() {
|
|
@@ -8268,11 +8603,11 @@ export async function requireRole(allowedRoles: UserRole[]): Promise<User> {
|
|
|
8268
8603
|
// src/init/scaffolders/auth.ts
|
|
8269
8604
|
function scaffoldAuth({ cwd, config }) {
|
|
8270
8605
|
const created = [];
|
|
8271
|
-
const authDir =
|
|
8606
|
+
const authDir = path33.resolve(cwd, config.paths.cms, "lib", "auth");
|
|
8272
8607
|
function write(filename, content) {
|
|
8273
|
-
const fullPath =
|
|
8608
|
+
const fullPath = path33.join(authDir, filename);
|
|
8274
8609
|
if (safeWriteFile(fullPath, content)) {
|
|
8275
|
-
created.push(
|
|
8610
|
+
created.push(path33.join(config.paths.cms, "lib", "auth", filename));
|
|
8276
8611
|
}
|
|
8277
8612
|
}
|
|
8278
8613
|
write("auth.ts", authTemplate());
|
|
@@ -8282,51 +8617,51 @@ function scaffoldAuth({ cwd, config }) {
|
|
|
8282
8617
|
}
|
|
8283
8618
|
|
|
8284
8619
|
// src/init/scaffolders/base.ts
|
|
8285
|
-
import
|
|
8286
|
-
import
|
|
8620
|
+
import fs31 from "fs";
|
|
8621
|
+
import path34 from "path";
|
|
8287
8622
|
function scaffoldBase({ cwd, config }) {
|
|
8288
8623
|
const created = [];
|
|
8289
8624
|
const cmsDirs = [
|
|
8290
8625
|
config.paths.cms,
|
|
8291
8626
|
config.paths.schemas,
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8627
|
+
path34.join(config.paths.cms, "db"),
|
|
8628
|
+
path34.join(config.paths.cms, "db", "migrations"),
|
|
8629
|
+
path34.join(config.paths.cms, "lib", "auth"),
|
|
8630
|
+
path34.join(config.paths.cms, "lib", "actions"),
|
|
8631
|
+
path34.join(config.paths.cms, "lib", "cache"),
|
|
8632
|
+
path34.join(config.paths.cms, "lib", "markdown"),
|
|
8633
|
+
path34.join(config.paths.cms, "lib", "emails"),
|
|
8634
|
+
path34.join(config.paths.cms, "lib"),
|
|
8635
|
+
path34.join(config.paths.cms, "hooks"),
|
|
8636
|
+
path34.join(config.paths.cms, "components", "ui"),
|
|
8637
|
+
path34.join(config.paths.cms, "components", "form"),
|
|
8638
|
+
path34.join(config.paths.cms, "components", "data-table"),
|
|
8639
|
+
path34.join(config.paths.cms, "components", "layout"),
|
|
8640
|
+
path34.join(config.paths.cms, "components", "shared"),
|
|
8641
|
+
path34.join(config.paths.cms, "types"),
|
|
8642
|
+
path34.join(config.paths.cms, "utils"),
|
|
8643
|
+
path34.join(config.paths.cms, "data")
|
|
8309
8644
|
];
|
|
8310
8645
|
for (const dir of cmsDirs) {
|
|
8311
|
-
ensureDir(
|
|
8646
|
+
ensureDir(path34.resolve(cwd, dir));
|
|
8312
8647
|
}
|
|
8313
8648
|
const appDirs = [config.paths.pages, config.paths.login, config.paths.api];
|
|
8314
8649
|
for (const dir of appDirs) {
|
|
8315
|
-
ensureDir(
|
|
8650
|
+
ensureDir(path34.resolve(cwd, dir));
|
|
8316
8651
|
}
|
|
8317
8652
|
const configContent = generateConfigFile(config);
|
|
8318
|
-
if (safeWriteFile(
|
|
8653
|
+
if (safeWriteFile(path34.resolve(cwd, "cms.config.ts"), configContent)) {
|
|
8319
8654
|
created.push("cms.config.ts");
|
|
8320
8655
|
}
|
|
8321
8656
|
const cmsDoc = generateCmsDoc(config, {});
|
|
8322
|
-
if (safeWriteFile(
|
|
8657
|
+
if (safeWriteFile(path34.resolve(cwd, "CMS.md"), cmsDoc)) {
|
|
8323
8658
|
created.push("CMS.md");
|
|
8324
8659
|
}
|
|
8325
8660
|
return created;
|
|
8326
8661
|
}
|
|
8327
8662
|
function regenerateCmsDoc(cwd, config, options) {
|
|
8328
8663
|
const content = generateCmsDoc(config, options);
|
|
8329
|
-
|
|
8664
|
+
fs31.writeFileSync(path34.resolve(cwd, "CMS.md"), content, "utf-8");
|
|
8330
8665
|
}
|
|
8331
8666
|
function generateConfigFile(config) {
|
|
8332
8667
|
return `import { defineConfig } from '@betterstart/cli'
|
|
@@ -8480,8 +8815,8 @@ Edit \`cms.config.ts\` to customize paths, database provider, and features.`);
|
|
|
8480
8815
|
}
|
|
8481
8816
|
|
|
8482
8817
|
// src/init/scaffolders/biome.ts
|
|
8483
|
-
import
|
|
8484
|
-
import
|
|
8818
|
+
import fs32 from "fs";
|
|
8819
|
+
import path35 from "path";
|
|
8485
8820
|
function scaffoldBiome(cwd, linter) {
|
|
8486
8821
|
if (linter.type !== "none") {
|
|
8487
8822
|
return {
|
|
@@ -8489,8 +8824,8 @@ function scaffoldBiome(cwd, linter) {
|
|
|
8489
8824
|
skippedReason: `${linter.type} already configured (${linter.configFile})`
|
|
8490
8825
|
};
|
|
8491
8826
|
}
|
|
8492
|
-
const configPath =
|
|
8493
|
-
if (
|
|
8827
|
+
const configPath = path35.join(cwd, "biome.json");
|
|
8828
|
+
if (fs32.existsSync(configPath)) {
|
|
8494
8829
|
return { installed: false, skippedReason: "biome.json already exists" };
|
|
8495
8830
|
}
|
|
8496
8831
|
const config = {
|
|
@@ -8541,52 +8876,52 @@ function scaffoldBiome(cwd, linter) {
|
|
|
8541
8876
|
]
|
|
8542
8877
|
}
|
|
8543
8878
|
};
|
|
8544
|
-
|
|
8879
|
+
fs32.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
8545
8880
|
`, "utf-8");
|
|
8546
8881
|
return { installed: true, skippedReason: null };
|
|
8547
8882
|
}
|
|
8548
8883
|
|
|
8549
8884
|
// src/init/scaffolders/components.ts
|
|
8550
|
-
import
|
|
8551
|
-
import
|
|
8885
|
+
import path37 from "path";
|
|
8886
|
+
import fs34 from "fs-extra";
|
|
8552
8887
|
|
|
8553
8888
|
// src/utils/detect.ts
|
|
8554
|
-
import
|
|
8555
|
-
import
|
|
8889
|
+
import fs33 from "fs";
|
|
8890
|
+
import path36 from "path";
|
|
8556
8891
|
var NEXT_CONFIG_FILES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
8557
8892
|
function detectProjectName(cwd) {
|
|
8558
|
-
const pkgPath =
|
|
8559
|
-
if (
|
|
8893
|
+
const pkgPath = path36.join(cwd, "package.json");
|
|
8894
|
+
if (fs33.existsSync(pkgPath)) {
|
|
8560
8895
|
try {
|
|
8561
|
-
const pkg = JSON.parse(
|
|
8896
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
8562
8897
|
if (typeof pkg.name === "string" && pkg.name.length > 0) {
|
|
8563
8898
|
return formatProjectName(pkg.name);
|
|
8564
8899
|
}
|
|
8565
8900
|
} catch {
|
|
8566
8901
|
}
|
|
8567
8902
|
}
|
|
8568
|
-
return formatProjectName(
|
|
8903
|
+
return formatProjectName(path36.basename(cwd));
|
|
8569
8904
|
}
|
|
8570
8905
|
function formatProjectName(name) {
|
|
8571
8906
|
const base = name.includes("/") ? name.split("/").pop() : name;
|
|
8572
8907
|
return base.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
8573
8908
|
}
|
|
8574
8909
|
function detectProject(cwd) {
|
|
8575
|
-
const isExisting = NEXT_CONFIG_FILES.some((f) =>
|
|
8576
|
-
const hasSrcDir =
|
|
8577
|
-
const hasTypeScript =
|
|
8910
|
+
const isExisting = NEXT_CONFIG_FILES.some((f) => fs33.existsSync(path36.join(cwd, f)));
|
|
8911
|
+
const hasSrcDir = fs33.existsSync(path36.join(cwd, "src"));
|
|
8912
|
+
const hasTypeScript = fs33.existsSync(path36.join(cwd, "tsconfig.json")) || fs33.existsSync(path36.join(cwd, "tsconfig.app.json"));
|
|
8578
8913
|
const hasTailwind = detectTailwind(cwd);
|
|
8579
8914
|
const linter = detectLinter(cwd);
|
|
8580
8915
|
const conflicts = [];
|
|
8581
8916
|
if (isExisting) {
|
|
8582
|
-
if (
|
|
8917
|
+
if (fs33.existsSync(path36.join(cwd, "cms"))) {
|
|
8583
8918
|
conflicts.push("cms/ directory already exists");
|
|
8584
8919
|
}
|
|
8585
|
-
if (
|
|
8920
|
+
if (fs33.existsSync(path36.join(cwd, "cms.config.ts"))) {
|
|
8586
8921
|
conflicts.push("cms.config.ts already exists");
|
|
8587
8922
|
}
|
|
8588
8923
|
const appBase = hasSrcDir ? "src/app" : "app";
|
|
8589
|
-
if (
|
|
8924
|
+
if (fs33.existsSync(path36.join(cwd, appBase, "(cms)"))) {
|
|
8590
8925
|
conflicts.push(`${appBase}/(cms)/ route group already exists`);
|
|
8591
8926
|
}
|
|
8592
8927
|
if (hasTsconfigCmsAliases(cwd)) {
|
|
@@ -8613,19 +8948,19 @@ var ESLINT_CONFIG_FILES = [
|
|
|
8613
8948
|
];
|
|
8614
8949
|
function detectLinter(cwd) {
|
|
8615
8950
|
for (const f of BIOME_CONFIG_FILES) {
|
|
8616
|
-
if (
|
|
8951
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8617
8952
|
return { type: "biome", configFile: f };
|
|
8618
8953
|
}
|
|
8619
8954
|
}
|
|
8620
8955
|
for (const f of ESLINT_CONFIG_FILES) {
|
|
8621
|
-
if (
|
|
8956
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8622
8957
|
return { type: "eslint", configFile: f };
|
|
8623
8958
|
}
|
|
8624
8959
|
}
|
|
8625
|
-
const pkgPath =
|
|
8626
|
-
if (
|
|
8960
|
+
const pkgPath = path36.join(cwd, "package.json");
|
|
8961
|
+
if (fs33.existsSync(pkgPath)) {
|
|
8627
8962
|
try {
|
|
8628
|
-
const pkg = JSON.parse(
|
|
8963
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
8629
8964
|
if (pkg.eslintConfig) {
|
|
8630
8965
|
return { type: "eslint", configFile: "package.json (eslintConfig)" };
|
|
8631
8966
|
}
|
|
@@ -8636,14 +8971,14 @@ function detectLinter(cwd) {
|
|
|
8636
8971
|
}
|
|
8637
8972
|
function detectTailwind(cwd) {
|
|
8638
8973
|
const cssFiles = ["globals.css", "app.css", "index.css"].flatMap((f) => [
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8974
|
+
path36.join(cwd, "src", "app", f),
|
|
8975
|
+
path36.join(cwd, "app", f),
|
|
8976
|
+
path36.join(cwd, "src", f),
|
|
8977
|
+
path36.join(cwd, f)
|
|
8643
8978
|
]);
|
|
8644
8979
|
for (const cssFile of cssFiles) {
|
|
8645
|
-
if (
|
|
8646
|
-
const content =
|
|
8980
|
+
if (fs33.existsSync(cssFile)) {
|
|
8981
|
+
const content = fs33.readFileSync(cssFile, "utf-8");
|
|
8647
8982
|
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'") || content.includes("@theme")) {
|
|
8648
8983
|
return true;
|
|
8649
8984
|
}
|
|
@@ -8651,8 +8986,8 @@ function detectTailwind(cwd) {
|
|
|
8651
8986
|
}
|
|
8652
8987
|
const postcssFiles = ["postcss.config.js", "postcss.config.mjs", "postcss.config.cjs"];
|
|
8653
8988
|
for (const f of postcssFiles) {
|
|
8654
|
-
if (
|
|
8655
|
-
const content =
|
|
8989
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8990
|
+
const content = fs33.readFileSync(path36.join(cwd, f), "utf-8");
|
|
8656
8991
|
if (content.includes("tailwindcss") || content.includes("@tailwindcss")) {
|
|
8657
8992
|
return true;
|
|
8658
8993
|
}
|
|
@@ -8661,20 +8996,20 @@ function detectTailwind(cwd) {
|
|
|
8661
8996
|
return false;
|
|
8662
8997
|
}
|
|
8663
8998
|
function hasTsconfigCmsAliases(cwd) {
|
|
8664
|
-
const tsconfigPath =
|
|
8665
|
-
if (!
|
|
8999
|
+
const tsconfigPath = path36.join(cwd, "tsconfig.json");
|
|
9000
|
+
if (!fs33.existsSync(tsconfigPath)) return false;
|
|
8666
9001
|
try {
|
|
8667
|
-
const content =
|
|
9002
|
+
const content = fs33.readFileSync(tsconfigPath, "utf-8");
|
|
8668
9003
|
return content.includes("@cms/");
|
|
8669
9004
|
} catch {
|
|
8670
9005
|
return false;
|
|
8671
9006
|
}
|
|
8672
9007
|
}
|
|
8673
9008
|
function hasEnvBetterstartVars(cwd) {
|
|
8674
|
-
const envPath =
|
|
8675
|
-
if (!
|
|
9009
|
+
const envPath = path36.join(cwd, ".env.local");
|
|
9010
|
+
if (!fs33.existsSync(envPath)) return false;
|
|
8676
9011
|
try {
|
|
8677
|
-
const content =
|
|
9012
|
+
const content = fs33.readFileSync(envPath, "utf-8");
|
|
8678
9013
|
return content.includes("BETTERSTART_");
|
|
8679
9014
|
} catch {
|
|
8680
9015
|
return false;
|
|
@@ -11373,12 +11708,12 @@ export function addToMailchimpAudience(email: string): void {
|
|
|
11373
11708
|
|
|
11374
11709
|
// src/init/scaffolders/components.ts
|
|
11375
11710
|
function scaffoldComponents({ cwd, config }) {
|
|
11376
|
-
const cms =
|
|
11711
|
+
const cms = path37.resolve(cwd, config.paths.cms);
|
|
11377
11712
|
const created = [];
|
|
11378
11713
|
function write(relPath, content) {
|
|
11379
|
-
const fullPath =
|
|
11714
|
+
const fullPath = path37.join(cms, relPath);
|
|
11380
11715
|
if (safeWriteFile(fullPath, content)) {
|
|
11381
|
-
created.push(
|
|
11716
|
+
created.push(path37.join(config.paths.cms, relPath));
|
|
11382
11717
|
}
|
|
11383
11718
|
}
|
|
11384
11719
|
write("cms-globals.css", cmsGlobalsCssTemplate());
|
|
@@ -11427,18 +11762,18 @@ function scaffoldComponents({ cwd, config }) {
|
|
|
11427
11762
|
}
|
|
11428
11763
|
function copyUiTemplates(cwd, config) {
|
|
11429
11764
|
const created = [];
|
|
11430
|
-
const destDir =
|
|
11765
|
+
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui");
|
|
11431
11766
|
const cliRoot = findCliRoot();
|
|
11432
|
-
const srcDir =
|
|
11433
|
-
if (!
|
|
11767
|
+
const srcDir = path37.join(cliRoot, "templates", "ui");
|
|
11768
|
+
if (!fs34.existsSync(srcDir)) {
|
|
11434
11769
|
return created;
|
|
11435
11770
|
}
|
|
11436
|
-
const files =
|
|
11771
|
+
const files = fs34.readdirSync(srcDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
|
|
11437
11772
|
for (const file of files) {
|
|
11438
|
-
const destPath =
|
|
11439
|
-
if (!
|
|
11440
|
-
|
|
11441
|
-
created.push(
|
|
11773
|
+
const destPath = path37.join(destDir, file);
|
|
11774
|
+
if (!fs34.existsSync(destPath)) {
|
|
11775
|
+
fs34.copyFileSync(path37.join(srcDir, file), destPath);
|
|
11776
|
+
created.push(path37.join(config.paths.cms, "components", "ui", file));
|
|
11442
11777
|
}
|
|
11443
11778
|
}
|
|
11444
11779
|
return created;
|
|
@@ -11446,25 +11781,25 @@ function copyUiTemplates(cwd, config) {
|
|
|
11446
11781
|
function copyTiptapTemplates(cwd, config) {
|
|
11447
11782
|
const created = [];
|
|
11448
11783
|
const cliRoot = findCliRoot();
|
|
11449
|
-
const srcDir =
|
|
11450
|
-
const destDir =
|
|
11451
|
-
if (!
|
|
11784
|
+
const srcDir = path37.join(cliRoot, "templates", "tiptap");
|
|
11785
|
+
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui", "tiptap");
|
|
11786
|
+
if (!fs34.existsSync(srcDir)) {
|
|
11452
11787
|
return created;
|
|
11453
11788
|
}
|
|
11454
11789
|
copyDirRecursive(srcDir, destDir, config.paths.cms, created);
|
|
11455
11790
|
return created;
|
|
11456
11791
|
}
|
|
11457
11792
|
function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
11458
|
-
|
|
11459
|
-
const entries =
|
|
11793
|
+
fs34.ensureDirSync(dest);
|
|
11794
|
+
const entries = fs34.readdirSync(src, { withFileTypes: true });
|
|
11460
11795
|
for (const entry of entries) {
|
|
11461
|
-
const srcPath =
|
|
11462
|
-
const destPath =
|
|
11796
|
+
const srcPath = path37.join(src, entry.name);
|
|
11797
|
+
const destPath = path37.join(dest, entry.name);
|
|
11463
11798
|
if (entry.isDirectory()) {
|
|
11464
11799
|
copyDirRecursive(srcPath, destPath, cmsPrefix, created);
|
|
11465
|
-
} else if (!
|
|
11466
|
-
|
|
11467
|
-
const relFromCms =
|
|
11800
|
+
} else if (!fs34.existsSync(destPath)) {
|
|
11801
|
+
fs34.copyFileSync(srcPath, destPath);
|
|
11802
|
+
const relFromCms = path37.relative(path37.resolve(dest, "..", "..", "..", ".."), destPath);
|
|
11468
11803
|
created.push(relFromCms);
|
|
11469
11804
|
}
|
|
11470
11805
|
}
|
|
@@ -11472,35 +11807,35 @@ function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
|
11472
11807
|
function copySchemaMetaschema(cwd, config) {
|
|
11473
11808
|
const created = [];
|
|
11474
11809
|
const cliRoot = findCliRoot();
|
|
11475
|
-
const srcPath =
|
|
11476
|
-
const destPath =
|
|
11477
|
-
if (
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
created.push(
|
|
11810
|
+
const srcPath = path37.join(cliRoot, "templates", "schema.json");
|
|
11811
|
+
const destPath = path37.resolve(cwd, config.paths.schemas, "schema.json");
|
|
11812
|
+
if (fs34.existsSync(srcPath) && !fs34.existsSync(destPath)) {
|
|
11813
|
+
fs34.ensureDirSync(path37.dirname(destPath));
|
|
11814
|
+
fs34.copyFileSync(srcPath, destPath);
|
|
11815
|
+
created.push(path37.join(config.paths.schemas, "schema.json"));
|
|
11481
11816
|
}
|
|
11482
11817
|
return created;
|
|
11483
11818
|
}
|
|
11484
11819
|
function findCliRoot() {
|
|
11485
11820
|
let dir = new URL(".", import.meta.url).pathname;
|
|
11486
11821
|
for (let i = 0; i < 5; i++) {
|
|
11487
|
-
const pkgPath =
|
|
11488
|
-
if (
|
|
11822
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
11823
|
+
if (fs34.existsSync(pkgPath)) {
|
|
11489
11824
|
try {
|
|
11490
|
-
const pkg = JSON.parse(
|
|
11825
|
+
const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf-8"));
|
|
11491
11826
|
if (pkg.name === "@betterstart/cli") {
|
|
11492
11827
|
return dir;
|
|
11493
11828
|
}
|
|
11494
11829
|
} catch {
|
|
11495
11830
|
}
|
|
11496
11831
|
}
|
|
11497
|
-
dir =
|
|
11832
|
+
dir = path37.dirname(dir);
|
|
11498
11833
|
}
|
|
11499
|
-
return
|
|
11834
|
+
return path37.resolve(new URL(".", import.meta.url).pathname, "..", "..");
|
|
11500
11835
|
}
|
|
11501
11836
|
|
|
11502
11837
|
// src/init/scaffolders/database.ts
|
|
11503
|
-
import
|
|
11838
|
+
import path38 from "path";
|
|
11504
11839
|
|
|
11505
11840
|
// src/init/templates/db/client.ts
|
|
11506
11841
|
function dbClientTemplate() {
|
|
@@ -11611,16 +11946,16 @@ export const formSettings = pgTable(
|
|
|
11611
11946
|
// src/init/scaffolders/database.ts
|
|
11612
11947
|
function scaffoldDatabase({ cwd, config }) {
|
|
11613
11948
|
const created = [];
|
|
11614
|
-
const dbDir =
|
|
11949
|
+
const dbDir = path38.resolve(cwd, config.paths.cms, "db");
|
|
11615
11950
|
function write(filename, content) {
|
|
11616
|
-
const fullPath =
|
|
11951
|
+
const fullPath = path38.join(dbDir, filename);
|
|
11617
11952
|
if (safeWriteFile(fullPath, content)) {
|
|
11618
|
-
created.push(
|
|
11953
|
+
created.push(path38.join(config.paths.cms, "db", filename));
|
|
11619
11954
|
}
|
|
11620
11955
|
}
|
|
11621
11956
|
write("client.ts", dbClientTemplate());
|
|
11622
11957
|
write("schema.ts", dbSchemaTemplate());
|
|
11623
|
-
const drizzleConfigPath =
|
|
11958
|
+
const drizzleConfigPath = path38.resolve(cwd, "drizzle.config.ts");
|
|
11624
11959
|
if (safeWriteFile(drizzleConfigPath, drizzleConfigTemplate())) {
|
|
11625
11960
|
created.push("drizzle.config.ts");
|
|
11626
11961
|
}
|
|
@@ -11792,11 +12127,11 @@ import { existsSync, readFileSync } from "fs";
|
|
|
11792
12127
|
import { join } from "path";
|
|
11793
12128
|
|
|
11794
12129
|
// src/utils/env.ts
|
|
11795
|
-
import
|
|
11796
|
-
import
|
|
12130
|
+
import fs35 from "fs";
|
|
12131
|
+
import path39 from "path";
|
|
11797
12132
|
function appendEnvVars(cwd, sections, overwrite) {
|
|
11798
|
-
const envPath =
|
|
11799
|
-
let existing =
|
|
12133
|
+
const envPath = path39.join(cwd, ".env.local");
|
|
12134
|
+
let existing = fs35.existsSync(envPath) ? fs35.readFileSync(envPath, "utf-8") : "";
|
|
11800
12135
|
const existingKeys = new Set(
|
|
11801
12136
|
existing.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#")).map((line) => line.split("=")[0]?.trim()).filter(Boolean)
|
|
11802
12137
|
);
|
|
@@ -11840,7 +12175,7 @@ function appendEnvVars(cwd, sections, overwrite) {
|
|
|
11840
12175
|
const header = existing.trim() ? "" : "# ============================================\n# BetterStart CMS\n# ============================================\n";
|
|
11841
12176
|
const content = existing.trim() ? `${existing.trimEnd()}
|
|
11842
12177
|
${lines.join("\n")}` : header + lines.join("\n");
|
|
11843
|
-
|
|
12178
|
+
fs35.writeFileSync(envPath, content);
|
|
11844
12179
|
}
|
|
11845
12180
|
return { added, skipped, updated };
|
|
11846
12181
|
}
|
|
@@ -11919,7 +12254,7 @@ function scaffoldEnv(cwd, options) {
|
|
|
11919
12254
|
}
|
|
11920
12255
|
|
|
11921
12256
|
// src/init/scaffolders/layout.ts
|
|
11922
|
-
import
|
|
12257
|
+
import path40 from "path";
|
|
11923
12258
|
|
|
11924
12259
|
// src/init/templates/pages/authenticated-layout.ts
|
|
11925
12260
|
function authenticatedLayoutTemplate() {
|
|
@@ -12806,30 +13141,30 @@ export function UsersTable<TValue>({ columns }: UsersTableProps<TValue>) {
|
|
|
12806
13141
|
function scaffoldLayout({ cwd, config }) {
|
|
12807
13142
|
const created = [];
|
|
12808
13143
|
function write(relPath, content) {
|
|
12809
|
-
const fullPath =
|
|
12810
|
-
ensureDir(
|
|
13144
|
+
const fullPath = path40.resolve(cwd, relPath);
|
|
13145
|
+
ensureDir(path40.dirname(fullPath));
|
|
12811
13146
|
if (safeWriteFile(fullPath, content)) {
|
|
12812
13147
|
created.push(relPath);
|
|
12813
13148
|
}
|
|
12814
13149
|
}
|
|
12815
|
-
const cmsDir =
|
|
12816
|
-
write(
|
|
12817
|
-
write(
|
|
12818
|
-
write(
|
|
12819
|
-
write(
|
|
12820
|
-
write(
|
|
12821
|
-
const usersDir =
|
|
12822
|
-
write(
|
|
12823
|
-
write(
|
|
12824
|
-
write(
|
|
12825
|
-
write(
|
|
12826
|
-
write(
|
|
13150
|
+
const cmsDir = path40.dirname(config.paths.pages);
|
|
13151
|
+
write(path40.join(cmsDir, "layout.tsx"), cmsLayoutTemplate());
|
|
13152
|
+
write(path40.join(config.paths.pages, "layout.tsx"), authenticatedLayoutTemplate());
|
|
13153
|
+
write(path40.join(config.paths.login, "page.tsx"), loginPageTemplate());
|
|
13154
|
+
write(path40.join(config.paths.login, "login-form.tsx"), loginFormTemplate());
|
|
13155
|
+
write(path40.join(config.paths.pages, "page.tsx"), dashboardPageTemplate());
|
|
13156
|
+
const usersDir = path40.join(config.paths.pages, "users");
|
|
13157
|
+
write(path40.join(usersDir, "page.tsx"), usersPageTemplate());
|
|
13158
|
+
write(path40.join(usersDir, "users-table.tsx"), usersTableTemplate());
|
|
13159
|
+
write(path40.join(usersDir, "columns.tsx"), usersColumnsTemplate());
|
|
13160
|
+
write(path40.join(usersDir, "create-user-dialog.tsx"), createUserDialogTemplate());
|
|
13161
|
+
write(path40.join(usersDir, "edit-role-dialog.tsx"), editRoleDialogTemplate());
|
|
12827
13162
|
return created;
|
|
12828
13163
|
}
|
|
12829
13164
|
|
|
12830
13165
|
// src/init/scaffolders/preset.ts
|
|
12831
|
-
import
|
|
12832
|
-
import
|
|
13166
|
+
import fs36 from "fs";
|
|
13167
|
+
import path41 from "path";
|
|
12833
13168
|
|
|
12834
13169
|
// src/init/templates/presets/blog-categories.ts
|
|
12835
13170
|
function blogCategoriesSchema() {
|
|
@@ -13234,15 +13569,15 @@ function scaffoldPreset({
|
|
|
13234
13569
|
generatedFiles: [],
|
|
13235
13570
|
errors: []
|
|
13236
13571
|
};
|
|
13237
|
-
const schemasDir =
|
|
13572
|
+
const schemasDir = path41.join(cwd, config.paths?.schemas ?? "./cms/schemas");
|
|
13238
13573
|
const presetSchemas = getPresetSchemas(preset);
|
|
13239
13574
|
for (const ps of presetSchemas) {
|
|
13240
|
-
const filePath =
|
|
13241
|
-
const dir =
|
|
13242
|
-
if (!
|
|
13243
|
-
|
|
13575
|
+
const filePath = path41.join(schemasDir, ps.filename);
|
|
13576
|
+
const dir = path41.dirname(filePath);
|
|
13577
|
+
if (!fs36.existsSync(dir)) {
|
|
13578
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
13244
13579
|
}
|
|
13245
|
-
|
|
13580
|
+
fs36.writeFileSync(filePath, ps.content, "utf-8");
|
|
13246
13581
|
result.schemas.push(ps.filename);
|
|
13247
13582
|
}
|
|
13248
13583
|
for (const ps of presetSchemas) {
|
|
@@ -13283,8 +13618,8 @@ function scaffoldPreset({
|
|
|
13283
13618
|
}
|
|
13284
13619
|
|
|
13285
13620
|
// src/init/scaffolders/tailwind.ts
|
|
13286
|
-
import
|
|
13287
|
-
import
|
|
13621
|
+
import fs37 from "fs";
|
|
13622
|
+
import path42 from "path";
|
|
13288
13623
|
var SOURCE_LINES = ['@source "../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
13289
13624
|
var SOURCE_LINES_SRC = ['@source "../../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
13290
13625
|
var CMS_THEME_BLOCK = `
|
|
@@ -13339,8 +13674,8 @@ function findMainCss(cwd) {
|
|
|
13339
13674
|
"globals.css"
|
|
13340
13675
|
];
|
|
13341
13676
|
for (const candidate of candidates) {
|
|
13342
|
-
const filePath =
|
|
13343
|
-
if (
|
|
13677
|
+
const filePath = path42.join(cwd, candidate);
|
|
13678
|
+
if (fs37.existsSync(filePath)) {
|
|
13344
13679
|
return filePath;
|
|
13345
13680
|
}
|
|
13346
13681
|
}
|
|
@@ -13351,7 +13686,7 @@ function scaffoldTailwind(cwd, hasSrcDir) {
|
|
|
13351
13686
|
if (!cssFile) {
|
|
13352
13687
|
return { file: null, appended: false };
|
|
13353
13688
|
}
|
|
13354
|
-
let content =
|
|
13689
|
+
let content = fs37.readFileSync(cssFile, "utf-8");
|
|
13355
13690
|
let changed = false;
|
|
13356
13691
|
const sourceLines = hasSrcDir ? SOURCE_LINES_SRC : SOURCE_LINES;
|
|
13357
13692
|
const missingLines = sourceLines.filter((sl) => !content.includes(sl));
|
|
@@ -13403,14 +13738,14 @@ ${CMS_THEME_BLOCK}
|
|
|
13403
13738
|
}
|
|
13404
13739
|
}
|
|
13405
13740
|
if (changed) {
|
|
13406
|
-
|
|
13741
|
+
fs37.writeFileSync(cssFile, content, "utf-8");
|
|
13407
13742
|
}
|
|
13408
13743
|
return { file: cssFile, appended: changed };
|
|
13409
13744
|
}
|
|
13410
13745
|
|
|
13411
13746
|
// src/init/scaffolders/tsconfig.ts
|
|
13412
|
-
import
|
|
13413
|
-
import
|
|
13747
|
+
import fs38 from "fs";
|
|
13748
|
+
import path43 from "path";
|
|
13414
13749
|
function stripJsonComments(input) {
|
|
13415
13750
|
let result = "";
|
|
13416
13751
|
let i = 0;
|
|
@@ -13460,14 +13795,14 @@ var CMS_PATH_ALIASES = {
|
|
|
13460
13795
|
"@cms/cache/*": ["./cms/lib/cache/*"]
|
|
13461
13796
|
};
|
|
13462
13797
|
function scaffoldTsconfig(cwd) {
|
|
13463
|
-
const tsconfigPath =
|
|
13798
|
+
const tsconfigPath = path43.join(cwd, "tsconfig.json");
|
|
13464
13799
|
const added = [];
|
|
13465
13800
|
const skipped = [];
|
|
13466
|
-
if (!
|
|
13801
|
+
if (!fs38.existsSync(tsconfigPath)) {
|
|
13467
13802
|
skipped.push("tsconfig.json not found");
|
|
13468
13803
|
return { added, skipped };
|
|
13469
13804
|
}
|
|
13470
|
-
const raw =
|
|
13805
|
+
const raw = fs38.readFileSync(tsconfigPath, "utf-8");
|
|
13471
13806
|
const stripped = stripJsonComments(raw).replace(/,\s*([\]}])/g, "$1");
|
|
13472
13807
|
let tsconfig;
|
|
13473
13808
|
try {
|
|
@@ -13488,14 +13823,14 @@ function scaffoldTsconfig(cwd) {
|
|
|
13488
13823
|
}
|
|
13489
13824
|
compilerOptions.paths = paths;
|
|
13490
13825
|
tsconfig.compilerOptions = compilerOptions;
|
|
13491
|
-
|
|
13826
|
+
fs38.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
13492
13827
|
`, "utf-8");
|
|
13493
13828
|
return { added, skipped };
|
|
13494
13829
|
}
|
|
13495
13830
|
|
|
13496
13831
|
// src/commands/seed.ts
|
|
13497
|
-
import
|
|
13498
|
-
import
|
|
13832
|
+
import fs39 from "fs";
|
|
13833
|
+
import path44 from "path";
|
|
13499
13834
|
import * as clack from "@clack/prompts";
|
|
13500
13835
|
import { Command as Command2 } from "commander";
|
|
13501
13836
|
function buildSeedScript() {
|
|
@@ -13614,7 +13949,7 @@ main().catch((err) => {
|
|
|
13614
13949
|
`;
|
|
13615
13950
|
}
|
|
13616
13951
|
var seedCommand = new Command2("seed").description("Create the initial admin user").option("--cwd <path>", "Project root path").action(async (options) => {
|
|
13617
|
-
const cwd = options.cwd ?
|
|
13952
|
+
const cwd = options.cwd ? path44.resolve(options.cwd) : process.cwd();
|
|
13618
13953
|
clack.intro("BetterStart Seed");
|
|
13619
13954
|
let config;
|
|
13620
13955
|
try {
|
|
@@ -13654,14 +13989,14 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13654
13989
|
clack.cancel("Cancelled.");
|
|
13655
13990
|
process.exit(0);
|
|
13656
13991
|
}
|
|
13657
|
-
const scriptsDir =
|
|
13658
|
-
const seedPath =
|
|
13659
|
-
if (!
|
|
13660
|
-
|
|
13992
|
+
const scriptsDir = path44.join(cwd, cmsDir, "scripts");
|
|
13993
|
+
const seedPath = path44.join(scriptsDir, "seed.ts");
|
|
13994
|
+
if (!fs39.existsSync(scriptsDir)) {
|
|
13995
|
+
fs39.mkdirSync(scriptsDir, { recursive: true });
|
|
13661
13996
|
}
|
|
13662
|
-
|
|
13997
|
+
fs39.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
13663
13998
|
const { execFile } = await import("child_process");
|
|
13664
|
-
const tsxBin =
|
|
13999
|
+
const tsxBin = path44.join(cwd, "node_modules", ".bin", "tsx");
|
|
13665
14000
|
const runSeed2 = (overwrite) => new Promise((resolve, reject) => {
|
|
13666
14001
|
execFile(
|
|
13667
14002
|
tsxBin,
|
|
@@ -13700,7 +14035,7 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13700
14035
|
if (clack.isCancel(overwrite) || !overwrite) {
|
|
13701
14036
|
clack.cancel("Seed cancelled.");
|
|
13702
14037
|
try {
|
|
13703
|
-
|
|
14038
|
+
fs39.unlinkSync(seedPath);
|
|
13704
14039
|
} catch {
|
|
13705
14040
|
}
|
|
13706
14041
|
process.exit(0);
|
|
@@ -13717,15 +14052,15 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13717
14052
|
clack.log.error(errMsg);
|
|
13718
14053
|
clack.log.info("You can run the seed script manually:");
|
|
13719
14054
|
clack.log.info(
|
|
13720
|
-
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${
|
|
14055
|
+
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${path44.relative(cwd, seedPath)}`
|
|
13721
14056
|
);
|
|
13722
14057
|
clack.outro("");
|
|
13723
14058
|
process.exit(1);
|
|
13724
14059
|
}
|
|
13725
14060
|
try {
|
|
13726
|
-
|
|
13727
|
-
if (
|
|
13728
|
-
|
|
14061
|
+
fs39.unlinkSync(seedPath);
|
|
14062
|
+
if (fs39.existsSync(scriptsDir) && fs39.readdirSync(scriptsDir).length === 0) {
|
|
14063
|
+
fs39.rmdirSync(scriptsDir);
|
|
13729
14064
|
}
|
|
13730
14065
|
} catch {
|
|
13731
14066
|
}
|
|
@@ -13756,16 +14091,16 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13756
14091
|
const nukeFiles = ["cms.config.ts", "CMS.md", "drizzle.config.ts"];
|
|
13757
14092
|
let nuked = 0;
|
|
13758
14093
|
for (const dir of nukeDirs) {
|
|
13759
|
-
const fullPath =
|
|
13760
|
-
if (
|
|
13761
|
-
|
|
14094
|
+
const fullPath = path45.resolve(cwd, dir);
|
|
14095
|
+
if (fs40.existsSync(fullPath)) {
|
|
14096
|
+
fs40.rmSync(fullPath, { recursive: true, force: true });
|
|
13762
14097
|
nuked++;
|
|
13763
14098
|
}
|
|
13764
14099
|
}
|
|
13765
14100
|
for (const file of nukeFiles) {
|
|
13766
|
-
const fullPath =
|
|
13767
|
-
if (
|
|
13768
|
-
|
|
14101
|
+
const fullPath = path45.resolve(cwd, file);
|
|
14102
|
+
if (fs40.existsSync(fullPath)) {
|
|
14103
|
+
fs40.unlinkSync(fullPath);
|
|
13769
14104
|
nuked++;
|
|
13770
14105
|
}
|
|
13771
14106
|
}
|
|
@@ -13812,7 +14147,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13812
14147
|
}
|
|
13813
14148
|
pm = pmChoice;
|
|
13814
14149
|
}
|
|
13815
|
-
const displayName = projectPrompt.projectName === "." ?
|
|
14150
|
+
const displayName = projectPrompt.projectName === "." ? path45.basename(cwd) : projectPrompt.projectName;
|
|
13816
14151
|
const { bin, prefix } = createNextAppCommand(pm);
|
|
13817
14152
|
const cnaArgs = [
|
|
13818
14153
|
...prefix,
|
|
@@ -13844,10 +14179,10 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13844
14179
|
);
|
|
13845
14180
|
process.exit(1);
|
|
13846
14181
|
}
|
|
13847
|
-
cwd =
|
|
13848
|
-
const hasPackageJson =
|
|
14182
|
+
cwd = path45.resolve(cwd, projectPrompt.projectName);
|
|
14183
|
+
const hasPackageJson = fs40.existsSync(path45.join(cwd, "package.json"));
|
|
13849
14184
|
const hasNextConfig = ["next.config.ts", "next.config.js", "next.config.mjs"].some(
|
|
13850
|
-
(f) =>
|
|
14185
|
+
(f) => fs40.existsSync(path45.join(cwd, f))
|
|
13851
14186
|
);
|
|
13852
14187
|
if (!hasPackageJson || !hasNextConfig) {
|
|
13853
14188
|
p4.log.error(
|
|
@@ -13948,11 +14283,11 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13948
14283
|
s.stop("");
|
|
13949
14284
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
13950
14285
|
p4.note(noteLines.join("\n"), "Scaffolded CMS");
|
|
13951
|
-
const drizzleConfigPath =
|
|
13952
|
-
if (!dbFiles.includes("drizzle.config.ts") &&
|
|
14286
|
+
const drizzleConfigPath = path45.join(cwd, "drizzle.config.ts");
|
|
14287
|
+
if (!dbFiles.includes("drizzle.config.ts") && fs40.existsSync(drizzleConfigPath)) {
|
|
13953
14288
|
if (options.force) {
|
|
13954
14289
|
const { drizzleConfigTemplate: drizzleConfigTemplate2 } = await import("./drizzle-config-EDKOEZ6G.js");
|
|
13955
|
-
|
|
14290
|
+
fs40.writeFileSync(drizzleConfigPath, drizzleConfigTemplate2(), "utf-8");
|
|
13956
14291
|
p4.log.success("Updated drizzle.config.ts");
|
|
13957
14292
|
} else if (!options.yes) {
|
|
13958
14293
|
const overwrite = await p4.confirm({
|
|
@@ -13961,7 +14296,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13961
14296
|
});
|
|
13962
14297
|
if (!p4.isCancel(overwrite) && overwrite) {
|
|
13963
14298
|
const { drizzleConfigTemplate: drizzleConfigTemplate2 } = await import("./drizzle-config-EDKOEZ6G.js");
|
|
13964
|
-
|
|
14299
|
+
fs40.writeFileSync(drizzleConfigPath, drizzleConfigTemplate2(), "utf-8");
|
|
13965
14300
|
p4.log.success("Updated drizzle.config.ts");
|
|
13966
14301
|
}
|
|
13967
14302
|
}
|
|
@@ -13994,15 +14329,15 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13994
14329
|
{
|
|
13995
14330
|
const entityNames = [];
|
|
13996
14331
|
const formNames = [];
|
|
13997
|
-
const schemasDir =
|
|
13998
|
-
const formsDir =
|
|
13999
|
-
if (
|
|
14000
|
-
for (const f of
|
|
14332
|
+
const schemasDir = path45.join(cwd, config.paths.schemas);
|
|
14333
|
+
const formsDir = path45.join(schemasDir, "forms");
|
|
14334
|
+
if (fs40.existsSync(schemasDir)) {
|
|
14335
|
+
for (const f of fs40.readdirSync(schemasDir)) {
|
|
14001
14336
|
if (f.endsWith(".json")) entityNames.push(f.replace(".json", ""));
|
|
14002
14337
|
}
|
|
14003
14338
|
}
|
|
14004
|
-
if (
|
|
14005
|
-
for (const f of
|
|
14339
|
+
if (fs40.existsSync(formsDir)) {
|
|
14340
|
+
for (const f of fs40.readdirSync(formsDir)) {
|
|
14006
14341
|
if (f.endsWith(".json")) formNames.push(f.replace(".json", ""));
|
|
14007
14342
|
}
|
|
14008
14343
|
}
|
|
@@ -14181,9 +14516,9 @@ function isValidDbUrl(url) {
|
|
|
14181
14516
|
return url.startsWith("postgres://") || url.startsWith("postgresql://");
|
|
14182
14517
|
}
|
|
14183
14518
|
function readExistingDbUrl(cwd) {
|
|
14184
|
-
const envPath =
|
|
14185
|
-
if (!
|
|
14186
|
-
const content =
|
|
14519
|
+
const envPath = path45.join(cwd, ".env.local");
|
|
14520
|
+
if (!fs40.existsSync(envPath)) return void 0;
|
|
14521
|
+
const content = fs40.readFileSync(envPath, "utf-8");
|
|
14187
14522
|
for (const line of content.split("\n")) {
|
|
14188
14523
|
const trimmed = line.trim();
|
|
14189
14524
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -14206,9 +14541,9 @@ function maskDbUrl(url) {
|
|
|
14206
14541
|
}
|
|
14207
14542
|
}
|
|
14208
14543
|
function hasDbUrl(cwd) {
|
|
14209
|
-
const envPath =
|
|
14210
|
-
if (!
|
|
14211
|
-
const content =
|
|
14544
|
+
const envPath = path45.join(cwd, ".env.local");
|
|
14545
|
+
if (!fs40.existsSync(envPath)) return false;
|
|
14546
|
+
const content = fs40.readFileSync(envPath, "utf-8");
|
|
14212
14547
|
for (const line of content.split("\n")) {
|
|
14213
14548
|
const trimmed = line.trim();
|
|
14214
14549
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -14222,23 +14557,23 @@ function hasDbUrl(cwd) {
|
|
|
14222
14557
|
return false;
|
|
14223
14558
|
}
|
|
14224
14559
|
function runSeed(cwd, cmsDir, email, password4, overwrite = false) {
|
|
14225
|
-
const scriptsDir =
|
|
14226
|
-
const seedPath =
|
|
14227
|
-
if (!
|
|
14228
|
-
|
|
14560
|
+
const scriptsDir = path45.join(cwd, cmsDir, "scripts");
|
|
14561
|
+
const seedPath = path45.join(scriptsDir, "seed.ts");
|
|
14562
|
+
if (!fs40.existsSync(scriptsDir)) {
|
|
14563
|
+
fs40.mkdirSync(scriptsDir, { recursive: true });
|
|
14229
14564
|
}
|
|
14230
|
-
|
|
14565
|
+
fs40.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
14231
14566
|
const cleanup = () => {
|
|
14232
14567
|
try {
|
|
14233
|
-
|
|
14234
|
-
if (
|
|
14235
|
-
|
|
14568
|
+
fs40.unlinkSync(seedPath);
|
|
14569
|
+
if (fs40.existsSync(scriptsDir) && fs40.readdirSync(scriptsDir).length === 0) {
|
|
14570
|
+
fs40.rmdirSync(scriptsDir);
|
|
14236
14571
|
}
|
|
14237
14572
|
} catch {
|
|
14238
14573
|
}
|
|
14239
14574
|
};
|
|
14240
14575
|
return new Promise((resolve) => {
|
|
14241
|
-
const tsxBin =
|
|
14576
|
+
const tsxBin = path45.join(cwd, "node_modules", ".bin", "tsx");
|
|
14242
14577
|
const child = spawn2(tsxBin, [seedPath], {
|
|
14243
14578
|
cwd,
|
|
14244
14579
|
stdio: "pipe",
|
|
@@ -14303,7 +14638,7 @@ ${stderr}`;
|
|
|
14303
14638
|
}
|
|
14304
14639
|
function runDrizzlePush(cwd) {
|
|
14305
14640
|
return new Promise((resolve) => {
|
|
14306
|
-
const drizzleBin =
|
|
14641
|
+
const drizzleBin = path45.join(cwd, "node_modules", ".bin", "drizzle-kit");
|
|
14307
14642
|
const child = spawn2(drizzleBin, ["push", "--force"], {
|
|
14308
14643
|
cwd,
|
|
14309
14644
|
stdio: "pipe",
|
|
@@ -14324,8 +14659,8 @@ function runDrizzlePush(cwd) {
|
|
|
14324
14659
|
}
|
|
14325
14660
|
|
|
14326
14661
|
// src/commands/remove.ts
|
|
14327
|
-
import
|
|
14328
|
-
import
|
|
14662
|
+
import fs41 from "fs";
|
|
14663
|
+
import path46 from "path";
|
|
14329
14664
|
import readline from "readline";
|
|
14330
14665
|
import { Command as Command4 } from "commander";
|
|
14331
14666
|
function findTableEnd3(content, startIndex) {
|
|
@@ -14356,8 +14691,8 @@ function findTableEnd3(content, startIndex) {
|
|
|
14356
14691
|
return content.length;
|
|
14357
14692
|
}
|
|
14358
14693
|
function removeTableFromSchema(schemaFilePath, name) {
|
|
14359
|
-
if (!
|
|
14360
|
-
let content =
|
|
14694
|
+
if (!fs41.existsSync(schemaFilePath)) return false;
|
|
14695
|
+
let content = fs41.readFileSync(schemaFilePath, "utf-8");
|
|
14361
14696
|
const variableName = toCamelCase(name);
|
|
14362
14697
|
let changed = false;
|
|
14363
14698
|
if (content.includes(`export const ${variableName} =`)) {
|
|
@@ -14385,13 +14720,13 @@ function removeTableFromSchema(schemaFilePath, name) {
|
|
|
14385
14720
|
}
|
|
14386
14721
|
if (changed) {
|
|
14387
14722
|
content = content.replace(/\n{3,}/g, "\n\n");
|
|
14388
|
-
|
|
14723
|
+
fs41.writeFileSync(schemaFilePath, content, "utf-8");
|
|
14389
14724
|
}
|
|
14390
14725
|
return changed;
|
|
14391
14726
|
}
|
|
14392
14727
|
function removeFromNavigation(navFilePath, name) {
|
|
14393
|
-
if (!
|
|
14394
|
-
const content =
|
|
14728
|
+
if (!fs41.existsSync(navFilePath)) return false;
|
|
14729
|
+
const content = fs41.readFileSync(navFilePath, "utf-8");
|
|
14395
14730
|
const href = `/cms/${name}`;
|
|
14396
14731
|
if (!content.includes(`'${href}'`)) return false;
|
|
14397
14732
|
const lines = content.split("\n");
|
|
@@ -14422,7 +14757,7 @@ function removeFromNavigation(navFilePath, name) {
|
|
|
14422
14757
|
if (startLine === -1 || endLine === -1) return false;
|
|
14423
14758
|
lines.splice(startLine, endLine - startLine + 1);
|
|
14424
14759
|
const updated = lines.join("\n").replace(/,\s*,/g, ",").replace(/\[\s*,/, "[");
|
|
14425
|
-
|
|
14760
|
+
fs41.writeFileSync(navFilePath, updated, "utf-8");
|
|
14426
14761
|
return true;
|
|
14427
14762
|
}
|
|
14428
14763
|
async function promptConfirm(message) {
|
|
@@ -14438,7 +14773,7 @@ async function promptConfirm(message) {
|
|
|
14438
14773
|
});
|
|
14439
14774
|
}
|
|
14440
14775
|
var removeCommand = new Command4("remove").alias("rm").description("Remove all generated files for an entity or form").argument("<schema>", "Schema name to remove (e.g. posts, categories, contact)").option("-f, --force", "Skip confirmation prompt", false).option("--cwd <path>", "Project root path").action(async (schemaName, options) => {
|
|
14441
|
-
const cwd = options.cwd ?
|
|
14776
|
+
const cwd = options.cwd ? path46.resolve(options.cwd) : process.cwd();
|
|
14442
14777
|
console.log("\n BetterStart Remove\n");
|
|
14443
14778
|
let config;
|
|
14444
14779
|
try {
|
|
@@ -14451,34 +14786,34 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14451
14786
|
const pagesDir = config.paths?.pages ?? "./src/app/(cms)/cms/(authenticated)";
|
|
14452
14787
|
const kebabName = toKebabCase(schemaName);
|
|
14453
14788
|
const targets = [];
|
|
14454
|
-
const entityPagesDir =
|
|
14455
|
-
if (
|
|
14789
|
+
const entityPagesDir = path46.join(cwd, pagesDir, schemaName);
|
|
14790
|
+
if (fs41.existsSync(entityPagesDir)) {
|
|
14456
14791
|
targets.push({
|
|
14457
14792
|
path: entityPagesDir,
|
|
14458
|
-
label: `${
|
|
14793
|
+
label: `${path46.join(pagesDir, schemaName)}/`,
|
|
14459
14794
|
isDir: true
|
|
14460
14795
|
});
|
|
14461
14796
|
}
|
|
14462
|
-
const actionsFile =
|
|
14463
|
-
if (
|
|
14797
|
+
const actionsFile = path46.join(cwd, cmsDir, "lib", "actions", `${kebabName}.ts`);
|
|
14798
|
+
if (fs41.existsSync(actionsFile)) {
|
|
14464
14799
|
targets.push({
|
|
14465
14800
|
path: actionsFile,
|
|
14466
|
-
label:
|
|
14801
|
+
label: path46.join(cmsDir, "lib", "actions", `${kebabName}.ts`),
|
|
14467
14802
|
isDir: false
|
|
14468
14803
|
});
|
|
14469
14804
|
}
|
|
14470
|
-
const hookFile =
|
|
14471
|
-
if (
|
|
14805
|
+
const hookFile = path46.join(cwd, cmsDir, "hooks", `use-${kebabName}.ts`);
|
|
14806
|
+
if (fs41.existsSync(hookFile)) {
|
|
14472
14807
|
targets.push({
|
|
14473
14808
|
path: hookFile,
|
|
14474
|
-
label:
|
|
14809
|
+
label: path46.join(cmsDir, "hooks", `use-${kebabName}.ts`),
|
|
14475
14810
|
isDir: false
|
|
14476
14811
|
});
|
|
14477
14812
|
}
|
|
14478
|
-
const schemaFilePath =
|
|
14479
|
-
const hasTable =
|
|
14480
|
-
const navFilePath =
|
|
14481
|
-
const hasNavEntry =
|
|
14813
|
+
const schemaFilePath = path46.join(cwd, cmsDir, "db", "schema.ts");
|
|
14814
|
+
const hasTable = fs41.existsSync(schemaFilePath) && fs41.readFileSync(schemaFilePath, "utf-8").includes(`export const ${toCamelCase(schemaName)} =`);
|
|
14815
|
+
const navFilePath = path46.join(cwd, cmsDir, "data", "navigation.ts");
|
|
14816
|
+
const hasNavEntry = fs41.existsSync(navFilePath) && fs41.readFileSync(navFilePath, "utf-8").includes(`'/cms/${schemaName}'`);
|
|
14482
14817
|
if (targets.length === 0 && !hasTable && !hasNavEntry) {
|
|
14483
14818
|
console.log(` No generated files found for: ${schemaName}`);
|
|
14484
14819
|
return;
|
|
@@ -14488,10 +14823,10 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14488
14823
|
console.log(` ${t.isDir ? "[dir]" : " "} ${t.label}`);
|
|
14489
14824
|
}
|
|
14490
14825
|
if (hasTable) {
|
|
14491
|
-
console.log(` [edit] ${
|
|
14826
|
+
console.log(` [edit] ${path46.join(cmsDir, "db", "schema.ts")} (remove table)`);
|
|
14492
14827
|
}
|
|
14493
14828
|
if (hasNavEntry) {
|
|
14494
|
-
console.log(` [edit] ${
|
|
14829
|
+
console.log(` [edit] ${path46.join(cmsDir, "data", "navigation.ts")} (remove entry)`);
|
|
14495
14830
|
}
|
|
14496
14831
|
if (!options.force) {
|
|
14497
14832
|
console.log("");
|
|
@@ -14504,19 +14839,19 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14504
14839
|
console.log("");
|
|
14505
14840
|
for (const t of targets) {
|
|
14506
14841
|
if (t.isDir) {
|
|
14507
|
-
|
|
14842
|
+
fs41.rmSync(t.path, { recursive: true, force: true });
|
|
14508
14843
|
} else {
|
|
14509
|
-
|
|
14844
|
+
fs41.unlinkSync(t.path);
|
|
14510
14845
|
}
|
|
14511
14846
|
console.log(` Removed: ${t.label}`);
|
|
14512
14847
|
}
|
|
14513
14848
|
if (hasTable) {
|
|
14514
14849
|
removeTableFromSchema(schemaFilePath, schemaName);
|
|
14515
|
-
console.log(` Cleaned: ${
|
|
14850
|
+
console.log(` Cleaned: ${path46.join(cmsDir, "db", "schema.ts")}`);
|
|
14516
14851
|
}
|
|
14517
14852
|
if (hasNavEntry) {
|
|
14518
14853
|
removeFromNavigation(navFilePath, schemaName);
|
|
14519
|
-
console.log(` Cleaned: ${
|
|
14854
|
+
console.log(` Cleaned: ${path46.join(cmsDir, "data", "navigation.ts")}`);
|
|
14520
14855
|
}
|
|
14521
14856
|
console.log("\n Removal complete!");
|
|
14522
14857
|
console.log("\n Note: You may need to manually:");
|
|
@@ -14528,14 +14863,14 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14528
14863
|
|
|
14529
14864
|
// src/commands/setup-r2.ts
|
|
14530
14865
|
import { execFileSync as execFileSync5, spawnSync } from "child_process";
|
|
14531
|
-
import
|
|
14866
|
+
import fs42 from "fs";
|
|
14532
14867
|
import os from "os";
|
|
14533
|
-
import
|
|
14868
|
+
import path47 from "path";
|
|
14534
14869
|
import * as p5 from "@clack/prompts";
|
|
14535
14870
|
import { Command as Command5 } from "commander";
|
|
14536
14871
|
import pc3 from "picocolors";
|
|
14537
14872
|
var setupR2Command = new Command5("setup-r2").description("Create a Cloudflare R2 bucket and configure storage env vars").option("--cwd <path>", "Project root path").option("--bucket <name>", "Bucket name (skips prompt)").action(async (options) => {
|
|
14538
|
-
const cwd = options.cwd ?
|
|
14873
|
+
const cwd = options.cwd ? path47.resolve(options.cwd) : process.cwd();
|
|
14539
14874
|
p5.intro(pc3.bgCyan(pc3.black(" BetterStart \u2014 R2 Storage Setup ")));
|
|
14540
14875
|
const s = p5.spinner();
|
|
14541
14876
|
s.start("Looking for wrangler CLI");
|
|
@@ -14727,8 +15062,8 @@ var setupR2Command = new Command5("setup-r2").description("Create a Cloudflare R
|
|
|
14727
15062
|
p5.outro("Done! Your CMS can now upload files to R2.");
|
|
14728
15063
|
});
|
|
14729
15064
|
function findWrangler(cwd) {
|
|
14730
|
-
const localBin =
|
|
14731
|
-
if (
|
|
15065
|
+
const localBin = path47.join(cwd, "node_modules", ".bin", "wrangler");
|
|
15066
|
+
if (fs42.existsSync(localBin)) return { bin: localBin, prefix: [] };
|
|
14732
15067
|
const result = spawnSync("which", ["wrangler"], { stdio: "pipe", timeout: 5e3 });
|
|
14733
15068
|
if (result.status === 0) {
|
|
14734
15069
|
const found = result.stdout?.toString().trim();
|
|
@@ -14760,11 +15095,11 @@ function extractAccountId(ref, cwd) {
|
|
|
14760
15095
|
}
|
|
14761
15096
|
function readWranglerToken() {
|
|
14762
15097
|
const candidates = [
|
|
14763
|
-
|
|
15098
|
+
path47.join(os.homedir(), "Library", "Preferences", ".wrangler", "config", "default.toml"),
|
|
14764
15099
|
// macOS
|
|
14765
|
-
|
|
15100
|
+
path47.join(os.homedir(), ".config", ".wrangler", "config", "default.toml"),
|
|
14766
15101
|
// Linux
|
|
14767
|
-
|
|
15102
|
+
path47.join(os.homedir(), ".wrangler", "config", "default.toml")
|
|
14768
15103
|
// fallback
|
|
14769
15104
|
];
|
|
14770
15105
|
if (process.env.WRANGLER_CONFIG_PATH) {
|
|
@@ -14772,13 +15107,13 @@ function readWranglerToken() {
|
|
|
14772
15107
|
}
|
|
14773
15108
|
if (process.env.XDG_CONFIG_HOME) {
|
|
14774
15109
|
candidates.unshift(
|
|
14775
|
-
|
|
15110
|
+
path47.join(process.env.XDG_CONFIG_HOME, ".wrangler", "config", "default.toml")
|
|
14776
15111
|
);
|
|
14777
15112
|
}
|
|
14778
15113
|
for (const configPath of candidates) {
|
|
14779
|
-
if (!
|
|
15114
|
+
if (!fs42.existsSync(configPath)) continue;
|
|
14780
15115
|
try {
|
|
14781
|
-
const content =
|
|
15116
|
+
const content = fs42.readFileSync(configPath, "utf-8");
|
|
14782
15117
|
const match = content.match(/^oauth_token\s*=\s*"([^"]+)"/m);
|
|
14783
15118
|
if (match) return match[1];
|
|
14784
15119
|
} catch {
|
|
@@ -14810,14 +15145,14 @@ async function enablePublicDomain(accountId, bucketName, token) {
|
|
|
14810
15145
|
}
|
|
14811
15146
|
|
|
14812
15147
|
// src/commands/uninstall.ts
|
|
14813
|
-
import
|
|
14814
|
-
import
|
|
15148
|
+
import fs44 from "fs";
|
|
15149
|
+
import path48 from "path";
|
|
14815
15150
|
import * as p6 from "@clack/prompts";
|
|
14816
15151
|
import { Command as Command6 } from "commander";
|
|
14817
15152
|
import pc4 from "picocolors";
|
|
14818
15153
|
|
|
14819
15154
|
// src/commands/uninstall-cleaners.ts
|
|
14820
|
-
import
|
|
15155
|
+
import fs43 from "fs";
|
|
14821
15156
|
function stripJsonComments2(input) {
|
|
14822
15157
|
let result = "";
|
|
14823
15158
|
let i = 0;
|
|
@@ -14851,8 +15186,8 @@ function stripJsonComments2(input) {
|
|
|
14851
15186
|
return result;
|
|
14852
15187
|
}
|
|
14853
15188
|
function cleanTsconfig(tsconfigPath) {
|
|
14854
|
-
if (!
|
|
14855
|
-
const raw =
|
|
15189
|
+
if (!fs43.existsSync(tsconfigPath)) return [];
|
|
15190
|
+
const raw = fs43.readFileSync(tsconfigPath, "utf-8");
|
|
14856
15191
|
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
14857
15192
|
let tsconfig;
|
|
14858
15193
|
try {
|
|
@@ -14876,13 +15211,13 @@ function cleanTsconfig(tsconfigPath) {
|
|
|
14876
15211
|
compilerOptions.paths = paths;
|
|
14877
15212
|
}
|
|
14878
15213
|
tsconfig.compilerOptions = compilerOptions;
|
|
14879
|
-
|
|
15214
|
+
fs43.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
14880
15215
|
`, "utf-8");
|
|
14881
15216
|
return removed;
|
|
14882
15217
|
}
|
|
14883
15218
|
function cleanCss(cssPath) {
|
|
14884
|
-
if (!
|
|
14885
|
-
const content =
|
|
15219
|
+
if (!fs43.existsSync(cssPath)) return [];
|
|
15220
|
+
const content = fs43.readFileSync(cssPath, "utf-8");
|
|
14886
15221
|
const lines = content.split("\n");
|
|
14887
15222
|
const sourcePattern = /^@source\s+"[^"]*cms[^"]*";\s*$/;
|
|
14888
15223
|
const removed = [];
|
|
@@ -14896,12 +15231,12 @@ function cleanCss(cssPath) {
|
|
|
14896
15231
|
}
|
|
14897
15232
|
if (removed.length === 0) return [];
|
|
14898
15233
|
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
14899
|
-
|
|
15234
|
+
fs43.writeFileSync(cssPath, cleaned, "utf-8");
|
|
14900
15235
|
return removed;
|
|
14901
15236
|
}
|
|
14902
15237
|
function cleanEnvFile(envPath) {
|
|
14903
|
-
if (!
|
|
14904
|
-
const content =
|
|
15238
|
+
if (!fs43.existsSync(envPath)) return [];
|
|
15239
|
+
const content = fs43.readFileSync(envPath, "utf-8");
|
|
14905
15240
|
const lines = content.split("\n");
|
|
14906
15241
|
const removed = [];
|
|
14907
15242
|
const kept = [];
|
|
@@ -14934,9 +15269,9 @@ function cleanEnvFile(envPath) {
|
|
|
14934
15269
|
if (removed.length === 0) return [];
|
|
14935
15270
|
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
14936
15271
|
if (result === "") {
|
|
14937
|
-
|
|
15272
|
+
fs43.unlinkSync(envPath);
|
|
14938
15273
|
} else {
|
|
14939
|
-
|
|
15274
|
+
fs43.writeFileSync(envPath, `${result}
|
|
14940
15275
|
`, "utf-8");
|
|
14941
15276
|
}
|
|
14942
15277
|
return removed;
|
|
@@ -14962,15 +15297,15 @@ function findMainCss2(cwd) {
|
|
|
14962
15297
|
"globals.css"
|
|
14963
15298
|
];
|
|
14964
15299
|
for (const candidate of candidates) {
|
|
14965
|
-
const filePath =
|
|
14966
|
-
if (
|
|
15300
|
+
const filePath = path48.join(cwd, candidate);
|
|
15301
|
+
if (fs44.existsSync(filePath)) return filePath;
|
|
14967
15302
|
}
|
|
14968
15303
|
return void 0;
|
|
14969
15304
|
}
|
|
14970
15305
|
function isCLICreatedBiome(biomePath) {
|
|
14971
|
-
if (!
|
|
15306
|
+
if (!fs44.existsSync(biomePath)) return false;
|
|
14972
15307
|
try {
|
|
14973
|
-
const content = JSON.parse(
|
|
15308
|
+
const content = JSON.parse(fs44.readFileSync(biomePath, "utf-8"));
|
|
14974
15309
|
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");
|
|
14975
15310
|
} catch {
|
|
14976
15311
|
return false;
|
|
@@ -14978,13 +15313,13 @@ function isCLICreatedBiome(biomePath) {
|
|
|
14978
15313
|
}
|
|
14979
15314
|
function buildUninstallPlan(cwd) {
|
|
14980
15315
|
const steps = [];
|
|
14981
|
-
const hasSrc =
|
|
15316
|
+
const hasSrc = fs44.existsSync(path48.join(cwd, "src"));
|
|
14982
15317
|
const appBase = hasSrc ? "src/app" : "app";
|
|
14983
15318
|
const dirs = [];
|
|
14984
|
-
const cmsDir =
|
|
14985
|
-
const cmsRouteGroup =
|
|
14986
|
-
if (
|
|
14987
|
-
if (
|
|
15319
|
+
const cmsDir = path48.join(cwd, "cms");
|
|
15320
|
+
const cmsRouteGroup = path48.join(cwd, appBase, "(cms)");
|
|
15321
|
+
if (fs44.existsSync(cmsDir)) dirs.push("cms/");
|
|
15322
|
+
if (fs44.existsSync(cmsRouteGroup)) dirs.push(`${appBase}/(cms)/`);
|
|
14988
15323
|
if (dirs.length > 0) {
|
|
14989
15324
|
steps.push({
|
|
14990
15325
|
label: "CMS directories",
|
|
@@ -14992,25 +15327,25 @@ function buildUninstallPlan(cwd) {
|
|
|
14992
15327
|
count: dirs.length,
|
|
14993
15328
|
unit: dirs.length === 1 ? "directory" : "directories",
|
|
14994
15329
|
execute() {
|
|
14995
|
-
if (
|
|
14996
|
-
if (
|
|
15330
|
+
if (fs44.existsSync(cmsDir)) fs44.rmSync(cmsDir, { recursive: true, force: true });
|
|
15331
|
+
if (fs44.existsSync(cmsRouteGroup)) fs44.rmSync(cmsRouteGroup, { recursive: true, force: true });
|
|
14997
15332
|
}
|
|
14998
15333
|
});
|
|
14999
15334
|
}
|
|
15000
15335
|
const configFiles = [];
|
|
15001
15336
|
const configPaths = [];
|
|
15002
15337
|
const candidates = [
|
|
15003
|
-
["cms.config.ts",
|
|
15004
|
-
["drizzle.config.ts",
|
|
15005
|
-
["CMS.md",
|
|
15338
|
+
["cms.config.ts", path48.join(cwd, "cms.config.ts")],
|
|
15339
|
+
["drizzle.config.ts", path48.join(cwd, "drizzle.config.ts")],
|
|
15340
|
+
["CMS.md", path48.join(cwd, "CMS.md")]
|
|
15006
15341
|
];
|
|
15007
15342
|
for (const [label, fullPath] of candidates) {
|
|
15008
|
-
if (
|
|
15343
|
+
if (fs44.existsSync(fullPath)) {
|
|
15009
15344
|
configFiles.push(label);
|
|
15010
15345
|
configPaths.push(fullPath);
|
|
15011
15346
|
}
|
|
15012
15347
|
}
|
|
15013
|
-
const biomePath =
|
|
15348
|
+
const biomePath = path48.join(cwd, "biome.json");
|
|
15014
15349
|
if (isCLICreatedBiome(biomePath)) {
|
|
15015
15350
|
configFiles.push("biome.json (CLI-created)");
|
|
15016
15351
|
configPaths.push(biomePath);
|
|
@@ -15023,14 +15358,14 @@ function buildUninstallPlan(cwd) {
|
|
|
15023
15358
|
unit: configFiles.length === 1 ? "file" : "files",
|
|
15024
15359
|
execute() {
|
|
15025
15360
|
for (const p7 of configPaths) {
|
|
15026
|
-
if (
|
|
15361
|
+
if (fs44.existsSync(p7)) fs44.unlinkSync(p7);
|
|
15027
15362
|
}
|
|
15028
15363
|
}
|
|
15029
15364
|
});
|
|
15030
15365
|
}
|
|
15031
|
-
const tsconfigPath =
|
|
15032
|
-
if (
|
|
15033
|
-
const content =
|
|
15366
|
+
const tsconfigPath = path48.join(cwd, "tsconfig.json");
|
|
15367
|
+
if (fs44.existsSync(tsconfigPath)) {
|
|
15368
|
+
const content = fs44.readFileSync(tsconfigPath, "utf-8");
|
|
15034
15369
|
const aliasMatches = content.match(/"@cms\//g);
|
|
15035
15370
|
if (aliasMatches && aliasMatches.length > 0) {
|
|
15036
15371
|
const aliasCount = aliasMatches.length;
|
|
@@ -15047,10 +15382,10 @@ function buildUninstallPlan(cwd) {
|
|
|
15047
15382
|
}
|
|
15048
15383
|
const cssFile = findMainCss2(cwd);
|
|
15049
15384
|
if (cssFile) {
|
|
15050
|
-
const cssContent =
|
|
15385
|
+
const cssContent = fs44.readFileSync(cssFile, "utf-8");
|
|
15051
15386
|
const sourceLines = cssContent.split("\n").filter((l) => /^@source\s+"[^"]*cms[^"]*";\s*$/.test(l));
|
|
15052
15387
|
if (sourceLines.length > 0) {
|
|
15053
|
-
const relCss =
|
|
15388
|
+
const relCss = path48.relative(cwd, cssFile);
|
|
15054
15389
|
steps.push({
|
|
15055
15390
|
label: `CSS @source lines (${relCss})`,
|
|
15056
15391
|
items: [`@source lines in ${relCss}`],
|
|
@@ -15062,9 +15397,9 @@ function buildUninstallPlan(cwd) {
|
|
|
15062
15397
|
});
|
|
15063
15398
|
}
|
|
15064
15399
|
}
|
|
15065
|
-
const envPath =
|
|
15066
|
-
if (
|
|
15067
|
-
const envContent =
|
|
15400
|
+
const envPath = path48.join(cwd, ".env.local");
|
|
15401
|
+
if (fs44.existsSync(envPath)) {
|
|
15402
|
+
const envContent = fs44.readFileSync(envPath, "utf-8");
|
|
15068
15403
|
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
15069
15404
|
if (bsVars.length > 0) {
|
|
15070
15405
|
steps.push({
|
|
@@ -15081,7 +15416,7 @@ function buildUninstallPlan(cwd) {
|
|
|
15081
15416
|
return steps;
|
|
15082
15417
|
}
|
|
15083
15418
|
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) => {
|
|
15084
|
-
const cwd = options.cwd ?
|
|
15419
|
+
const cwd = options.cwd ? path48.resolve(options.cwd) : process.cwd();
|
|
15085
15420
|
p6.intro(pc4.bgRed(pc4.white(" BetterStart Uninstall ")));
|
|
15086
15421
|
const steps = buildUninstallPlan(cwd);
|
|
15087
15422
|
if (steps.length === 0) {
|
|
@@ -15118,11 +15453,11 @@ var uninstallCommand = new Command6("uninstall").description("Remove all CMS fil
|
|
|
15118
15453
|
});
|
|
15119
15454
|
|
|
15120
15455
|
// src/commands/update-deps.ts
|
|
15121
|
-
import
|
|
15456
|
+
import path49 from "path";
|
|
15122
15457
|
import * as clack2 from "@clack/prompts";
|
|
15123
15458
|
import { Command as Command7 } from "commander";
|
|
15124
15459
|
var updateDepsCommand = new Command7("update-deps").description("Install or update all CMS dependencies").option("--cwd <path>", "Project root path").action(async (options) => {
|
|
15125
|
-
const cwd = options.cwd ?
|
|
15460
|
+
const cwd = options.cwd ? path49.resolve(options.cwd) : process.cwd();
|
|
15126
15461
|
clack2.intro("BetterStart Update Dependencies");
|
|
15127
15462
|
const pm = detectPackageManager(cwd);
|
|
15128
15463
|
clack2.log.info(`Package manager: ${pm}`);
|
|
@@ -15147,22 +15482,22 @@ var updateDepsCommand = new Command7("update-deps").description("Install or upda
|
|
|
15147
15482
|
});
|
|
15148
15483
|
|
|
15149
15484
|
// src/commands/update-styles.ts
|
|
15150
|
-
import
|
|
15151
|
-
import
|
|
15485
|
+
import fs45 from "fs";
|
|
15486
|
+
import path50 from "path";
|
|
15152
15487
|
import * as clack3 from "@clack/prompts";
|
|
15153
15488
|
import { Command as Command8 } from "commander";
|
|
15154
15489
|
var updateStylesCommand = new Command8("update-styles").description("Replace cms-globals.css with the latest version from the CLI").option("--cwd <path>", "Project root path").action(async (options) => {
|
|
15155
|
-
const cwd = options.cwd ?
|
|
15490
|
+
const cwd = options.cwd ? path50.resolve(options.cwd) : process.cwd();
|
|
15156
15491
|
clack3.intro("BetterStart Update Styles");
|
|
15157
15492
|
const config = await resolveConfig(cwd);
|
|
15158
15493
|
const cmsDir = config.paths?.cms ?? "./cms";
|
|
15159
|
-
const targetPath =
|
|
15160
|
-
if (!
|
|
15161
|
-
clack3.cancel(`cms-globals.css not found at ${
|
|
15494
|
+
const targetPath = path50.join(cwd, cmsDir, "cms-globals.css");
|
|
15495
|
+
if (!fs45.existsSync(targetPath)) {
|
|
15496
|
+
clack3.cancel(`cms-globals.css not found at ${path50.relative(cwd, targetPath)}`);
|
|
15162
15497
|
process.exit(1);
|
|
15163
15498
|
}
|
|
15164
|
-
|
|
15165
|
-
clack3.log.success(`Updated ${
|
|
15499
|
+
fs45.writeFileSync(targetPath, cmsGlobalsCssTemplate(), "utf-8");
|
|
15500
|
+
clack3.log.success(`Updated ${path50.relative(cwd, targetPath)}`);
|
|
15166
15501
|
clack3.outro("Styles updated");
|
|
15167
15502
|
});
|
|
15168
15503
|
|