@betterstart/cli 0.1.40 → 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 +977 -639
- 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,23 +2248,31 @@ 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
|
|
2252
|
+
function resolveUiImport(cwd, componentName) {
|
|
2253
|
+
const locations = ["components/ui", "src/components/ui"];
|
|
2254
|
+
for (const loc of locations) {
|
|
2255
|
+
if (fs7.existsSync(path7.join(cwd, loc, `${componentName}.tsx`)) || fs7.existsSync(path7.join(cwd, loc, `${componentName}.ts`))) {
|
|
2256
|
+
return `@/components/ui/${componentName}`;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
return `@cms/components/ui/${componentName}`;
|
|
2260
|
+
}
|
|
2229
2261
|
function escapeJsx(str) {
|
|
2230
2262
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2231
2263
|
}
|
|
2232
|
-
function
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
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) => {
|
|
2245
2276
|
if (f.type === "checkbox") return ` ${f.name}: false`;
|
|
2246
2277
|
if (f.type === "number") return ` ${f.name}: undefined`;
|
|
2247
2278
|
if (f.type === "multiselect" || f.type === "list") return ` ${f.name}: []`;
|
|
@@ -2249,108 +2280,43 @@ function generateFormComponent(schema, cwd, cmsDir, options) {
|
|
|
2249
2280
|
if (f.defaultValue !== void 0) return ` ${f.name}: '${f.defaultValue}'`;
|
|
2250
2281
|
return ` ${f.name}: ''`;
|
|
2251
2282
|
}).join(",\n");
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
const hasListFields = listFields.length > 0;
|
|
2256
|
-
const fieldArrayDecls = listFields.map(
|
|
2283
|
+
}
|
|
2284
|
+
function buildFieldArrayDecls(listFields) {
|
|
2285
|
+
return listFields.map(
|
|
2257
2286
|
(f) => ` const ${f.name}FieldArray = useFieldArray({ control: form.control, name: '${f.name}' })`
|
|
2258
2287
|
).join("\n");
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
const
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
` : "";
|
|
2266
|
-
const content = `'use client'
|
|
2267
|
-
|
|
2268
|
-
import { zodResolver } from '@hookform/resolvers/zod'
|
|
2269
|
-
import { useState } from 'react'
|
|
2270
|
-
${rhfImport}
|
|
2271
|
-
import { z } from 'zod/v3'
|
|
2272
|
-
import { create${pascal}Submission } from '@cms/actions/${kebab}-form'
|
|
2273
|
-
import { Button } from '@cms/components/ui/button'
|
|
2274
|
-
import {
|
|
2275
|
-
Form,
|
|
2276
|
-
FormControl,
|
|
2277
|
-
FormDescription,
|
|
2278
|
-
FormField,
|
|
2279
|
-
FormItem,
|
|
2280
|
-
FormLabel,
|
|
2281
|
-
FormMessage,
|
|
2282
|
-
} from '@cms/components/ui/form'
|
|
2283
|
-
import { Input } from '@cms/components/ui/input'
|
|
2284
|
-
import { Textarea } from '@cms/components/ui/textarea'
|
|
2285
|
-
import {
|
|
2286
|
-
Select,
|
|
2287
|
-
SelectContent,
|
|
2288
|
-
SelectItem,
|
|
2289
|
-
SelectTrigger,
|
|
2290
|
-
SelectValue,
|
|
2291
|
-
} from '@cms/components/ui/select'
|
|
2292
|
-
|
|
2293
|
-
const formSchema = z.object({
|
|
2294
|
-
${zodFields}
|
|
2295
|
-
})
|
|
2296
|
-
|
|
2297
|
-
type FormValues = z.infer<typeof formSchema>
|
|
2298
|
-
|
|
2299
|
-
export function ${pascal}Form() {
|
|
2300
|
-
const [submitted, setSubmitted] = useState(false)
|
|
2301
|
-
const [submitting, setSubmitting] = useState(false)
|
|
2302
|
-
|
|
2303
|
-
const form = useForm<FormValues>({
|
|
2304
|
-
resolver: zodResolver(formSchema),
|
|
2305
|
-
defaultValues: {
|
|
2306
|
-
${defaults}
|
|
2307
|
-
},
|
|
2308
|
-
})
|
|
2309
|
-
${fieldArraySetup}
|
|
2310
|
-
async function onSubmit(values: FormValues) {
|
|
2311
|
-
setSubmitting(true)
|
|
2312
|
-
try {
|
|
2313
|
-
const result = await create${pascal}Submission(values)
|
|
2314
|
-
if (result.success) {
|
|
2315
|
-
setSubmitted(true)
|
|
2316
|
-
} else {
|
|
2317
|
-
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
2318
|
-
}
|
|
2319
|
-
} catch {
|
|
2320
|
-
form.setError('root', { message: 'Something went wrong. Please try again.' })
|
|
2321
|
-
} finally {
|
|
2322
|
-
setSubmitting(false)
|
|
2288
|
+
}
|
|
2289
|
+
function buildWatchDecls(fields) {
|
|
2290
|
+
const watchFields = /* @__PURE__ */ new Set();
|
|
2291
|
+
for (const f of fields) {
|
|
2292
|
+
if (f.showWhen) {
|
|
2293
|
+
watchFields.add(f.showWhen.field);
|
|
2323
2294
|
}
|
|
2324
2295
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
<p className="mt-2 text-muted-foreground">${successMessage}</p>
|
|
2331
|
-
</div>
|
|
2332
|
-
)
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
return (
|
|
2336
|
-
<Form {...form}>
|
|
2337
|
-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
2338
|
-
${fieldJSX}
|
|
2339
|
-
|
|
2340
|
-
{form.formState.errors.root && (
|
|
2341
|
-
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
2342
|
-
)}
|
|
2343
|
-
|
|
2344
|
-
<Button type="submit" disabled={submitting}>
|
|
2345
|
-
{submitting ? 'Submitting...' : '${submitText}'}
|
|
2346
|
-
</Button>
|
|
2347
|
-
</form>
|
|
2348
|
-
</Form>
|
|
2349
|
-
)
|
|
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 };
|
|
2350
2301
|
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
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) && (
|
|
2314
|
+
${jsx}
|
|
2315
|
+
)}`;
|
|
2316
|
+
}
|
|
2317
|
+
return ` {${watchVar} === '${value}' && (
|
|
2318
|
+
${jsx}
|
|
2319
|
+
)}`;
|
|
2354
2320
|
}
|
|
2355
2321
|
function generateFieldJSX(field) {
|
|
2356
2322
|
const name = field.name || "";
|
|
@@ -2427,36 +2393,15 @@ ${optionItems}
|
|
|
2427
2393
|
)}
|
|
2428
2394
|
/>`;
|
|
2429
2395
|
case "email":
|
|
2430
|
-
return generateTextFieldJSX(
|
|
2431
|
-
name,
|
|
2432
|
-
label,
|
|
2433
|
-
placeholder || "email@example.com",
|
|
2434
|
-
hintJSX,
|
|
2435
|
-
requiredStar,
|
|
2436
|
-
"email"
|
|
2437
|
-
);
|
|
2396
|
+
return generateTextFieldJSX(name, label, placeholder || "email@example.com", hintJSX, requiredStar, "email");
|
|
2438
2397
|
case "number":
|
|
2439
2398
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "number");
|
|
2440
2399
|
case "date":
|
|
2441
2400
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "date");
|
|
2442
2401
|
case "url":
|
|
2443
|
-
return generateTextFieldJSX(
|
|
2444
|
-
name,
|
|
2445
|
-
label,
|
|
2446
|
-
placeholder || "https://",
|
|
2447
|
-
hintJSX,
|
|
2448
|
-
requiredStar,
|
|
2449
|
-
"url"
|
|
2450
|
-
);
|
|
2402
|
+
return generateTextFieldJSX(name, label, placeholder || "https://", hintJSX, requiredStar, "url");
|
|
2451
2403
|
case "phone":
|
|
2452
|
-
return generateTextFieldJSX(
|
|
2453
|
-
name,
|
|
2454
|
-
label,
|
|
2455
|
-
placeholder || "+1 (555) 000-0000",
|
|
2456
|
-
hintJSX,
|
|
2457
|
-
requiredStar,
|
|
2458
|
-
"tel"
|
|
2459
|
-
);
|
|
2404
|
+
return generateTextFieldJSX(name, label, placeholder || "+1 (555) 000-0000", hintJSX, requiredStar, "tel");
|
|
2460
2405
|
case "file":
|
|
2461
2406
|
case "upload":
|
|
2462
2407
|
return generateTextFieldJSX(name, label, placeholder, hintJSX, requiredStar, "file");
|
|
@@ -2557,9 +2502,402 @@ ${nestedFieldsJSX}
|
|
|
2557
2502
|
</div>`;
|
|
2558
2503
|
}
|
|
2559
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
|
+
|
|
2560
2898
|
// src/generators/form-pipeline/form-database.ts
|
|
2561
|
-
import
|
|
2562
|
-
import
|
|
2899
|
+
import fs10 from "fs";
|
|
2900
|
+
import path10 from "path";
|
|
2563
2901
|
function findTableEnd(content, startIndex) {
|
|
2564
2902
|
let depth = 0;
|
|
2565
2903
|
let inString = false;
|
|
@@ -2614,8 +2952,8 @@ ${fieldDefs}${customFieldsCol}
|
|
|
2614
2952
|
updatedAt: timestamp({ precision: 3, mode: 'string' }).default(sql\`CURRENT_TIMESTAMP\`).notNull()
|
|
2615
2953
|
})
|
|
2616
2954
|
`;
|
|
2617
|
-
const filePath =
|
|
2618
|
-
let content =
|
|
2955
|
+
const filePath = path10.join(cwd, dbSchemaPath);
|
|
2956
|
+
let content = fs10.readFileSync(filePath, "utf-8");
|
|
2619
2957
|
const pgCoreMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]drizzle-orm\/pg-core['"]/);
|
|
2620
2958
|
if (pgCoreMatch) {
|
|
2621
2959
|
const existing = new Set(
|
|
@@ -2651,23 +2989,23 @@ ${match}`
|
|
|
2651
2989
|
}
|
|
2652
2990
|
content += `
|
|
2653
2991
|
${tableSchema}`;
|
|
2654
|
-
|
|
2992
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
2655
2993
|
return { files: [dbSchemaPath] };
|
|
2656
2994
|
}
|
|
2657
2995
|
|
|
2658
2996
|
// src/generators/form-pipeline/form-hook.ts
|
|
2659
|
-
import
|
|
2660
|
-
import
|
|
2997
|
+
import fs11 from "fs";
|
|
2998
|
+
import path11 from "path";
|
|
2661
2999
|
function generateFormHook(schema, cwd, hooksDir, options) {
|
|
2662
3000
|
const formName = schema.name;
|
|
2663
3001
|
const pascal = toPascalCase(formName);
|
|
2664
3002
|
const camel = toCamelCase(formName);
|
|
2665
3003
|
const kebab = toKebabCase(formName);
|
|
2666
|
-
const filePath =
|
|
2667
|
-
const dir =
|
|
2668
|
-
if (!
|
|
2669
|
-
if (
|
|
2670
|
-
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)] };
|
|
2671
3009
|
}
|
|
2672
3010
|
const successMsg = (schema.successMessage || "Form submitted successfully").replace(/'/g, "\\'");
|
|
2673
3011
|
const content = `import {
|
|
@@ -2773,8 +3111,8 @@ export function useExport${pascal}SubmissionsJSON() {
|
|
|
2773
3111
|
})
|
|
2774
3112
|
}
|
|
2775
3113
|
`;
|
|
2776
|
-
|
|
2777
|
-
return { files: [
|
|
3114
|
+
fs11.writeFileSync(filePath, content, "utf-8");
|
|
3115
|
+
return { files: [path11.relative(cwd, filePath)] };
|
|
2778
3116
|
}
|
|
2779
3117
|
|
|
2780
3118
|
// src/generators/form-pipeline/pipeline.ts
|
|
@@ -2840,8 +3178,8 @@ function runFormPipeline(schema, cwd, config, options = {}) {
|
|
|
2840
3178
|
}
|
|
2841
3179
|
|
|
2842
3180
|
// src/generators/actions/entity-actions.ts
|
|
2843
|
-
import
|
|
2844
|
-
import
|
|
3181
|
+
import fs12 from "fs";
|
|
3182
|
+
import path12 from "path";
|
|
2845
3183
|
|
|
2846
3184
|
// src/generators/actions/action-helpers.ts
|
|
2847
3185
|
function generateFieldMapping(field, source = "input") {
|
|
@@ -2980,9 +3318,9 @@ ${blocks.join("\n\n")}
|
|
|
2980
3318
|
|
|
2981
3319
|
// src/generators/actions/entity-actions.ts
|
|
2982
3320
|
function generateActions(schema, cwd, actionsDir, options = {}) {
|
|
2983
|
-
const absActionsDir =
|
|
2984
|
-
const filePath =
|
|
2985
|
-
if (
|
|
3321
|
+
const absActionsDir = path12.join(cwd, actionsDir);
|
|
3322
|
+
const filePath = path12.join(absActionsDir, `${schema.name}.ts`);
|
|
3323
|
+
if (fs12.existsSync(filePath) && !options.force) {
|
|
2986
3324
|
return { files: [] };
|
|
2987
3325
|
}
|
|
2988
3326
|
const singular = singularize(schema.name);
|
|
@@ -3468,20 +3806,20 @@ export async function bulkUpdate${Plural}SortOrder(
|
|
|
3468
3806
|
}
|
|
3469
3807
|
}${m2mHelpers}
|
|
3470
3808
|
`;
|
|
3471
|
-
if (!
|
|
3472
|
-
|
|
3809
|
+
if (!fs12.existsSync(absActionsDir)) {
|
|
3810
|
+
fs12.mkdirSync(absActionsDir, { recursive: true });
|
|
3473
3811
|
}
|
|
3474
|
-
|
|
3475
|
-
return { files: [
|
|
3812
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
3813
|
+
return { files: [path12.join(actionsDir, `${schema.name}.ts`)] };
|
|
3476
3814
|
}
|
|
3477
3815
|
|
|
3478
3816
|
// src/generators/actions/single-actions.ts
|
|
3479
|
-
import
|
|
3480
|
-
import
|
|
3817
|
+
import fs13 from "fs";
|
|
3818
|
+
import path13 from "path";
|
|
3481
3819
|
function generateSingleActions(schema, cwd, actionsDir, options = {}) {
|
|
3482
|
-
const absActionsDir =
|
|
3483
|
-
const filePath =
|
|
3484
|
-
if (
|
|
3820
|
+
const absActionsDir = path13.join(cwd, actionsDir);
|
|
3821
|
+
const filePath = path13.join(absActionsDir, `${schema.name}.ts`);
|
|
3822
|
+
if (fs13.existsSync(filePath) && !options.force) {
|
|
3485
3823
|
return { files: [] };
|
|
3486
3824
|
}
|
|
3487
3825
|
const singular = singularize(schema.name);
|
|
@@ -3604,24 +3942,24 @@ ${upsertMappings},${singleHasHtmlOutput ? "\n" + singleHtmlOutputFields.map((f)
|
|
|
3604
3942
|
}
|
|
3605
3943
|
}
|
|
3606
3944
|
`;
|
|
3607
|
-
if (!
|
|
3608
|
-
|
|
3945
|
+
if (!fs13.existsSync(absActionsDir)) {
|
|
3946
|
+
fs13.mkdirSync(absActionsDir, { recursive: true });
|
|
3609
3947
|
}
|
|
3610
|
-
|
|
3611
|
-
return { files: [
|
|
3948
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
3949
|
+
return { files: [path13.join(actionsDir, `${schema.name}.ts`)] };
|
|
3612
3950
|
}
|
|
3613
3951
|
|
|
3614
3952
|
// src/generators/cache.ts
|
|
3615
|
-
import
|
|
3616
|
-
import
|
|
3953
|
+
import fs14 from "fs";
|
|
3954
|
+
import path14 from "path";
|
|
3617
3955
|
function loadAllSchemas(cwd, schemasDir) {
|
|
3618
|
-
const dir =
|
|
3619
|
-
if (!
|
|
3620
|
-
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"));
|
|
3621
3959
|
const seen = /* @__PURE__ */ new Map();
|
|
3622
3960
|
for (const file of files) {
|
|
3623
3961
|
if (file === "schema.json") continue;
|
|
3624
|
-
const content =
|
|
3962
|
+
const content = fs14.readFileSync(path14.join(dir, file), "utf-8");
|
|
3625
3963
|
const schema = JSON.parse(content);
|
|
3626
3964
|
if (!schema.name) continue;
|
|
3627
3965
|
seen.set(schema.name, schema);
|
|
@@ -3811,27 +4149,27 @@ export * from './revalidate'
|
|
|
3811
4149
|
`;
|
|
3812
4150
|
}
|
|
3813
4151
|
function generateCache(_schema, cwd, cmsDir, _options = {}) {
|
|
3814
|
-
const cacheDir =
|
|
3815
|
-
const schemasDir =
|
|
4152
|
+
const cacheDir = path14.join(cwd, cmsDir, "lib", "cache");
|
|
4153
|
+
const schemasDir = path14.join(cmsDir, "schemas");
|
|
3816
4154
|
const schemas = loadAllSchemas(cwd, schemasDir);
|
|
3817
4155
|
const configs = schemas.map(buildCacheConfig).sort((a, b) => a.name.localeCompare(b.name));
|
|
3818
|
-
if (!
|
|
3819
|
-
|
|
4156
|
+
if (!fs14.existsSync(cacheDir)) {
|
|
4157
|
+
fs14.mkdirSync(cacheDir, { recursive: true });
|
|
3820
4158
|
}
|
|
3821
4159
|
const files = [];
|
|
3822
|
-
|
|
3823
|
-
files.push(
|
|
3824
|
-
|
|
3825
|
-
|
|
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"),
|
|
3826
4164
|
generateCachedQueries(configs),
|
|
3827
4165
|
"utf-8"
|
|
3828
4166
|
);
|
|
3829
|
-
files.push(
|
|
3830
|
-
|
|
3831
|
-
files.push(
|
|
3832
|
-
const customQueriesPath =
|
|
3833
|
-
if (!
|
|
3834
|
-
|
|
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(
|
|
3835
4173
|
customQueriesPath,
|
|
3836
4174
|
`/**
|
|
3837
4175
|
* Custom cached query functions (not auto-generated)
|
|
@@ -3842,16 +4180,16 @@ export {}
|
|
|
3842
4180
|
`,
|
|
3843
4181
|
"utf-8"
|
|
3844
4182
|
);
|
|
3845
|
-
files.push(
|
|
4183
|
+
files.push(path14.join(cmsDir, "lib", "cache", "cached-queries-custom.ts"));
|
|
3846
4184
|
}
|
|
3847
|
-
|
|
3848
|
-
files.push(
|
|
4185
|
+
fs14.writeFileSync(path14.join(cacheDir, "index.ts"), generateIndex(), "utf-8");
|
|
4186
|
+
files.push(path14.join(cmsDir, "lib", "cache", "index.ts"));
|
|
3849
4187
|
return { files };
|
|
3850
4188
|
}
|
|
3851
4189
|
|
|
3852
4190
|
// src/generators/columns/generate-columns.ts
|
|
3853
|
-
import
|
|
3854
|
-
import
|
|
4191
|
+
import fs16 from "fs";
|
|
4192
|
+
import path16 from "path";
|
|
3855
4193
|
|
|
3856
4194
|
// src/generators/columns/column-defs.ts
|
|
3857
4195
|
function isSortableColumn(column) {
|
|
@@ -4469,12 +4807,12 @@ function truncateStr(str: string, maxLength: number): string {
|
|
|
4469
4807
|
}
|
|
4470
4808
|
|
|
4471
4809
|
// src/generators/columns/custom-cell.ts
|
|
4472
|
-
import
|
|
4473
|
-
import
|
|
4810
|
+
import fs15 from "fs";
|
|
4811
|
+
import path15 from "path";
|
|
4474
4812
|
function createCustomCellComponent(schema, componentName, cwd, pagesDir, options) {
|
|
4475
|
-
const cellsDir =
|
|
4476
|
-
const componentFilePath =
|
|
4477
|
-
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;
|
|
4478
4816
|
const singular = singularize(schema.name);
|
|
4479
4817
|
const Singular = toPascalCase(singular);
|
|
4480
4818
|
const content = `import type { ${Singular}Data } from '@cms/actions/${schema.name}'
|
|
@@ -4491,17 +4829,17 @@ export function ${componentName}({ data }: ${componentName}Props) {
|
|
|
4491
4829
|
)
|
|
4492
4830
|
}
|
|
4493
4831
|
`;
|
|
4494
|
-
if (!
|
|
4495
|
-
|
|
4832
|
+
if (!fs15.existsSync(cellsDir)) {
|
|
4833
|
+
fs15.mkdirSync(cellsDir, { recursive: true });
|
|
4496
4834
|
}
|
|
4497
|
-
|
|
4835
|
+
fs15.writeFileSync(componentFilePath, content, "utf-8");
|
|
4498
4836
|
}
|
|
4499
4837
|
|
|
4500
4838
|
// src/generators/columns/generate-columns.ts
|
|
4501
4839
|
function generateColumns2(schema, cwd, pagesDir, options = {}) {
|
|
4502
|
-
const entityDir =
|
|
4503
|
-
const columnsFilePath =
|
|
4504
|
-
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) {
|
|
4505
4843
|
return { files: [] };
|
|
4506
4844
|
}
|
|
4507
4845
|
const singular = singularize(schema.name);
|
|
@@ -4643,22 +4981,22 @@ ${restColDefs}` : ""},
|
|
|
4643
4981
|
${actionsColumn}
|
|
4644
4982
|
]
|
|
4645
4983
|
`;
|
|
4646
|
-
if (!
|
|
4647
|
-
|
|
4984
|
+
if (!fs16.existsSync(entityDir)) {
|
|
4985
|
+
fs16.mkdirSync(entityDir, { recursive: true });
|
|
4648
4986
|
}
|
|
4649
|
-
|
|
4987
|
+
fs16.writeFileSync(columnsFilePath, content, "utf-8");
|
|
4650
4988
|
return {
|
|
4651
|
-
files: [
|
|
4989
|
+
files: [path16.join(pagesDir, schema.name, "columns.tsx")]
|
|
4652
4990
|
};
|
|
4653
4991
|
}
|
|
4654
4992
|
|
|
4655
4993
|
// src/generators/create-page.ts
|
|
4656
|
-
import
|
|
4657
|
-
import
|
|
4994
|
+
import fs17 from "fs";
|
|
4995
|
+
import path17 from "path";
|
|
4658
4996
|
function generateCreatePage(schema, cwd, pagesDir, options = {}) {
|
|
4659
|
-
const newDir =
|
|
4660
|
-
const pageFilePath =
|
|
4661
|
-
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) {
|
|
4662
5000
|
return { files: [] };
|
|
4663
5001
|
}
|
|
4664
5002
|
const singular = singularize(schema.name);
|
|
@@ -4685,18 +5023,18 @@ export default async function Create${Singular}Page() {
|
|
|
4685
5023
|
)
|
|
4686
5024
|
}
|
|
4687
5025
|
`;
|
|
4688
|
-
if (!
|
|
4689
|
-
|
|
5026
|
+
if (!fs17.existsSync(newDir)) {
|
|
5027
|
+
fs17.mkdirSync(newDir, { recursive: true });
|
|
4690
5028
|
}
|
|
4691
|
-
|
|
5029
|
+
fs17.writeFileSync(pageFilePath, content, "utf-8");
|
|
4692
5030
|
return {
|
|
4693
|
-
files: [
|
|
5031
|
+
files: [path17.join(pagesDir, schema.name, "new", "page.tsx")]
|
|
4694
5032
|
};
|
|
4695
5033
|
}
|
|
4696
5034
|
|
|
4697
5035
|
// src/generators/database.ts
|
|
4698
|
-
import
|
|
4699
|
-
import
|
|
5036
|
+
import fs18 from "fs";
|
|
5037
|
+
import path18 from "path";
|
|
4700
5038
|
function getFieldModifiers(field, needsSql) {
|
|
4701
5039
|
const modifiers = [];
|
|
4702
5040
|
if (field.primaryKey) {
|
|
@@ -4877,11 +5215,11 @@ function findTableEnd2(content, startIndex) {
|
|
|
4877
5215
|
return content.length;
|
|
4878
5216
|
}
|
|
4879
5217
|
function generateDatabase(schema, cwd, schemaDir, options = {}) {
|
|
4880
|
-
const schemaFilePath =
|
|
5218
|
+
const schemaFilePath = path18.join(cwd, schemaDir);
|
|
4881
5219
|
const files = [];
|
|
4882
5220
|
let content = "";
|
|
4883
|
-
if (
|
|
4884
|
-
content =
|
|
5221
|
+
if (fs18.existsSync(schemaFilePath)) {
|
|
5222
|
+
content = fs18.readFileSync(schemaFilePath, "utf-8");
|
|
4885
5223
|
}
|
|
4886
5224
|
const variableName = toCamelCase(schema.name);
|
|
4887
5225
|
if (content.includes(`export const ${variableName} =`) && !options.force) {
|
|
@@ -4921,11 +5259,11 @@ ${junctionDefs[i]}`;
|
|
|
4921
5259
|
}
|
|
4922
5260
|
}
|
|
4923
5261
|
updated = mergeImports(updated, requiredImports, needsSql.value);
|
|
4924
|
-
const dir =
|
|
4925
|
-
if (!
|
|
4926
|
-
|
|
5262
|
+
const dir = path18.dirname(schemaFilePath);
|
|
5263
|
+
if (!fs18.existsSync(dir)) {
|
|
5264
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
4927
5265
|
}
|
|
4928
|
-
|
|
5266
|
+
fs18.writeFileSync(schemaFilePath, updated, "utf-8");
|
|
4929
5267
|
files.push(schemaDir);
|
|
4930
5268
|
return {
|
|
4931
5269
|
files,
|
|
@@ -4935,12 +5273,12 @@ ${junctionDefs[i]}`;
|
|
|
4935
5273
|
}
|
|
4936
5274
|
|
|
4937
5275
|
// src/generators/edit-page.ts
|
|
4938
|
-
import
|
|
4939
|
-
import
|
|
5276
|
+
import fs19 from "fs";
|
|
5277
|
+
import path19 from "path";
|
|
4940
5278
|
function generateEditPage(schema, cwd, pagesDir, options = {}) {
|
|
4941
|
-
const editDir =
|
|
4942
|
-
const pageFilePath =
|
|
4943
|
-
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) {
|
|
4944
5282
|
return { files: [] };
|
|
4945
5283
|
}
|
|
4946
5284
|
const singular = singularize(schema.name);
|
|
@@ -4979,18 +5317,18 @@ export default async function Edit${Singular}Page({ params }: PageProps) {
|
|
|
4979
5317
|
)
|
|
4980
5318
|
}
|
|
4981
5319
|
`;
|
|
4982
|
-
if (!
|
|
4983
|
-
|
|
5320
|
+
if (!fs19.existsSync(editDir)) {
|
|
5321
|
+
fs19.mkdirSync(editDir, { recursive: true });
|
|
4984
5322
|
}
|
|
4985
|
-
|
|
5323
|
+
fs19.writeFileSync(pageFilePath, content, "utf-8");
|
|
4986
5324
|
return {
|
|
4987
|
-
files: [
|
|
5325
|
+
files: [path19.join(pagesDir, schema.name, "[id]", "edit", "page.tsx")]
|
|
4988
5326
|
};
|
|
4989
5327
|
}
|
|
4990
5328
|
|
|
4991
5329
|
// src/generators/form/form-entity.ts
|
|
4992
|
-
import
|
|
4993
|
-
import
|
|
5330
|
+
import fs20 from "fs";
|
|
5331
|
+
import path20 from "path";
|
|
4994
5332
|
|
|
4995
5333
|
// src/generators/form/zod-schema.ts
|
|
4996
5334
|
function getFormFieldType(field) {
|
|
@@ -5788,7 +6126,7 @@ function generateDefaultValue(f) {
|
|
|
5788
6126
|
}
|
|
5789
6127
|
return ` ${f.name}: initialData?.${f.name} ?? ''`;
|
|
5790
6128
|
}
|
|
5791
|
-
function
|
|
6129
|
+
function buildZodFields2(flatFields) {
|
|
5792
6130
|
return flatFields.filter((f) => f.type !== "tabs").flatMap((f) => {
|
|
5793
6131
|
const defs = [];
|
|
5794
6132
|
const zodType = getZodType(f);
|
|
@@ -5800,7 +6138,7 @@ function buildZodFields(flatFields) {
|
|
|
5800
6138
|
return defs;
|
|
5801
6139
|
}).join(",\n");
|
|
5802
6140
|
}
|
|
5803
|
-
function
|
|
6141
|
+
function buildDefaultValues2(flatFields) {
|
|
5804
6142
|
return flatFields.filter((f) => f.type !== "tabs").flatMap((f) => {
|
|
5805
6143
|
const defs = [generateDefaultValue(f)];
|
|
5806
6144
|
if (f.hasIcon) defs.push(` ${f.name}Icon: initialData?.${f.name}Icon ?? ''`);
|
|
@@ -5887,9 +6225,9 @@ function buildUiImports(ctx) {
|
|
|
5887
6225
|
|
|
5888
6226
|
// src/generators/form/form-entity.ts
|
|
5889
6227
|
function generateForm(schema, cwd, pagesDir, options = {}) {
|
|
5890
|
-
const entityDir =
|
|
5891
|
-
const formFilePath =
|
|
5892
|
-
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) {
|
|
5893
6231
|
return { files: [] };
|
|
5894
6232
|
}
|
|
5895
6233
|
const singular = singularize(schema.name);
|
|
@@ -5943,8 +6281,8 @@ function generateForm(schema, cwd, pagesDir, options = {}) {
|
|
|
5943
6281
|
const hasList = hasFieldType(schema.fields, "list");
|
|
5944
6282
|
const hasNestedList = listFieldsWithNested.length > 0;
|
|
5945
6283
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
5946
|
-
const zodFields =
|
|
5947
|
-
const defaultValues =
|
|
6284
|
+
const zodFields = buildZodFields2(flatFields);
|
|
6285
|
+
const defaultValues = buildDefaultValues2(flatFields);
|
|
5948
6286
|
const formFieldsJSX = allFormFields.filter((f) => !(hasDraft && f.name === "published")).map((f) => {
|
|
5949
6287
|
if (f.type === "tabs" && f.tabs) {
|
|
5950
6288
|
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
@@ -6145,22 +6483,22 @@ ${hasDraft ? ` <Button
|
|
|
6145
6483
|
)
|
|
6146
6484
|
}
|
|
6147
6485
|
`;
|
|
6148
|
-
if (!
|
|
6149
|
-
|
|
6486
|
+
if (!fs20.existsSync(entityDir)) {
|
|
6487
|
+
fs20.mkdirSync(entityDir, { recursive: true });
|
|
6150
6488
|
}
|
|
6151
|
-
|
|
6489
|
+
fs20.writeFileSync(formFilePath, content, "utf-8");
|
|
6152
6490
|
return {
|
|
6153
|
-
files: [
|
|
6491
|
+
files: [path20.join(pagesDir, schema.name, `${schema.name}-form.tsx`)]
|
|
6154
6492
|
};
|
|
6155
6493
|
}
|
|
6156
6494
|
|
|
6157
6495
|
// src/generators/form/form-single.ts
|
|
6158
|
-
import
|
|
6159
|
-
import
|
|
6496
|
+
import fs21 from "fs";
|
|
6497
|
+
import path21 from "path";
|
|
6160
6498
|
function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
6161
|
-
const entityDir =
|
|
6162
|
-
const formFilePath =
|
|
6163
|
-
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) {
|
|
6164
6502
|
return { files: [] };
|
|
6165
6503
|
}
|
|
6166
6504
|
const singular = singularize(schema.name);
|
|
@@ -6212,8 +6550,8 @@ function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
|
6212
6550
|
const hasList = hasFieldType(schema.fields, "list");
|
|
6213
6551
|
const hasNestedList = listFieldsWithNested.length > 0;
|
|
6214
6552
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
6215
|
-
const zodFields =
|
|
6216
|
-
const defaultValues =
|
|
6553
|
+
const zodFields = buildZodFields2(flatFields);
|
|
6554
|
+
const defaultValues = buildDefaultValues2(flatFields);
|
|
6217
6555
|
const formFieldsJSX = allFormFields.map((f) => {
|
|
6218
6556
|
if (f.type === "tabs" && f.tabs) {
|
|
6219
6557
|
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
@@ -6363,21 +6701,21 @@ ${formFieldsJSX}
|
|
|
6363
6701
|
)
|
|
6364
6702
|
}
|
|
6365
6703
|
`;
|
|
6366
|
-
if (!
|
|
6367
|
-
|
|
6704
|
+
if (!fs21.existsSync(entityDir)) {
|
|
6705
|
+
fs21.mkdirSync(entityDir, { recursive: true });
|
|
6368
6706
|
}
|
|
6369
|
-
|
|
6707
|
+
fs21.writeFileSync(formFilePath, content, "utf-8");
|
|
6370
6708
|
return {
|
|
6371
|
-
files: [
|
|
6709
|
+
files: [path21.join(pagesDir, schema.name, `${schema.name}-form.tsx`)]
|
|
6372
6710
|
};
|
|
6373
6711
|
}
|
|
6374
6712
|
|
|
6375
6713
|
// src/generators/hook.ts
|
|
6376
|
-
import
|
|
6377
|
-
import
|
|
6714
|
+
import fs22 from "fs";
|
|
6715
|
+
import path22 from "path";
|
|
6378
6716
|
function generateHook(schema, cwd, hooksDir, options = {}) {
|
|
6379
6717
|
const hookFileName = `use-${schema.name}.ts`;
|
|
6380
|
-
const hookFilePath =
|
|
6718
|
+
const hookFilePath = path22.join(cwd, hooksDir, hookFileName);
|
|
6381
6719
|
const singular = singularize(schema.name);
|
|
6382
6720
|
const plural = pluralize(schema.name);
|
|
6383
6721
|
const Singular = toPascalCase(singular);
|
|
@@ -6387,7 +6725,7 @@ function generateHook(schema, cwd, hooksDir, options = {}) {
|
|
|
6387
6725
|
const hasHtmlOutput = dbFields.some(
|
|
6388
6726
|
(f) => (f.type === "richtext" || f.type === "markdown") && f.output === "html"
|
|
6389
6727
|
);
|
|
6390
|
-
if (
|
|
6728
|
+
if (fs22.existsSync(hookFilePath) && !options.force) {
|
|
6391
6729
|
return { files: [], hookName: `use${Plural}` };
|
|
6392
6730
|
}
|
|
6393
6731
|
const hasFilters = schema.filters && schema.filters.length > 0;
|
|
@@ -6462,22 +6800,22 @@ export function use${Plural}(
|
|
|
6462
6800
|
}
|
|
6463
6801
|
${singularHook}${slugHook}${distinctHooks}
|
|
6464
6802
|
`;
|
|
6465
|
-
const dir =
|
|
6466
|
-
if (!
|
|
6467
|
-
|
|
6803
|
+
const dir = path22.dirname(hookFilePath);
|
|
6804
|
+
if (!fs22.existsSync(dir)) {
|
|
6805
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
6468
6806
|
}
|
|
6469
|
-
|
|
6807
|
+
fs22.writeFileSync(hookFilePath, content, "utf-8");
|
|
6470
6808
|
return {
|
|
6471
|
-
files: [
|
|
6809
|
+
files: [path22.join(hooksDir, hookFileName)],
|
|
6472
6810
|
hookName: `use${Plural}`
|
|
6473
6811
|
};
|
|
6474
6812
|
}
|
|
6475
6813
|
function generateSingleHook(schema, cwd, hooksDir, options = {}) {
|
|
6476
6814
|
const hookFileName = `use-${schema.name}.ts`;
|
|
6477
|
-
const hookFilePath =
|
|
6815
|
+
const hookFilePath = path22.join(cwd, hooksDir, hookFileName);
|
|
6478
6816
|
const singular = singularize(schema.name);
|
|
6479
6817
|
const Singular = toPascalCase(singular);
|
|
6480
|
-
if (
|
|
6818
|
+
if (fs22.existsSync(hookFilePath) && !options.force) {
|
|
6481
6819
|
return { files: [], hookName: `use${Singular}` };
|
|
6482
6820
|
}
|
|
6483
6821
|
const content = `import {
|
|
@@ -6494,20 +6832,20 @@ export function use${Singular}(): UseQueryResult<${Singular}Data | null, Error>
|
|
|
6494
6832
|
})
|
|
6495
6833
|
}
|
|
6496
6834
|
`;
|
|
6497
|
-
const dir =
|
|
6498
|
-
if (!
|
|
6499
|
-
|
|
6835
|
+
const dir = path22.dirname(hookFilePath);
|
|
6836
|
+
if (!fs22.existsSync(dir)) {
|
|
6837
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
6500
6838
|
}
|
|
6501
|
-
|
|
6839
|
+
fs22.writeFileSync(hookFilePath, content, "utf-8");
|
|
6502
6840
|
return {
|
|
6503
|
-
files: [
|
|
6841
|
+
files: [path22.join(hooksDir, hookFileName)],
|
|
6504
6842
|
hookName: `use${Singular}`
|
|
6505
6843
|
};
|
|
6506
6844
|
}
|
|
6507
6845
|
|
|
6508
6846
|
// src/generators/navigation.ts
|
|
6509
|
-
import
|
|
6510
|
-
import
|
|
6847
|
+
import fs23 from "fs";
|
|
6848
|
+
import path23 from "path";
|
|
6511
6849
|
function parseNavigationFile2(content) {
|
|
6512
6850
|
const iconImportMatch = content.match(/import\s*\{([^}]+)\}\s*from\s*['"]lucide-react['"]/);
|
|
6513
6851
|
const iconImports = iconImportMatch ? iconImportMatch[1].split(",").map((s) => s.trim()).filter((s) => s && s !== "LucideIcon") : [];
|
|
@@ -6610,14 +6948,14 @@ function appendItem2(lines, item, isLast) {
|
|
|
6610
6948
|
lines.push(` }${isLast ? "" : ","}`);
|
|
6611
6949
|
}
|
|
6612
6950
|
function updateNavigation(schema, cwd, cmsDir, options = {}) {
|
|
6613
|
-
const navFilePath =
|
|
6951
|
+
const navFilePath = path23.join(cwd, cmsDir, "data", "navigation.ts");
|
|
6614
6952
|
if (schema.name === "settings") {
|
|
6615
6953
|
return { files: [] };
|
|
6616
6954
|
}
|
|
6617
6955
|
let items = [];
|
|
6618
6956
|
let iconImports = [];
|
|
6619
|
-
if (
|
|
6620
|
-
const content =
|
|
6957
|
+
if (fs23.existsSync(navFilePath)) {
|
|
6958
|
+
const content = fs23.readFileSync(navFilePath, "utf-8");
|
|
6621
6959
|
const parsed = parseNavigationFile2(content);
|
|
6622
6960
|
items = parsed.items;
|
|
6623
6961
|
iconImports = parsed.iconImports;
|
|
@@ -6654,24 +6992,24 @@ function updateNavigation(schema, cwd, cmsDir, options = {}) {
|
|
|
6654
6992
|
iconImports.push(schema.icon);
|
|
6655
6993
|
}
|
|
6656
6994
|
iconImports.sort();
|
|
6657
|
-
const dir =
|
|
6658
|
-
if (!
|
|
6659
|
-
|
|
6995
|
+
const dir = path23.dirname(navFilePath);
|
|
6996
|
+
if (!fs23.existsSync(dir)) {
|
|
6997
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
6660
6998
|
}
|
|
6661
6999
|
const code = generateNavigationCode2(items, iconImports);
|
|
6662
|
-
|
|
7000
|
+
fs23.writeFileSync(navFilePath, code, "utf-8");
|
|
6663
7001
|
return {
|
|
6664
|
-
files: [
|
|
7002
|
+
files: [path23.join(cmsDir, "data", "navigation.ts")]
|
|
6665
7003
|
};
|
|
6666
7004
|
}
|
|
6667
7005
|
|
|
6668
7006
|
// src/generators/page.ts
|
|
6669
|
-
import
|
|
6670
|
-
import
|
|
7007
|
+
import fs24 from "fs";
|
|
7008
|
+
import path24 from "path";
|
|
6671
7009
|
function generatePage2(schema, cwd, pagesDir, options = {}) {
|
|
6672
|
-
const entityDir =
|
|
6673
|
-
const pageFilePath =
|
|
6674
|
-
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) {
|
|
6675
7013
|
return { files: [] };
|
|
6676
7014
|
}
|
|
6677
7015
|
const plural = pluralize(schema.name);
|
|
@@ -6697,27 +7035,27 @@ export default function ${Plural}Page() {
|
|
|
6697
7035
|
)
|
|
6698
7036
|
}
|
|
6699
7037
|
`;
|
|
6700
|
-
if (!
|
|
6701
|
-
|
|
7038
|
+
if (!fs24.existsSync(entityDir)) {
|
|
7039
|
+
fs24.mkdirSync(entityDir, { recursive: true });
|
|
6702
7040
|
}
|
|
6703
|
-
|
|
7041
|
+
fs24.writeFileSync(pageFilePath, content, "utf-8");
|
|
6704
7042
|
return {
|
|
6705
|
-
files: [
|
|
7043
|
+
files: [path24.join(pagesDir, schema.name, "page.tsx")]
|
|
6706
7044
|
};
|
|
6707
7045
|
}
|
|
6708
7046
|
|
|
6709
7047
|
// src/generators/page-content.ts
|
|
6710
|
-
import
|
|
6711
|
-
import
|
|
7048
|
+
import fs25 from "fs";
|
|
7049
|
+
import path25 from "path";
|
|
6712
7050
|
function generatePageContent2(schema, cwd, pagesDir, options = {}) {
|
|
6713
|
-
const entityDir =
|
|
7051
|
+
const entityDir = path25.join(cwd, pagesDir, schema.name);
|
|
6714
7052
|
const singular = singularize(schema.name);
|
|
6715
7053
|
const plural = pluralize(schema.name);
|
|
6716
7054
|
const Singular = toPascalCase(singular);
|
|
6717
7055
|
const Plural = toPascalCase(plural);
|
|
6718
7056
|
const fileName = `${toKebabCase(plural)}-page-content.tsx`;
|
|
6719
|
-
const filePath =
|
|
6720
|
-
if (
|
|
7057
|
+
const filePath = path25.join(entityDir, fileName);
|
|
7058
|
+
if (fs25.existsSync(filePath) && !options.force) {
|
|
6721
7059
|
return { files: [] };
|
|
6722
7060
|
}
|
|
6723
7061
|
const hasCreate = schema.actions?.create ?? false;
|
|
@@ -6970,22 +7308,22 @@ ${searchLogic}${deleteLogic}
|
|
|
6970
7308
|
)
|
|
6971
7309
|
}
|
|
6972
7310
|
`;
|
|
6973
|
-
if (!
|
|
6974
|
-
|
|
7311
|
+
if (!fs25.existsSync(entityDir)) {
|
|
7312
|
+
fs25.mkdirSync(entityDir, { recursive: true });
|
|
6975
7313
|
}
|
|
6976
|
-
|
|
7314
|
+
fs25.writeFileSync(filePath, content, "utf-8");
|
|
6977
7315
|
return {
|
|
6978
|
-
files: [
|
|
7316
|
+
files: [path25.join(pagesDir, schema.name, fileName)]
|
|
6979
7317
|
};
|
|
6980
7318
|
}
|
|
6981
7319
|
|
|
6982
7320
|
// src/generators/single-page.ts
|
|
6983
|
-
import
|
|
6984
|
-
import
|
|
7321
|
+
import fs26 from "fs";
|
|
7322
|
+
import path26 from "path";
|
|
6985
7323
|
function generateSinglePage(schema, cwd, pagesDir, options = {}) {
|
|
6986
|
-
const entityDir =
|
|
6987
|
-
const pageFilePath =
|
|
6988
|
-
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) {
|
|
6989
7327
|
return { files: [] };
|
|
6990
7328
|
}
|
|
6991
7329
|
const singular = singularize(schema.name);
|
|
@@ -7008,20 +7346,20 @@ export default async function ${PageName}Page() {
|
|
|
7008
7346
|
)
|
|
7009
7347
|
}
|
|
7010
7348
|
`;
|
|
7011
|
-
if (!
|
|
7012
|
-
|
|
7349
|
+
if (!fs26.existsSync(entityDir)) {
|
|
7350
|
+
fs26.mkdirSync(entityDir, { recursive: true });
|
|
7013
7351
|
}
|
|
7014
|
-
|
|
7352
|
+
fs26.writeFileSync(pageFilePath, content, "utf-8");
|
|
7015
7353
|
return {
|
|
7016
|
-
files: [
|
|
7354
|
+
files: [path26.join(pagesDir, schema.name, "page.tsx")]
|
|
7017
7355
|
};
|
|
7018
7356
|
}
|
|
7019
7357
|
|
|
7020
7358
|
// src/generators/table.ts
|
|
7021
|
-
import
|
|
7022
|
-
import
|
|
7359
|
+
import fs27 from "fs";
|
|
7360
|
+
import path27 from "path";
|
|
7023
7361
|
function generateTable2(schema, cwd, pagesDir, options = {}) {
|
|
7024
|
-
const entityDir =
|
|
7362
|
+
const entityDir = path27.join(cwd, pagesDir, schema.name);
|
|
7025
7363
|
const singular = singularize(schema.name);
|
|
7026
7364
|
const plural = pluralize(schema.name);
|
|
7027
7365
|
const Singular = toPascalCase(singular);
|
|
@@ -7029,8 +7367,8 @@ function generateTable2(schema, cwd, pagesDir, options = {}) {
|
|
|
7029
7367
|
const camelPlural = toCamelCase(plural);
|
|
7030
7368
|
const camelSingular = toCamelCase(singular);
|
|
7031
7369
|
const tableFileName = `${toKebabCase(plural)}-table.tsx`;
|
|
7032
|
-
const tableFilePath =
|
|
7033
|
-
if (
|
|
7370
|
+
const tableFilePath = path27.join(entityDir, tableFileName);
|
|
7371
|
+
if (fs27.existsSync(tableFilePath) && !options.force) {
|
|
7034
7372
|
return { files: [] };
|
|
7035
7373
|
}
|
|
7036
7374
|
const hasFilters = schema.filters && schema.filters.length > 0;
|
|
@@ -7394,12 +7732,12 @@ export function ${Plural}Table<TValue>({ columns, selectedIds, setSelectedIds, $
|
|
|
7394
7732
|
)
|
|
7395
7733
|
}
|
|
7396
7734
|
`;
|
|
7397
|
-
if (!
|
|
7398
|
-
|
|
7735
|
+
if (!fs27.existsSync(entityDir)) {
|
|
7736
|
+
fs27.mkdirSync(entityDir, { recursive: true });
|
|
7399
7737
|
}
|
|
7400
|
-
|
|
7738
|
+
fs27.writeFileSync(tableFilePath, content, "utf-8");
|
|
7401
7739
|
return {
|
|
7402
|
-
files: [
|
|
7740
|
+
files: [path27.join(pagesDir, schema.name, tableFileName)]
|
|
7403
7741
|
};
|
|
7404
7742
|
}
|
|
7405
7743
|
|
|
@@ -7557,13 +7895,13 @@ function runSinglePipeline(schema, cwd, config, options = {}) {
|
|
|
7557
7895
|
|
|
7558
7896
|
// src/generators/post-generate.ts
|
|
7559
7897
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
7560
|
-
import
|
|
7561
|
-
import
|
|
7898
|
+
import fs29 from "fs";
|
|
7899
|
+
import path29 from "path";
|
|
7562
7900
|
|
|
7563
7901
|
// src/utils/package-manager.ts
|
|
7564
7902
|
import { execFileSync } from "child_process";
|
|
7565
|
-
import
|
|
7566
|
-
import
|
|
7903
|
+
import fs28 from "fs";
|
|
7904
|
+
import path28 from "path";
|
|
7567
7905
|
var LOCKFILE_MAP = {
|
|
7568
7906
|
"pnpm-lock.yaml": "pnpm",
|
|
7569
7907
|
"package-lock.json": "npm",
|
|
@@ -7572,18 +7910,18 @@ var LOCKFILE_MAP = {
|
|
|
7572
7910
|
"bun.lock": "bun"
|
|
7573
7911
|
};
|
|
7574
7912
|
function detectPackageManager(cwd) {
|
|
7575
|
-
let dir =
|
|
7576
|
-
const root =
|
|
7913
|
+
let dir = path28.resolve(cwd);
|
|
7914
|
+
const root = path28.parse(dir).root;
|
|
7577
7915
|
while (dir !== root) {
|
|
7578
7916
|
for (const [lockfile, pm] of Object.entries(LOCKFILE_MAP)) {
|
|
7579
|
-
if (
|
|
7917
|
+
if (fs28.existsSync(path28.join(dir, lockfile))) {
|
|
7580
7918
|
return pm;
|
|
7581
7919
|
}
|
|
7582
7920
|
}
|
|
7583
|
-
const pkgPath =
|
|
7584
|
-
if (
|
|
7921
|
+
const pkgPath = path28.join(dir, "package.json");
|
|
7922
|
+
if (fs28.existsSync(pkgPath)) {
|
|
7585
7923
|
try {
|
|
7586
|
-
const pkg = JSON.parse(
|
|
7924
|
+
const pkg = JSON.parse(fs28.readFileSync(pkgPath, "utf-8"));
|
|
7587
7925
|
if (typeof pkg.packageManager === "string") {
|
|
7588
7926
|
const name = pkg.packageManager.split("@")[0];
|
|
7589
7927
|
if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
|
|
@@ -7593,7 +7931,7 @@ function detectPackageManager(cwd) {
|
|
|
7593
7931
|
} catch {
|
|
7594
7932
|
}
|
|
7595
7933
|
}
|
|
7596
|
-
dir =
|
|
7934
|
+
dir = path28.dirname(dir);
|
|
7597
7935
|
}
|
|
7598
7936
|
return "npm";
|
|
7599
7937
|
}
|
|
@@ -7624,9 +7962,9 @@ function createNextAppCommand(pm) {
|
|
|
7624
7962
|
|
|
7625
7963
|
// src/generators/post-generate.ts
|
|
7626
7964
|
function loadEnvFile(cwd) {
|
|
7627
|
-
const envPath =
|
|
7628
|
-
if (!
|
|
7629
|
-
const content =
|
|
7965
|
+
const envPath = path29.join(cwd, ".env.local");
|
|
7966
|
+
if (!fs29.existsSync(envPath)) return;
|
|
7967
|
+
const content = fs29.readFileSync(envPath, "utf-8");
|
|
7630
7968
|
for (const line of content.split("\n")) {
|
|
7631
7969
|
const trimmed = line.trim();
|
|
7632
7970
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -7656,10 +7994,10 @@ function runPmScript(pm, script, cwd) {
|
|
|
7656
7994
|
}
|
|
7657
7995
|
}
|
|
7658
7996
|
function hasPkgScript(cwd, script) {
|
|
7659
|
-
const pkgPath =
|
|
7660
|
-
if (!
|
|
7997
|
+
const pkgPath = path29.join(cwd, "package.json");
|
|
7998
|
+
if (!fs29.existsSync(pkgPath)) return false;
|
|
7661
7999
|
try {
|
|
7662
|
-
const pkg = JSON.parse(
|
|
8000
|
+
const pkg = JSON.parse(fs29.readFileSync(pkgPath, "utf-8"));
|
|
7663
8001
|
const scripts = pkg.scripts;
|
|
7664
8002
|
return !!scripts?.[script];
|
|
7665
8003
|
} catch {
|
|
@@ -7686,7 +8024,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7686
8024
|
console.log(ok ? " Database schema synced" : " Database push failed (run db:push manually)");
|
|
7687
8025
|
} else {
|
|
7688
8026
|
console.log("\n Running drizzle-kit push...");
|
|
7689
|
-
const drizzleBin =
|
|
8027
|
+
const drizzleBin = path29.join(cwd, "node_modules", ".bin", "drizzle-kit");
|
|
7690
8028
|
try {
|
|
7691
8029
|
execFileSync2(drizzleBin, ["push", "--force"], { cwd, stdio: "inherit" });
|
|
7692
8030
|
result.dbPush = "success";
|
|
@@ -7705,7 +8043,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7705
8043
|
result.lintFix = ok ? "success" : "failed";
|
|
7706
8044
|
console.log(ok ? " Code formatted" : " Lint fix had issues (run lint:fix manually)");
|
|
7707
8045
|
} else {
|
|
7708
|
-
const biomeBin =
|
|
8046
|
+
const biomeBin = path29.join(cwd, "node_modules", ".bin", "biome");
|
|
7709
8047
|
try {
|
|
7710
8048
|
execFileSync2(biomeBin, ["check", "--write", "."], { cwd, stdio: "pipe" });
|
|
7711
8049
|
result.lintFix = "success";
|
|
@@ -7728,7 +8066,7 @@ function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
7728
8066
|
// src/commands/generate.ts
|
|
7729
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(
|
|
7730
8068
|
async (schemaName, options) => {
|
|
7731
|
-
const cwd = options.cwd ?
|
|
8069
|
+
const cwd = options.cwd ? path30.resolve(options.cwd) : process.cwd();
|
|
7732
8070
|
console.log("\n BetterStart Generator\n");
|
|
7733
8071
|
let config;
|
|
7734
8072
|
try {
|
|
@@ -7737,7 +8075,7 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7737
8075
|
console.error(` Error loading config: ${err instanceof Error ? err.message : String(err)}`);
|
|
7738
8076
|
process.exit(1);
|
|
7739
8077
|
}
|
|
7740
|
-
const schemasDir =
|
|
8078
|
+
const schemasDir = path30.join(cwd, config.paths?.schemas ?? "./cms/schemas");
|
|
7741
8079
|
console.log(` Project root: ${cwd}`);
|
|
7742
8080
|
console.log(` Loading schema: ${schemaName}.json
|
|
7743
8081
|
`);
|
|
@@ -7749,7 +8087,7 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7749
8087
|
console.error(` ${err.message}`);
|
|
7750
8088
|
console.error(
|
|
7751
8089
|
`
|
|
7752
|
-
Create a schema file at: ${
|
|
8090
|
+
Create a schema file at: ${path30.join(schemasDir, `${schemaName}.json`)}`
|
|
7753
8091
|
);
|
|
7754
8092
|
} else {
|
|
7755
8093
|
console.error(
|
|
@@ -7842,8 +8180,8 @@ var generateCommand = new Command("generate").alias("g").description("Generate e
|
|
|
7842
8180
|
|
|
7843
8181
|
// src/commands/init.ts
|
|
7844
8182
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
7845
|
-
import
|
|
7846
|
-
import
|
|
8183
|
+
import fs40 from "fs";
|
|
8184
|
+
import path45 from "path";
|
|
7847
8185
|
import * as p4 from "@clack/prompts";
|
|
7848
8186
|
import { Command as Command3 } from "commander";
|
|
7849
8187
|
import pc2 from "picocolors";
|
|
@@ -7986,21 +8324,21 @@ async function promptProject(defaultName) {
|
|
|
7986
8324
|
}
|
|
7987
8325
|
|
|
7988
8326
|
// src/init/scaffolders/api-routes.ts
|
|
7989
|
-
import
|
|
8327
|
+
import path32 from "path";
|
|
7990
8328
|
|
|
7991
8329
|
// src/utils/fs.ts
|
|
7992
|
-
import
|
|
7993
|
-
import
|
|
8330
|
+
import fs30 from "fs";
|
|
8331
|
+
import path31 from "path";
|
|
7994
8332
|
import fse from "fs-extra";
|
|
7995
8333
|
function ensureDir(dirPath) {
|
|
7996
8334
|
fse.ensureDirSync(dirPath);
|
|
7997
8335
|
}
|
|
7998
8336
|
function safeWriteFile(filePath, content, force = false) {
|
|
7999
|
-
if (!force &&
|
|
8337
|
+
if (!force && fs30.existsSync(filePath)) {
|
|
8000
8338
|
return false;
|
|
8001
8339
|
}
|
|
8002
|
-
ensureDir(
|
|
8003
|
-
|
|
8340
|
+
ensureDir(path31.dirname(filePath));
|
|
8341
|
+
fs30.writeFileSync(filePath, content, "utf-8");
|
|
8004
8342
|
return true;
|
|
8005
8343
|
}
|
|
8006
8344
|
|
|
@@ -8094,21 +8432,21 @@ export async function POST(request: NextRequest) {
|
|
|
8094
8432
|
// src/init/scaffolders/api-routes.ts
|
|
8095
8433
|
function scaffoldApiRoutes({ cwd, config }) {
|
|
8096
8434
|
const created = [];
|
|
8097
|
-
const apiDir =
|
|
8435
|
+
const apiDir = path32.resolve(cwd, config.paths.api);
|
|
8098
8436
|
function write(relPath, content) {
|
|
8099
|
-
const fullPath =
|
|
8100
|
-
ensureDir(
|
|
8437
|
+
const fullPath = path32.join(apiDir, relPath);
|
|
8438
|
+
ensureDir(path32.dirname(fullPath));
|
|
8101
8439
|
if (safeWriteFile(fullPath, content)) {
|
|
8102
|
-
created.push(
|
|
8440
|
+
created.push(path32.join(config.paths.api, relPath));
|
|
8103
8441
|
}
|
|
8104
8442
|
}
|
|
8105
|
-
write(
|
|
8106
|
-
write(
|
|
8443
|
+
write(path32.join("auth", "[...all]", "route.ts"), authRouteTemplate());
|
|
8444
|
+
write(path32.join("upload", "route.ts"), uploadRouteTemplate());
|
|
8107
8445
|
return created;
|
|
8108
8446
|
}
|
|
8109
8447
|
|
|
8110
8448
|
// src/init/scaffolders/auth.ts
|
|
8111
|
-
import
|
|
8449
|
+
import path33 from "path";
|
|
8112
8450
|
|
|
8113
8451
|
// src/init/templates/lib/auth/auth.ts
|
|
8114
8452
|
function authTemplate() {
|
|
@@ -8227,11 +8565,11 @@ export async function requireRole(allowedRoles: UserRole[]): Promise<User> {
|
|
|
8227
8565
|
// src/init/scaffolders/auth.ts
|
|
8228
8566
|
function scaffoldAuth({ cwd, config }) {
|
|
8229
8567
|
const created = [];
|
|
8230
|
-
const authDir =
|
|
8568
|
+
const authDir = path33.resolve(cwd, config.paths.cms, "lib", "auth");
|
|
8231
8569
|
function write(filename, content) {
|
|
8232
|
-
const fullPath =
|
|
8570
|
+
const fullPath = path33.join(authDir, filename);
|
|
8233
8571
|
if (safeWriteFile(fullPath, content)) {
|
|
8234
|
-
created.push(
|
|
8572
|
+
created.push(path33.join(config.paths.cms, "lib", "auth", filename));
|
|
8235
8573
|
}
|
|
8236
8574
|
}
|
|
8237
8575
|
write("auth.ts", authTemplate());
|
|
@@ -8241,51 +8579,51 @@ function scaffoldAuth({ cwd, config }) {
|
|
|
8241
8579
|
}
|
|
8242
8580
|
|
|
8243
8581
|
// src/init/scaffolders/base.ts
|
|
8244
|
-
import
|
|
8245
|
-
import
|
|
8582
|
+
import fs31 from "fs";
|
|
8583
|
+
import path34 from "path";
|
|
8246
8584
|
function scaffoldBase({ cwd, config }) {
|
|
8247
8585
|
const created = [];
|
|
8248
8586
|
const cmsDirs = [
|
|
8249
8587
|
config.paths.cms,
|
|
8250
8588
|
config.paths.schemas,
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
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")
|
|
8268
8606
|
];
|
|
8269
8607
|
for (const dir of cmsDirs) {
|
|
8270
|
-
ensureDir(
|
|
8608
|
+
ensureDir(path34.resolve(cwd, dir));
|
|
8271
8609
|
}
|
|
8272
8610
|
const appDirs = [config.paths.pages, config.paths.login, config.paths.api];
|
|
8273
8611
|
for (const dir of appDirs) {
|
|
8274
|
-
ensureDir(
|
|
8612
|
+
ensureDir(path34.resolve(cwd, dir));
|
|
8275
8613
|
}
|
|
8276
8614
|
const configContent = generateConfigFile(config);
|
|
8277
|
-
if (safeWriteFile(
|
|
8615
|
+
if (safeWriteFile(path34.resolve(cwd, "cms.config.ts"), configContent)) {
|
|
8278
8616
|
created.push("cms.config.ts");
|
|
8279
8617
|
}
|
|
8280
8618
|
const cmsDoc = generateCmsDoc(config, {});
|
|
8281
|
-
if (safeWriteFile(
|
|
8619
|
+
if (safeWriteFile(path34.resolve(cwd, "CMS.md"), cmsDoc)) {
|
|
8282
8620
|
created.push("CMS.md");
|
|
8283
8621
|
}
|
|
8284
8622
|
return created;
|
|
8285
8623
|
}
|
|
8286
8624
|
function regenerateCmsDoc(cwd, config, options) {
|
|
8287
8625
|
const content = generateCmsDoc(config, options);
|
|
8288
|
-
|
|
8626
|
+
fs31.writeFileSync(path34.resolve(cwd, "CMS.md"), content, "utf-8");
|
|
8289
8627
|
}
|
|
8290
8628
|
function generateConfigFile(config) {
|
|
8291
8629
|
return `import { defineConfig } from '@betterstart/cli'
|
|
@@ -8439,8 +8777,8 @@ Edit \`cms.config.ts\` to customize paths, database provider, and features.`);
|
|
|
8439
8777
|
}
|
|
8440
8778
|
|
|
8441
8779
|
// src/init/scaffolders/biome.ts
|
|
8442
|
-
import
|
|
8443
|
-
import
|
|
8780
|
+
import fs32 from "fs";
|
|
8781
|
+
import path35 from "path";
|
|
8444
8782
|
function scaffoldBiome(cwd, linter) {
|
|
8445
8783
|
if (linter.type !== "none") {
|
|
8446
8784
|
return {
|
|
@@ -8448,8 +8786,8 @@ function scaffoldBiome(cwd, linter) {
|
|
|
8448
8786
|
skippedReason: `${linter.type} already configured (${linter.configFile})`
|
|
8449
8787
|
};
|
|
8450
8788
|
}
|
|
8451
|
-
const configPath =
|
|
8452
|
-
if (
|
|
8789
|
+
const configPath = path35.join(cwd, "biome.json");
|
|
8790
|
+
if (fs32.existsSync(configPath)) {
|
|
8453
8791
|
return { installed: false, skippedReason: "biome.json already exists" };
|
|
8454
8792
|
}
|
|
8455
8793
|
const config = {
|
|
@@ -8500,52 +8838,52 @@ function scaffoldBiome(cwd, linter) {
|
|
|
8500
8838
|
]
|
|
8501
8839
|
}
|
|
8502
8840
|
};
|
|
8503
|
-
|
|
8841
|
+
fs32.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
8504
8842
|
`, "utf-8");
|
|
8505
8843
|
return { installed: true, skippedReason: null };
|
|
8506
8844
|
}
|
|
8507
8845
|
|
|
8508
8846
|
// src/init/scaffolders/components.ts
|
|
8509
|
-
import
|
|
8510
|
-
import
|
|
8847
|
+
import path37 from "path";
|
|
8848
|
+
import fs34 from "fs-extra";
|
|
8511
8849
|
|
|
8512
8850
|
// src/utils/detect.ts
|
|
8513
|
-
import
|
|
8514
|
-
import
|
|
8851
|
+
import fs33 from "fs";
|
|
8852
|
+
import path36 from "path";
|
|
8515
8853
|
var NEXT_CONFIG_FILES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
8516
8854
|
function detectProjectName(cwd) {
|
|
8517
|
-
const pkgPath =
|
|
8518
|
-
if (
|
|
8855
|
+
const pkgPath = path36.join(cwd, "package.json");
|
|
8856
|
+
if (fs33.existsSync(pkgPath)) {
|
|
8519
8857
|
try {
|
|
8520
|
-
const pkg = JSON.parse(
|
|
8858
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
8521
8859
|
if (typeof pkg.name === "string" && pkg.name.length > 0) {
|
|
8522
8860
|
return formatProjectName(pkg.name);
|
|
8523
8861
|
}
|
|
8524
8862
|
} catch {
|
|
8525
8863
|
}
|
|
8526
8864
|
}
|
|
8527
|
-
return formatProjectName(
|
|
8865
|
+
return formatProjectName(path36.basename(cwd));
|
|
8528
8866
|
}
|
|
8529
8867
|
function formatProjectName(name) {
|
|
8530
8868
|
const base = name.includes("/") ? name.split("/").pop() : name;
|
|
8531
8869
|
return base.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
8532
8870
|
}
|
|
8533
8871
|
function detectProject(cwd) {
|
|
8534
|
-
const isExisting = NEXT_CONFIG_FILES.some((f) =>
|
|
8535
|
-
const hasSrcDir =
|
|
8536
|
-
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"));
|
|
8537
8875
|
const hasTailwind = detectTailwind(cwd);
|
|
8538
8876
|
const linter = detectLinter(cwd);
|
|
8539
8877
|
const conflicts = [];
|
|
8540
8878
|
if (isExisting) {
|
|
8541
|
-
if (
|
|
8879
|
+
if (fs33.existsSync(path36.join(cwd, "cms"))) {
|
|
8542
8880
|
conflicts.push("cms/ directory already exists");
|
|
8543
8881
|
}
|
|
8544
|
-
if (
|
|
8882
|
+
if (fs33.existsSync(path36.join(cwd, "cms.config.ts"))) {
|
|
8545
8883
|
conflicts.push("cms.config.ts already exists");
|
|
8546
8884
|
}
|
|
8547
8885
|
const appBase = hasSrcDir ? "src/app" : "app";
|
|
8548
|
-
if (
|
|
8886
|
+
if (fs33.existsSync(path36.join(cwd, appBase, "(cms)"))) {
|
|
8549
8887
|
conflicts.push(`${appBase}/(cms)/ route group already exists`);
|
|
8550
8888
|
}
|
|
8551
8889
|
if (hasTsconfigCmsAliases(cwd)) {
|
|
@@ -8572,19 +8910,19 @@ var ESLINT_CONFIG_FILES = [
|
|
|
8572
8910
|
];
|
|
8573
8911
|
function detectLinter(cwd) {
|
|
8574
8912
|
for (const f of BIOME_CONFIG_FILES) {
|
|
8575
|
-
if (
|
|
8913
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8576
8914
|
return { type: "biome", configFile: f };
|
|
8577
8915
|
}
|
|
8578
8916
|
}
|
|
8579
8917
|
for (const f of ESLINT_CONFIG_FILES) {
|
|
8580
|
-
if (
|
|
8918
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8581
8919
|
return { type: "eslint", configFile: f };
|
|
8582
8920
|
}
|
|
8583
8921
|
}
|
|
8584
|
-
const pkgPath =
|
|
8585
|
-
if (
|
|
8922
|
+
const pkgPath = path36.join(cwd, "package.json");
|
|
8923
|
+
if (fs33.existsSync(pkgPath)) {
|
|
8586
8924
|
try {
|
|
8587
|
-
const pkg = JSON.parse(
|
|
8925
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
8588
8926
|
if (pkg.eslintConfig) {
|
|
8589
8927
|
return { type: "eslint", configFile: "package.json (eslintConfig)" };
|
|
8590
8928
|
}
|
|
@@ -8595,14 +8933,14 @@ function detectLinter(cwd) {
|
|
|
8595
8933
|
}
|
|
8596
8934
|
function detectTailwind(cwd) {
|
|
8597
8935
|
const cssFiles = ["globals.css", "app.css", "index.css"].flatMap((f) => [
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8936
|
+
path36.join(cwd, "src", "app", f),
|
|
8937
|
+
path36.join(cwd, "app", f),
|
|
8938
|
+
path36.join(cwd, "src", f),
|
|
8939
|
+
path36.join(cwd, f)
|
|
8602
8940
|
]);
|
|
8603
8941
|
for (const cssFile of cssFiles) {
|
|
8604
|
-
if (
|
|
8605
|
-
const content =
|
|
8942
|
+
if (fs33.existsSync(cssFile)) {
|
|
8943
|
+
const content = fs33.readFileSync(cssFile, "utf-8");
|
|
8606
8944
|
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'") || content.includes("@theme")) {
|
|
8607
8945
|
return true;
|
|
8608
8946
|
}
|
|
@@ -8610,8 +8948,8 @@ function detectTailwind(cwd) {
|
|
|
8610
8948
|
}
|
|
8611
8949
|
const postcssFiles = ["postcss.config.js", "postcss.config.mjs", "postcss.config.cjs"];
|
|
8612
8950
|
for (const f of postcssFiles) {
|
|
8613
|
-
if (
|
|
8614
|
-
const content =
|
|
8951
|
+
if (fs33.existsSync(path36.join(cwd, f))) {
|
|
8952
|
+
const content = fs33.readFileSync(path36.join(cwd, f), "utf-8");
|
|
8615
8953
|
if (content.includes("tailwindcss") || content.includes("@tailwindcss")) {
|
|
8616
8954
|
return true;
|
|
8617
8955
|
}
|
|
@@ -8620,20 +8958,20 @@ function detectTailwind(cwd) {
|
|
|
8620
8958
|
return false;
|
|
8621
8959
|
}
|
|
8622
8960
|
function hasTsconfigCmsAliases(cwd) {
|
|
8623
|
-
const tsconfigPath =
|
|
8624
|
-
if (!
|
|
8961
|
+
const tsconfigPath = path36.join(cwd, "tsconfig.json");
|
|
8962
|
+
if (!fs33.existsSync(tsconfigPath)) return false;
|
|
8625
8963
|
try {
|
|
8626
|
-
const content =
|
|
8964
|
+
const content = fs33.readFileSync(tsconfigPath, "utf-8");
|
|
8627
8965
|
return content.includes("@cms/");
|
|
8628
8966
|
} catch {
|
|
8629
8967
|
return false;
|
|
8630
8968
|
}
|
|
8631
8969
|
}
|
|
8632
8970
|
function hasEnvBetterstartVars(cwd) {
|
|
8633
|
-
const envPath =
|
|
8634
|
-
if (!
|
|
8971
|
+
const envPath = path36.join(cwd, ".env.local");
|
|
8972
|
+
if (!fs33.existsSync(envPath)) return false;
|
|
8635
8973
|
try {
|
|
8636
|
-
const content =
|
|
8974
|
+
const content = fs33.readFileSync(envPath, "utf-8");
|
|
8637
8975
|
return content.includes("BETTERSTART_");
|
|
8638
8976
|
} catch {
|
|
8639
8977
|
return false;
|
|
@@ -11332,12 +11670,12 @@ export function addToMailchimpAudience(email: string): void {
|
|
|
11332
11670
|
|
|
11333
11671
|
// src/init/scaffolders/components.ts
|
|
11334
11672
|
function scaffoldComponents({ cwd, config }) {
|
|
11335
|
-
const cms =
|
|
11673
|
+
const cms = path37.resolve(cwd, config.paths.cms);
|
|
11336
11674
|
const created = [];
|
|
11337
11675
|
function write(relPath, content) {
|
|
11338
|
-
const fullPath =
|
|
11676
|
+
const fullPath = path37.join(cms, relPath);
|
|
11339
11677
|
if (safeWriteFile(fullPath, content)) {
|
|
11340
|
-
created.push(
|
|
11678
|
+
created.push(path37.join(config.paths.cms, relPath));
|
|
11341
11679
|
}
|
|
11342
11680
|
}
|
|
11343
11681
|
write("cms-globals.css", cmsGlobalsCssTemplate());
|
|
@@ -11386,18 +11724,18 @@ function scaffoldComponents({ cwd, config }) {
|
|
|
11386
11724
|
}
|
|
11387
11725
|
function copyUiTemplates(cwd, config) {
|
|
11388
11726
|
const created = [];
|
|
11389
|
-
const destDir =
|
|
11727
|
+
const destDir = path37.resolve(cwd, config.paths.cms, "components", "ui");
|
|
11390
11728
|
const cliRoot = findCliRoot();
|
|
11391
|
-
const srcDir =
|
|
11392
|
-
if (!
|
|
11729
|
+
const srcDir = path37.join(cliRoot, "templates", "ui");
|
|
11730
|
+
if (!fs34.existsSync(srcDir)) {
|
|
11393
11731
|
return created;
|
|
11394
11732
|
}
|
|
11395
|
-
const files =
|
|
11733
|
+
const files = fs34.readdirSync(srcDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
|
|
11396
11734
|
for (const file of files) {
|
|
11397
|
-
const destPath =
|
|
11398
|
-
if (!
|
|
11399
|
-
|
|
11400
|
-
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));
|
|
11401
11739
|
}
|
|
11402
11740
|
}
|
|
11403
11741
|
return created;
|
|
@@ -11405,25 +11743,25 @@ function copyUiTemplates(cwd, config) {
|
|
|
11405
11743
|
function copyTiptapTemplates(cwd, config) {
|
|
11406
11744
|
const created = [];
|
|
11407
11745
|
const cliRoot = findCliRoot();
|
|
11408
|
-
const srcDir =
|
|
11409
|
-
const destDir =
|
|
11410
|
-
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)) {
|
|
11411
11749
|
return created;
|
|
11412
11750
|
}
|
|
11413
11751
|
copyDirRecursive(srcDir, destDir, config.paths.cms, created);
|
|
11414
11752
|
return created;
|
|
11415
11753
|
}
|
|
11416
11754
|
function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
11417
|
-
|
|
11418
|
-
const entries =
|
|
11755
|
+
fs34.ensureDirSync(dest);
|
|
11756
|
+
const entries = fs34.readdirSync(src, { withFileTypes: true });
|
|
11419
11757
|
for (const entry of entries) {
|
|
11420
|
-
const srcPath =
|
|
11421
|
-
const destPath =
|
|
11758
|
+
const srcPath = path37.join(src, entry.name);
|
|
11759
|
+
const destPath = path37.join(dest, entry.name);
|
|
11422
11760
|
if (entry.isDirectory()) {
|
|
11423
11761
|
copyDirRecursive(srcPath, destPath, cmsPrefix, created);
|
|
11424
|
-
} else if (!
|
|
11425
|
-
|
|
11426
|
-
const relFromCms =
|
|
11762
|
+
} else if (!fs34.existsSync(destPath)) {
|
|
11763
|
+
fs34.copyFileSync(srcPath, destPath);
|
|
11764
|
+
const relFromCms = path37.relative(path37.resolve(dest, "..", "..", "..", ".."), destPath);
|
|
11427
11765
|
created.push(relFromCms);
|
|
11428
11766
|
}
|
|
11429
11767
|
}
|
|
@@ -11431,35 +11769,35 @@ function copyDirRecursive(src, dest, cmsPrefix, created) {
|
|
|
11431
11769
|
function copySchemaMetaschema(cwd, config) {
|
|
11432
11770
|
const created = [];
|
|
11433
11771
|
const cliRoot = findCliRoot();
|
|
11434
|
-
const srcPath =
|
|
11435
|
-
const destPath =
|
|
11436
|
-
if (
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
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"));
|
|
11440
11778
|
}
|
|
11441
11779
|
return created;
|
|
11442
11780
|
}
|
|
11443
11781
|
function findCliRoot() {
|
|
11444
11782
|
let dir = new URL(".", import.meta.url).pathname;
|
|
11445
11783
|
for (let i = 0; i < 5; i++) {
|
|
11446
|
-
const pkgPath =
|
|
11447
|
-
if (
|
|
11784
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
11785
|
+
if (fs34.existsSync(pkgPath)) {
|
|
11448
11786
|
try {
|
|
11449
|
-
const pkg = JSON.parse(
|
|
11787
|
+
const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf-8"));
|
|
11450
11788
|
if (pkg.name === "@betterstart/cli") {
|
|
11451
11789
|
return dir;
|
|
11452
11790
|
}
|
|
11453
11791
|
} catch {
|
|
11454
11792
|
}
|
|
11455
11793
|
}
|
|
11456
|
-
dir =
|
|
11794
|
+
dir = path37.dirname(dir);
|
|
11457
11795
|
}
|
|
11458
|
-
return
|
|
11796
|
+
return path37.resolve(new URL(".", import.meta.url).pathname, "..", "..");
|
|
11459
11797
|
}
|
|
11460
11798
|
|
|
11461
11799
|
// src/init/scaffolders/database.ts
|
|
11462
|
-
import
|
|
11800
|
+
import path38 from "path";
|
|
11463
11801
|
|
|
11464
11802
|
// src/init/templates/db/client.ts
|
|
11465
11803
|
function dbClientTemplate() {
|
|
@@ -11570,16 +11908,16 @@ export const formSettings = pgTable(
|
|
|
11570
11908
|
// src/init/scaffolders/database.ts
|
|
11571
11909
|
function scaffoldDatabase({ cwd, config }) {
|
|
11572
11910
|
const created = [];
|
|
11573
|
-
const dbDir =
|
|
11911
|
+
const dbDir = path38.resolve(cwd, config.paths.cms, "db");
|
|
11574
11912
|
function write(filename, content) {
|
|
11575
|
-
const fullPath =
|
|
11913
|
+
const fullPath = path38.join(dbDir, filename);
|
|
11576
11914
|
if (safeWriteFile(fullPath, content)) {
|
|
11577
|
-
created.push(
|
|
11915
|
+
created.push(path38.join(config.paths.cms, "db", filename));
|
|
11578
11916
|
}
|
|
11579
11917
|
}
|
|
11580
11918
|
write("client.ts", dbClientTemplate());
|
|
11581
11919
|
write("schema.ts", dbSchemaTemplate());
|
|
11582
|
-
const drizzleConfigPath =
|
|
11920
|
+
const drizzleConfigPath = path38.resolve(cwd, "drizzle.config.ts");
|
|
11583
11921
|
if (safeWriteFile(drizzleConfigPath, drizzleConfigTemplate())) {
|
|
11584
11922
|
created.push("drizzle.config.ts");
|
|
11585
11923
|
}
|
|
@@ -11751,11 +12089,11 @@ import { existsSync, readFileSync } from "fs";
|
|
|
11751
12089
|
import { join } from "path";
|
|
11752
12090
|
|
|
11753
12091
|
// src/utils/env.ts
|
|
11754
|
-
import
|
|
11755
|
-
import
|
|
12092
|
+
import fs35 from "fs";
|
|
12093
|
+
import path39 from "path";
|
|
11756
12094
|
function appendEnvVars(cwd, sections, overwrite) {
|
|
11757
|
-
const envPath =
|
|
11758
|
-
let existing =
|
|
12095
|
+
const envPath = path39.join(cwd, ".env.local");
|
|
12096
|
+
let existing = fs35.existsSync(envPath) ? fs35.readFileSync(envPath, "utf-8") : "";
|
|
11759
12097
|
const existingKeys = new Set(
|
|
11760
12098
|
existing.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#")).map((line) => line.split("=")[0]?.trim()).filter(Boolean)
|
|
11761
12099
|
);
|
|
@@ -11799,7 +12137,7 @@ function appendEnvVars(cwd, sections, overwrite) {
|
|
|
11799
12137
|
const header = existing.trim() ? "" : "# ============================================\n# BetterStart CMS\n# ============================================\n";
|
|
11800
12138
|
const content = existing.trim() ? `${existing.trimEnd()}
|
|
11801
12139
|
${lines.join("\n")}` : header + lines.join("\n");
|
|
11802
|
-
|
|
12140
|
+
fs35.writeFileSync(envPath, content);
|
|
11803
12141
|
}
|
|
11804
12142
|
return { added, skipped, updated };
|
|
11805
12143
|
}
|
|
@@ -11878,7 +12216,7 @@ function scaffoldEnv(cwd, options) {
|
|
|
11878
12216
|
}
|
|
11879
12217
|
|
|
11880
12218
|
// src/init/scaffolders/layout.ts
|
|
11881
|
-
import
|
|
12219
|
+
import path40 from "path";
|
|
11882
12220
|
|
|
11883
12221
|
// src/init/templates/pages/authenticated-layout.ts
|
|
11884
12222
|
function authenticatedLayoutTemplate() {
|
|
@@ -12765,30 +13103,30 @@ export function UsersTable<TValue>({ columns }: UsersTableProps<TValue>) {
|
|
|
12765
13103
|
function scaffoldLayout({ cwd, config }) {
|
|
12766
13104
|
const created = [];
|
|
12767
13105
|
function write(relPath, content) {
|
|
12768
|
-
const fullPath =
|
|
12769
|
-
ensureDir(
|
|
13106
|
+
const fullPath = path40.resolve(cwd, relPath);
|
|
13107
|
+
ensureDir(path40.dirname(fullPath));
|
|
12770
13108
|
if (safeWriteFile(fullPath, content)) {
|
|
12771
13109
|
created.push(relPath);
|
|
12772
13110
|
}
|
|
12773
13111
|
}
|
|
12774
|
-
const cmsDir =
|
|
12775
|
-
write(
|
|
12776
|
-
write(
|
|
12777
|
-
write(
|
|
12778
|
-
write(
|
|
12779
|
-
write(
|
|
12780
|
-
const usersDir =
|
|
12781
|
-
write(
|
|
12782
|
-
write(
|
|
12783
|
-
write(
|
|
12784
|
-
write(
|
|
12785
|
-
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());
|
|
12786
13124
|
return created;
|
|
12787
13125
|
}
|
|
12788
13126
|
|
|
12789
13127
|
// src/init/scaffolders/preset.ts
|
|
12790
|
-
import
|
|
12791
|
-
import
|
|
13128
|
+
import fs36 from "fs";
|
|
13129
|
+
import path41 from "path";
|
|
12792
13130
|
|
|
12793
13131
|
// src/init/templates/presets/blog-categories.ts
|
|
12794
13132
|
function blogCategoriesSchema() {
|
|
@@ -13193,15 +13531,15 @@ function scaffoldPreset({
|
|
|
13193
13531
|
generatedFiles: [],
|
|
13194
13532
|
errors: []
|
|
13195
13533
|
};
|
|
13196
|
-
const schemasDir =
|
|
13534
|
+
const schemasDir = path41.join(cwd, config.paths?.schemas ?? "./cms/schemas");
|
|
13197
13535
|
const presetSchemas = getPresetSchemas(preset);
|
|
13198
13536
|
for (const ps of presetSchemas) {
|
|
13199
|
-
const filePath =
|
|
13200
|
-
const dir =
|
|
13201
|
-
if (!
|
|
13202
|
-
|
|
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 });
|
|
13203
13541
|
}
|
|
13204
|
-
|
|
13542
|
+
fs36.writeFileSync(filePath, ps.content, "utf-8");
|
|
13205
13543
|
result.schemas.push(ps.filename);
|
|
13206
13544
|
}
|
|
13207
13545
|
for (const ps of presetSchemas) {
|
|
@@ -13242,8 +13580,8 @@ function scaffoldPreset({
|
|
|
13242
13580
|
}
|
|
13243
13581
|
|
|
13244
13582
|
// src/init/scaffolders/tailwind.ts
|
|
13245
|
-
import
|
|
13246
|
-
import
|
|
13583
|
+
import fs37 from "fs";
|
|
13584
|
+
import path42 from "path";
|
|
13247
13585
|
var SOURCE_LINES = ['@source "../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
13248
13586
|
var SOURCE_LINES_SRC = ['@source "../../cms/**/*.{ts,tsx}";', '@source "./(cms)/**/*.{ts,tsx}";'];
|
|
13249
13587
|
var CMS_THEME_BLOCK = `
|
|
@@ -13298,8 +13636,8 @@ function findMainCss(cwd) {
|
|
|
13298
13636
|
"globals.css"
|
|
13299
13637
|
];
|
|
13300
13638
|
for (const candidate of candidates) {
|
|
13301
|
-
const filePath =
|
|
13302
|
-
if (
|
|
13639
|
+
const filePath = path42.join(cwd, candidate);
|
|
13640
|
+
if (fs37.existsSync(filePath)) {
|
|
13303
13641
|
return filePath;
|
|
13304
13642
|
}
|
|
13305
13643
|
}
|
|
@@ -13310,7 +13648,7 @@ function scaffoldTailwind(cwd, hasSrcDir) {
|
|
|
13310
13648
|
if (!cssFile) {
|
|
13311
13649
|
return { file: null, appended: false };
|
|
13312
13650
|
}
|
|
13313
|
-
let content =
|
|
13651
|
+
let content = fs37.readFileSync(cssFile, "utf-8");
|
|
13314
13652
|
let changed = false;
|
|
13315
13653
|
const sourceLines = hasSrcDir ? SOURCE_LINES_SRC : SOURCE_LINES;
|
|
13316
13654
|
const missingLines = sourceLines.filter((sl) => !content.includes(sl));
|
|
@@ -13362,14 +13700,14 @@ ${CMS_THEME_BLOCK}
|
|
|
13362
13700
|
}
|
|
13363
13701
|
}
|
|
13364
13702
|
if (changed) {
|
|
13365
|
-
|
|
13703
|
+
fs37.writeFileSync(cssFile, content, "utf-8");
|
|
13366
13704
|
}
|
|
13367
13705
|
return { file: cssFile, appended: changed };
|
|
13368
13706
|
}
|
|
13369
13707
|
|
|
13370
13708
|
// src/init/scaffolders/tsconfig.ts
|
|
13371
|
-
import
|
|
13372
|
-
import
|
|
13709
|
+
import fs38 from "fs";
|
|
13710
|
+
import path43 from "path";
|
|
13373
13711
|
function stripJsonComments(input) {
|
|
13374
13712
|
let result = "";
|
|
13375
13713
|
let i = 0;
|
|
@@ -13419,14 +13757,14 @@ var CMS_PATH_ALIASES = {
|
|
|
13419
13757
|
"@cms/cache/*": ["./cms/lib/cache/*"]
|
|
13420
13758
|
};
|
|
13421
13759
|
function scaffoldTsconfig(cwd) {
|
|
13422
|
-
const tsconfigPath =
|
|
13760
|
+
const tsconfigPath = path43.join(cwd, "tsconfig.json");
|
|
13423
13761
|
const added = [];
|
|
13424
13762
|
const skipped = [];
|
|
13425
|
-
if (!
|
|
13763
|
+
if (!fs38.existsSync(tsconfigPath)) {
|
|
13426
13764
|
skipped.push("tsconfig.json not found");
|
|
13427
13765
|
return { added, skipped };
|
|
13428
13766
|
}
|
|
13429
|
-
const raw =
|
|
13767
|
+
const raw = fs38.readFileSync(tsconfigPath, "utf-8");
|
|
13430
13768
|
const stripped = stripJsonComments(raw).replace(/,\s*([\]}])/g, "$1");
|
|
13431
13769
|
let tsconfig;
|
|
13432
13770
|
try {
|
|
@@ -13447,14 +13785,14 @@ function scaffoldTsconfig(cwd) {
|
|
|
13447
13785
|
}
|
|
13448
13786
|
compilerOptions.paths = paths;
|
|
13449
13787
|
tsconfig.compilerOptions = compilerOptions;
|
|
13450
|
-
|
|
13788
|
+
fs38.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
13451
13789
|
`, "utf-8");
|
|
13452
13790
|
return { added, skipped };
|
|
13453
13791
|
}
|
|
13454
13792
|
|
|
13455
13793
|
// src/commands/seed.ts
|
|
13456
|
-
import
|
|
13457
|
-
import
|
|
13794
|
+
import fs39 from "fs";
|
|
13795
|
+
import path44 from "path";
|
|
13458
13796
|
import * as clack from "@clack/prompts";
|
|
13459
13797
|
import { Command as Command2 } from "commander";
|
|
13460
13798
|
function buildSeedScript() {
|
|
@@ -13573,7 +13911,7 @@ main().catch((err) => {
|
|
|
13573
13911
|
`;
|
|
13574
13912
|
}
|
|
13575
13913
|
var seedCommand = new Command2("seed").description("Create the initial admin user").option("--cwd <path>", "Project root path").action(async (options) => {
|
|
13576
|
-
const cwd = options.cwd ?
|
|
13914
|
+
const cwd = options.cwd ? path44.resolve(options.cwd) : process.cwd();
|
|
13577
13915
|
clack.intro("BetterStart Seed");
|
|
13578
13916
|
let config;
|
|
13579
13917
|
try {
|
|
@@ -13613,14 +13951,14 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13613
13951
|
clack.cancel("Cancelled.");
|
|
13614
13952
|
process.exit(0);
|
|
13615
13953
|
}
|
|
13616
|
-
const scriptsDir =
|
|
13617
|
-
const seedPath =
|
|
13618
|
-
if (!
|
|
13619
|
-
|
|
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 });
|
|
13620
13958
|
}
|
|
13621
|
-
|
|
13959
|
+
fs39.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
13622
13960
|
const { execFile } = await import("child_process");
|
|
13623
|
-
const tsxBin =
|
|
13961
|
+
const tsxBin = path44.join(cwd, "node_modules", ".bin", "tsx");
|
|
13624
13962
|
const runSeed2 = (overwrite) => new Promise((resolve, reject) => {
|
|
13625
13963
|
execFile(
|
|
13626
13964
|
tsxBin,
|
|
@@ -13659,7 +13997,7 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13659
13997
|
if (clack.isCancel(overwrite) || !overwrite) {
|
|
13660
13998
|
clack.cancel("Seed cancelled.");
|
|
13661
13999
|
try {
|
|
13662
|
-
|
|
14000
|
+
fs39.unlinkSync(seedPath);
|
|
13663
14001
|
} catch {
|
|
13664
14002
|
}
|
|
13665
14003
|
process.exit(0);
|
|
@@ -13676,15 +14014,15 @@ var seedCommand = new Command2("seed").description("Create the initial admin use
|
|
|
13676
14014
|
clack.log.error(errMsg);
|
|
13677
14015
|
clack.log.info("You can run the seed script manually:");
|
|
13678
14016
|
clack.log.info(
|
|
13679
|
-
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${
|
|
14017
|
+
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${path44.relative(cwd, seedPath)}`
|
|
13680
14018
|
);
|
|
13681
14019
|
clack.outro("");
|
|
13682
14020
|
process.exit(1);
|
|
13683
14021
|
}
|
|
13684
14022
|
try {
|
|
13685
|
-
|
|
13686
|
-
if (
|
|
13687
|
-
|
|
14023
|
+
fs39.unlinkSync(seedPath);
|
|
14024
|
+
if (fs39.existsSync(scriptsDir) && fs39.readdirSync(scriptsDir).length === 0) {
|
|
14025
|
+
fs39.rmdirSync(scriptsDir);
|
|
13688
14026
|
}
|
|
13689
14027
|
} catch {
|
|
13690
14028
|
}
|
|
@@ -13715,16 +14053,16 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13715
14053
|
const nukeFiles = ["cms.config.ts", "CMS.md", "drizzle.config.ts"];
|
|
13716
14054
|
let nuked = 0;
|
|
13717
14055
|
for (const dir of nukeDirs) {
|
|
13718
|
-
const fullPath =
|
|
13719
|
-
if (
|
|
13720
|
-
|
|
14056
|
+
const fullPath = path45.resolve(cwd, dir);
|
|
14057
|
+
if (fs40.existsSync(fullPath)) {
|
|
14058
|
+
fs40.rmSync(fullPath, { recursive: true, force: true });
|
|
13721
14059
|
nuked++;
|
|
13722
14060
|
}
|
|
13723
14061
|
}
|
|
13724
14062
|
for (const file of nukeFiles) {
|
|
13725
|
-
const fullPath =
|
|
13726
|
-
if (
|
|
13727
|
-
|
|
14063
|
+
const fullPath = path45.resolve(cwd, file);
|
|
14064
|
+
if (fs40.existsSync(fullPath)) {
|
|
14065
|
+
fs40.unlinkSync(fullPath);
|
|
13728
14066
|
nuked++;
|
|
13729
14067
|
}
|
|
13730
14068
|
}
|
|
@@ -13771,7 +14109,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13771
14109
|
}
|
|
13772
14110
|
pm = pmChoice;
|
|
13773
14111
|
}
|
|
13774
|
-
const displayName = projectPrompt.projectName === "." ?
|
|
14112
|
+
const displayName = projectPrompt.projectName === "." ? path45.basename(cwd) : projectPrompt.projectName;
|
|
13775
14113
|
const { bin, prefix } = createNextAppCommand(pm);
|
|
13776
14114
|
const cnaArgs = [
|
|
13777
14115
|
...prefix,
|
|
@@ -13803,10 +14141,10 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13803
14141
|
);
|
|
13804
14142
|
process.exit(1);
|
|
13805
14143
|
}
|
|
13806
|
-
cwd =
|
|
13807
|
-
const hasPackageJson =
|
|
14144
|
+
cwd = path45.resolve(cwd, projectPrompt.projectName);
|
|
14145
|
+
const hasPackageJson = fs40.existsSync(path45.join(cwd, "package.json"));
|
|
13808
14146
|
const hasNextConfig = ["next.config.ts", "next.config.js", "next.config.mjs"].some(
|
|
13809
|
-
(f) =>
|
|
14147
|
+
(f) => fs40.existsSync(path45.join(cwd, f))
|
|
13810
14148
|
);
|
|
13811
14149
|
if (!hasPackageJson || !hasNextConfig) {
|
|
13812
14150
|
p4.log.error(
|
|
@@ -13907,11 +14245,11 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13907
14245
|
s.stop("");
|
|
13908
14246
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
13909
14247
|
p4.note(noteLines.join("\n"), "Scaffolded CMS");
|
|
13910
|
-
const drizzleConfigPath =
|
|
13911
|
-
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)) {
|
|
13912
14250
|
if (options.force) {
|
|
13913
14251
|
const { drizzleConfigTemplate: drizzleConfigTemplate2 } = await import("./drizzle-config-EDKOEZ6G.js");
|
|
13914
|
-
|
|
14252
|
+
fs40.writeFileSync(drizzleConfigPath, drizzleConfigTemplate2(), "utf-8");
|
|
13915
14253
|
p4.log.success("Updated drizzle.config.ts");
|
|
13916
14254
|
} else if (!options.yes) {
|
|
13917
14255
|
const overwrite = await p4.confirm({
|
|
@@ -13920,7 +14258,7 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13920
14258
|
});
|
|
13921
14259
|
if (!p4.isCancel(overwrite) && overwrite) {
|
|
13922
14260
|
const { drizzleConfigTemplate: drizzleConfigTemplate2 } = await import("./drizzle-config-EDKOEZ6G.js");
|
|
13923
|
-
|
|
14261
|
+
fs40.writeFileSync(drizzleConfigPath, drizzleConfigTemplate2(), "utf-8");
|
|
13924
14262
|
p4.log.success("Updated drizzle.config.ts");
|
|
13925
14263
|
}
|
|
13926
14264
|
}
|
|
@@ -13953,15 +14291,15 @@ var initCommand = new Command3("init").description("Scaffold CMS into a new or e
|
|
|
13953
14291
|
{
|
|
13954
14292
|
const entityNames = [];
|
|
13955
14293
|
const formNames = [];
|
|
13956
|
-
const schemasDir =
|
|
13957
|
-
const formsDir =
|
|
13958
|
-
if (
|
|
13959
|
-
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)) {
|
|
13960
14298
|
if (f.endsWith(".json")) entityNames.push(f.replace(".json", ""));
|
|
13961
14299
|
}
|
|
13962
14300
|
}
|
|
13963
|
-
if (
|
|
13964
|
-
for (const f of
|
|
14301
|
+
if (fs40.existsSync(formsDir)) {
|
|
14302
|
+
for (const f of fs40.readdirSync(formsDir)) {
|
|
13965
14303
|
if (f.endsWith(".json")) formNames.push(f.replace(".json", ""));
|
|
13966
14304
|
}
|
|
13967
14305
|
}
|
|
@@ -14140,9 +14478,9 @@ function isValidDbUrl(url) {
|
|
|
14140
14478
|
return url.startsWith("postgres://") || url.startsWith("postgresql://");
|
|
14141
14479
|
}
|
|
14142
14480
|
function readExistingDbUrl(cwd) {
|
|
14143
|
-
const envPath =
|
|
14144
|
-
if (!
|
|
14145
|
-
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");
|
|
14146
14484
|
for (const line of content.split("\n")) {
|
|
14147
14485
|
const trimmed = line.trim();
|
|
14148
14486
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -14165,9 +14503,9 @@ function maskDbUrl(url) {
|
|
|
14165
14503
|
}
|
|
14166
14504
|
}
|
|
14167
14505
|
function hasDbUrl(cwd) {
|
|
14168
|
-
const envPath =
|
|
14169
|
-
if (!
|
|
14170
|
-
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");
|
|
14171
14509
|
for (const line of content.split("\n")) {
|
|
14172
14510
|
const trimmed = line.trim();
|
|
14173
14511
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -14181,23 +14519,23 @@ function hasDbUrl(cwd) {
|
|
|
14181
14519
|
return false;
|
|
14182
14520
|
}
|
|
14183
14521
|
function runSeed(cwd, cmsDir, email, password4, overwrite = false) {
|
|
14184
|
-
const scriptsDir =
|
|
14185
|
-
const seedPath =
|
|
14186
|
-
if (!
|
|
14187
|
-
|
|
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 });
|
|
14188
14526
|
}
|
|
14189
|
-
|
|
14527
|
+
fs40.writeFileSync(seedPath, buildSeedScript(), "utf-8");
|
|
14190
14528
|
const cleanup = () => {
|
|
14191
14529
|
try {
|
|
14192
|
-
|
|
14193
|
-
if (
|
|
14194
|
-
|
|
14530
|
+
fs40.unlinkSync(seedPath);
|
|
14531
|
+
if (fs40.existsSync(scriptsDir) && fs40.readdirSync(scriptsDir).length === 0) {
|
|
14532
|
+
fs40.rmdirSync(scriptsDir);
|
|
14195
14533
|
}
|
|
14196
14534
|
} catch {
|
|
14197
14535
|
}
|
|
14198
14536
|
};
|
|
14199
14537
|
return new Promise((resolve) => {
|
|
14200
|
-
const tsxBin =
|
|
14538
|
+
const tsxBin = path45.join(cwd, "node_modules", ".bin", "tsx");
|
|
14201
14539
|
const child = spawn2(tsxBin, [seedPath], {
|
|
14202
14540
|
cwd,
|
|
14203
14541
|
stdio: "pipe",
|
|
@@ -14262,7 +14600,7 @@ ${stderr}`;
|
|
|
14262
14600
|
}
|
|
14263
14601
|
function runDrizzlePush(cwd) {
|
|
14264
14602
|
return new Promise((resolve) => {
|
|
14265
|
-
const drizzleBin =
|
|
14603
|
+
const drizzleBin = path45.join(cwd, "node_modules", ".bin", "drizzle-kit");
|
|
14266
14604
|
const child = spawn2(drizzleBin, ["push", "--force"], {
|
|
14267
14605
|
cwd,
|
|
14268
14606
|
stdio: "pipe",
|
|
@@ -14283,8 +14621,8 @@ function runDrizzlePush(cwd) {
|
|
|
14283
14621
|
}
|
|
14284
14622
|
|
|
14285
14623
|
// src/commands/remove.ts
|
|
14286
|
-
import
|
|
14287
|
-
import
|
|
14624
|
+
import fs41 from "fs";
|
|
14625
|
+
import path46 from "path";
|
|
14288
14626
|
import readline from "readline";
|
|
14289
14627
|
import { Command as Command4 } from "commander";
|
|
14290
14628
|
function findTableEnd3(content, startIndex) {
|
|
@@ -14315,8 +14653,8 @@ function findTableEnd3(content, startIndex) {
|
|
|
14315
14653
|
return content.length;
|
|
14316
14654
|
}
|
|
14317
14655
|
function removeTableFromSchema(schemaFilePath, name) {
|
|
14318
|
-
if (!
|
|
14319
|
-
let content =
|
|
14656
|
+
if (!fs41.existsSync(schemaFilePath)) return false;
|
|
14657
|
+
let content = fs41.readFileSync(schemaFilePath, "utf-8");
|
|
14320
14658
|
const variableName = toCamelCase(name);
|
|
14321
14659
|
let changed = false;
|
|
14322
14660
|
if (content.includes(`export const ${variableName} =`)) {
|
|
@@ -14344,13 +14682,13 @@ function removeTableFromSchema(schemaFilePath, name) {
|
|
|
14344
14682
|
}
|
|
14345
14683
|
if (changed) {
|
|
14346
14684
|
content = content.replace(/\n{3,}/g, "\n\n");
|
|
14347
|
-
|
|
14685
|
+
fs41.writeFileSync(schemaFilePath, content, "utf-8");
|
|
14348
14686
|
}
|
|
14349
14687
|
return changed;
|
|
14350
14688
|
}
|
|
14351
14689
|
function removeFromNavigation(navFilePath, name) {
|
|
14352
|
-
if (!
|
|
14353
|
-
const content =
|
|
14690
|
+
if (!fs41.existsSync(navFilePath)) return false;
|
|
14691
|
+
const content = fs41.readFileSync(navFilePath, "utf-8");
|
|
14354
14692
|
const href = `/cms/${name}`;
|
|
14355
14693
|
if (!content.includes(`'${href}'`)) return false;
|
|
14356
14694
|
const lines = content.split("\n");
|
|
@@ -14381,7 +14719,7 @@ function removeFromNavigation(navFilePath, name) {
|
|
|
14381
14719
|
if (startLine === -1 || endLine === -1) return false;
|
|
14382
14720
|
lines.splice(startLine, endLine - startLine + 1);
|
|
14383
14721
|
const updated = lines.join("\n").replace(/,\s*,/g, ",").replace(/\[\s*,/, "[");
|
|
14384
|
-
|
|
14722
|
+
fs41.writeFileSync(navFilePath, updated, "utf-8");
|
|
14385
14723
|
return true;
|
|
14386
14724
|
}
|
|
14387
14725
|
async function promptConfirm(message) {
|
|
@@ -14397,7 +14735,7 @@ async function promptConfirm(message) {
|
|
|
14397
14735
|
});
|
|
14398
14736
|
}
|
|
14399
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) => {
|
|
14400
|
-
const cwd = options.cwd ?
|
|
14738
|
+
const cwd = options.cwd ? path46.resolve(options.cwd) : process.cwd();
|
|
14401
14739
|
console.log("\n BetterStart Remove\n");
|
|
14402
14740
|
let config;
|
|
14403
14741
|
try {
|
|
@@ -14410,34 +14748,34 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14410
14748
|
const pagesDir = config.paths?.pages ?? "./src/app/(cms)/cms/(authenticated)";
|
|
14411
14749
|
const kebabName = toKebabCase(schemaName);
|
|
14412
14750
|
const targets = [];
|
|
14413
|
-
const entityPagesDir =
|
|
14414
|
-
if (
|
|
14751
|
+
const entityPagesDir = path46.join(cwd, pagesDir, schemaName);
|
|
14752
|
+
if (fs41.existsSync(entityPagesDir)) {
|
|
14415
14753
|
targets.push({
|
|
14416
14754
|
path: entityPagesDir,
|
|
14417
|
-
label: `${
|
|
14755
|
+
label: `${path46.join(pagesDir, schemaName)}/`,
|
|
14418
14756
|
isDir: true
|
|
14419
14757
|
});
|
|
14420
14758
|
}
|
|
14421
|
-
const actionsFile =
|
|
14422
|
-
if (
|
|
14759
|
+
const actionsFile = path46.join(cwd, cmsDir, "lib", "actions", `${kebabName}.ts`);
|
|
14760
|
+
if (fs41.existsSync(actionsFile)) {
|
|
14423
14761
|
targets.push({
|
|
14424
14762
|
path: actionsFile,
|
|
14425
|
-
label:
|
|
14763
|
+
label: path46.join(cmsDir, "lib", "actions", `${kebabName}.ts`),
|
|
14426
14764
|
isDir: false
|
|
14427
14765
|
});
|
|
14428
14766
|
}
|
|
14429
|
-
const hookFile =
|
|
14430
|
-
if (
|
|
14767
|
+
const hookFile = path46.join(cwd, cmsDir, "hooks", `use-${kebabName}.ts`);
|
|
14768
|
+
if (fs41.existsSync(hookFile)) {
|
|
14431
14769
|
targets.push({
|
|
14432
14770
|
path: hookFile,
|
|
14433
|
-
label:
|
|
14771
|
+
label: path46.join(cmsDir, "hooks", `use-${kebabName}.ts`),
|
|
14434
14772
|
isDir: false
|
|
14435
14773
|
});
|
|
14436
14774
|
}
|
|
14437
|
-
const schemaFilePath =
|
|
14438
|
-
const hasTable =
|
|
14439
|
-
const navFilePath =
|
|
14440
|
-
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}'`);
|
|
14441
14779
|
if (targets.length === 0 && !hasTable && !hasNavEntry) {
|
|
14442
14780
|
console.log(` No generated files found for: ${schemaName}`);
|
|
14443
14781
|
return;
|
|
@@ -14447,10 +14785,10 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14447
14785
|
console.log(` ${t.isDir ? "[dir]" : " "} ${t.label}`);
|
|
14448
14786
|
}
|
|
14449
14787
|
if (hasTable) {
|
|
14450
|
-
console.log(` [edit] ${
|
|
14788
|
+
console.log(` [edit] ${path46.join(cmsDir, "db", "schema.ts")} (remove table)`);
|
|
14451
14789
|
}
|
|
14452
14790
|
if (hasNavEntry) {
|
|
14453
|
-
console.log(` [edit] ${
|
|
14791
|
+
console.log(` [edit] ${path46.join(cmsDir, "data", "navigation.ts")} (remove entry)`);
|
|
14454
14792
|
}
|
|
14455
14793
|
if (!options.force) {
|
|
14456
14794
|
console.log("");
|
|
@@ -14463,19 +14801,19 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14463
14801
|
console.log("");
|
|
14464
14802
|
for (const t of targets) {
|
|
14465
14803
|
if (t.isDir) {
|
|
14466
|
-
|
|
14804
|
+
fs41.rmSync(t.path, { recursive: true, force: true });
|
|
14467
14805
|
} else {
|
|
14468
|
-
|
|
14806
|
+
fs41.unlinkSync(t.path);
|
|
14469
14807
|
}
|
|
14470
14808
|
console.log(` Removed: ${t.label}`);
|
|
14471
14809
|
}
|
|
14472
14810
|
if (hasTable) {
|
|
14473
14811
|
removeTableFromSchema(schemaFilePath, schemaName);
|
|
14474
|
-
console.log(` Cleaned: ${
|
|
14812
|
+
console.log(` Cleaned: ${path46.join(cmsDir, "db", "schema.ts")}`);
|
|
14475
14813
|
}
|
|
14476
14814
|
if (hasNavEntry) {
|
|
14477
14815
|
removeFromNavigation(navFilePath, schemaName);
|
|
14478
|
-
console.log(` Cleaned: ${
|
|
14816
|
+
console.log(` Cleaned: ${path46.join(cmsDir, "data", "navigation.ts")}`);
|
|
14479
14817
|
}
|
|
14480
14818
|
console.log("\n Removal complete!");
|
|
14481
14819
|
console.log("\n Note: You may need to manually:");
|
|
@@ -14487,14 +14825,14 @@ var removeCommand = new Command4("remove").alias("rm").description("Remove all g
|
|
|
14487
14825
|
|
|
14488
14826
|
// src/commands/setup-r2.ts
|
|
14489
14827
|
import { execFileSync as execFileSync5, spawnSync } from "child_process";
|
|
14490
|
-
import
|
|
14828
|
+
import fs42 from "fs";
|
|
14491
14829
|
import os from "os";
|
|
14492
|
-
import
|
|
14830
|
+
import path47 from "path";
|
|
14493
14831
|
import * as p5 from "@clack/prompts";
|
|
14494
14832
|
import { Command as Command5 } from "commander";
|
|
14495
14833
|
import pc3 from "picocolors";
|
|
14496
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) => {
|
|
14497
|
-
const cwd = options.cwd ?
|
|
14835
|
+
const cwd = options.cwd ? path47.resolve(options.cwd) : process.cwd();
|
|
14498
14836
|
p5.intro(pc3.bgCyan(pc3.black(" BetterStart \u2014 R2 Storage Setup ")));
|
|
14499
14837
|
const s = p5.spinner();
|
|
14500
14838
|
s.start("Looking for wrangler CLI");
|
|
@@ -14686,8 +15024,8 @@ var setupR2Command = new Command5("setup-r2").description("Create a Cloudflare R
|
|
|
14686
15024
|
p5.outro("Done! Your CMS can now upload files to R2.");
|
|
14687
15025
|
});
|
|
14688
15026
|
function findWrangler(cwd) {
|
|
14689
|
-
const localBin =
|
|
14690
|
-
if (
|
|
15027
|
+
const localBin = path47.join(cwd, "node_modules", ".bin", "wrangler");
|
|
15028
|
+
if (fs42.existsSync(localBin)) return { bin: localBin, prefix: [] };
|
|
14691
15029
|
const result = spawnSync("which", ["wrangler"], { stdio: "pipe", timeout: 5e3 });
|
|
14692
15030
|
if (result.status === 0) {
|
|
14693
15031
|
const found = result.stdout?.toString().trim();
|
|
@@ -14719,11 +15057,11 @@ function extractAccountId(ref, cwd) {
|
|
|
14719
15057
|
}
|
|
14720
15058
|
function readWranglerToken() {
|
|
14721
15059
|
const candidates = [
|
|
14722
|
-
|
|
15060
|
+
path47.join(os.homedir(), "Library", "Preferences", ".wrangler", "config", "default.toml"),
|
|
14723
15061
|
// macOS
|
|
14724
|
-
|
|
15062
|
+
path47.join(os.homedir(), ".config", ".wrangler", "config", "default.toml"),
|
|
14725
15063
|
// Linux
|
|
14726
|
-
|
|
15064
|
+
path47.join(os.homedir(), ".wrangler", "config", "default.toml")
|
|
14727
15065
|
// fallback
|
|
14728
15066
|
];
|
|
14729
15067
|
if (process.env.WRANGLER_CONFIG_PATH) {
|
|
@@ -14731,13 +15069,13 @@ function readWranglerToken() {
|
|
|
14731
15069
|
}
|
|
14732
15070
|
if (process.env.XDG_CONFIG_HOME) {
|
|
14733
15071
|
candidates.unshift(
|
|
14734
|
-
|
|
15072
|
+
path47.join(process.env.XDG_CONFIG_HOME, ".wrangler", "config", "default.toml")
|
|
14735
15073
|
);
|
|
14736
15074
|
}
|
|
14737
15075
|
for (const configPath of candidates) {
|
|
14738
|
-
if (!
|
|
15076
|
+
if (!fs42.existsSync(configPath)) continue;
|
|
14739
15077
|
try {
|
|
14740
|
-
const content =
|
|
15078
|
+
const content = fs42.readFileSync(configPath, "utf-8");
|
|
14741
15079
|
const match = content.match(/^oauth_token\s*=\s*"([^"]+)"/m);
|
|
14742
15080
|
if (match) return match[1];
|
|
14743
15081
|
} catch {
|
|
@@ -14769,14 +15107,14 @@ async function enablePublicDomain(accountId, bucketName, token) {
|
|
|
14769
15107
|
}
|
|
14770
15108
|
|
|
14771
15109
|
// src/commands/uninstall.ts
|
|
14772
|
-
import
|
|
14773
|
-
import
|
|
15110
|
+
import fs44 from "fs";
|
|
15111
|
+
import path48 from "path";
|
|
14774
15112
|
import * as p6 from "@clack/prompts";
|
|
14775
15113
|
import { Command as Command6 } from "commander";
|
|
14776
15114
|
import pc4 from "picocolors";
|
|
14777
15115
|
|
|
14778
15116
|
// src/commands/uninstall-cleaners.ts
|
|
14779
|
-
import
|
|
15117
|
+
import fs43 from "fs";
|
|
14780
15118
|
function stripJsonComments2(input) {
|
|
14781
15119
|
let result = "";
|
|
14782
15120
|
let i = 0;
|
|
@@ -14810,8 +15148,8 @@ function stripJsonComments2(input) {
|
|
|
14810
15148
|
return result;
|
|
14811
15149
|
}
|
|
14812
15150
|
function cleanTsconfig(tsconfigPath) {
|
|
14813
|
-
if (!
|
|
14814
|
-
const raw =
|
|
15151
|
+
if (!fs43.existsSync(tsconfigPath)) return [];
|
|
15152
|
+
const raw = fs43.readFileSync(tsconfigPath, "utf-8");
|
|
14815
15153
|
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
14816
15154
|
let tsconfig;
|
|
14817
15155
|
try {
|
|
@@ -14835,13 +15173,13 @@ function cleanTsconfig(tsconfigPath) {
|
|
|
14835
15173
|
compilerOptions.paths = paths;
|
|
14836
15174
|
}
|
|
14837
15175
|
tsconfig.compilerOptions = compilerOptions;
|
|
14838
|
-
|
|
15176
|
+
fs43.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
14839
15177
|
`, "utf-8");
|
|
14840
15178
|
return removed;
|
|
14841
15179
|
}
|
|
14842
15180
|
function cleanCss(cssPath) {
|
|
14843
|
-
if (!
|
|
14844
|
-
const content =
|
|
15181
|
+
if (!fs43.existsSync(cssPath)) return [];
|
|
15182
|
+
const content = fs43.readFileSync(cssPath, "utf-8");
|
|
14845
15183
|
const lines = content.split("\n");
|
|
14846
15184
|
const sourcePattern = /^@source\s+"[^"]*cms[^"]*";\s*$/;
|
|
14847
15185
|
const removed = [];
|
|
@@ -14855,12 +15193,12 @@ function cleanCss(cssPath) {
|
|
|
14855
15193
|
}
|
|
14856
15194
|
if (removed.length === 0) return [];
|
|
14857
15195
|
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
14858
|
-
|
|
15196
|
+
fs43.writeFileSync(cssPath, cleaned, "utf-8");
|
|
14859
15197
|
return removed;
|
|
14860
15198
|
}
|
|
14861
15199
|
function cleanEnvFile(envPath) {
|
|
14862
|
-
if (!
|
|
14863
|
-
const content =
|
|
15200
|
+
if (!fs43.existsSync(envPath)) return [];
|
|
15201
|
+
const content = fs43.readFileSync(envPath, "utf-8");
|
|
14864
15202
|
const lines = content.split("\n");
|
|
14865
15203
|
const removed = [];
|
|
14866
15204
|
const kept = [];
|
|
@@ -14893,9 +15231,9 @@ function cleanEnvFile(envPath) {
|
|
|
14893
15231
|
if (removed.length === 0) return [];
|
|
14894
15232
|
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
14895
15233
|
if (result === "") {
|
|
14896
|
-
|
|
15234
|
+
fs43.unlinkSync(envPath);
|
|
14897
15235
|
} else {
|
|
14898
|
-
|
|
15236
|
+
fs43.writeFileSync(envPath, `${result}
|
|
14899
15237
|
`, "utf-8");
|
|
14900
15238
|
}
|
|
14901
15239
|
return removed;
|
|
@@ -14921,15 +15259,15 @@ function findMainCss2(cwd) {
|
|
|
14921
15259
|
"globals.css"
|
|
14922
15260
|
];
|
|
14923
15261
|
for (const candidate of candidates) {
|
|
14924
|
-
const filePath =
|
|
14925
|
-
if (
|
|
15262
|
+
const filePath = path48.join(cwd, candidate);
|
|
15263
|
+
if (fs44.existsSync(filePath)) return filePath;
|
|
14926
15264
|
}
|
|
14927
15265
|
return void 0;
|
|
14928
15266
|
}
|
|
14929
15267
|
function isCLICreatedBiome(biomePath) {
|
|
14930
|
-
if (!
|
|
15268
|
+
if (!fs44.existsSync(biomePath)) return false;
|
|
14931
15269
|
try {
|
|
14932
|
-
const content = JSON.parse(
|
|
15270
|
+
const content = JSON.parse(fs44.readFileSync(biomePath, "utf-8"));
|
|
14933
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");
|
|
14934
15272
|
} catch {
|
|
14935
15273
|
return false;
|
|
@@ -14937,13 +15275,13 @@ function isCLICreatedBiome(biomePath) {
|
|
|
14937
15275
|
}
|
|
14938
15276
|
function buildUninstallPlan(cwd) {
|
|
14939
15277
|
const steps = [];
|
|
14940
|
-
const hasSrc =
|
|
15278
|
+
const hasSrc = fs44.existsSync(path48.join(cwd, "src"));
|
|
14941
15279
|
const appBase = hasSrc ? "src/app" : "app";
|
|
14942
15280
|
const dirs = [];
|
|
14943
|
-
const cmsDir =
|
|
14944
|
-
const cmsRouteGroup =
|
|
14945
|
-
if (
|
|
14946
|
-
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)/`);
|
|
14947
15285
|
if (dirs.length > 0) {
|
|
14948
15286
|
steps.push({
|
|
14949
15287
|
label: "CMS directories",
|
|
@@ -14951,25 +15289,25 @@ function buildUninstallPlan(cwd) {
|
|
|
14951
15289
|
count: dirs.length,
|
|
14952
15290
|
unit: dirs.length === 1 ? "directory" : "directories",
|
|
14953
15291
|
execute() {
|
|
14954
|
-
if (
|
|
14955
|
-
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 });
|
|
14956
15294
|
}
|
|
14957
15295
|
});
|
|
14958
15296
|
}
|
|
14959
15297
|
const configFiles = [];
|
|
14960
15298
|
const configPaths = [];
|
|
14961
15299
|
const candidates = [
|
|
14962
|
-
["cms.config.ts",
|
|
14963
|
-
["drizzle.config.ts",
|
|
14964
|
-
["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")]
|
|
14965
15303
|
];
|
|
14966
15304
|
for (const [label, fullPath] of candidates) {
|
|
14967
|
-
if (
|
|
15305
|
+
if (fs44.existsSync(fullPath)) {
|
|
14968
15306
|
configFiles.push(label);
|
|
14969
15307
|
configPaths.push(fullPath);
|
|
14970
15308
|
}
|
|
14971
15309
|
}
|
|
14972
|
-
const biomePath =
|
|
15310
|
+
const biomePath = path48.join(cwd, "biome.json");
|
|
14973
15311
|
if (isCLICreatedBiome(biomePath)) {
|
|
14974
15312
|
configFiles.push("biome.json (CLI-created)");
|
|
14975
15313
|
configPaths.push(biomePath);
|
|
@@ -14982,14 +15320,14 @@ function buildUninstallPlan(cwd) {
|
|
|
14982
15320
|
unit: configFiles.length === 1 ? "file" : "files",
|
|
14983
15321
|
execute() {
|
|
14984
15322
|
for (const p7 of configPaths) {
|
|
14985
|
-
if (
|
|
15323
|
+
if (fs44.existsSync(p7)) fs44.unlinkSync(p7);
|
|
14986
15324
|
}
|
|
14987
15325
|
}
|
|
14988
15326
|
});
|
|
14989
15327
|
}
|
|
14990
|
-
const tsconfigPath =
|
|
14991
|
-
if (
|
|
14992
|
-
const content =
|
|
15328
|
+
const tsconfigPath = path48.join(cwd, "tsconfig.json");
|
|
15329
|
+
if (fs44.existsSync(tsconfigPath)) {
|
|
15330
|
+
const content = fs44.readFileSync(tsconfigPath, "utf-8");
|
|
14993
15331
|
const aliasMatches = content.match(/"@cms\//g);
|
|
14994
15332
|
if (aliasMatches && aliasMatches.length > 0) {
|
|
14995
15333
|
const aliasCount = aliasMatches.length;
|
|
@@ -15006,10 +15344,10 @@ function buildUninstallPlan(cwd) {
|
|
|
15006
15344
|
}
|
|
15007
15345
|
const cssFile = findMainCss2(cwd);
|
|
15008
15346
|
if (cssFile) {
|
|
15009
|
-
const cssContent =
|
|
15347
|
+
const cssContent = fs44.readFileSync(cssFile, "utf-8");
|
|
15010
15348
|
const sourceLines = cssContent.split("\n").filter((l) => /^@source\s+"[^"]*cms[^"]*";\s*$/.test(l));
|
|
15011
15349
|
if (sourceLines.length > 0) {
|
|
15012
|
-
const relCss =
|
|
15350
|
+
const relCss = path48.relative(cwd, cssFile);
|
|
15013
15351
|
steps.push({
|
|
15014
15352
|
label: `CSS @source lines (${relCss})`,
|
|
15015
15353
|
items: [`@source lines in ${relCss}`],
|
|
@@ -15021,9 +15359,9 @@ function buildUninstallPlan(cwd) {
|
|
|
15021
15359
|
});
|
|
15022
15360
|
}
|
|
15023
15361
|
}
|
|
15024
|
-
const envPath =
|
|
15025
|
-
if (
|
|
15026
|
-
const envContent =
|
|
15362
|
+
const envPath = path48.join(cwd, ".env.local");
|
|
15363
|
+
if (fs44.existsSync(envPath)) {
|
|
15364
|
+
const envContent = fs44.readFileSync(envPath, "utf-8");
|
|
15027
15365
|
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
15028
15366
|
if (bsVars.length > 0) {
|
|
15029
15367
|
steps.push({
|
|
@@ -15040,7 +15378,7 @@ function buildUninstallPlan(cwd) {
|
|
|
15040
15378
|
return steps;
|
|
15041
15379
|
}
|
|
15042
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) => {
|
|
15043
|
-
const cwd = options.cwd ?
|
|
15381
|
+
const cwd = options.cwd ? path48.resolve(options.cwd) : process.cwd();
|
|
15044
15382
|
p6.intro(pc4.bgRed(pc4.white(" BetterStart Uninstall ")));
|
|
15045
15383
|
const steps = buildUninstallPlan(cwd);
|
|
15046
15384
|
if (steps.length === 0) {
|
|
@@ -15077,11 +15415,11 @@ var uninstallCommand = new Command6("uninstall").description("Remove all CMS fil
|
|
|
15077
15415
|
});
|
|
15078
15416
|
|
|
15079
15417
|
// src/commands/update-deps.ts
|
|
15080
|
-
import
|
|
15418
|
+
import path49 from "path";
|
|
15081
15419
|
import * as clack2 from "@clack/prompts";
|
|
15082
15420
|
import { Command as Command7 } from "commander";
|
|
15083
15421
|
var updateDepsCommand = new Command7("update-deps").description("Install or update all CMS dependencies").option("--cwd <path>", "Project root path").action(async (options) => {
|
|
15084
|
-
const cwd = options.cwd ?
|
|
15422
|
+
const cwd = options.cwd ? path49.resolve(options.cwd) : process.cwd();
|
|
15085
15423
|
clack2.intro("BetterStart Update Dependencies");
|
|
15086
15424
|
const pm = detectPackageManager(cwd);
|
|
15087
15425
|
clack2.log.info(`Package manager: ${pm}`);
|
|
@@ -15106,22 +15444,22 @@ var updateDepsCommand = new Command7("update-deps").description("Install or upda
|
|
|
15106
15444
|
});
|
|
15107
15445
|
|
|
15108
15446
|
// src/commands/update-styles.ts
|
|
15109
|
-
import
|
|
15110
|
-
import
|
|
15447
|
+
import fs45 from "fs";
|
|
15448
|
+
import path50 from "path";
|
|
15111
15449
|
import * as clack3 from "@clack/prompts";
|
|
15112
15450
|
import { Command as Command8 } from "commander";
|
|
15113
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) => {
|
|
15114
|
-
const cwd = options.cwd ?
|
|
15452
|
+
const cwd = options.cwd ? path50.resolve(options.cwd) : process.cwd();
|
|
15115
15453
|
clack3.intro("BetterStart Update Styles");
|
|
15116
15454
|
const config = await resolveConfig(cwd);
|
|
15117
15455
|
const cmsDir = config.paths?.cms ?? "./cms";
|
|
15118
|
-
const targetPath =
|
|
15119
|
-
if (!
|
|
15120
|
-
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)}`);
|
|
15121
15459
|
process.exit(1);
|
|
15122
15460
|
}
|
|
15123
|
-
|
|
15124
|
-
clack3.log.success(`Updated ${
|
|
15461
|
+
fs45.writeFileSync(targetPath, cmsGlobalsCssTemplate(), "utf-8");
|
|
15462
|
+
clack3.log.success(`Updated ${path50.relative(cwd, targetPath)}`);
|
|
15125
15463
|
clack3.outro("Styles updated");
|
|
15126
15464
|
});
|
|
15127
15465
|
|