@betterstart/cli 0.1.41 → 0.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +961 -664
- 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,19 +2261,18 @@ 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}: []`;
|
|
@@ -2258,140 +2280,43 @@ function generateFormComponent(schema, cwd, cmsDir, options) {
|
|
|
2258
2280
|
if (f.defaultValue !== void 0) return ` ${f.name}: '${f.defaultValue}'`;
|
|
2259
2281
|
return ` ${f.name}: ''`;
|
|
2260
2282
|
}).join(",\n");
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
const hasListFields = listFields.length > 0;
|
|
2265
|
-
const fieldArrayDecls = listFields.map(
|
|
2283
|
+
}
|
|
2284
|
+
function buildFieldArrayDecls(listFields) {
|
|
2285
|
+
return listFields.map(
|
|
2266
2286
|
(f) => ` const ${f.name}FieldArray = useFieldArray({ control: form.control, name: '${f.name}' })`
|
|
2267
2287
|
).join("\n");
|
|
2288
|
+
}
|
|
2289
|
+
function buildWatchDecls(fields) {
|
|
2268
2290
|
const watchFields = /* @__PURE__ */ new Set();
|
|
2269
2291
|
for (const f of fields) {
|
|
2270
2292
|
if (f.showWhen) {
|
|
2271
2293
|
watchFields.add(f.showWhen.field);
|
|
2272
2294
|
}
|
|
2273
2295
|
}
|
|
2274
|
-
|
|
2275
|
-
const
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2296
|
+
if (watchFields.size === 0) return { setup: "", hasWatch: false };
|
|
2297
|
+
const decls = Array.from(watchFields).map((wf) => ` const ${wf}Value = form.watch('${wf}')`).join("\n");
|
|
2298
|
+
return { setup: `
|
|
2299
|
+
${decls}
|
|
2300
|
+
`, hasWatch: true };
|
|
2301
|
+
}
|
|
2302
|
+
function getListFields(fields) {
|
|
2303
|
+
return fields.filter(
|
|
2304
|
+
(f) => f.name && f.type === "list" && f.fields && f.fields.length > 0
|
|
2305
|
+
);
|
|
2306
|
+
}
|
|
2307
|
+
function wrapShowWhen(field, jsx) {
|
|
2308
|
+
if (!field.showWhen) return jsx;
|
|
2309
|
+
const watchVar = `${field.showWhen.field}Value`;
|
|
2310
|
+
const { value } = field.showWhen;
|
|
2311
|
+
if (Array.isArray(value)) {
|
|
2312
|
+
const vals = value.map((v) => `'${v}'`).join(", ");
|
|
2313
|
+
return ` {[${vals}].includes(${watchVar} as string) && (
|
|
2286
2314
|
${jsx}
|
|
2287
2315
|
)}`;
|
|
2288
|
-
|
|
2289
|
-
|
|
2316
|
+
}
|
|
2317
|
+
return ` {${watchVar} === '${value}' && (
|
|
2290
2318
|
${jsx}
|
|
2291
2319
|
)}`;
|
|
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
2320
|
}
|
|
2396
2321
|
function generateFieldJSX(field) {
|
|
2397
2322
|
const name = field.name || "";
|
|
@@ -2468,36 +2393,15 @@ ${optionItems}
|
|
|
2468
2393
|
)}
|
|
2469
2394
|
/>`;
|
|
2470
2395
|
case "email":
|
|
2471
|
-
return generateTextFieldJSX(
|
|
2472
|
-
name,
|
|
2473
|
-
label,
|
|
2474
|
-
placeholder || "email@example.com",
|
|
2475
|
-
hintJSX,
|
|
2476
|
-
requiredStar,
|
|
2477
|
-
"email"
|
|
2478
|
-
);
|
|
2396
|
+
return generateTextFieldJSX(name, label, placeholder || "email@example.com", hintJSX, requiredStar, "email");
|
|
2479
2397
|
case "number":
|
|
2480
2398
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "number");
|
|
2481
2399
|
case "date":
|
|
2482
2400
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "date");
|
|
2483
2401
|
case "url":
|
|
2484
|
-
return generateTextFieldJSX(
|
|
2485
|
-
name,
|
|
2486
|
-
label,
|
|
2487
|
-
placeholder || "https://",
|
|
2488
|
-
hintJSX,
|
|
2489
|
-
requiredStar,
|
|
2490
|
-
"url"
|
|
2491
|
-
);
|
|
2402
|
+
return generateTextFieldJSX(name, label, placeholder || "https://", hintJSX, requiredStar, "url");
|
|
2492
2403
|
case "phone":
|
|
2493
|
-
return generateTextFieldJSX(
|
|
2494
|
-
name,
|
|
2495
|
-
label,
|
|
2496
|
-
placeholder || "+1 (555) 000-0000",
|
|
2497
|
-
hintJSX,
|
|
2498
|
-
requiredStar,
|
|
2499
|
-
"tel"
|
|
2500
|
-
);
|
|
2404
|
+
return generateTextFieldJSX(name, label, placeholder || "+1 (555) 000-0000", hintJSX, requiredStar, "tel");
|
|
2501
2405
|
case "file":
|
|
2502
2406
|
case "upload":
|
|
2503
2407
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "file");
|
|
@@ -2598,9 +2502,402 @@ ${nestedFieldsJSX}
|
|
|
2598
2502
|
</div>`;
|
|
2599
2503
|
}
|
|
2600
2504
|
|
|
2505
|
+
// src/generators/form-pipeline/form-component-multistep.ts
|
|
2506
|
+
function generateMultiStepForm(schema, cwd, cmsDir, options) {
|
|
2507
|
+
const formName = schema.name;
|
|
2508
|
+
const pascal = toPascalCase(formName);
|
|
2509
|
+
const kebab = toKebabCase(formName);
|
|
2510
|
+
const steps = schema.steps;
|
|
2511
|
+
const filePath = path8.join(cwd, cmsDir, "components", "forms", `${kebab}-form.tsx`);
|
|
2512
|
+
const dir = path8.dirname(filePath);
|
|
2513
|
+
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
2514
|
+
if (fs8.existsSync(filePath) && !options.force) {
|
|
2515
|
+
return { files: [path8.relative(cwd, filePath)] };
|
|
2516
|
+
}
|
|
2517
|
+
const allFields = getAllFormSchemaFields(schema);
|
|
2518
|
+
const zodFields = buildZodFields(allFields);
|
|
2519
|
+
const defaults = buildDefaultValues(allFields);
|
|
2520
|
+
const listFields = getListFields(allFields);
|
|
2521
|
+
const hasListFields = listFields.length > 0;
|
|
2522
|
+
const { setup: watchSetup } = buildWatchDecls(allFields);
|
|
2523
|
+
const rhfImport = hasListFields ? `import { useFieldArray, useForm } from 'react-hook-form'` : `import { useForm } from 'react-hook-form'`;
|
|
2524
|
+
const fieldArraySetup = hasListFields ? `
|
|
2525
|
+
${buildFieldArrayDecls(listFields)}
|
|
2526
|
+
` : "";
|
|
2527
|
+
const stepsConst = buildStepsConstant(steps, schema);
|
|
2528
|
+
const stepContentBlocks = steps.map((step, index) => {
|
|
2529
|
+
const stepFields = flattenFormFields(step.fields);
|
|
2530
|
+
const fieldsJSX = stepFields.filter((f) => f.name && !f.hidden).map((f) => wrapShowWhen(f, generateFieldJSX(f))).join("\n\n");
|
|
2531
|
+
return ` {currentStep === ${index} && (
|
|
2532
|
+
<>
|
|
2533
|
+
${fieldsJSX}
|
|
2534
|
+
</>
|
|
2535
|
+
)}`;
|
|
2536
|
+
}).join("\n");
|
|
2537
|
+
const submitText = schema.submitButtonText || "Submit";
|
|
2538
|
+
const successMessage = escapeJsx(schema.successMessage || "Form submitted successfully!");
|
|
2539
|
+
const buttonImport = resolveUiImport(cwd, "button");
|
|
2540
|
+
const formImport = resolveUiImport(cwd, "form");
|
|
2541
|
+
const inputImport = resolveUiImport(cwd, "input");
|
|
2542
|
+
const textareaImport = resolveUiImport(cwd, "textarea");
|
|
2543
|
+
const selectImport = resolveUiImport(cwd, "select");
|
|
2544
|
+
const progressImport = resolveUiImport(cwd, "progress");
|
|
2545
|
+
const content = buildComponentSource({
|
|
2546
|
+
pascal,
|
|
2547
|
+
kebab,
|
|
2548
|
+
rhfImport,
|
|
2549
|
+
buttonImport,
|
|
2550
|
+
formImport,
|
|
2551
|
+
inputImport,
|
|
2552
|
+
textareaImport,
|
|
2553
|
+
selectImport,
|
|
2554
|
+
progressImport,
|
|
2555
|
+
zodFields,
|
|
2556
|
+
defaults,
|
|
2557
|
+
stepsConst,
|
|
2558
|
+
fieldArraySetup,
|
|
2559
|
+
watchSetup,
|
|
2560
|
+
stepContentBlocks,
|
|
2561
|
+
submitText,
|
|
2562
|
+
successMessage
|
|
2563
|
+
});
|
|
2564
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
2565
|
+
return { files: [path8.relative(cwd, filePath)] };
|
|
2566
|
+
}
|
|
2567
|
+
function buildStepsConstant(steps, schema) {
|
|
2568
|
+
const entries = steps.map((step) => {
|
|
2569
|
+
const fieldNames = getStepFieldNames(step);
|
|
2570
|
+
const fieldsStr = fieldNames.map((n) => `'${n}'`).join(", ");
|
|
2571
|
+
const desc = step.description ? `, description: '${escapeQuotes(step.description)}'` : "";
|
|
2572
|
+
return ` { name: '${step.name}', label: '${escapeQuotes(step.label)}'${desc}, fields: [${fieldsStr}] }`;
|
|
2573
|
+
});
|
|
2574
|
+
return `const STEPS = [
|
|
2575
|
+
${entries.join(",\n")}
|
|
2576
|
+
]`;
|
|
2577
|
+
}
|
|
2578
|
+
function escapeQuotes(str) {
|
|
2579
|
+
return str.replace(/'/g, "\\'");
|
|
2580
|
+
}
|
|
2581
|
+
function buildComponentSource(p7) {
|
|
2582
|
+
return `'use client'
|
|
2583
|
+
|
|
2584
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
2585
|
+
import { Check, ChevronLeft, ChevronRight } from 'lucide-react'
|
|
2586
|
+
import { useState } from 'react'
|
|
2587
|
+
${p7.rhfImport}
|
|
2588
|
+
import { z } from 'zod/v3'
|
|
2589
|
+
import { create${p7.pascal}Submission } from '@cms/actions/${p7.kebab}-form'
|
|
2590
|
+
import { Button } from '${p7.buttonImport}'
|
|
2591
|
+
import {
|
|
2592
|
+
Form,
|
|
2593
|
+
FormControl,
|
|
2594
|
+
FormDescription,
|
|
2595
|
+
FormField,
|
|
2596
|
+
FormItem,
|
|
2597
|
+
FormLabel,
|
|
2598
|
+
FormMessage,
|
|
2599
|
+
} from '${p7.formImport}'
|
|
2600
|
+
import { Input } from '${p7.inputImport}'
|
|
2601
|
+
import { Progress } from '${p7.progressImport}'
|
|
2602
|
+
import { Textarea } from '${p7.textareaImport}'
|
|
2603
|
+
import {
|
|
2604
|
+
Select,
|
|
2605
|
+
SelectContent,
|
|
2606
|
+
SelectItem,
|
|
2607
|
+
SelectTrigger,
|
|
2608
|
+
SelectValue,
|
|
2609
|
+
} from '${p7.selectImport}'
|
|
2610
|
+
|
|
2611
|
+
const formSchema = z.object({
|
|
2612
|
+
${p7.zodFields}
|
|
2613
|
+
})
|
|
2614
|
+
|
|
2615
|
+
type FormValues = z.infer<typeof formSchema>
|
|
2616
|
+
|
|
2617
|
+
${p7.stepsConst}
|
|
2618
|
+
|
|
2619
|
+
export function ${p7.pascal}Form() {
|
|
2620
|
+
const [currentStep, setCurrentStep] = useState(0)
|
|
2621
|
+
const [completedSteps, setCompletedSteps] = useState<Set<number>>(new Set())
|
|
2622
|
+
const [submitted, setSubmitted] = useState(false)
|
|
2623
|
+
const [submitting, setSubmitting] = useState(false)
|
|
2624
|
+
|
|
2625
|
+
const form = useForm<FormValues>({
|
|
2626
|
+
resolver: zodResolver(formSchema),
|
|
2627
|
+
mode: 'onTouched',
|
|
2628
|
+
defaultValues: {
|
|
2629
|
+
${p7.defaults}
|
|
2630
|
+
},
|
|
2631
|
+
})
|
|
2632
|
+
${p7.fieldArraySetup}${p7.watchSetup}
|
|
2633
|
+
async function handleNext() {
|
|
2634
|
+
const isValid = await form.trigger(
|
|
2635
|
+
STEPS[currentStep].fields as (keyof FormValues)[]
|
|
2636
|
+
)
|
|
2637
|
+
if (isValid) {
|
|
2638
|
+
setCompletedSteps((prev) => new Set(prev).add(currentStep))
|
|
2639
|
+
setCurrentStep((prev) => prev + 1)
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
function handleBack() {
|
|
2644
|
+
setCurrentStep((prev) => prev - 1)
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
function handleStepClick(index: number) {
|
|
2648
|
+
if (index < currentStep || completedSteps.has(index)) {
|
|
2649
|
+
setCurrentStep(index)
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
async function onSubmit(values: FormValues) {
|
|
2654
|
+
setSubmitting(true)
|
|
2655
|
+
try {
|
|
2656
|
+
const result = await create${p7.pascal}Submission(values)
|
|
2657
|
+
if (result.success) {
|
|
2658
|
+
setSubmitted(true)
|
|
2659
|
+
} else {
|
|
2660
|
+
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
2661
|
+
}
|
|
2662
|
+
} catch {
|
|
2663
|
+
form.setError('root', { message: 'Something went wrong. Please try again.' })
|
|
2664
|
+
} finally {
|
|
2665
|
+
setSubmitting(false)
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
if (submitted) {
|
|
2670
|
+
return (
|
|
2671
|
+
<div className="rounded-lg border p-6 text-center">
|
|
2672
|
+
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
2673
|
+
<p className="mt-2 text-muted-foreground">${p7.successMessage}</p>
|
|
2674
|
+
</div>
|
|
2675
|
+
)
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
return (
|
|
2679
|
+
<Form {...form}>
|
|
2680
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
2681
|
+
{/* Mobile step indicator */}
|
|
2682
|
+
<div className="md:hidden space-y-2">
|
|
2683
|
+
<p className="text-sm text-muted-foreground">
|
|
2684
|
+
Step {currentStep + 1} of {STEPS.length} — {STEPS[currentStep].label}
|
|
2685
|
+
</p>
|
|
2686
|
+
<Progress value={((currentStep + 1) / STEPS.length) * 100} />
|
|
2687
|
+
</div>
|
|
2688
|
+
|
|
2689
|
+
{/* Desktop step indicator */}
|
|
2690
|
+
<nav className="hidden md:flex items-center justify-center" aria-label="Form progress">
|
|
2691
|
+
{STEPS.map((step, index) => (
|
|
2692
|
+
<div key={step.name} className="flex items-center">
|
|
2693
|
+
<button
|
|
2694
|
+
type="button"
|
|
2695
|
+
onClick={() => handleStepClick(index)}
|
|
2696
|
+
disabled={index > currentStep && !completedSteps.has(index)}
|
|
2697
|
+
aria-current={index === currentStep ? 'step' : undefined}
|
|
2698
|
+
className={\`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium transition-colors \${
|
|
2699
|
+
completedSteps.has(index)
|
|
2700
|
+
? 'bg-primary text-primary-foreground cursor-pointer'
|
|
2701
|
+
: index === currentStep
|
|
2702
|
+
? 'border-2 border-primary text-primary'
|
|
2703
|
+
: 'border-2 border-muted text-muted-foreground'
|
|
2704
|
+
}\`}
|
|
2705
|
+
>
|
|
2706
|
+
{completedSteps.has(index) ? (
|
|
2707
|
+
<Check className="h-4 w-4" />
|
|
2708
|
+
) : (
|
|
2709
|
+
index + 1
|
|
2710
|
+
)}
|
|
2711
|
+
</button>
|
|
2712
|
+
{index < STEPS.length - 1 && (
|
|
2713
|
+
<div
|
|
2714
|
+
className={\`h-0.5 w-8 mx-1 \${
|
|
2715
|
+
completedSteps.has(index) ? 'bg-primary' : 'bg-muted'
|
|
2716
|
+
}\`}
|
|
2717
|
+
/>
|
|
2718
|
+
)}
|
|
2719
|
+
</div>
|
|
2720
|
+
))}
|
|
2721
|
+
</nav>
|
|
2722
|
+
|
|
2723
|
+
{/* Step header */}
|
|
2724
|
+
<div>
|
|
2725
|
+
<h3 className="text-lg font-semibold">{STEPS[currentStep].label}</h3>
|
|
2726
|
+
{'description' in STEPS[currentStep] && STEPS[currentStep].description && (
|
|
2727
|
+
<p className="text-sm text-muted-foreground mt-1">{STEPS[currentStep].description}</p>
|
|
2728
|
+
)}
|
|
2729
|
+
</div>
|
|
2730
|
+
|
|
2731
|
+
{/* Step content */}
|
|
2732
|
+
<div key={currentStep} className="animate-in fade-in duration-300 space-y-6">
|
|
2733
|
+
${p7.stepContentBlocks}
|
|
2734
|
+
</div>
|
|
2735
|
+
|
|
2736
|
+
{form.formState.errors.root && (
|
|
2737
|
+
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
2738
|
+
)}
|
|
2739
|
+
|
|
2740
|
+
{/* Navigation */}
|
|
2741
|
+
<div className="flex justify-between">
|
|
2742
|
+
<Button
|
|
2743
|
+
type="button"
|
|
2744
|
+
variant="outline"
|
|
2745
|
+
onClick={handleBack}
|
|
2746
|
+
disabled={currentStep === 0}
|
|
2747
|
+
>
|
|
2748
|
+
<ChevronLeft className="mr-2 h-4 w-4" />
|
|
2749
|
+
Back
|
|
2750
|
+
</Button>
|
|
2751
|
+
{currentStep < STEPS.length - 1 ? (
|
|
2752
|
+
<Button type="button" onClick={handleNext}>
|
|
2753
|
+
Next
|
|
2754
|
+
<ChevronRight className="ml-2 h-4 w-4" />
|
|
2755
|
+
</Button>
|
|
2756
|
+
) : (
|
|
2757
|
+
<Button type="submit" disabled={submitting}>
|
|
2758
|
+
{submitting ? 'Submitting...' : '${p7.submitText}'}
|
|
2759
|
+
</Button>
|
|
2760
|
+
)}
|
|
2761
|
+
</div>
|
|
2762
|
+
</form>
|
|
2763
|
+
</Form>
|
|
2764
|
+
)
|
|
2765
|
+
}
|
|
2766
|
+
`;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
// src/generators/form-pipeline/form-component-single.ts
|
|
2770
|
+
import fs9 from "fs";
|
|
2771
|
+
import path9 from "path";
|
|
2772
|
+
function generateSingleStepForm(schema, cwd, cmsDir, options) {
|
|
2773
|
+
const formName = schema.name;
|
|
2774
|
+
const pascal = toPascalCase(formName);
|
|
2775
|
+
const fields = getAllFormSchemaFields(schema);
|
|
2776
|
+
const kebab = toKebabCase(formName);
|
|
2777
|
+
const filePath = path9.join(cwd, cmsDir, "components", "forms", `${kebab}-form.tsx`);
|
|
2778
|
+
const dir = path9.dirname(filePath);
|
|
2779
|
+
if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
|
|
2780
|
+
if (fs9.existsSync(filePath) && !options.force) {
|
|
2781
|
+
return { files: [path9.relative(cwd, filePath)] };
|
|
2782
|
+
}
|
|
2783
|
+
const zodFields = buildZodFields(fields);
|
|
2784
|
+
const defaults = buildDefaultValues(fields);
|
|
2785
|
+
const listFields = getListFields(fields);
|
|
2786
|
+
const hasListFields = listFields.length > 0;
|
|
2787
|
+
const { setup: watchSetup } = buildWatchDecls(fields);
|
|
2788
|
+
const fieldJSX = fields.filter((f) => f.name && !f.hidden).map((f) => wrapShowWhen(f, generateFieldJSX(f))).join("\n\n");
|
|
2789
|
+
const submitText = schema.submitButtonText || "Submit";
|
|
2790
|
+
const successMessage = escapeJsx(schema.successMessage || "Form submitted successfully!");
|
|
2791
|
+
const rhfImport = hasListFields ? `import { useFieldArray, useForm } from 'react-hook-form'` : `import { useForm } from 'react-hook-form'`;
|
|
2792
|
+
const fieldArraySetup = hasListFields ? `
|
|
2793
|
+
${buildFieldArrayDecls(listFields)}
|
|
2794
|
+
` : "";
|
|
2795
|
+
const buttonImport = resolveUiImport(cwd, "button");
|
|
2796
|
+
const formImport = resolveUiImport(cwd, "form");
|
|
2797
|
+
const inputImport = resolveUiImport(cwd, "input");
|
|
2798
|
+
const textareaImport = resolveUiImport(cwd, "textarea");
|
|
2799
|
+
const selectImport = resolveUiImport(cwd, "select");
|
|
2800
|
+
const content = `'use client'
|
|
2801
|
+
|
|
2802
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
2803
|
+
import { useState } from 'react'
|
|
2804
|
+
${rhfImport}
|
|
2805
|
+
import { z } from 'zod/v3'
|
|
2806
|
+
import { create${pascal}Submission } from '@cms/actions/${kebab}-form'
|
|
2807
|
+
import { Button } from '${buttonImport}'
|
|
2808
|
+
import {
|
|
2809
|
+
Form,
|
|
2810
|
+
FormControl,
|
|
2811
|
+
FormDescription,
|
|
2812
|
+
FormField,
|
|
2813
|
+
FormItem,
|
|
2814
|
+
FormLabel,
|
|
2815
|
+
FormMessage,
|
|
2816
|
+
} from '${formImport}'
|
|
2817
|
+
import { Input } from '${inputImport}'
|
|
2818
|
+
import { Textarea } from '${textareaImport}'
|
|
2819
|
+
import {
|
|
2820
|
+
Select,
|
|
2821
|
+
SelectContent,
|
|
2822
|
+
SelectItem,
|
|
2823
|
+
SelectTrigger,
|
|
2824
|
+
SelectValue,
|
|
2825
|
+
} from '${selectImport}'
|
|
2826
|
+
|
|
2827
|
+
const formSchema = z.object({
|
|
2828
|
+
${zodFields}
|
|
2829
|
+
})
|
|
2830
|
+
|
|
2831
|
+
type FormValues = z.infer<typeof formSchema>
|
|
2832
|
+
|
|
2833
|
+
export function ${pascal}Form() {
|
|
2834
|
+
const [submitted, setSubmitted] = useState(false)
|
|
2835
|
+
const [submitting, setSubmitting] = useState(false)
|
|
2836
|
+
|
|
2837
|
+
const form = useForm<FormValues>({
|
|
2838
|
+
resolver: zodResolver(formSchema),
|
|
2839
|
+
defaultValues: {
|
|
2840
|
+
${defaults}
|
|
2841
|
+
},
|
|
2842
|
+
})
|
|
2843
|
+
${fieldArraySetup}${watchSetup}
|
|
2844
|
+
async function onSubmit(values: FormValues) {
|
|
2845
|
+
setSubmitting(true)
|
|
2846
|
+
try {
|
|
2847
|
+
const result = await create${pascal}Submission(values)
|
|
2848
|
+
if (result.success) {
|
|
2849
|
+
setSubmitted(true)
|
|
2850
|
+
} else {
|
|
2851
|
+
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
2852
|
+
}
|
|
2853
|
+
} catch {
|
|
2854
|
+
form.setError('root', { message: 'Something went wrong. Please try again.' })
|
|
2855
|
+
} finally {
|
|
2856
|
+
setSubmitting(false)
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
if (submitted) {
|
|
2861
|
+
return (
|
|
2862
|
+
<div className="rounded-lg border p-6 text-center">
|
|
2863
|
+
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
2864
|
+
<p className="mt-2 text-muted-foreground">${successMessage}</p>
|
|
2865
|
+
</div>
|
|
2866
|
+
)
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
return (
|
|
2870
|
+
<Form {...form}>
|
|
2871
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
2872
|
+
${fieldJSX}
|
|
2873
|
+
|
|
2874
|
+
{form.formState.errors.root && (
|
|
2875
|
+
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
2876
|
+
)}
|
|
2877
|
+
|
|
2878
|
+
<Button type="submit" disabled={submitting}>
|
|
2879
|
+
{submitting ? 'Submitting...' : '${submitText}'}
|
|
2880
|
+
</Button>
|
|
2881
|
+
</form>
|
|
2882
|
+
</Form>
|
|
2883
|
+
)
|
|
2884
|
+
}
|
|
2885
|
+
`;
|
|
2886
|
+
fs9.writeFileSync(filePath, content, "utf-8");
|
|
2887
|
+
return { files: [path9.relative(cwd, filePath)] };
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
// src/generators/form-pipeline/form-component.ts
|
|
2891
|
+
function generateFormComponent(schema, cwd, cmsDir, options) {
|
|
2892
|
+
if (isMultiStepForm(schema) && schema.steps.length > 1) {
|
|
2893
|
+
return generateMultiStepForm(schema, cwd, cmsDir, options);
|
|
2894
|
+
}
|
|
2895
|
+
return generateSingleStepForm(schema, cwd, cmsDir, options);
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2601
2898
|
// src/generators/form-pipeline/form-database.ts
|
|
2602
|
-
import
|
|
2603
|
-
import
|
|
2899
|
+
import fs10 from "fs";
|
|
2900
|
+
import path10 from "path";
|
|
2604
2901
|
function findTableEnd(content, startIndex) {
|
|
2605
2902
|
let depth = 0;
|
|
2606
2903
|
let inString = false;
|
|
@@ -2655,8 +2952,8 @@ ${fieldDefs}${customFieldsCol}
|
|
|
2655
2952
|
updatedAt: timestamp({ precision: 3, mode: 'string' }).default(sql\`CURRENT_TIMESTAMP\`).notNull()
|
|
2656
2953
|
})
|
|
2657
2954
|
`;
|
|
2658
|
-
const filePath =
|
|
2659
|
-
let content =
|
|
2955
|
+
const filePath = path10.join(cwd, dbSchemaPath);
|
|
2956
|
+
let content = fs10.readFileSync(filePath, "utf-8");
|
|
2660
2957
|
const pgCoreMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]drizzle-orm\/pg-core['"]/);
|
|
2661
2958
|
if (pgCoreMatch) {
|
|
2662
2959
|
const existing = new Set(
|
|
@@ -2692,23 +2989,23 @@ ${match}`
|
|
|
2692
2989
|
}
|
|
2693
2990
|
content += `
|
|
2694
2991
|
${tableSchema}`;
|
|
2695
|
-
|
|
2992
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
2696
2993
|
return { files: [dbSchemaPath] };
|
|
2697
2994
|
}
|
|
2698
2995
|
|
|
2699
2996
|
// src/generators/form-pipeline/form-hook.ts
|
|
2700
|
-
import
|
|
2701
|
-
import
|
|
2997
|
+
import fs11 from "fs";
|
|
2998
|
+
import path11 from "path";
|
|
2702
2999
|
function generateFormHook(schema, cwd, hooksDir, options) {
|
|
2703
3000
|
const formName = schema.name;
|
|
2704
3001
|
const pascal = toPascalCase(formName);
|
|
2705
3002
|
const camel = toCamelCase(formName);
|
|
2706
3003
|
const kebab = toKebabCase(formName);
|
|
2707
|
-
const filePath =
|
|
2708
|
-
const dir =
|
|
2709
|
-
if (!
|
|
2710
|
-
if (
|
|
2711
|
-
return { files: [
|
|
3004
|
+
const filePath = path11.join(cwd, hooksDir, `use-${kebab}-form.ts`);
|
|
3005
|
+
const dir = path11.dirname(filePath);
|
|
3006
|
+
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
3007
|
+
if (fs11.existsSync(filePath) && !options.force) {
|
|
3008
|
+
return { files: [path11.relative(cwd, filePath)] };
|
|
2712
3009
|
}
|
|
2713
3010
|
const successMsg = (schema.successMessage || "Form submitted successfully").replace(/'/g, "\\'");
|
|
2714
3011
|
const content = `import {
|
|
@@ -2814,8 +3111,8 @@ export function useExport${pascal}SubmissionsJSON() {
|
|
|
2814
3111
|
})
|
|
2815
3112
|
}
|
|
2816
3113
|
`;
|
|
2817
|
-
|
|
2818
|
-
return { files: [
|
|
3114
|
+
fs11.writeFileSync(filePath, content, "utf-8");
|
|
3115
|
+
return { files: [path11.relative(cwd, filePath)] };
|
|
2819
3116
|
}
|
|
2820
3117
|
|
|
2821
3118
|
// src/generators/form-pipeline/pipeline.ts
|
|
@@ -2881,8 +3178,8 @@ function runFormPipeline(schema, cwd, config, options = {}) {
|
|
|
2881
3178
|
}
|
|
2882
3179
|
|
|
2883
3180
|
// src/generators/actions/entity-actions.ts
|
|
2884
|
-
import
|
|
2885
|
-
import
|
|
3181
|
+
import fs12 from "fs";
|
|
3182
|
+
import path12 from "path";
|
|
2886
3183
|
|
|
2887
3184
|
// src/generators/actions/action-helpers.ts
|
|
2888
3185
|
function generateFieldMapping(field, source = "input") {
|
|
@@ -3021,9 +3318,9 @@ ${blocks.join("\n\n")}
|
|
|
3021
3318
|
|
|
3022
3319
|
// src/generators/actions/entity-actions.ts
|
|
3023
3320
|
function generateActions(schema, cwd, actionsDir, options = {}) {
|
|
3024
|
-
const absActionsDir =
|
|
3025
|
-
const filePath =
|
|
3026
|
-
if (
|
|
3321
|
+
const absActionsDir = path12.join(cwd, actionsDir);
|
|
3322
|
+
const filePath = path12.join(absActionsDir, `${schema.name}.ts`);
|
|
3323
|
+
if (fs12.existsSync(filePath) && !options.force) {
|
|
3027
3324
|
return { files: [] };
|
|
3028
3325
|
}
|
|
3029
3326
|
const singular = singularize(schema.name);
|
|
@@ -3509,20 +3806,20 @@ export async function bulkUpdate${Plural}SortOrder(
|
|
|
3509
3806
|
}
|
|
3510
3807
|
}${m2mHelpers}
|
|
3511
3808
|
`;
|
|
3512
|
-
if (!
|
|
3513
|
-
|
|
3809
|
+
if (!fs12.existsSync(absActionsDir)) {
|
|
3810
|
+
fs12.mkdirSync(absActionsDir, { recursive: true });
|
|
3514
3811
|
}
|
|
3515
|
-
|
|
3516
|
-
return { files: [
|
|
3812
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
3813
|
+
return { files: [path12.join(actionsDir, `${schema.name}.ts`)] };
|
|
3517
3814
|
}
|
|
3518
3815
|
|
|
3519
3816
|
// src/generators/actions/single-actions.ts
|
|
3520
|
-
import
|
|
3521
|
-
import
|
|
3817
|
+
import fs13 from "fs";
|
|
3818
|
+
import path13 from "path";
|
|
3522
3819
|
function generateSingleActions(schema, cwd, actionsDir, options = {}) {
|
|
3523
|
-
const absActionsDir =
|
|
3524
|
-
const filePath =
|
|
3525
|
-
if (
|
|
3820
|
+
const absActionsDir = path13.join(cwd, actionsDir);
|
|
3821
|
+
const filePath = path13.join(absActionsDir, `${schema.name}.ts`);
|
|
3822
|
+
if (fs13.existsSync(filePath) && !options.force) {
|
|
3526
3823
|
return { files: [] };
|
|
3527
3824
|
}
|
|
3528
3825
|
const singular = singularize(schema.name);
|
|
@@ -3645,24 +3942,24 @@ ${upsertMappings},${singleHasHtmlOutput ? "\n" + singleHtmlOutputFields.map((f)
|
|
|
3645
3942
|
}
|
|
3646
3943
|
}
|
|
3647
3944
|
`;
|
|
3648
|
-
if (!
|
|
3649
|
-
|
|
3945
|
+
if (!fs13.existsSync(absActionsDir)) {
|
|
3946
|
+
fs13.mkdirSync(absActionsDir, { recursive: true });
|
|
3650
3947
|
}
|
|
3651
|
-
|
|
3652
|
-
return { files: [
|
|
3948
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
3949
|
+
return { files: [path13.join(actionsDir, `${schema.name}.ts`)] };
|
|
3653
3950
|
}
|
|
3654
3951
|
|
|
3655
3952
|
// src/generators/cache.ts
|
|
3656
|
-
import
|
|
3657
|
-
import
|
|
3953
|
+
import fs14 from "fs";
|
|
3954
|
+
import path14 from "path";
|
|
3658
3955
|
function loadAllSchemas(cwd, schemasDir) {
|
|
3659
|
-
const dir =
|
|
3660
|
-
if (!
|
|
3661
|
-
const files =
|
|
3956
|
+
const dir = path14.join(cwd, schemasDir);
|
|
3957
|
+
if (!fs14.existsSync(dir)) return [];
|
|
3958
|
+
const files = fs14.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
3662
3959
|
const seen = /* @__PURE__ */ new Map();
|
|
3663
3960
|
for (const file of files) {
|
|
3664
3961
|
if (file === "schema.json") continue;
|
|
3665
|
-
const content =
|
|
3962
|
+
const content = fs14.readFileSync(path14.join(dir, file), "utf-8");
|
|
3666
3963
|
const schema = JSON.parse(content);
|
|
3667
3964
|
if (!schema.name) continue;
|
|
3668
3965
|
seen.set(schema.name, schema);
|
|
@@ -3852,27 +4149,27 @@ export * from './revalidate'
|
|
|
3852
4149
|
`;
|
|
3853
4150
|
}
|
|
3854
4151
|
function generateCache(_schema, cwd, cmsDir, _options = {}) {
|
|
3855
|
-
const cacheDir =
|
|
3856
|
-
const schemasDir =
|
|
4152
|
+
const cacheDir = path14.join(cwd, cmsDir, "lib", "cache");
|
|
4153
|
+
const schemasDir = path14.join(cmsDir, "schemas");
|
|
3857
4154
|
const schemas = loadAllSchemas(cwd, schemasDir);
|
|
3858
4155
|
const configs = schemas.map(buildCacheConfig).sort((a, b) => a.name.localeCompare(b.name));
|
|
3859
|
-
if (!
|
|
3860
|
-
|
|
4156
|
+
if (!fs14.existsSync(cacheDir)) {
|
|
4157
|
+
fs14.mkdirSync(cacheDir, { recursive: true });
|
|
3861
4158
|
}
|
|
3862
4159
|
const files = [];
|
|
3863
|
-
|
|
3864
|
-
files.push(
|
|
3865
|
-
|
|
3866
|
-
|
|
4160
|
+
fs14.writeFileSync(path14.join(cacheDir, "tags.ts"), generateTags(configs), "utf-8");
|
|
4161
|
+
files.push(path14.join(cmsDir, "lib", "cache", "tags.ts"));
|
|
4162
|
+
fs14.writeFileSync(
|
|
4163
|
+
path14.join(cacheDir, "cached-queries.ts"),
|
|
3867
4164
|
generateCachedQueries(configs),
|
|
3868
4165
|
"utf-8"
|
|
3869
4166
|
);
|
|
3870
|
-
files.push(
|
|
3871
|
-
|
|
3872
|
-
files.push(
|
|
3873
|
-
const customQueriesPath =
|
|
3874
|
-
if (!
|
|
3875
|
-
|
|
4167
|
+
files.push(path14.join(cmsDir, "lib", "cache", "cached-queries.ts"));
|
|
4168
|
+
fs14.writeFileSync(path14.join(cacheDir, "revalidate.ts"), generateRevalidate(configs), "utf-8");
|
|
4169
|
+
files.push(path14.join(cmsDir, "lib", "cache", "revalidate.ts"));
|
|
4170
|
+
const customQueriesPath = path14.join(cacheDir, "cached-queries-custom.ts");
|
|
4171
|
+
if (!fs14.existsSync(customQueriesPath)) {
|
|
4172
|
+
fs14.writeFileSync(
|
|
3876
4173
|
customQueriesPath,
|
|
3877
4174
|
`/**
|
|
3878
4175
|
* Custom cached query functions (not auto-generated)
|
|
@@ -3883,16 +4180,16 @@ export {}
|
|
|
3883
4180
|
`,
|
|
3884
4181
|
"utf-8"
|
|
3885
4182
|
);
|
|
3886
|
-
files.push(
|
|
4183
|
+
files.push(path14.join(cmsDir, "lib", "cache", "cached-queries-custom.ts"));
|
|
3887
4184
|
}
|
|
3888
|
-
|
|
3889
|
-
files.push(
|
|
4185
|
+
fs14.writeFileSync(path14.join(cacheDir, "index.ts"), generateIndex(), "utf-8");
|
|
4186
|
+
files.push(path14.join(cmsDir, "lib", "cache", "index.ts"));
|
|
3890
4187
|
return { files };
|
|
3891
4188
|
}
|
|
3892
4189
|
|
|
3893
4190
|
// src/generators/columns/generate-columns.ts
|
|
3894
|
-
import
|
|
3895
|
-
import
|
|
4191
|
+
import fs16 from "fs";
|
|
4192
|
+
import path16 from "path";
|
|
3896
4193
|
|
|
3897
4194
|
// src/generators/columns/column-defs.ts
|
|
3898
4195
|
function isSortableColumn(column) {
|
|
@@ -4510,12 +4807,12 @@ function truncateStr(str: string, maxLength: number): string {
|
|
|
4510
4807
|
}
|
|
4511
4808
|
|
|
4512
4809
|
// src/generators/columns/custom-cell.ts
|
|
4513
|
-
import
|
|
4514
|
-
import
|
|
4810
|
+
import fs15 from "fs";
|
|
4811
|
+
import path15 from "path";
|
|
4515
4812
|
function createCustomCellComponent(schema, componentName, cwd, pagesDir, options) {
|
|
4516
|
-
const cellsDir =
|
|
4517
|
-
const componentFilePath =
|
|
4518
|
-
if (
|
|
4813
|
+
const cellsDir = path15.join(cwd, pagesDir, schema.name, "cells");
|
|
4814
|
+
const componentFilePath = path15.join(cellsDir, `${componentName}.tsx`);
|
|
4815
|
+
if (fs15.existsSync(componentFilePath) && !options.force) return;
|
|
4519
4816
|
const singular = singularize(schema.name);
|
|
4520
4817
|
const Singular = toPascalCase(singular);
|
|
4521
4818
|
const content = `import type { ${Singular}Data } from '@cms/actions/${schema.name}'
|
|
@@ -4532,17 +4829,17 @@ export function ${componentName}({ data }: ${componentName}Props) {
|
|
|
4532
4829
|
)
|
|
4533
4830
|
}
|
|
4534
4831
|
`;
|
|
4535
|
-
if (!
|
|
4536
|
-
|
|
4832
|
+
if (!fs15.existsSync(cellsDir)) {
|
|
4833
|
+
fs15.mkdirSync(cellsDir, { recursive: true });
|
|
4537
4834
|
}
|
|
4538
|
-
|
|
4835
|
+
fs15.writeFileSync(componentFilePath, content, "utf-8");
|
|
4539
4836
|
}
|
|
4540
4837
|
|
|
4541
4838
|
// src/generators/columns/generate-columns.ts
|
|
4542
4839
|
function generateColumns2(schema, cwd, pagesDir, options = {}) {
|
|
4543
|
-
const entityDir =
|
|
4544
|
-
const columnsFilePath =
|
|
4545
|
-
if (
|
|
4840
|
+
const entityDir = path16.join(cwd, pagesDir, schema.name);
|
|
4841
|
+
const columnsFilePath = path16.join(entityDir, "columns.tsx");
|
|
4842
|
+
if (fs16.existsSync(columnsFilePath) && !options.force) {
|
|
4546
4843
|
return { files: [] };
|
|
4547
4844
|
}
|
|
4548
4845
|
const singular = singularize(schema.name);
|
|
@@ -4684,22 +4981,22 @@ ${restColDefs}` : ""},
|
|
|
4684
4981
|
${actionsColumn}
|
|
4685
4982
|
]
|
|
4686
4983
|
`;
|
|
4687
|
-
if (!
|
|
4688
|
-
|
|
4984
|
+
if (!fs16.existsSync(entityDir)) {
|
|
4985
|
+
fs16.mkdirSync(entityDir, { recursive: true });
|
|
4689
4986
|
}
|
|
4690
|
-
|
|
4987
|
+
fs16.writeFileSync(columnsFilePath, content, "utf-8");
|
|
4691
4988
|
return {
|
|
4692
|
-
files: [
|
|
4989
|
+
files: [path16.join(pagesDir, schema.name, "columns.tsx")]
|
|
4693
4990
|
};
|
|
4694
4991
|
}
|
|
4695
4992
|
|
|
4696
4993
|
// src/generators/create-page.ts
|
|
4697
|
-
import
|
|
4698
|
-
import
|
|
4994
|
+
import fs17 from "fs";
|
|
4995
|
+
import path17 from "path";
|
|
4699
4996
|
function generateCreatePage(schema, cwd, pagesDir, options = {}) {
|
|
4700
|
-
const newDir =
|
|
4701
|
-
const pageFilePath =
|
|
4702
|
-
if (
|
|
4997
|
+
const newDir = path17.join(cwd, pagesDir, schema.name, "new");
|
|
4998
|
+
const pageFilePath = path17.join(newDir, "page.tsx");
|
|
4999
|
+
if (fs17.existsSync(pageFilePath) && !options.force) {
|
|
4703
5000
|
return { files: [] };
|
|
4704
5001
|
}
|
|
4705
5002
|
const singular = singularize(schema.name);
|
|
@@ -4726,18 +5023,18 @@ export default async function Create${Singular}Page() {
|
|
|
4726
5023
|
)
|
|
4727
5024
|
}
|
|
4728
5025
|
`;
|
|
4729
|
-
if (!
|
|
4730
|
-
|
|
5026
|
+
if (!fs17.existsSync(newDir)) {
|
|
5027
|
+
fs17.mkdirSync(newDir, { recursive: true });
|
|
4731
5028
|
}
|
|
4732
|
-
|
|
5029
|
+
fs17.writeFileSync(pageFilePath, content, "utf-8");
|
|
4733
5030
|
return {
|
|
4734
|
-
files: [
|
|
5031
|
+
files: [path17.join(pagesDir, schema.name, "new", "page.tsx")]
|
|
4735
5032
|
};
|
|
4736
5033
|
}
|
|
4737
5034
|
|
|
4738
5035
|
// src/generators/database.ts
|
|
4739
|
-
import
|
|
4740
|
-
import
|
|
5036
|
+
import fs18 from "fs";
|
|
5037
|
+
import path18 from "path";
|
|
4741
5038
|
function getFieldModifiers(field, needsSql) {
|
|
4742
5039
|
const modifiers = [];
|
|
4743
5040
|
if (field.primaryKey) {
|
|
@@ -4918,11 +5215,11 @@ function findTableEnd2(content, startIndex) {
|
|
|
4918
5215
|
return content.length;
|
|
4919
5216
|
}
|
|
4920
5217
|
function generateDatabase(schema, cwd, schemaDir, options = {}) {
|
|
4921
|
-
const schemaFilePath =
|
|
5218
|
+
const schemaFilePath = path18.join(cwd, schemaDir);
|
|
4922
5219
|
const files = [];
|
|
4923
5220
|
let content = "";
|
|
4924
|
-
if (
|
|
4925
|
-
content =
|
|
5221
|
+
if (fs18.existsSync(schemaFilePath)) {
|
|
5222
|
+
content = fs18.readFileSync(schemaFilePath, "utf-8");
|
|
4926
5223
|
}
|
|
4927
5224
|
const variableName = toCamelCase(schema.name);
|
|
4928
5225
|
if (content.includes(`export const ${variableName} =`) && !options.force) {
|
|
@@ -4962,11 +5259,11 @@ ${junctionDefs[i]}`;
|
|
|
4962
5259
|
}
|
|
4963
5260
|
}
|
|
4964
5261
|
updated = mergeImports(updated, requiredImports, needsSql.value);
|
|
4965
|
-
const dir =
|
|
4966
|
-
if (!
|
|
4967
|
-
|
|
5262
|
+
const dir = path18.dirname(schemaFilePath);
|
|
5263
|
+
if (!fs18.existsSync(dir)) {
|
|
5264
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
4968
5265
|
}
|
|
4969
|
-
|
|
5266
|
+
fs18.writeFileSync(schemaFilePath, updated, "utf-8");
|
|
4970
5267
|
files.push(schemaDir);
|
|
4971
5268
|
return {
|
|
4972
5269
|
files,
|
|
@@ -4976,12 +5273,12 @@ ${junctionDefs[i]}`;
|
|
|
4976
5273
|
}
|
|
4977
5274
|
|
|
4978
5275
|
// src/generators/edit-page.ts
|
|
4979
|
-
import
|
|
4980
|
-
import
|
|
5276
|
+
import fs19 from "fs";
|
|
5277
|
+
import path19 from "path";
|
|
4981
5278
|
function generateEditPage(schema, cwd, pagesDir, options = {}) {
|
|
4982
|
-
const editDir =
|
|
4983
|
-
const pageFilePath =
|
|
4984
|
-
if (
|
|
5279
|
+
const editDir = path19.join(cwd, pagesDir, schema.name, "[id]", "edit");
|
|
5280
|
+
const pageFilePath = path19.join(editDir, "page.tsx");
|
|
5281
|
+
if (fs19.existsSync(pageFilePath) && !options.force) {
|
|
4985
5282
|
return { files: [] };
|
|
4986
5283
|
}
|
|
4987
5284
|
const singular = singularize(schema.name);
|
|
@@ -5020,18 +5317,18 @@ export default async function Edit${Singular}Page({ params }: PageProps) {
|
|
|
5020
5317
|
)
|
|
5021
5318
|
}
|
|
5022
5319
|
`;
|
|
5023
|
-
if (!
|
|
5024
|
-
|
|
5320
|
+
if (!fs19.existsSync(editDir)) {
|
|
5321
|
+
fs19.mkdirSync(editDir, { recursive: true });
|
|
5025
5322
|
}
|
|
5026
|
-
|
|
5323
|
+
fs19.writeFileSync(pageFilePath, content, "utf-8");
|
|
5027
5324
|
return {
|
|
5028
|
-
files: [
|
|
5325
|
+
files: [path19.join(pagesDir, schema.name, "[id]", "edit", "page.tsx")]
|
|
5029
5326
|
};
|
|
5030
5327
|
}
|
|
5031
5328
|
|
|
5032
5329
|
// src/generators/form/form-entity.ts
|
|
5033
|
-
import
|
|
5034
|
-
import
|
|
5330
|
+
import fs20 from "fs";
|
|
5331
|
+
import path20 from "path";
|
|
5035
5332
|
|
|
5036
5333
|
// src/generators/form/zod-schema.ts
|
|
5037
5334
|
function getFormFieldType(field) {
|
|
@@ -5829,7 +6126,7 @@ function generateDefaultValue(f) {
|
|
|
5829
6126
|
}
|
|
5830
6127
|
return ` ${f.name}: initialData?.${f.name} ?? ''`;
|
|
5831
6128
|
}
|
|
5832
|
-
function
|
|
6129
|
+
function buildZodFields2(flatFields) {
|
|
5833
6130
|
return flatFields.filter((f) => f.type !== "tabs").flatMap((f) => {
|
|
5834
6131
|
const defs = [];
|
|
5835
6132
|
const zodType = getZodType(f);
|
|
@@ -5841,7 +6138,7 @@ function buildZodFields(flatFields) {
|
|
|
5841
6138
|
return defs;
|
|
5842
6139
|
}).join(",\n");
|
|
5843
6140
|
}
|
|
5844
|
-
function
|
|
6141
|
+
function buildDefaultValues2(flatFields) {
|
|
5845
6142
|
return flatFields.filter((f) => f.type !== "tabs").flatMap((f) => {
|
|
5846
6143
|
const defs = [generateDefaultValue(f)];
|
|
5847
6144
|
if (f.hasIcon) defs.push(` ${f.name}Icon: initialData?.${f.name}Icon ?? ''`);
|
|
@@ -5928,9 +6225,9 @@ function buildUiImports(ctx) {
|
|
|
5928
6225
|
|
|
5929
6226
|
// src/generators/form/form-entity.ts
|
|
5930
6227
|
function generateForm(schema, cwd, pagesDir, options = {}) {
|
|
5931
|
-
const entityDir =
|
|
5932
|
-
const formFilePath =
|
|
5933
|
-
if (
|
|
6228
|
+
const entityDir = path20.join(cwd, pagesDir, schema.name);
|
|
6229
|
+
const formFilePath = path20.join(entityDir, `${schema.name}-form.tsx`);
|
|
6230
|
+
if (fs20.existsSync(formFilePath) && !options.force) {
|
|
5934
6231
|
return { files: [] };
|
|
5935
6232
|
}
|
|
5936
6233
|
const singular = singularize(schema.name);
|
|
@@ -5984,8 +6281,8 @@ function generateForm(schema, cwd, pagesDir, options = {}) {
|
|
|
5984
6281
|
const hasList = hasFieldType(schema.fields, "list");
|
|
5985
6282
|
const hasNestedList = listFieldsWithNested.length > 0;
|
|
5986
6283
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
5987
|
-
const zodFields =
|
|
5988
|
-
const defaultValues =
|
|
6284
|
+
const zodFields = buildZodFields2(flatFields);
|
|
6285
|
+
const defaultValues = buildDefaultValues2(flatFields);
|
|
5989
6286
|
const formFieldsJSX = allFormFields.filter((f) => !(hasDraft && f.name === "published")).map((f) => {
|
|
5990
6287
|
if (f.type === "tabs" && f.tabs) {
|
|
5991
6288
|
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
@@ -6186,22 +6483,22 @@ ${hasDraft ? ` <Button
|
|
|
6186
6483
|
)
|
|
6187
6484
|
}
|
|
6188
6485
|
`;
|
|
6189
|
-
if (!
|
|
6190
|
-
|
|
6486
|
+
if (!fs20.existsSync(entityDir)) {
|
|
6487
|
+
fs20.mkdirSync(entityDir, { recursive: true });
|
|
6191
6488
|
}
|
|
6192
|
-
|
|
6489
|
+
fs20.writeFileSync(formFilePath, content, "utf-8");
|
|
6193
6490
|
return {
|
|
6194
|
-
files: [
|
|
6491
|
+
files: [path20.join(pagesDir, schema.name, `${schema.name}-form.tsx`)]
|
|
6195
6492
|
};
|
|
6196
6493
|
}
|
|
6197
6494
|
|
|
6198
6495
|
// src/generators/form/form-single.ts
|
|
6199
|
-
import
|
|
6200
|
-
import
|
|
6496
|
+
import fs21 from "fs";
|
|
6497
|
+
import path21 from "path";
|
|
6201
6498
|
function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
6202
|
-
const entityDir =
|
|
6203
|
-
const formFilePath =
|
|
6204
|
-
if (
|
|
6499
|
+
const entityDir = path21.join(cwd, pagesDir, schema.name);
|
|
6500
|
+
const formFilePath = path21.join(entityDir, `${schema.name}-form.tsx`);
|
|
6501
|
+
if (fs21.existsSync(formFilePath) && !options.force) {
|
|
6205
6502
|
return { files: [] };
|
|
6206
6503
|
}
|
|
6207
6504
|
const singular = singularize(schema.name);
|
|
@@ -6253,8 +6550,8 @@ function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
|
6253
6550
|
const hasList = hasFieldType(schema.fields, "list");
|
|
6254
6551
|
const hasNestedList = listFieldsWithNested.length > 0;
|
|
6255
6552
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
6256
|
-
const zodFields =
|
|
6257
|
-
const defaultValues =
|
|
6553
|
+
const zodFields = buildZodFields2(flatFields);
|
|
6554
|
+
const defaultValues = buildDefaultValues2(flatFields);
|
|
6258
6555
|
const formFieldsJSX = allFormFields.map((f) => {
|
|
6259
6556
|
if (f.type === "tabs" && f.tabs) {
|
|
6260
6557
|
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
@@ -6404,21 +6701,21 @@ ${formFieldsJSX}
|
|
|
6404
6701
|
)
|
|
6405
6702
|
}
|
|
6406
6703
|
`;
|
|
6407
|
-
if (!
|
|
6408
|
-
|
|
6704
|
+
if (!fs21.existsSync(entityDir)) {
|
|
6705
|
+
fs21.mkdirSync(entityDir, { recursive: true });
|
|
6409
6706
|
}
|
|
6410
|
-
|
|
6707
|
+
fs21.writeFileSync(formFilePath, content, "utf-8");
|
|
6411
6708
|
return {
|
|
6412
|
-
files: [
|
|
6709
|
+
files: [path21.join(pagesDir, schema.name, `${schema.name}-form.tsx`)]
|
|
6413
6710
|
};
|
|
6414
6711
|
}
|
|
6415
6712
|
|
|
6416
6713
|
// src/generators/hook.ts
|
|
6417
|
-
import
|
|
6418
|
-
import
|
|
6714
|
+
import fs22 from "fs";
|
|
6715
|
+
import path22 from "path";
|
|
6419
6716
|
function generateHook(schema, cwd, hooksDir, options = {}) {
|
|
6420
6717
|
const hookFileName = `use-${schema.name}.ts`;
|
|
6421
|
-
const hookFilePath =
|
|
6718
|
+
const hookFilePath = path22.join(cwd, hooksDir, hookFileName);
|
|
6422
6719
|
const singular = singularize(schema.name);
|
|
6423
6720
|
const plural = pluralize(schema.name);
|
|
6424
6721
|
const Singular = toPascalCase(singular);
|
|
@@ -6428,7 +6725,7 @@ function generateHook(schema, cwd, hooksDir, options = {}) {
|
|
|
6428
6725
|
const hasHtmlOutput = dbFields.some(
|
|
6429
6726
|
(f) => (f.type === "richtext" || f.type === "markdown") && f.output === "html"
|
|
6430
6727
|
);
|
|
6431
|
-
if (
|
|
6728
|
+
if (fs22.existsSync(hookFilePath) && !options.force) {
|
|
6432
6729
|
return { files: [], hookName: `use${Plural}` };
|
|
6433
6730
|
}
|
|
6434
6731
|
const hasFilters = schema.filters && schema.filters.length > 0;
|
|
@@ -6503,22 +6800,22 @@ export function use${Plural}(
|
|
|
6503
6800
|
}
|
|
6504
6801
|
${singularHook}${slugHook}${distinctHooks}
|
|
6505
6802
|
`;
|
|
6506
|
-
const dir =
|
|
6507
|
-
if (!
|
|
6508
|
-
|
|
6803
|
+
const dir = path22.dirname(hookFilePath);
|
|
6804
|
+
if (!fs22.existsSync(dir)) {
|
|
6805
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
6509
6806
|
}
|
|
6510
|
-
|
|
6807
|
+
fs22.writeFileSync(hookFilePath, content, "utf-8");
|
|
6511
6808
|
return {
|
|
6512
|
-
files: [
|
|
6809
|
+
files: [path22.join(hooksDir, hookFileName)],
|
|
6513
6810
|
hookName: `use${Plural}`
|
|
6514
6811
|
};
|
|
6515
6812
|
}
|
|
6516
6813
|
function generateSingleHook(schema, cwd, hooksDir, options = {}) {
|
|
6517
6814
|
const hookFileName = `use-${schema.name}.ts`;
|
|
6518
|
-
const hookFilePath =
|
|
6815
|
+
const hookFilePath = path22.join(cwd, hooksDir, hookFileName);
|
|
6519
6816
|
const singular = singularize(schema.name);
|
|
6520
6817
|
const Singular = toPascalCase(singular);
|
|
6521
|
-
if (
|
|
6818
|
+
if (fs22.existsSync(hookFilePath) && !options.force) {
|
|
6522
6819
|
return { files: [], hookName: `use${Singular}` };
|
|
6523
6820
|
}
|
|
6524
6821
|
const content = `import {
|
|
@@ -6535,20 +6832,20 @@ export function use${Singular}(): UseQueryResult<${Singular}Data | null, Error>
|
|
|
6535
6832
|
})
|
|
6536
6833
|
}
|
|
6537
6834
|
`;
|
|
6538
|
-
const dir =
|
|
6539
|
-
if (!
|
|
6540
|
-
|
|
6835
|
+
const dir = path22.dirname(hookFilePath);
|
|
6836
|
+
if (!fs22.existsSync(dir)) {
|
|
6837
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
6541
6838
|
}
|
|
6542
|
-
|
|
6839
|
+
fs22.writeFileSync(hookFilePath, content, "utf-8");
|
|
6543
6840
|
return {
|
|
6544
|
-
files: [
|
|
6841
|
+
files: [path22.join(hooksDir, hookFileName)],
|
|
6545
6842
|
hookName: `use${Singular}`
|
|
6546
6843
|
};
|
|
6547
6844
|
}
|
|
6548
6845
|
|
|
6549
6846
|
// src/generators/navigation.ts
|
|
6550
|
-
import
|
|
6551
|
-
import
|
|
6847
|
+
import fs23 from "fs";
|
|
6848
|
+
import path23 from "path";
|
|
6552
6849
|
function parseNavigationFile2(content) {
|
|
6553
6850
|
const iconImportMatch = content.match(/import\s*\{([^}]+)\}\s*from\s*['"]lucide-react['"]/);
|
|
6554
6851
|
const iconImports = iconImportMatch ? iconImportMatch[1].split(",").map((s) => s.trim()).filter((s) => s && s !== "LucideIcon") : [];
|
|
@@ -6651,14 +6948,14 @@ function appendItem2(lines, item, isLast) {
|
|
|
6651
6948
|
lines.push(` }${isLast ? "" : ","}`);
|
|
6652
6949
|
}
|
|
6653
6950
|
function updateNavigation(schema, cwd, cmsDir, options = {}) {
|
|
6654
|
-
const navFilePath =
|
|
6951
|
+
const navFilePath = path23.join(cwd, cmsDir, "data", "navigation.ts");
|
|
6655
6952
|
if (schema.name === "settings") {
|
|
6656
6953
|
return { files: [] };
|
|
6657
6954
|
}
|
|
6658
6955
|
let items = [];
|
|
6659
6956
|
let iconImports = [];
|
|
6660
|
-
if (
|
|
6661
|
-
const content =
|
|
6957
|
+
if (fs23.existsSync(navFilePath)) {
|
|
6958
|
+
const content = fs23.readFileSync(navFilePath, "utf-8");
|
|
6662
6959
|
const parsed = parseNavigationFile2(content);
|
|
6663
6960
|
items = parsed.items;
|
|
6664
6961
|
iconImports = parsed.iconImports;
|
|
@@ -6695,24 +6992,24 @@ function updateNavigation(schema, cwd, cmsDir, options = {}) {
|
|
|
6695
6992
|
iconImports.push(schema.icon);
|
|
6696
6993
|
}
|
|
6697
6994
|
iconImports.sort();
|
|
6698
|
-
const dir =
|
|
6699
|
-
if (!
|
|
6700
|
-
|
|
6995
|
+
const dir = path23.dirname(navFilePath);
|
|
6996
|
+
if (!fs23.existsSync(dir)) {
|
|
6997
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
6701
6998
|
}
|
|
6702
6999
|
const code = generateNavigationCode2(items, iconImports);
|
|
6703
|
-
|
|
7000
|
+
fs23.writeFileSync(navFilePath, code, "utf-8");
|
|
6704
7001
|
return {
|
|
6705
|
-
files: [
|
|
7002
|
+
files: [path23.join(cmsDir, "data", "navigation.ts")]
|
|
6706
7003
|
};
|
|
6707
7004
|
}
|
|
6708
7005
|
|
|
6709
7006
|
// src/generators/page.ts
|
|
6710
|
-
import
|
|
6711
|
-
import
|
|
7007
|
+
import fs24 from "fs";
|
|
7008
|
+
import path24 from "path";
|
|
6712
7009
|
function generatePage2(schema, cwd, pagesDir, options = {}) {
|
|
6713
|
-
const entityDir =
|
|
6714
|
-
const pageFilePath =
|
|
6715
|
-
if (
|
|
7010
|
+
const entityDir = path24.join(cwd, pagesDir, schema.name);
|
|
7011
|
+
const pageFilePath = path24.join(entityDir, "page.tsx");
|
|
7012
|
+
if (fs24.existsSync(pageFilePath) && !options.force) {
|
|
6716
7013
|
return { files: [] };
|
|
6717
7014
|
}
|
|
6718
7015
|
const plural = pluralize(schema.name);
|
|
@@ -6738,27 +7035,27 @@ export default function ${Plural}Page() {
|
|
|
6738
7035
|
)
|
|
6739
7036
|
}
|
|
6740
7037
|
`;
|
|
6741
|
-
if (!
|
|
6742
|
-
|
|
7038
|
+
if (!fs24.existsSync(entityDir)) {
|
|
7039
|
+
fs24.mkdirSync(entityDir, { recursive: true });
|
|
6743
7040
|
}
|
|
6744
|
-
|
|
7041
|
+
fs24.writeFileSync(pageFilePath, content, "utf-8");
|
|
6745
7042
|
return {
|
|
6746
|
-
files: [
|
|
7043
|
+
files: [path24.join(pagesDir, schema.name, "page.tsx")]
|
|
6747
7044
|
};
|
|
6748
7045
|
}
|
|
6749
7046
|
|
|
6750
7047
|
// src/generators/page-content.ts
|
|
6751
|
-
import
|
|
6752
|
-
import
|
|
7048
|
+
import fs25 from "fs";
|
|
7049
|
+
import path25 from "path";
|
|
6753
7050
|
function generatePageContent2(schema, cwd, pagesDir, options = {}) {
|
|
6754
|
-
const entityDir =
|
|
7051
|
+
const entityDir = path25.join(cwd, pagesDir, schema.name);
|
|
6755
7052
|
const singular = singularize(schema.name);
|
|
6756
7053
|
const plural = pluralize(schema.name);
|
|
6757
7054
|
const Singular = toPascalCase(singular);
|
|
6758
7055
|
const Plural = toPascalCase(plural);
|
|
6759
7056
|
const fileName = `${toKebabCase(plural)}-page-content.tsx`;
|
|
6760
|
-
const filePath =
|
|
6761
|
-
if (
|
|
7057
|
+
const filePath = path25.join(entityDir, fileName);
|
|
7058
|
+
if (fs25.existsSync(filePath) && !options.force) {
|
|
6762
7059
|
return { files: [] };
|
|
6763
7060
|
}
|
|
6764
7061
|
const hasCreate = schema.actions?.create ?? false;
|
|
@@ -7011,22 +7308,22 @@ ${searchLogic}${deleteLogic}
|
|
|
7011
7308
|
)
|
|
7012
7309
|
}
|
|
7013
7310
|
`;
|
|
7014
|
-
if (!
|
|
7015
|
-
|
|
7311
|
+
if (!fs25.existsSync(entityDir)) {
|
|
7312
|
+
fs25.mkdirSync(entityDir, { recursive: true });
|
|
7016
7313
|
}
|
|
7017
|
-
|
|
7314
|
+
fs25.writeFileSync(filePath, content, "utf-8");
|
|
7018
7315
|
return {
|
|
7019
|
-
files: [
|
|
7316
|
+
files: [path25.join(pagesDir, schema.name, fileName)]
|
|
7020
7317
|
};
|
|
7021
7318
|
}
|
|
7022
7319
|
|
|
7023
7320
|
// src/generators/single-page.ts
|
|
7024
|
-
import
|
|
7025
|
-
import
|
|
7321
|
+
import fs26 from "fs";
|
|
7322
|
+
import path26 from "path";
|
|
7026
7323
|
function generateSinglePage(schema, cwd, pagesDir, options = {}) {
|
|
7027
|
-
const entityDir =
|
|
7028
|
-
const pageFilePath =
|
|
7029
|
-
if (
|
|
7324
|
+
const entityDir = path26.join(cwd, pagesDir, schema.name);
|
|
7325
|
+
const pageFilePath = path26.join(entityDir, "page.tsx");
|
|
7326
|
+
if (fs26.existsSync(pageFilePath) && !options.force) {
|
|
7030
7327
|
return { files: [] };
|
|
7031
7328
|
}
|
|
7032
7329
|
const singular = singularize(schema.name);
|
|
@@ -7049,20 +7346,20 @@ export default async function ${PageName}Page() {
|
|
|
7049
7346
|
)
|
|
7050
7347
|
}
|
|
7051
7348
|
`;
|
|
7052
|
-
if (!
|
|
7053
|
-
|
|
7349
|
+
if (!fs26.existsSync(entityDir)) {
|
|
7350
|
+
fs26.mkdirSync(entityDir, { recursive: true });
|
|
7054
7351
|
}
|
|
7055
|
-
|
|
7352
|
+
fs26.writeFileSync(pageFilePath, content, "utf-8");
|
|
7056
7353
|
return {
|
|
7057
|
-
files: [
|
|
7354
|
+
files: [path26.join(pagesDir, schema.name, "page.tsx")]
|
|
7058
7355
|
};
|
|
7059
7356
|
}
|
|
7060
7357
|
|
|
7061
7358
|
// src/generators/table.ts
|
|
7062
|
-
import
|
|
7063
|
-
import
|
|
7359
|
+
import fs27 from "fs";
|
|
7360
|
+
import path27 from "path";
|
|
7064
7361
|
function generateTable2(schema, cwd, pagesDir, options = {}) {
|
|
7065
|
-
const entityDir =
|
|
7362
|
+
const entityDir = path27.join(cwd, pagesDir, schema.name);
|
|
7066
7363
|
const singular = singularize(schema.name);
|
|
7067
7364
|
const plural = pluralize(schema.name);
|
|
7068
7365
|
const Singular = toPascalCase(singular);
|
|
@@ -7070,8 +7367,8 @@ function generateTable2(schema, cwd, pagesDir, options = {}) {
|
|
|
7070
7367
|
const camelPlural = toCamelCase(plural);
|
|
7071
7368
|
const camelSingular = toCamelCase(singular);
|
|
7072
7369
|
const tableFileName = `${toKebabCase(plural)}-table.tsx`;
|
|
7073
|
-
const tableFilePath =
|
|
7074
|
-
if (
|
|
7370
|
+
const tableFilePath = path27.join(entityDir, tableFileName);
|
|
7371
|
+
if (fs27.existsSync(tableFilePath) && !options.force) {
|
|
7075
7372
|
return { files: [] };
|
|
7076
7373
|
}
|
|
7077
7374
|
const hasFilters = schema.filters && schema.filters.length > 0;
|
|
@@ -7435,12 +7732,12 @@ export function ${Plural}Table<TValue>({ columns, selectedIds, setSelectedIds, $
|
|
|
7435
7732
|
)
|
|
7436
7733
|
}
|
|
7437
7734
|
`;
|
|
7438
|
-
if (!
|
|
7439
|
-
|
|
7735
|
+
if (!fs27.existsSync(entityDir)) {
|
|
7736
|
+
fs27.mkdirSync(entityDir, { recursive: true });
|
|
7440
7737
|
}
|
|
7441
|
-
|
|
7738
|
+
fs27.writeFileSync(tableFilePath, content, "utf-8");
|
|
7442
7739
|
return {
|
|
7443
|
-
files: [
|
|
7740
|
+
files: [path27.join(pagesDir, schema.name, tableFileName)]
|
|
7444
7741
|
};
|
|
7445
7742
|
}
|
|
7446
7743
|
|
|
@@ -7598,13 +7895,13 @@ function runSinglePipeline(schema, cwd, config, options = {}) {
|
|
|
7598
7895
|
|
|
7599
7896
|
// src/generators/post-generate.ts
|
|
7600
7897
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
7601
|
-
import
|
|
7602
|
-
import
|
|
7898
|
+
import fs29 from "fs";
|
|
7899
|
+
import path29 from "path";
|
|
7603
7900
|
|
|
7604
7901
|
// src/utils/package-manager.ts
|
|
7605
7902
|
import { execFileSync } from "child_process";
|
|
7606
|
-
import
|
|
7607
|
-
import
|
|
7903
|
+
import fs28 from "fs";
|
|
7904
|
+
import path28 from "path";
|
|
7608
7905
|
var LOCKFILE_MAP = {
|
|
7609
7906
|
"pnpm-lock.yaml": "pnpm",
|
|
7610
7907
|
"package-lock.json": "npm",
|
|
@@ -7613,18 +7910,18 @@ var LOCKFILE_MAP = {
|
|
|
7613
7910
|
"bun.lock": "bun"
|
|
7614
7911
|
};
|
|
7615
7912
|
function detectPackageManager(cwd) {
|
|
7616
|
-
let dir =
|
|
7617
|
-
const root =
|
|
7913
|
+
let dir = path28.resolve(cwd);
|
|
7914
|
+
const root = path28.parse(dir).root;
|
|
7618
7915
|
while (dir !== root) {
|
|
7619
7916
|
for (const [lockfile, pm] of Object.entries(LOCKFILE_MAP)) {
|
|
7620
|
-
if (
|
|
7917
|
+
if (fs28.existsSync(path28.join(dir, lockfile))) {
|
|
7621
7918
|
return pm;
|
|
7622
7919
|
}
|
|
7623
7920
|
}
|
|
7624
|
-
const pkgPath =
|
|
7625
|
-
if (
|
|
7921
|
+
const pkgPath = path28.join(dir, "package.json");
|
|
7922
|
+
if (fs28.existsSync(pkgPath)) {
|
|
7626
7923
|
try {
|
|
7627
|
-
const pkg = JSON.parse(
|
|
7924
|
+
const pkg = JSON.parse(fs28.readFileSync(pkgPath, "utf-8"));
|
|
7628
7925
|
if (typeof pkg.packageManager === "string") {
|
|
7629
7926
|
const name = pkg.packageManager.split("@")[0];
|
|
7630
7927
|
if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
|
|
@@ -7634,7 +7931,7 @@ function detectPackageManager(cwd) {
|
|
|
7634
7931
|
} catch {
|
|
7635
7932
|
}
|
|
7636
7933
|
}
|
|
7637
|
-
dir =
|
|
7934
|
+
dir = path28.dirname(dir);
|
|
7638
7935
|
}
|
|
7639
7936
|
return "npm";
|
|
7640
7937
|
}
|
|
@@ -7665,9 +7962,9 @@ function createNextAppCommand(pm) {
|
|
|
7665
7962
|
|
|
7666
7963
|
// src/generators/post-generate.ts
|
|
7667
7964
|
function loadEnvFile(cwd) {
|
|
7668
|
-
const envPath =
|
|
7669
|
-
if (!
|
|
7670
|
-
const content =
|
|
7965
|
+
const envPath = path29.join(cwd, ".env.local");
|
|
7966
|
+
if (!fs29.existsSync(envPath)) return;
|
|
7967
|
+
const content = fs29.readFileSync(envPath, "utf-8");
|
|
7671
7968
|
for (const line of content.split("\n")) {
|
|
7672
7969
|
const trimmed = line.trim();
|
|
7673
7970
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -7697,10 +7994,10 @@ function runPmScript(pm, script, cwd) {
|
|
|
7697
7994
|
}
|
|
7698
7995
|
}
|
|
7699
7996
|
function hasPkgScript(cwd, script) {
|
|
7700
|
-
const pkgPath =
|
|
7701
|
-
if (!
|
|
7997
|
+
const pkgPath = path29.join(cwd, "package.json");
|
|
7998
|
+
if (!fs29.existsSync(pkgPath)) return false;
|
|
7702
7999
|
try {
|
|
7703
|
-
const pkg = JSON.parse(
|
|
8000
|
+
const pkg = JSON.parse(fs29.readFileSync(pkgPath, "utf-8"));
|
|
7704
8001
|
const scripts = pkg.scripts;
|
|
7705
8002
|
return !!scripts?.[script];
|
|
7706
8003
|
} catch {
|
|
@@ -7727,7 +8024,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7727
8024
|
console.log(ok ? " Database schema synced" : " Database push failed (run db:push manually)");
|
|
7728
8025
|
} else {
|
|
7729
8026
|
console.log("\n Running drizzle-kit push...");
|
|
7730
|
-
const drizzleBin =
|
|
8027
|
+
const drizzleBin = path29.join(cwd, "node_modules", ".bin", "drizzle-kit");
|
|
7731
8028
|
try {
|
|
7732
8029
|
execFileSync2(drizzleBin, ["push", "--force"], { cwd, stdio: "inherit" });
|
|
7733
8030
|
result.dbPush = "success";
|
|
@@ -7746,7 +8043,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7746
8043
|
result.lintFix = ok ? "success" : "failed";
|
|
7747
8044
|
console.log(ok ? " Code formatted" : " Lint fix had issues (run lint:fix manually)");
|
|
7748
8045
|
} else {
|
|
7749
|
-
const biomeBin =
|
|
8046
|
+
const biomeBin = path29.join(cwd, "node_modules", ".bin", "biome");
|
|
7750
8047
|
try {
|
|
7751
8048
|
execFileSync2(biomeBin, ["check", "--write", "."], { cwd, stdio: "pipe" });
|
|
7752
8049
|
result.lintFix = "success";
|
|
@@ -7769,7 +8066,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7769
8066
|
// src/commands/generate.ts
|
|
7770
8067
|
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
8068
|
async (schemaName, options) => {
|
|
7772
|
-
const cwd = options.cwd ?
|
|
8069
|
+
const cwd = options.cwd ? path30.resolve(options.cwd) : process.cwd();
|
|
7773
8070
|
console.log("\n BetterStart Generator\n");
|
|
7774
8071
|
let config;
|
|
7775
8072
|
try {
|
|
@@ -7778,7 +8075,7 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7778
8075
|
console.error(` Error loading config: ${err instanceof Error ? err.message : String(err)}`);
|
|
7779
8076
|
process.exit(1);
|
|
7780
8077
|
}
|
|
7781
|
-
const schemasDir =
|
|
8078
|
+
const schemasDir = path30.join(cwd, config.paths?.schemas ?? "./cms/schemas");
|
|
7782
8079
|
console.log(` Project root: ${cwd}`);
|
|
7783
8080
|
console.log(` Loading schema: ${schemaName}.json
|
|
7784
8081
|
`);
|
|
@@ -7790,7 +8087,7 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7790
8087
|
console.error(` ${err.message}`);
|
|
7791
8088
|
console.error(
|
|
7792
8089
|
`
|
|
7793
|
-
Create a schema file at: ${
|
|
8090
|
+
Create a schema file at: ${path30.join(schemasDir, `${schemaName}.json`)}`
|
|
7794
8091
|
);
|
|
7795
8092
|
} else {
|
|
7796
8093
|
console.error(
|
|
@@ -7883,8 +8180,8 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7883
8180
|
|
|
7884
8181
|
// src/commands/init.ts
|
|
7885
8182
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
7886
|
-
import
|
|
7887
|
-
import
|
|
8183
|
+
import fs40 from "fs";
|
|
8184
|
+
import path45 from "path";
|
|
7888
8185
|
import * as p4 from "@clack/prompts";
|
|
7889
8186
|
import { Command as Command3 } from "commander";
|
|
7890
8187
|
import pc2 from "picocolors";
|
|
@@ -8027,21 +8324,21 @@ async function promptProject(defaultName) {
|
|
|
8027
8324
|
}
|
|
8028
8325
|
|
|
8029
8326
|
// src/init/scaffolders/api-routes.ts
|
|
8030
|
-
import
|
|
8327
|
+
import path32 from "path";
|
|
8031
8328
|
|
|
8032
8329
|
// src/utils/fs.ts
|
|
8033
|
-
import
|
|
8034
|
-
import
|
|
8330
|
+
import fs30 from "fs";
|
|
8331
|
+
import path31 from "path";
|
|
8035
8332
|
import fse from "fs-extra";
|
|
8036
8333
|
function ensureDir(dirPath) {
|
|
8037
8334
|
fse.ensureDirSync(dirPath);
|
|
8038
8335
|
}
|
|
8039
8336
|
function safeWriteFile(filePath, content, force = false) {
|
|
8040
|
-
if (!force &&
|
|
8337
|
+
if (!force && fs30.existsSync(filePath)) {
|
|
8041
8338
|
return false;
|
|
8042
8339
|
}
|
|
8043
|
-
ensureDir(
|
|
8044
|
-
|
|
8340
|
+
ensureDir(path31.dirname(filePath));
|
|
8341
|
+
fs30.writeFileSync(filePath, content, "utf-8");
|
|
8045
8342
|
return true;
|
|
8046
8343
|
}
|
|
8047
8344
|
|
|
@@ -8135,21 +8432,21 @@ export async function POST(request: NextRequest) {
|
|
|
8135
8432
|
// src/init/scaffolders/api-routes.ts
|
|
8136
8433
|
function scaffoldApiRoutes({ cwd, config }) {
|
|
8137
8434
|
const created = [];
|
|
8138
|
-
const apiDir =
|
|
8435
|
+
const apiDir = path32.resolve(cwd, config.paths.api);
|
|
8139
8436
|
function write(relPath, content) {
|
|
8140
|
-
const fullPath =
|
|
8141
|
-
ensureDir(
|
|
8437
|
+
const fullPath = path32.join(apiDir, relPath);
|
|
8438
|
+
ensureDir(path32.dirname(fullPath));
|
|
8142
8439
|
if (safeWriteFile(fullPath, content)) {
|
|
8143
|
-
created.push(
|
|
8440
|
+
created.push(path32.join(config.paths.api, relPath));
|
|
8144
8441
|
}
|
|
8145
8442
|
}
|
|
8146
|
-
write(
|
|
8147
|
-
write(
|
|
8443
|
+
write(path32.join("auth", "[...all]", "route.ts"), authRouteTemplate());
|
|
8444
|
+
write(path32.join("upload", "route.ts"), uploadRouteTemplate());
|
|
8148
8445
|
return created;
|
|
8149
8446
|
}
|
|
8150
8447
|
|
|
8151
8448
|
// src/init/scaffolders/auth.ts
|
|
8152
|
-
import
|
|
8449
|
+
import path33 from "path";
|
|
8153
8450
|
|
|
8154
8451
|
// src/init/templates/lib/auth/auth.ts
|
|
8155
8452
|
function authTemplate() {
|
|
@@ -8268,11 +8565,11 @@ export async function requireRole(allowedRoles: UserRole[]): Promise<User> {
|
|
|
8268
8565
|
// src/init/scaffolders/auth.ts
|
|
8269
8566
|
function scaffoldAuth({ cwd, config }) {
|
|
8270
8567
|
const created = [];
|
|
8271
|
-
const authDir =
|
|
8568
|
+
const authDir = path33.resolve(cwd, config.paths.cms, "lib", "auth");
|
|
8272
8569
|
function write(filename, content) {
|
|
8273
|
-
const fullPath =
|
|
8570
|
+
const fullPath = path33.join(authDir, filename);
|
|
8274
8571
|
if (safeWriteFile(fullPath, content)) {
|
|
8275
|
-
created.push(
|
|
8572
|
+
created.push(path33.join(config.paths.cms, "lib", "auth", filename));
|
|
8276
8573
|
}
|
|
8277
8574
|
}
|
|
8278
8575
|
write("auth.ts", authTemplate());
|
|
@@ -8282,51 +8579,51 @@ function scaffoldAuth({ cwd, config }) {
|
|
|
8282
8579
|
}
|
|
8283
8580
|
|
|
8284
8581
|
// src/init/scaffolders/base.ts
|
|
8285
|
-
import
|
|
8286
|
-
import
|
|
8582
|
+
import fs31 from "fs";
|
|
8583
|
+
import path34 from "path";
|
|
8287
8584
|
function scaffoldBase({ cwd, config }) {
|
|
8288
8585
|
const created = [];
|
|
8289
8586
|
const cmsDirs = [
|
|
8290
8587
|
config.paths.cms,
|
|
8291
8588
|
config.paths.schemas,
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8589
|
+
path34.join(config.paths.cms, "db"),
|
|
8590
|
+
path34.join(config.paths.cms, "db", "migrations"),
|
|
8591
|
+
path34.join(config.paths.cms, "lib", "auth"),
|
|
8592
|
+
path34.join(config.paths.cms, "lib", "actions"),
|
|
8593
|
+
path34.join(config.paths.cms, "lib", "cache"),
|
|
8594
|
+
path34.join(config.paths.cms, "lib", "markdown"),
|
|
8595
|
+
path34.join(config.paths.cms, "lib", "emails"),
|
|
8596
|
+
path34.join(config.paths.cms, "lib"),
|
|
8597
|
+
path34.join(config.paths.cms, "hooks"),
|
|
8598
|
+
path34.join(config.paths.cms, "components", "ui"),
|
|
8599
|
+
path34.join(config.paths.cms, "components", "form"),
|
|
8600
|
+
path34.join(config.paths.cms, "components", "data-table"),
|
|
8601
|
+
path34.join(config.paths.cms, "components", "layout"),
|
|
8602
|
+
path34.join(config.paths.cms, "components", "shared"),
|
|
8603
|
+
path34.join(config.paths.cms, "types"),
|
|
8604
|
+
path34.join(config.paths.cms, "utils"),
|
|
8605
|
+
path34.join(config.paths.cms, "data")
|
|
8309
8606
|
];
|
|
8310
8607
|
for (const dir of cmsDirs) {
|
|
8311
|
-
ensureDir(
|
|
8608
|
+
ensureDir(path34.resolve(cwd, dir));
|
|
8312
8609
|
}
|
|
8313
8610
|
const appDirs = [config.paths.pages, config.paths.login, config.paths.api];
|
|
8314
8611
|
for (const dir of appDirs) {
|
|
8315
|
-
ensureDir(
|
|
8612
|
+
ensureDir(path34.resolve(cwd, dir));
|
|
8316
8613
|
}
|
|
8317
8614
|
const configContent = generateConfigFile(config);
|
|
8318
|
-
if (safeWriteFile(
|
|
8615
|
+
if (safeWriteFile(path34.resolve(cwd, "cms.config.ts"), configContent)) {
|
|
8319
8616
|
created.push("cms.config.ts");
|
|
8320
8617
|
}
|
|
8321
8618
|
const cmsDoc = generateCmsDoc(config, {});
|
|
8322
|
-
if (safeWriteFile(
|
|
8619
|
+
if (safeWriteFile(path34.resolve(cwd, "CMS.md"), cmsDoc)) {
|
|
8323
8620
|
created.push("CMS.md");
|
|
8324
8621
|
}
|
|
8325
8622
|
return created;
|
|
8326
8623
|
}
|
|
8327
8624
|
function regenerateCmsDoc(cwd, config, options) {
|
|
8328
8625
|
const content = generateCmsDoc(config, options);
|
|
8329
|
-
|
|
8626
|
+
fs31.writeFileSync(path34.resolve(cwd, "CMS.md"), content, "utf-8");
|
|
8330
8627
|
}
|
|
8331
8628
|
function generateConfigFile(config) {
|
|
8332
8629
|
return `import { defineConfig } from '@betterstart/cli'
|
|
@@ -8480,8 +8777,8 @@ Edit \`cms.config.ts\` to customize paths, database provider, and features.`);
|
|
|
8480
8777
|
}
|
|
8481
8778
|
|
|
8482
8779
|
// src/init/scaffolders/biome.ts
|
|
8483
|
-
import
|
|
8484
|
-
import
|
|
8780
|
+
import fs32 from "fs";
|
|
8781
|
+
import path35 from "path";
|
|
8485
8782
|
function scaffoldBiome(cwd, linter) {
|
|
8486
8783
|
if (linter.type !== "none") {
|
|
8487
8784
|
return {
|
|
@@ -8489,8 +8786,8 @@ function scaffoldBiome(cwd, linter) {
|
|
|
8489
8786
|
skippedReason: `${linter.type} already configured (${linter.configFile})`
|
|
8490
8787
|
};
|
|
8491
8788
|
}
|
|
8492
|
-
const configPath =
|
|
8493
|
-
if (
|
|
8789
|
+
const configPath = path35.join(cwd, "biome.json");
|
|
8790
|
+
if (fs32.existsSync(configPath)) {
|
|
8494
8791
|
return { installed: false, skippedReason: "biome.json already exists" };
|
|
8495
8792
|
}
|
|
8496
8793
|
const config = {
|
|
@@ -8541,52 +8838,52 @@ function scaffoldBiome(cwd, linter) {
|
|
|
8541
8838
|
]
|
|
8542
8839
|
}
|
|
8543
8840
|
};
|
|
8544
|
-
|
|
8841
|
+
fs32.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
8545
8842
|
`, "utf-8");
|
|
8546
8843
|
return { installed: true, skippedReason: null };
|
|
8547
8844
|
}
|
|
8548
8845
|
|
|
8549
8846
|
// src/init/scaffolders/components.ts
|
|
8550
|
-
import
|
|
8551
|
-
import
|
|
8847
|
+
import path37 from "path";
|
|
8848
|
+
import fs34 from "fs-extra";
|
|
8552
8849
|
|
|
8553
8850
|
// src/utils/detect.ts
|
|
8554
|
-
import
|
|
8555
|
-
import
|
|
8851
|
+
import fs33 from "fs";
|
|
8852
|
+
import path36 from "path";
|
|
8556
8853
|
var NEXT_CONFIG_FILES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
8557
8854
|
function detectProjectName(cwd) {
|
|
8558
|
-
const pkgPath =
|
|
8559
|
-
if (
|
|
8855
|
+
const pkgPath = path36.join(cwd, "package.json");
|
|
8856
|
+
if (fs33.existsSync(pkgPath)) {
|
|
8560
8857
|
try {
|
|
8561
|
-
const pkg = JSON.parse(
|
|
8858
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
8562
8859
|
if (typeof pkg.name === "string" && pkg.name.length > 0) {
|
|
8563
8860
|
return formatProjectName(pkg.name);
|
|
8564
8861
|
}
|
|
8565
8862
|
} catch {
|
|
8566
8863
|
}
|
|
8567
8864
|
}
|
|
8568
|
-
return formatProjectName(
|
|
8865
|
+
return formatProjectName(path36.basename(cwd));
|
|
8569
8866
|
}
|
|
8570
8867
|
function formatProjectName(name) {
|
|
8571
8868
|
const base = name.includes("/") ? name.split("/").pop() : name;
|
|
8572
8869
|
return base.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
8573
8870
|
}
|
|
8574
8871
|
function detectProject(cwd) {
|
|
8575
|
-
const isExisting = NEXT_CONFIG_FILES.some((f) =>
|
|
8576
|
-
const hasSrcDir =
|
|
8577
|
-
const hasTypeScript =
|
|
8872
|
+
const isExisting = NEXT_CONFIG_FILES.some((f) => fs33.existsSync(path36.join(cwd, f)));
|
|
8873
|
+
const hasSrcDir = fs33.existsSync(path36.join(cwd, "src"));
|
|
8874
|
+
const hasTypeScript = fs33.existsSync(path36.join(cwd, "tsconfig.json")) || fs33.existsSync(path36.join(cwd, "tsconfig.app.json"));
|
|
8578
8875
|
const hasTailwind = detectTailwind(cwd);
|
|
8579
8876
|
const linter = detectLinter(cwd);
|
|
8580
8877
|
const conflicts = [];
|
|
8581
8878
|
if (isExisting) {
|
|
8582
|
-
if (
|
|
8879
|
+
if (fs33.existsSync(path36.join(cwd, "cms"))) {
|
|
8583
8880
|
conflicts.push("cms/ directory already exists");
|
|
8584
8881
|
}
|
|
8585
|
-
if (
|
|
8882
|
+
if (fs33.existsSync(path36.join(cwd, "cms.config.ts"))) {
|
|
8586
8883
|
conflicts.push("cms.config.ts already exists");
|
|
8587
8884
|
}
|
|
8588
8885
|
const appBase = hasSrcDir ? "src/app" : "app";
|
|
8589
|
-
if (
|
|
8886
|
+
if (fs33.existsSync(path36.join(cwd, appBase, "(cms)"))) {
|
|
8590
8887
|
conflicts.push(`${appBase}/(cms)/ route group already exists`);
|
|
8591
8888
|
}
|
|
8592
8889
|
if (hasTsconfigCmsAliases(cwd)) {
|
|
@@ -8613,19 +8910,19 @@ var ESLINT_CONFIG_FILES = [
|
|
|
8613
8910
|
];
|
|
8614
8911
|
function detectLinter(cwd) {
|
|
8615
8912
|
for (const f of BIOME_CONFIG_FILES) {
|
|
8616
|
-
if (
|
|
8913
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8617
8914
|
return { type: "biome", configFile: f };
|
|
8618
8915
|
}
|
|
8619
8916
|
}
|
|
8620
8917
|
for (const f of ESLINT_CONFIG_FILES) {
|
|
8621
|
-
if (
|
|
8918
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8622
8919
|
return { type: "eslint", configFile: f };
|
|
8623
8920
|
}
|
|
8624
8921
|
}
|
|
8625
|
-
const pkgPath =
|
|
8626
|
-
if (
|
|
8922
|
+
const pkgPath = path36.join(cwd, "package.json");
|
|
8923
|
+
if (fs33.existsSync(pkgPath)) {
|
|
8627
8924
|
try {
|
|
8628
|
-
const pkg = JSON.parse(
|
|
8925
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
8629
8926
|
if (pkg.eslintConfig) {
|
|
8630
8927
|
return { type: "eslint", configFile: "package.json (eslintConfig)" };
|
|
8631
8928
|
}
|
|
@@ -8636,14 +8933,14 @@ function detectLinter(cwd) {
|
|
|
8636
8933
|
}
|
|
8637
8934
|
function detectTailwind(cwd) {
|
|
8638
8935
|
const cssFiles = ["globals.css", "app.css", "index.css"].flatMap((f) => [
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8936
|
+
path36.join(cwd, "src", "app", f),
|
|
8937
|
+
path36.join(cwd, "app", f),
|
|
8938
|
+
path36.join(cwd, "src", f),
|
|
8939
|
+
path36.join(cwd, f)
|
|
8643
8940
|
]);
|
|
8644
8941
|
for (const cssFile of cssFiles) {
|
|
8645
|
-
if (
|
|
8646
|
-
const content =
|
|
8942
|
+
if (fs33.existsSync(cssFile)) {
|
|
8943
|
+
const content = fs33.readFileSync(cssFile, "utf-8");
|
|
8647
8944
|
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'") || content.includes("@theme")) {
|
|
8648
8945
|
return true;
|
|
8649
8946
|
}
|
|
@@ -8651,8 +8948,8 @@ function detectTailwind(cwd) {
|
|
|
8651
8948
|
}
|
|
8652
8949
|
const postcssFiles = ["postcss.config.js", "postcss.config.mjs", "postcss.config.cjs"];
|
|
8653
8950
|
for (const f of postcssFiles) {
|
|
8654
|
-
if (
|
|
8655
|
-
const content =
|
|
8951
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8952
|
+
const content = fs33.readFileSync(path36.join(cwd, f), "utf-8");
|
|
8656
8953
|
if (content.includes("tailwindcss") || content.includes("@tailwindcss")) {
|
|
8657
8954
|
return true;
|
|
8658
8955
|
}
|
|
@@ -8661,20 +8958,20 @@ function detectTailwind(cwd) {
|
|
|
8661
8958
|
return false;
|
|
8662
8959
|
}
|
|
8663
8960
|
function hasTsconfigCmsAliases(cwd) {
|
|
8664
|
-
const tsconfigPath =
|
|
8665
|
-
if (!
|
|
8961
|
+
const tsconfigPath = path36.join(cwd, "tsconfig.json");
|
|
8962
|
+
if (!fs33.existsSync(tsconfigPath)) return false;
|
|
8666
8963
|
try {
|
|
8667
|
-
const content =
|
|
8964
|
+
const content = fs33.readFileSync(tsconfigPath, "utf-8");
|
|
8668
8965
|
return content.includes("@cms/");
|
|
8669
8966
|
} catch {
|
|
8670
8967
|
return false;
|
|
8671
8968
|
}
|
|
8672
8969
|
}
|
|
8673
8970
|
function hasEnvBetterstartVars(cwd) {
|
|
8674
|
-
const envPath =
|
|
8675
|
-
if (!
|
|
8971
|
+
const envPath = path36.join(cwd, ".env.local");
|
|
8972
|
+
if (!fs33.existsSync(envPath)) return false;
|
|
8676
8973
|
try {
|
|
8677
|
-
const content =
|
|
8974
|
+
const content = fs33.readFileSync(envPath, "utf-8");
|
|
8678
8975
|
return content.includes("BETTERSTART_");
|
|
8679
8976
|
} catch {
|
|
8680
8977
|
return false;
|
|
@@ -11373,12 +11670,12 @@ export function addToMailchimpAudience(email: string): void {
|
|
|
11373
11670
|
|
|
11374
11671
|
// src/init/scaffolders/components.ts
|
|
11375
11672
|
function scaffoldComponents({ cwd, config }) {
|
|
11376
|
-
const cms =
|
|
11673
|
+
const cms = path37.resolve(cwd, config.paths.cms);
|
|
11377
11674
|
const created = [];
|
|
11378
11675
|
function write(relPath, content) {
|
|
11379
|
-
const fullPath =
|
|
11676
|
+
const fullPath = path37.join(cms, relPath);
|
|
11380
11677
|
if (safeWriteFile(fullPath, content)) {
|
|
11381
|
-
created.push(
|
|
11678
|
+
created.push(path37.join(config.paths.cms, relPath));
|
|
11382
11679
|
}
|
|
11383
11680
|
}
|
|
11384
11681
|
write("cms-globals.css", cmsGlobalsCssTemplate());
|
|
@@ -11427,18 +11724,18 @@ function scaffoldComponents({ cwd, config }) {
|
|
|
11427
11724
|
}
|
|
11428
11725
|
function copyUiTemplates(cwd, config) {
|
|
11429
11726
|
const created = [];
|
|
11430
|
-
const destDir =
|
|
11727
|
+
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui");
|
|
11431
11728
|
const cliRoot = findCliRoot();
|
|
11432
|
-
const srcDir =
|
|
11433
|
-
if (!
|
|
11729
|
+
const srcDir = path37.join(cliRoot, "templates", "ui");
|
|
11730
|
+
if (!fs34.existsSync(srcDir)) {
|
|
11434
11731
|
return created;
|
|
11435
11732
|
}
|
|
11436
|
-
const files =
|
|
11733
|
+
const files = fs34.readdirSync(srcDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
|
|
11437
11734
|
for (const file of files) {
|
|
11438
|
-
const destPath =
|
|
11439
|
-
if (!
|
|
11440
|
-
|
|
11441
|
-
created.push(
|
|
11735
|
+
const destPath = path37.join(destDir, file);
|
|
11736
|
+
if (!fs34.existsSync(destPath)) {
|
|
11737
|
+
fs34.copyFileSync(path37.join(srcDir, file), destPath);
|
|
11738
|
+
created.push(path37.join(config.paths.cms, "components", "ui", file));
|
|
11442
11739
|
}
|
|
11443
11740
|
}
|
|
11444
11741
|
return created;
|
|
@@ -11446,25 +11743,25 @@ function copyUiTemplates(cwd, config) {
|
|
|
11446
11743
|
function copyTiptapTemplates(cwd, config) {
|
|
11447
11744
|
const created = [];
|
|
11448
11745
|
const cliRoot = findCliRoot();
|
|
11449
|
-
const srcDir =
|
|
11450
|
-
const destDir =
|
|
11451
|
-
if (!
|
|
11746
|
+
const srcDir = path37.join(cliRoot, "templates", "tiptap");
|
|
11747
|
+
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui", "tiptap");
|
|
11748
|
+
if (!fs34.existsSync(srcDir)) {
|
|
11452
11749
|
return created;
|
|
11453
11750
|
}
|
|
11454
11751
|
copyDirRecursive(srcDir, destDir, config.paths.cms, created);
|
|
11455
11752
|
return created;
|
|
11456
11753
|
}
|
|
11457
11754
|
function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
11458
|
-
|
|
11459
|
-
const entries =
|
|
11755
|
+
fs34.ensureDirSync(dest);
|
|
11756
|
+
const entries = fs34.readdirSync(src, { withFileTypes: true });
|
|
11460
11757
|
for (const entry of entries) {
|
|
11461
|
-
const srcPath =
|
|
11462
|
-
const destPath =
|
|
11758
|
+
const srcPath = path37.join(src, entry.name);
|
|
11759
|
+
const destPath = path37.join(dest, entry.name);
|
|
11463
11760
|
if (entry.isDirectory()) {
|
|
11464
11761
|
copyDirRecursive(srcPath, destPath, cmsPrefix, created);
|
|
11465
|
-
} else if (!
|
|
11466
|
-
|
|
11467
|
-
const relFromCms =
|
|
11762
|
+
} else if (!fs34.existsSync(destPath)) {
|
|
11763
|
+
fs34.copyFileSync(srcPath, destPath);
|
|
11764
|
+
const relFromCms = path37.relative(path37.resolve(dest, "..", "..", "..", ".."), destPath);
|
|
11468
11765
|
created.push(relFromCms);
|
|
11469
11766
|
}
|
|
11470
11767
|
}
|
|
@@ -11472,35 +11769,35 @@ function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
|
11472
11769
|
function copySchemaMetaschema(cwd, config) {
|
|
11473
11770
|
const created = [];
|
|
11474
11771
|
const cliRoot = findCliRoot();
|
|
11475
|
-
const srcPath =
|
|
11476
|
-
const destPath =
|
|
11477
|
-
if (
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
created.push(
|
|
11772
|
+
const srcPath = path37.join(cliRoot, "templates", "schema.json");
|
|
11773
|
+
const destPath = path37.resolve(cwd, config.paths.schemas, "schema.json");
|
|
11774
|
+
if (fs34.existsSync(srcPath) && !fs34.existsSync(destPath)) {
|
|
11775
|
+
fs34.ensureDirSync(path37.dirname(destPath));
|
|
11776
|
+
fs34.copyFileSync(srcPath, destPath);
|
|
11777
|
+
created.push(path37.join(config.paths.schemas, "schema.json"));
|
|
11481
11778
|
}
|
|
11482
11779
|
return created;
|
|
11483
11780
|
}
|
|
11484
11781
|
function findCliRoot() {
|
|
11485
11782
|
let dir = new URL(".", import.meta.url).pathname;
|
|
11486
11783
|
for (let i = 0; i < 5; i++) {
|
|
11487
|
-
const pkgPath =
|
|
11488
|
-
if (
|
|
11784
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
11785
|
+
if (fs34.existsSync(pkgPath)) {
|
|
11489
11786
|
try {
|
|
11490
|
-
const pkg = JSON.parse(
|
|
11787
|
+
const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf-8"));
|
|
11491
11788
|
if (pkg.name === "@betterstart/cli") {
|
|
11492
11789
|
return dir;
|
|
11493
11790
|
}
|
|
11494
11791
|
} catch {
|
|
11495
11792
|
}
|
|
11496
11793
|
}
|
|
11497
|
-
dir =
|
|
11794
|
+
dir = path37.dirname(dir);
|
|
11498
11795
|
}
|
|
11499
|
-
return
|
|
11796
|
+
return path37.resolve(new URL(".", import.meta.url).pathname, "..", "..");
|
|
11500
11797
|
}
|
|
11501
11798
|
|
|
11502
11799
|
// src/init/scaffolders/database.ts
|
|
11503
|
-
import
|
|
11800
|
+
import path38 from "path";
|
|
11504
11801
|
|
|
11505
11802
|
// src/init/templates/db/client.ts
|
|
11506
11803
|
function dbClientTemplate() {
|
|
@@ -11611,16 +11908,16 @@ export const formSettings = pgTable(
|
|
|
11611
11908
|
// src/init/scaffolders/database.ts
|
|
11612
11909
|
function scaffoldDatabase({ cwd, config }) {
|
|
11613
11910
|
const created = [];
|
|
11614
|
-
const dbDir =
|
|
11911
|
+
const dbDir = path38.resolve(cwd, config.paths.cms, "db");
|
|
11615
11912
|
function write(filename, content) {
|
|
11616
|
-
const fullPath =
|
|
11913
|
+
const fullPath = path38.join(dbDir, filename);
|
|
11617
11914
|
if (safeWriteFile(fullPath, content)) {
|
|
11618
|
-
created.push(
|
|
11915
|
+
created.push(path38.join(config.paths.cms, "db", filename));
|
|
11619
11916
|
}
|
|
11620
11917
|
}
|
|
11621
11918
|
write("client.ts", dbClientTemplate());
|
|
11622
11919
|
write("schema.ts", dbSchemaTemplate());
|
|
11623
|
-
const drizzleConfigPath =
|
|
11920
|
+
const drizzleConfigPath = path38.resolve(cwd, "drizzle.config.ts");
|
|
11624
11921
|
if (safeWriteFile(drizzleConfigPath, drizzleConfigTemplate())) {
|
|
11625
11922
|
created.push("drizzle.config.ts");
|
|
11626
11923
|
}
|
|
@@ -11792,11 +12089,11 @@ import { existsSync, readFileSync } from "fs";
|
|
|
11792
12089
|
import { join } from "path";
|
|
11793
12090
|
|
|
11794
12091
|
// src/utils/env.ts
|
|
11795
|
-
import
|
|
11796
|
-
import
|
|
12092
|
+
import fs35 from "fs";
|
|
12093
|
+
import path39 from "path";
|
|
11797
12094
|
function appendEnvVars(cwd, sections, overwrite) {
|
|
11798
|
-
const envPath =
|
|
11799
|
-
let existing =
|
|
12095
|
+
const envPath = path39.join(cwd, ".env.local");
|
|
12096
|
+
let existing = fs35.existsSync(envPath) ? fs35.readFileSync(envPath, "utf-8") : "";
|
|
11800
12097
|
const existingKeys = new Set(
|
|
11801
12098
|
existing.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#")).map((line) => line.split("=")[0]?.trim()).filter(Boolean)
|
|
11802
12099
|
);
|
|
@@ -11840,7 +12137,7 @@ function appendEnvVars(cwd, sections, overwrite) {
|
|
|
11840
12137
|
const header = existing.trim() ? "" : "# ============================================\n# BetterStart CMS\n# ============================================\n";
|
|
11841
12138
|
const content = existing.trim() ? `${existing.trimEnd()}
|
|
11842
12139
|
${lines.join("\n")}` : header + lines.join("\n");
|
|
11843
|
-
|
|
12140
|
+
fs35.writeFileSync(envPath, content);
|
|
11844
12141
|
}
|
|
11845
12142
|
return { added, skipped, updated };
|
|
11846
12143
|
}
|
|
@@ -11919,7 +12216,7 @@ function scaffoldEnv(cwd, options) {
|
|
|
11919
12216
|
}
|
|
11920
12217
|
|
|
11921
12218
|
// src/init/scaffolders/layout.ts
|
|
11922
|
-
import
|
|
12219
|
+
import path40 from "path";
|
|
11923
12220
|
|
|
11924
12221
|
// src/init/templates/pages/authenticated-layout.ts
|
|
11925
12222
|
function authenticatedLayoutTemplate() {
|
|
@@ -12806,30 +13103,30 @@ export function UsersTable<TValue>({ columns }: UsersTableProps<TValue>) {
|
|
|
12806
13103
|
function scaffoldLayout({ cwd, config }) {
|
|
12807
13104
|
const created = [];
|
|
12808
13105
|
function write(relPath, content) {
|
|
12809
|
-
const fullPath =
|
|
12810
|
-
ensureDir(
|
|
13106
|
+
const fullPath = path40.resolve(cwd, relPath);
|
|
13107
|
+
ensureDir(path40.dirname(fullPath));
|
|
12811
13108
|
if (safeWriteFile(fullPath, content)) {
|
|
12812
13109
|
created.push(relPath);
|
|
12813
13110
|
}
|
|
12814
13111
|
}
|
|
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(
|
|
13112
|
+
const cmsDir = path40.dirname(config.paths.pages);
|
|
13113
|
+
write(path40.join(cmsDir, "layout.tsx"), cmsLayoutTemplate());
|
|
13114
|
+
write(path40.join(config.paths.pages, "layout.tsx"), authenticatedLayoutTemplate());
|
|
13115
|
+
write(path40.join(config.paths.login, "page.tsx"), loginPageTemplate());
|
|
13116
|
+
write(path40.join(config.paths.login, "login-form.tsx"), loginFormTemplate());
|
|
13117
|
+
write(path40.join(config.paths.pages, "page.tsx"), dashboardPageTemplate());
|
|
13118
|
+
const usersDir = path40.join(config.paths.pages, "users");
|
|
13119
|
+
write(path40.join(usersDir, "page.tsx"), usersPageTemplate());
|
|
13120
|
+
write(path40.join(usersDir, "users-table.tsx"), usersTableTemplate());
|
|
13121
|
+
write(path40.join(usersDir, "columns.tsx"), usersColumnsTemplate());
|
|
13122
|
+
write(path40.join(usersDir, "create-user-dialog.tsx"), createUserDialogTemplate());
|
|
13123
|
+
write(path40.join(usersDir, "edit-role-dialog.tsx"), editRoleDialogTemplate());
|
|
12827
13124
|
return created;
|
|
12828
13125
|
}
|
|
12829
13126
|
|
|
12830
13127
|
// src/init/scaffolders/preset.ts
|
|
12831
|
-
import
|
|
12832
|
-
import
|
|
13128
|
+
import fs36 from "fs";
|
|
13129
|
+
import path41 from "path";
|
|
12833
13130
|
|
|
12834
13131
|
// src/init/templates/presets/blog-categories.ts
|
|
12835
13132
|
function blogCategoriesSchema() {
|
|
@@ -13234,15 +13531,15 @@ function scaffoldPreset({
|
|
|
13234
13531
|
generatedFiles: [],
|
|
13235
13532
|
errors: []
|
|
13236
13533
|
};
|
|
13237
|
-
const schemasDir =
|
|
13534
|
+
const schemasDir = path41.join(cwd, config.paths?.schemas ?? "./cms/schemas");
|
|
13238
13535
|
const presetSchemas = getPresetSchemas(preset);
|
|
13239
13536
|
for (const ps of presetSchemas) {
|
|
13240
|
-
const filePath =
|
|
13241
|
-
const dir =
|
|
13242
|
-
if (!
|
|
13243
|
-
|
|
13537
|
+
const filePath = path41.join(schemasDir, ps.filename);
|
|
13538
|
+
const dir = path41.dirname(filePath);
|
|
13539
|
+
if (!fs36.existsSync(dir)) {
|
|
13540
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
13244
13541
|
}
|
|
13245
|
-
|
|
13542
|
+
fs36.writeFileSync(filePath, ps.content, "utf-8");
|
|
13246
13543
|
result.schemas.push(ps.filename);
|
|
13247
13544
|
}
|
|
13248
13545
|
for (const ps of presetSchemas) {
|
|
@@ -13283,8 +13580,8 @@ function scaffoldPreset({
|
|
|
13283
13580
|
}
|
|
13284
13581
|
|
|
13285
13582
|
// src/init/scaffolders/tailwind.ts
|
|
13286
|
-
import
|
|
13287
|
-
import
|
|
13583
|
+
import fs37 from "fs";
|
|
13584
|
+
import path42 from "path";
|
|
13288
13585
|
var SOURCE_LINES = ['@source "../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
13289
13586
|
var SOURCE_LINES_SRC = ['@source "../../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
13290
13587
|
var CMS_THEME_BLOCK = `
|
|
@@ -13339,8 +13636,8 @@ function findMainCss(cwd) {
|
|
|
13339
13636
|
"globals.css"
|
|
13340
13637
|
];
|
|
13341
13638
|
for (const candidate of candidates) {
|
|
13342
|
-
const filePath =
|
|
13343
|
-
if (
|
|
13639
|
+
const filePath = path42.join(cwd, candidate);
|
|
13640
|
+
if (fs37.existsSync(filePath)) {
|
|
13344
13641
|
return filePath;
|
|
13345
13642
|
}
|
|
13346
13643
|
}
|
|
@@ -13351,7 +13648,7 @@ function scaffoldTailwind(cwd, hasSrcDir) {
|
|
|
13351
13648
|
if (!cssFile) {
|
|
13352
13649
|
return { file: null, appended: false };
|
|
13353
13650
|
}
|
|
13354
|
-
let content =
|
|
13651
|
+
let content = fs37.readFileSync(cssFile, "utf-8");
|
|
13355
13652
|
let changed = false;
|
|
13356
13653
|
const sourceLines = hasSrcDir ? SOURCE_LINES_SRC : SOURCE_LINES;
|
|
13357
13654
|
const missingLines = sourceLines.filter((sl) => !content.includes(sl));
|
|
@@ -13403,14 +13700,14 @@ ${CMS_THEME_BLOCK}
|
|
|
13403
13700
|
}
|
|
13404
13701
|
}
|
|
13405
13702
|
if (changed) {
|
|
13406
|
-
|
|
13703
|
+
fs37.writeFileSync(cssFile, content, "utf-8");
|
|
13407
13704
|
}
|
|
13408
13705
|
return { file: cssFile, appended: changed };
|
|
13409
13706
|
}
|
|
13410
13707
|
|
|
13411
13708
|
// src/init/scaffolders/tsconfig.ts
|
|
13412
|
-
import
|
|
13413
|
-
import
|
|
13709
|
+
import fs38 from "fs";
|
|
13710
|
+
import path43 from "path";
|
|
13414
13711
|
function stripJsonComments(input) {
|
|
13415
13712
|
let result = "";
|
|
13416
13713
|
let i = 0;
|
|
@@ -13460,14 +13757,14 @@ var CMS_PATH_ALIASES = {
|
|
|
13460
13757
|
"@cms/cache/*": ["./cms/lib/cache/*"]
|
|
13461
13758
|
};
|
|
13462
13759
|
function scaffoldTsconfig(cwd) {
|
|
13463
|
-
const tsconfigPath =
|
|
13760
|
+
const tsconfigPath = path43.join(cwd, "tsconfig.json");
|
|
13464
13761
|
const added = [];
|
|
13465
13762
|
const skipped = [];
|
|
13466
|
-
if (!
|
|
13763
|
+
if (!fs38.existsSync(tsconfigPath)) {
|
|
13467
13764
|
skipped.push("tsconfig.json not found");
|
|
13468
13765
|
return { added, skipped };
|
|
13469
13766
|
}
|
|
13470
|
-
const raw =
|
|
13767
|
+
const raw = fs38.readFileSync(tsconfigPath, "utf-8");
|
|
13471
13768
|
const stripped = stripJsonComments(raw).replace(/,\s*([\]}])/g, "$1");
|
|
13472
13769
|
let tsconfig;
|
|
13473
13770
|
try {
|
|
@@ -13488,14 +13785,14 @@ function scaffoldTsconfig(cwd) {
|
|
|
13488
13785
|
}
|
|
13489
13786
|
compilerOptions.paths = paths;
|
|
13490
13787
|
tsconfig.compilerOptions = compilerOptions;
|
|
13491
|
-
|
|
13788
|
+
fs38.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
13492
13789
|
`, "utf-8");
|
|
13493
13790
|
return { added, skipped };
|
|
13494
13791
|
}
|
|
13495
13792
|
|
|
13496
13793
|
// src/commands/seed.ts
|
|
13497
|
-
import
|
|
13498
|
-
import
|
|
13794
|
+
import fs39 from "fs";
|
|
13795
|
+
import path44 from "path";
|
|
13499
13796
|
import * as clack from "@clack/prompts";
|
|
13500
13797
|
import { Command as Command2 } from "commander";
|
|
13501
13798
|
function buildSeedScript() {
|
|
@@ -13614,7 +13911,7 @@ main().catch((err) => {
|
|
|
13614
13911
|
`;
|
|
13615
13912
|
}
|
|
13616
13913
|
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 ?
|
|
13914
|
+
const cwd = options.cwd ? path44.resolve(options.cwd) : process.cwd();
|
|
13618
13915
|
clack.intro("BetterStart Seed");
|
|
13619
13916
|
let config;
|
|
13620
13917
|
try {
|
|
@@ -13654,14 +13951,14 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13654
13951
|
clack.cancel("Cancelled.");
|
|
13655
13952
|
process.exit(0);
|
|
13656
13953
|
}
|
|
13657
|
-
const scriptsDir =
|
|
13658
|
-
const seedPath =
|
|
13659
|
-
if (!
|
|
13660
|
-
|
|
13954
|
+
const scriptsDir = path44.join(cwd, cmsDir, "scripts");
|
|
13955
|
+
const seedPath = path44.join(scriptsDir, "seed.ts");
|
|
13956
|
+
if (!fs39.existsSync(scriptsDir)) {
|
|
13957
|
+
fs39.mkdirSync(scriptsDir, { recursive: true });
|
|
13661
13958
|
}
|
|
13662
|
-
|
|
13959
|
+
fs39.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
13663
13960
|
const { execFile } = await import("child_process");
|
|
13664
|
-
const tsxBin =
|
|
13961
|
+
const tsxBin = path44.join(cwd, "node_modules", ".bin", "tsx");
|
|
13665
13962
|
const runSeed2 = (overwrite) => new Promise((resolve, reject) => {
|
|
13666
13963
|
execFile(
|
|
13667
13964
|
tsxBin,
|
|
@@ -13700,7 +13997,7 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13700
13997
|
if (clack.isCancel(overwrite) || !overwrite) {
|
|
13701
13998
|
clack.cancel("Seed cancelled.");
|
|
13702
13999
|
try {
|
|
13703
|
-
|
|
14000
|
+
fs39.unlinkSync(seedPath);
|
|
13704
14001
|
} catch {
|
|
13705
14002
|
}
|
|
13706
14003
|
process.exit(0);
|
|
@@ -13717,15 +14014,15 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13717
14014
|
clack.log.error(errMsg);
|
|
13718
14015
|
clack.log.info("You can run the seed script manually:");
|
|
13719
14016
|
clack.log.info(
|
|
13720
|
-
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${
|
|
14017
|
+
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${path44.relative(cwd, seedPath)}`
|
|
13721
14018
|
);
|
|
13722
14019
|
clack.outro("");
|
|
13723
14020
|
process.exit(1);
|
|
13724
14021
|
}
|
|
13725
14022
|
try {
|
|
13726
|
-
|
|
13727
|
-
if (
|
|
13728
|
-
|
|
14023
|
+
fs39.unlinkSync(seedPath);
|
|
14024
|
+
if (fs39.existsSync(scriptsDir) && fs39.readdirSync(scriptsDir).length === 0) {
|
|
14025
|
+
fs39.rmdirSync(scriptsDir);
|
|
13729
14026
|
}
|
|
13730
14027
|
} catch {
|
|
13731
14028
|
}
|
|
@@ -13756,16 +14053,16 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13756
14053
|
const nukeFiles = ["cms.config.ts", "CMS.md", "drizzle.config.ts"];
|
|
13757
14054
|
let nuked = 0;
|
|
13758
14055
|
for (const dir of nukeDirs) {
|
|
13759
|
-
const fullPath =
|
|
13760
|
-
if (
|
|
13761
|
-
|
|
14056
|
+
const fullPath = path45.resolve(cwd, dir);
|
|
14057
|
+
if (fs40.existsSync(fullPath)) {
|
|
14058
|
+
fs40.rmSync(fullPath, { recursive: true, force: true });
|
|
13762
14059
|
nuked++;
|
|
13763
14060
|
}
|
|
13764
14061
|
}
|
|
13765
14062
|
for (const file of nukeFiles) {
|
|
13766
|
-
const fullPath =
|
|
13767
|
-
if (
|
|
13768
|
-
|
|
14063
|
+
const fullPath = path45.resolve(cwd, file);
|
|
14064
|
+
if (fs40.existsSync(fullPath)) {
|
|
14065
|
+
fs40.unlinkSync(fullPath);
|
|
13769
14066
|
nuked++;
|
|
13770
14067
|
}
|
|
13771
14068
|
}
|
|
@@ -13812,7 +14109,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13812
14109
|
}
|
|
13813
14110
|
pm = pmChoice;
|
|
13814
14111
|
}
|
|
13815
|
-
const displayName = projectPrompt.projectName === "." ?
|
|
14112
|
+
const displayName = projectPrompt.projectName === "." ? path45.basename(cwd) : projectPrompt.projectName;
|
|
13816
14113
|
const { bin, prefix } = createNextAppCommand(pm);
|
|
13817
14114
|
const cnaArgs = [
|
|
13818
14115
|
...prefix,
|
|
@@ -13844,10 +14141,10 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13844
14141
|
);
|
|
13845
14142
|
process.exit(1);
|
|
13846
14143
|
}
|
|
13847
|
-
cwd =
|
|
13848
|
-
const hasPackageJson =
|
|
14144
|
+
cwd = path45.resolve(cwd, projectPrompt.projectName);
|
|
14145
|
+
const hasPackageJson = fs40.existsSync(path45.join(cwd, "package.json"));
|
|
13849
14146
|
const hasNextConfig = ["next.config.ts", "next.config.js", "next.config.mjs"].some(
|
|
13850
|
-
(f) =>
|
|
14147
|
+
(f) => fs40.existsSync(path45.join(cwd, f))
|
|
13851
14148
|
);
|
|
13852
14149
|
if (!hasPackageJson || !hasNextConfig) {
|
|
13853
14150
|
p4.log.error(
|
|
@@ -13948,11 +14245,11 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13948
14245
|
s.stop("");
|
|
13949
14246
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
13950
14247
|
p4.note(noteLines.join("\n"), "Scaffolded CMS");
|
|
13951
|
-
const drizzleConfigPath =
|
|
13952
|
-
if (!dbFiles.includes("drizzle.config.ts") &&
|
|
14248
|
+
const drizzleConfigPath = path45.join(cwd, "drizzle.config.ts");
|
|
14249
|
+
if (!dbFiles.includes("drizzle.config.ts") && fs40.existsSync(drizzleConfigPath)) {
|
|
13953
14250
|
if (options.force) {
|
|
13954
14251
|
const { drizzleConfigTemplate: drizzleConfigTemplate2 } = await import("./drizzle-config-EDKOEZ6G.js");
|
|
13955
|
-
|
|
14252
|
+
fs40.writeFileSync(drizzleConfigPath, drizzleConfigTemplate2(), "utf-8");
|
|
13956
14253
|
p4.log.success("Updated drizzle.config.ts");
|
|
13957
14254
|
} else if (!options.yes) {
|
|
13958
14255
|
const overwrite = await p4.confirm({
|
|
@@ -13961,7 +14258,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13961
14258
|
});
|
|
13962
14259
|
if (!p4.isCancel(overwrite) && overwrite) {
|
|
13963
14260
|
const { drizzleConfigTemplate: drizzleConfigTemplate2 } = await import("./drizzle-config-EDKOEZ6G.js");
|
|
13964
|
-
|
|
14261
|
+
fs40.writeFileSync(drizzleConfigPath, drizzleConfigTemplate2(), "utf-8");
|
|
13965
14262
|
p4.log.success("Updated drizzle.config.ts");
|
|
13966
14263
|
}
|
|
13967
14264
|
}
|
|
@@ -13994,15 +14291,15 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13994
14291
|
{
|
|
13995
14292
|
const entityNames = [];
|
|
13996
14293
|
const formNames = [];
|
|
13997
|
-
const schemasDir =
|
|
13998
|
-
const formsDir =
|
|
13999
|
-
if (
|
|
14000
|
-
for (const f of
|
|
14294
|
+
const schemasDir = path45.join(cwd, config.paths.schemas);
|
|
14295
|
+
const formsDir = path45.join(schemasDir, "forms");
|
|
14296
|
+
if (fs40.existsSync(schemasDir)) {
|
|
14297
|
+
for (const f of fs40.readdirSync(schemasDir)) {
|
|
14001
14298
|
if (f.endsWith(".json")) entityNames.push(f.replace(".json", ""));
|
|
14002
14299
|
}
|
|
14003
14300
|
}
|
|
14004
|
-
if (
|
|
14005
|
-
for (const f of
|
|
14301
|
+
if (fs40.existsSync(formsDir)) {
|
|
14302
|
+
for (const f of fs40.readdirSync(formsDir)) {
|
|
14006
14303
|
if (f.endsWith(".json")) formNames.push(f.replace(".json", ""));
|
|
14007
14304
|
}
|
|
14008
14305
|
}
|
|
@@ -14181,9 +14478,9 @@ function isValidDbUrl(url) {
|
|
|
14181
14478
|
return url.startsWith("postgres://") || url.startsWith("postgresql://");
|
|
14182
14479
|
}
|
|
14183
14480
|
function readExistingDbUrl(cwd) {
|
|
14184
|
-
const envPath =
|
|
14185
|
-
if (!
|
|
14186
|
-
const content =
|
|
14481
|
+
const envPath = path45.join(cwd, ".env.local");
|
|
14482
|
+
if (!fs40.existsSync(envPath)) return void 0;
|
|
14483
|
+
const content = fs40.readFileSync(envPath, "utf-8");
|
|
14187
14484
|
for (const line of content.split("\n")) {
|
|
14188
14485
|
const trimmed = line.trim();
|
|
14189
14486
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -14206,9 +14503,9 @@ function maskDbUrl(url) {
|
|
|
14206
14503
|
}
|
|
14207
14504
|
}
|
|
14208
14505
|
function hasDbUrl(cwd) {
|
|
14209
|
-
const envPath =
|
|
14210
|
-
if (!
|
|
14211
|
-
const content =
|
|
14506
|
+
const envPath = path45.join(cwd, ".env.local");
|
|
14507
|
+
if (!fs40.existsSync(envPath)) return false;
|
|
14508
|
+
const content = fs40.readFileSync(envPath, "utf-8");
|
|
14212
14509
|
for (const line of content.split("\n")) {
|
|
14213
14510
|
const trimmed = line.trim();
|
|
14214
14511
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -14222,23 +14519,23 @@ function hasDbUrl(cwd) {
|
|
|
14222
14519
|
return false;
|
|
14223
14520
|
}
|
|
14224
14521
|
function runSeed(cwd, cmsDir, email, password4, overwrite = false) {
|
|
14225
|
-
const scriptsDir =
|
|
14226
|
-
const seedPath =
|
|
14227
|
-
if (!
|
|
14228
|
-
|
|
14522
|
+
const scriptsDir = path45.join(cwd, cmsDir, "scripts");
|
|
14523
|
+
const seedPath = path45.join(scriptsDir, "seed.ts");
|
|
14524
|
+
if (!fs40.existsSync(scriptsDir)) {
|
|
14525
|
+
fs40.mkdirSync(scriptsDir, { recursive: true });
|
|
14229
14526
|
}
|
|
14230
|
-
|
|
14527
|
+
fs40.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
14231
14528
|
const cleanup = () => {
|
|
14232
14529
|
try {
|
|
14233
|
-
|
|
14234
|
-
if (
|
|
14235
|
-
|
|
14530
|
+
fs40.unlinkSync(seedPath);
|
|
14531
|
+
if (fs40.existsSync(scriptsDir) && fs40.readdirSync(scriptsDir).length === 0) {
|
|
14532
|
+
fs40.rmdirSync(scriptsDir);
|
|
14236
14533
|
}
|
|
14237
14534
|
} catch {
|
|
14238
14535
|
}
|
|
14239
14536
|
};
|
|
14240
14537
|
return new Promise((resolve) => {
|
|
14241
|
-
const tsxBin =
|
|
14538
|
+
const tsxBin = path45.join(cwd, "node_modules", ".bin", "tsx");
|
|
14242
14539
|
const child = spawn2(tsxBin, [seedPath], {
|
|
14243
14540
|
cwd,
|
|
14244
14541
|
stdio: "pipe",
|
|
@@ -14303,7 +14600,7 @@ ${stderr}`;
|
|
|
14303
14600
|
}
|
|
14304
14601
|
function runDrizzlePush(cwd) {
|
|
14305
14602
|
return new Promise((resolve) => {
|
|
14306
|
-
const drizzleBin =
|
|
14603
|
+
const drizzleBin = path45.join(cwd, "node_modules", ".bin", "drizzle-kit");
|
|
14307
14604
|
const child = spawn2(drizzleBin, ["push", "--force"], {
|
|
14308
14605
|
cwd,
|
|
14309
14606
|
stdio: "pipe",
|
|
@@ -14324,8 +14621,8 @@ function runDrizzlePush(cwd) {
|
|
|
14324
14621
|
}
|
|
14325
14622
|
|
|
14326
14623
|
// src/commands/remove.ts
|
|
14327
|
-
import
|
|
14328
|
-
import
|
|
14624
|
+
import fs41 from "fs";
|
|
14625
|
+
import path46 from "path";
|
|
14329
14626
|
import readline from "readline";
|
|
14330
14627
|
import { Command as Command4 } from "commander";
|
|
14331
14628
|
function findTableEnd3(content, startIndex) {
|
|
@@ -14356,8 +14653,8 @@ function findTableEnd3(content, startIndex) {
|
|
|
14356
14653
|
return content.length;
|
|
14357
14654
|
}
|
|
14358
14655
|
function removeTableFromSchema(schemaFilePath, name) {
|
|
14359
|
-
if (!
|
|
14360
|
-
let content =
|
|
14656
|
+
if (!fs41.existsSync(schemaFilePath)) return false;
|
|
14657
|
+
let content = fs41.readFileSync(schemaFilePath, "utf-8");
|
|
14361
14658
|
const variableName = toCamelCase(name);
|
|
14362
14659
|
let changed = false;
|
|
14363
14660
|
if (content.includes(`export const ${variableName} =`)) {
|
|
@@ -14385,13 +14682,13 @@ function removeTableFromSchema(schemaFilePath, name) {
|
|
|
14385
14682
|
}
|
|
14386
14683
|
if (changed) {
|
|
14387
14684
|
content = content.replace(/\n{3,}/g, "\n\n");
|
|
14388
|
-
|
|
14685
|
+
fs41.writeFileSync(schemaFilePath, content, "utf-8");
|
|
14389
14686
|
}
|
|
14390
14687
|
return changed;
|
|
14391
14688
|
}
|
|
14392
14689
|
function removeFromNavigation(navFilePath, name) {
|
|
14393
|
-
if (!
|
|
14394
|
-
const content =
|
|
14690
|
+
if (!fs41.existsSync(navFilePath)) return false;
|
|
14691
|
+
const content = fs41.readFileSync(navFilePath, "utf-8");
|
|
14395
14692
|
const href = `/cms/${name}`;
|
|
14396
14693
|
if (!content.includes(`'${href}'`)) return false;
|
|
14397
14694
|
const lines = content.split("\n");
|
|
@@ -14422,7 +14719,7 @@ function removeFromNavigation(navFilePath, name) {
|
|
|
14422
14719
|
if (startLine === -1 || endLine === -1) return false;
|
|
14423
14720
|
lines.splice(startLine, endLine - startLine + 1);
|
|
14424
14721
|
const updated = lines.join("\n").replace(/,\s*,/g, ",").replace(/\[\s*,/, "[");
|
|
14425
|
-
|
|
14722
|
+
fs41.writeFileSync(navFilePath, updated, "utf-8");
|
|
14426
14723
|
return true;
|
|
14427
14724
|
}
|
|
14428
14725
|
async function promptConfirm(message) {
|
|
@@ -14438,7 +14735,7 @@ async function promptConfirm(message) {
|
|
|
14438
14735
|
});
|
|
14439
14736
|
}
|
|
14440
14737
|
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 ?
|
|
14738
|
+
const cwd = options.cwd ? path46.resolve(options.cwd) : process.cwd();
|
|
14442
14739
|
console.log("\n BetterStart Remove\n");
|
|
14443
14740
|
let config;
|
|
14444
14741
|
try {
|
|
@@ -14451,34 +14748,34 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14451
14748
|
const pagesDir = config.paths?.pages ?? "./src/app/(cms)/cms/(authenticated)";
|
|
14452
14749
|
const kebabName = toKebabCase(schemaName);
|
|
14453
14750
|
const targets = [];
|
|
14454
|
-
const entityPagesDir =
|
|
14455
|
-
if (
|
|
14751
|
+
const entityPagesDir = path46.join(cwd, pagesDir, schemaName);
|
|
14752
|
+
if (fs41.existsSync(entityPagesDir)) {
|
|
14456
14753
|
targets.push({
|
|
14457
14754
|
path: entityPagesDir,
|
|
14458
|
-
label: `${
|
|
14755
|
+
label: `${path46.join(pagesDir, schemaName)}/`,
|
|
14459
14756
|
isDir: true
|
|
14460
14757
|
});
|
|
14461
14758
|
}
|
|
14462
|
-
const actionsFile =
|
|
14463
|
-
if (
|
|
14759
|
+
const actionsFile = path46.join(cwd, cmsDir, "lib", "actions", `${kebabName}.ts`);
|
|
14760
|
+
if (fs41.existsSync(actionsFile)) {
|
|
14464
14761
|
targets.push({
|
|
14465
14762
|
path: actionsFile,
|
|
14466
|
-
label:
|
|
14763
|
+
label: path46.join(cmsDir, "lib", "actions", `${kebabName}.ts`),
|
|
14467
14764
|
isDir: false
|
|
14468
14765
|
});
|
|
14469
14766
|
}
|
|
14470
|
-
const hookFile =
|
|
14471
|
-
if (
|
|
14767
|
+
const hookFile = path46.join(cwd, cmsDir, "hooks", `use-${kebabName}.ts`);
|
|
14768
|
+
if (fs41.existsSync(hookFile)) {
|
|
14472
14769
|
targets.push({
|
|
14473
14770
|
path: hookFile,
|
|
14474
|
-
label:
|
|
14771
|
+
label: path46.join(cmsDir, "hooks", `use-${kebabName}.ts`),
|
|
14475
14772
|
isDir: false
|
|
14476
14773
|
});
|
|
14477
14774
|
}
|
|
14478
|
-
const schemaFilePath =
|
|
14479
|
-
const hasTable =
|
|
14480
|
-
const navFilePath =
|
|
14481
|
-
const hasNavEntry =
|
|
14775
|
+
const schemaFilePath = path46.join(cwd, cmsDir, "db", "schema.ts");
|
|
14776
|
+
const hasTable = fs41.existsSync(schemaFilePath) && fs41.readFileSync(schemaFilePath, "utf-8").includes(`export const ${toCamelCase(schemaName)} =`);
|
|
14777
|
+
const navFilePath = path46.join(cwd, cmsDir, "data", "navigation.ts");
|
|
14778
|
+
const hasNavEntry = fs41.existsSync(navFilePath) && fs41.readFileSync(navFilePath, "utf-8").includes(`'/cms/${schemaName}'`);
|
|
14482
14779
|
if (targets.length === 0 && !hasTable && !hasNavEntry) {
|
|
14483
14780
|
console.log(` No generated files found for: ${schemaName}`);
|
|
14484
14781
|
return;
|
|
@@ -14488,10 +14785,10 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14488
14785
|
console.log(` ${t.isDir ? "[dir]" : " "} ${t.label}`);
|
|
14489
14786
|
}
|
|
14490
14787
|
if (hasTable) {
|
|
14491
|
-
console.log(` [edit] ${
|
|
14788
|
+
console.log(` [edit] ${path46.join(cmsDir, "db", "schema.ts")} (remove table)`);
|
|
14492
14789
|
}
|
|
14493
14790
|
if (hasNavEntry) {
|
|
14494
|
-
console.log(` [edit] ${
|
|
14791
|
+
console.log(` [edit] ${path46.join(cmsDir, "data", "navigation.ts")} (remove entry)`);
|
|
14495
14792
|
}
|
|
14496
14793
|
if (!options.force) {
|
|
14497
14794
|
console.log("");
|
|
@@ -14504,19 +14801,19 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14504
14801
|
console.log("");
|
|
14505
14802
|
for (const t of targets) {
|
|
14506
14803
|
if (t.isDir) {
|
|
14507
|
-
|
|
14804
|
+
fs41.rmSync(t.path, { recursive: true, force: true });
|
|
14508
14805
|
} else {
|
|
14509
|
-
|
|
14806
|
+
fs41.unlinkSync(t.path);
|
|
14510
14807
|
}
|
|
14511
14808
|
console.log(` Removed: ${t.label}`);
|
|
14512
14809
|
}
|
|
14513
14810
|
if (hasTable) {
|
|
14514
14811
|
removeTableFromSchema(schemaFilePath, schemaName);
|
|
14515
|
-
console.log(` Cleaned: ${
|
|
14812
|
+
console.log(` Cleaned: ${path46.join(cmsDir, "db", "schema.ts")}`);
|
|
14516
14813
|
}
|
|
14517
14814
|
if (hasNavEntry) {
|
|
14518
14815
|
removeFromNavigation(navFilePath, schemaName);
|
|
14519
|
-
console.log(` Cleaned: ${
|
|
14816
|
+
console.log(` Cleaned: ${path46.join(cmsDir, "data", "navigation.ts")}`);
|
|
14520
14817
|
}
|
|
14521
14818
|
console.log("\n Removal complete!");
|
|
14522
14819
|
console.log("\n Note: You may need to manually:");
|
|
@@ -14528,14 +14825,14 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14528
14825
|
|
|
14529
14826
|
// src/commands/setup-r2.ts
|
|
14530
14827
|
import { execFileSync as execFileSync5, spawnSync } from "child_process";
|
|
14531
|
-
import
|
|
14828
|
+
import fs42 from "fs";
|
|
14532
14829
|
import os from "os";
|
|
14533
|
-
import
|
|
14830
|
+
import path47 from "path";
|
|
14534
14831
|
import * as p5 from "@clack/prompts";
|
|
14535
14832
|
import { Command as Command5 } from "commander";
|
|
14536
14833
|
import pc3 from "picocolors";
|
|
14537
14834
|
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 ?
|
|
14835
|
+
const cwd = options.cwd ? path47.resolve(options.cwd) : process.cwd();
|
|
14539
14836
|
p5.intro(pc3.bgCyan(pc3.black(" BetterStart \u2014 R2 Storage Setup ")));
|
|
14540
14837
|
const s = p5.spinner();
|
|
14541
14838
|
s.start("Looking for wrangler CLI");
|
|
@@ -14727,8 +15024,8 @@ var setupR2Command = new Command5("setup-r2").description("Create a Cloudflare R
|
|
|
14727
15024
|
p5.outro("Done! Your CMS can now upload files to R2.");
|
|
14728
15025
|
});
|
|
14729
15026
|
function findWrangler(cwd) {
|
|
14730
|
-
const localBin =
|
|
14731
|
-
if (
|
|
15027
|
+
const localBin = path47.join(cwd, "node_modules", ".bin", "wrangler");
|
|
15028
|
+
if (fs42.existsSync(localBin)) return { bin: localBin, prefix: [] };
|
|
14732
15029
|
const result = spawnSync("which", ["wrangler"], { stdio: "pipe", timeout: 5e3 });
|
|
14733
15030
|
if (result.status === 0) {
|
|
14734
15031
|
const found = result.stdout?.toString().trim();
|
|
@@ -14760,11 +15057,11 @@ function extractAccountId(ref, cwd) {
|
|
|
14760
15057
|
}
|
|
14761
15058
|
function readWranglerToken() {
|
|
14762
15059
|
const candidates = [
|
|
14763
|
-
|
|
15060
|
+
path47.join(os.homedir(), "Library", "Preferences", ".wrangler", "config", "default.toml"),
|
|
14764
15061
|
// macOS
|
|
14765
|
-
|
|
15062
|
+
path47.join(os.homedir(), ".config", ".wrangler", "config", "default.toml"),
|
|
14766
15063
|
// Linux
|
|
14767
|
-
|
|
15064
|
+
path47.join(os.homedir(), ".wrangler", "config", "default.toml")
|
|
14768
15065
|
// fallback
|
|
14769
15066
|
];
|
|
14770
15067
|
if (process.env.WRANGLER_CONFIG_PATH) {
|
|
@@ -14772,13 +15069,13 @@ function readWranglerToken() {
|
|
|
14772
15069
|
}
|
|
14773
15070
|
if (process.env.XDG_CONFIG_HOME) {
|
|
14774
15071
|
candidates.unshift(
|
|
14775
|
-
|
|
15072
|
+
path47.join(process.env.XDG_CONFIG_HOME, ".wrangler", "config", "default.toml")
|
|
14776
15073
|
);
|
|
14777
15074
|
}
|
|
14778
15075
|
for (const configPath of candidates) {
|
|
14779
|
-
if (!
|
|
15076
|
+
if (!fs42.existsSync(configPath)) continue;
|
|
14780
15077
|
try {
|
|
14781
|
-
const content =
|
|
15078
|
+
const content = fs42.readFileSync(configPath, "utf-8");
|
|
14782
15079
|
const match = content.match(/^oauth_token\s*=\s*"([^"]+)"/m);
|
|
14783
15080
|
if (match) return match[1];
|
|
14784
15081
|
} catch {
|
|
@@ -14810,14 +15107,14 @@ async function enablePublicDomain(accountId, bucketName, token) {
|
|
|
14810
15107
|
}
|
|
14811
15108
|
|
|
14812
15109
|
// src/commands/uninstall.ts
|
|
14813
|
-
import
|
|
14814
|
-
import
|
|
15110
|
+
import fs44 from "fs";
|
|
15111
|
+
import path48 from "path";
|
|
14815
15112
|
import * as p6 from "@clack/prompts";
|
|
14816
15113
|
import { Command as Command6 } from "commander";
|
|
14817
15114
|
import pc4 from "picocolors";
|
|
14818
15115
|
|
|
14819
15116
|
// src/commands/uninstall-cleaners.ts
|
|
14820
|
-
import
|
|
15117
|
+
import fs43 from "fs";
|
|
14821
15118
|
function stripJsonComments2(input) {
|
|
14822
15119
|
let result = "";
|
|
14823
15120
|
let i = 0;
|
|
@@ -14851,8 +15148,8 @@ function stripJsonComments2(input) {
|
|
|
14851
15148
|
return result;
|
|
14852
15149
|
}
|
|
14853
15150
|
function cleanTsconfig(tsconfigPath) {
|
|
14854
|
-
if (!
|
|
14855
|
-
const raw =
|
|
15151
|
+
if (!fs43.existsSync(tsconfigPath)) return [];
|
|
15152
|
+
const raw = fs43.readFileSync(tsconfigPath, "utf-8");
|
|
14856
15153
|
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
14857
15154
|
let tsconfig;
|
|
14858
15155
|
try {
|
|
@@ -14876,13 +15173,13 @@ function cleanTsconfig(tsconfigPath) {
|
|
|
14876
15173
|
compilerOptions.paths = paths;
|
|
14877
15174
|
}
|
|
14878
15175
|
tsconfig.compilerOptions = compilerOptions;
|
|
14879
|
-
|
|
15176
|
+
fs43.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
14880
15177
|
`, "utf-8");
|
|
14881
15178
|
return removed;
|
|
14882
15179
|
}
|
|
14883
15180
|
function cleanCss(cssPath) {
|
|
14884
|
-
if (!
|
|
14885
|
-
const content =
|
|
15181
|
+
if (!fs43.existsSync(cssPath)) return [];
|
|
15182
|
+
const content = fs43.readFileSync(cssPath, "utf-8");
|
|
14886
15183
|
const lines = content.split("\n");
|
|
14887
15184
|
const sourcePattern = /^@source\s+"[^"]*cms[^"]*";\s*$/;
|
|
14888
15185
|
const removed = [];
|
|
@@ -14896,12 +15193,12 @@ function cleanCss(cssPath) {
|
|
|
14896
15193
|
}
|
|
14897
15194
|
if (removed.length === 0) return [];
|
|
14898
15195
|
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
14899
|
-
|
|
15196
|
+
fs43.writeFileSync(cssPath, cleaned, "utf-8");
|
|
14900
15197
|
return removed;
|
|
14901
15198
|
}
|
|
14902
15199
|
function cleanEnvFile(envPath) {
|
|
14903
|
-
if (!
|
|
14904
|
-
const content =
|
|
15200
|
+
if (!fs43.existsSync(envPath)) return [];
|
|
15201
|
+
const content = fs43.readFileSync(envPath, "utf-8");
|
|
14905
15202
|
const lines = content.split("\n");
|
|
14906
15203
|
const removed = [];
|
|
14907
15204
|
const kept = [];
|
|
@@ -14934,9 +15231,9 @@ function cleanEnvFile(envPath) {
|
|
|
14934
15231
|
if (removed.length === 0) return [];
|
|
14935
15232
|
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
14936
15233
|
if (result === "") {
|
|
14937
|
-
|
|
15234
|
+
fs43.unlinkSync(envPath);
|
|
14938
15235
|
} else {
|
|
14939
|
-
|
|
15236
|
+
fs43.writeFileSync(envPath, `${result}
|
|
14940
15237
|
`, "utf-8");
|
|
14941
15238
|
}
|
|
14942
15239
|
return removed;
|
|
@@ -14962,15 +15259,15 @@ function findMainCss2(cwd) {
|
|
|
14962
15259
|
"globals.css"
|
|
14963
15260
|
];
|
|
14964
15261
|
for (const candidate of candidates) {
|
|
14965
|
-
const filePath =
|
|
14966
|
-
if (
|
|
15262
|
+
const filePath = path48.join(cwd, candidate);
|
|
15263
|
+
if (fs44.existsSync(filePath)) return filePath;
|
|
14967
15264
|
}
|
|
14968
15265
|
return void 0;
|
|
14969
15266
|
}
|
|
14970
15267
|
function isCLICreatedBiome(biomePath) {
|
|
14971
|
-
if (!
|
|
15268
|
+
if (!fs44.existsSync(biomePath)) return false;
|
|
14972
15269
|
try {
|
|
14973
|
-
const content = JSON.parse(
|
|
15270
|
+
const content = JSON.parse(fs44.readFileSync(biomePath, "utf-8"));
|
|
14974
15271
|
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
15272
|
} catch {
|
|
14976
15273
|
return false;
|
|
@@ -14978,13 +15275,13 @@ function isCLICreatedBiome(biomePath) {
|
|
|
14978
15275
|
}
|
|
14979
15276
|
function buildUninstallPlan(cwd) {
|
|
14980
15277
|
const steps = [];
|
|
14981
|
-
const hasSrc =
|
|
15278
|
+
const hasSrc = fs44.existsSync(path48.join(cwd, "src"));
|
|
14982
15279
|
const appBase = hasSrc ? "src/app" : "app";
|
|
14983
15280
|
const dirs = [];
|
|
14984
|
-
const cmsDir =
|
|
14985
|
-
const cmsRouteGroup =
|
|
14986
|
-
if (
|
|
14987
|
-
if (
|
|
15281
|
+
const cmsDir = path48.join(cwd, "cms");
|
|
15282
|
+
const cmsRouteGroup = path48.join(cwd, appBase, "(cms)");
|
|
15283
|
+
if (fs44.existsSync(cmsDir)) dirs.push("cms/");
|
|
15284
|
+
if (fs44.existsSync(cmsRouteGroup)) dirs.push(`${appBase}/(cms)/`);
|
|
14988
15285
|
if (dirs.length > 0) {
|
|
14989
15286
|
steps.push({
|
|
14990
15287
|
label: "CMS directories",
|
|
@@ -14992,25 +15289,25 @@ function buildUninstallPlan(cwd) {
|
|
|
14992
15289
|
count: dirs.length,
|
|
14993
15290
|
unit: dirs.length === 1 ? "directory" : "directories",
|
|
14994
15291
|
execute() {
|
|
14995
|
-
if (
|
|
14996
|
-
if (
|
|
15292
|
+
if (fs44.existsSync(cmsDir)) fs44.rmSync(cmsDir, { recursive: true, force: true });
|
|
15293
|
+
if (fs44.existsSync(cmsRouteGroup)) fs44.rmSync(cmsRouteGroup, { recursive: true, force: true });
|
|
14997
15294
|
}
|
|
14998
15295
|
});
|
|
14999
15296
|
}
|
|
15000
15297
|
const configFiles = [];
|
|
15001
15298
|
const configPaths = [];
|
|
15002
15299
|
const candidates = [
|
|
15003
|
-
["cms.config.ts",
|
|
15004
|
-
["drizzle.config.ts",
|
|
15005
|
-
["CMS.md",
|
|
15300
|
+
["cms.config.ts", path48.join(cwd, "cms.config.ts")],
|
|
15301
|
+
["drizzle.config.ts", path48.join(cwd, "drizzle.config.ts")],
|
|
15302
|
+
["CMS.md", path48.join(cwd, "CMS.md")]
|
|
15006
15303
|
];
|
|
15007
15304
|
for (const [label, fullPath] of candidates) {
|
|
15008
|
-
if (
|
|
15305
|
+
if (fs44.existsSync(fullPath)) {
|
|
15009
15306
|
configFiles.push(label);
|
|
15010
15307
|
configPaths.push(fullPath);
|
|
15011
15308
|
}
|
|
15012
15309
|
}
|
|
15013
|
-
const biomePath =
|
|
15310
|
+
const biomePath = path48.join(cwd, "biome.json");
|
|
15014
15311
|
if (isCLICreatedBiome(biomePath)) {
|
|
15015
15312
|
configFiles.push("biome.json (CLI-created)");
|
|
15016
15313
|
configPaths.push(biomePath);
|
|
@@ -15023,14 +15320,14 @@ function buildUninstallPlan(cwd) {
|
|
|
15023
15320
|
unit: configFiles.length === 1 ? "file" : "files",
|
|
15024
15321
|
execute() {
|
|
15025
15322
|
for (const p7 of configPaths) {
|
|
15026
|
-
if (
|
|
15323
|
+
if (fs44.existsSync(p7)) fs44.unlinkSync(p7);
|
|
15027
15324
|
}
|
|
15028
15325
|
}
|
|
15029
15326
|
});
|
|
15030
15327
|
}
|
|
15031
|
-
const tsconfigPath =
|
|
15032
|
-
if (
|
|
15033
|
-
const content =
|
|
15328
|
+
const tsconfigPath = path48.join(cwd, "tsconfig.json");
|
|
15329
|
+
if (fs44.existsSync(tsconfigPath)) {
|
|
15330
|
+
const content = fs44.readFileSync(tsconfigPath, "utf-8");
|
|
15034
15331
|
const aliasMatches = content.match(/"@cms\//g);
|
|
15035
15332
|
if (aliasMatches && aliasMatches.length > 0) {
|
|
15036
15333
|
const aliasCount = aliasMatches.length;
|
|
@@ -15047,10 +15344,10 @@ function buildUninstallPlan(cwd) {
|
|
|
15047
15344
|
}
|
|
15048
15345
|
const cssFile = findMainCss2(cwd);
|
|
15049
15346
|
if (cssFile) {
|
|
15050
|
-
const cssContent =
|
|
15347
|
+
const cssContent = fs44.readFileSync(cssFile, "utf-8");
|
|
15051
15348
|
const sourceLines = cssContent.split("\n").filter((l) => /^@source\s+"[^"]*cms[^"]*";\s*$/.test(l));
|
|
15052
15349
|
if (sourceLines.length > 0) {
|
|
15053
|
-
const relCss =
|
|
15350
|
+
const relCss = path48.relative(cwd, cssFile);
|
|
15054
15351
|
steps.push({
|
|
15055
15352
|
label: `CSS @source lines (${relCss})`,
|
|
15056
15353
|
items: [`@source lines in ${relCss}`],
|
|
@@ -15062,9 +15359,9 @@ function buildUninstallPlan(cwd) {
|
|
|
15062
15359
|
});
|
|
15063
15360
|
}
|
|
15064
15361
|
}
|
|
15065
|
-
const envPath =
|
|
15066
|
-
if (
|
|
15067
|
-
const envContent =
|
|
15362
|
+
const envPath = path48.join(cwd, ".env.local");
|
|
15363
|
+
if (fs44.existsSync(envPath)) {
|
|
15364
|
+
const envContent = fs44.readFileSync(envPath, "utf-8");
|
|
15068
15365
|
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
15069
15366
|
if (bsVars.length > 0) {
|
|
15070
15367
|
steps.push({
|
|
@@ -15081,7 +15378,7 @@ function buildUninstallPlan(cwd) {
|
|
|
15081
15378
|
return steps;
|
|
15082
15379
|
}
|
|
15083
15380
|
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 ?
|
|
15381
|
+
const cwd = options.cwd ? path48.resolve(options.cwd) : process.cwd();
|
|
15085
15382
|
p6.intro(pc4.bgRed(pc4.white(" BetterStart Uninstall ")));
|
|
15086
15383
|
const steps = buildUninstallPlan(cwd);
|
|
15087
15384
|
if (steps.length === 0) {
|
|
@@ -15118,11 +15415,11 @@ var uninstallCommand = new Command6("uninstall").description("Remove all CMS fil
|
|
|
15118
15415
|
});
|
|
15119
15416
|
|
|
15120
15417
|
// src/commands/update-deps.ts
|
|
15121
|
-
import
|
|
15418
|
+
import path49 from "path";
|
|
15122
15419
|
import * as clack2 from "@clack/prompts";
|
|
15123
15420
|
import { Command as Command7 } from "commander";
|
|
15124
15421
|
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 ?
|
|
15422
|
+
const cwd = options.cwd ? path49.resolve(options.cwd) : process.cwd();
|
|
15126
15423
|
clack2.intro("BetterStart Update Dependencies");
|
|
15127
15424
|
const pm = detectPackageManager(cwd);
|
|
15128
15425
|
clack2.log.info(`Package manager: ${pm}`);
|
|
@@ -15147,22 +15444,22 @@ var updateDepsCommand = new Command7("update-deps").description("Install or upda
|
|
|
15147
15444
|
});
|
|
15148
15445
|
|
|
15149
15446
|
// src/commands/update-styles.ts
|
|
15150
|
-
import
|
|
15151
|
-
import
|
|
15447
|
+
import fs45 from "fs";
|
|
15448
|
+
import path50 from "path";
|
|
15152
15449
|
import * as clack3 from "@clack/prompts";
|
|
15153
15450
|
import { Command as Command8 } from "commander";
|
|
15154
15451
|
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 ?
|
|
15452
|
+
const cwd = options.cwd ? path50.resolve(options.cwd) : process.cwd();
|
|
15156
15453
|
clack3.intro("BetterStart Update Styles");
|
|
15157
15454
|
const config = await resolveConfig(cwd);
|
|
15158
15455
|
const cmsDir = config.paths?.cms ?? "./cms";
|
|
15159
|
-
const targetPath =
|
|
15160
|
-
if (!
|
|
15161
|
-
clack3.cancel(`cms-globals.css not found at ${
|
|
15456
|
+
const targetPath = path50.join(cwd, cmsDir, "cms-globals.css");
|
|
15457
|
+
if (!fs45.existsSync(targetPath)) {
|
|
15458
|
+
clack3.cancel(`cms-globals.css not found at ${path50.relative(cwd, targetPath)}`);
|
|
15162
15459
|
process.exit(1);
|
|
15163
15460
|
}
|
|
15164
|
-
|
|
15165
|
-
clack3.log.success(`Updated ${
|
|
15461
|
+
fs45.writeFileSync(targetPath, cmsGlobalsCssTemplate(), "utf-8");
|
|
15462
|
+
clack3.log.success(`Updated ${path50.relative(cwd, targetPath)}`);
|
|
15166
15463
|
clack3.outro("Styles updated");
|
|
15167
15464
|
});
|
|
15168
15465
|
|