brizzle 0.2.5 → 0.2.6
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/index.js +194 -83
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -159,6 +159,9 @@ function toSnakeCase(str) {
|
|
|
159
159
|
function toKebabCase(str) {
|
|
160
160
|
return toSnakeCase(str).replace(/_/g, "-");
|
|
161
161
|
}
|
|
162
|
+
function escapeString(str) {
|
|
163
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
164
|
+
}
|
|
162
165
|
function pluralize(str) {
|
|
163
166
|
if (str.endsWith("y") && !/[aeiou]y$/.test(str)) {
|
|
164
167
|
return str.slice(0, -1) + "ies";
|
|
@@ -200,6 +203,82 @@ function createModelContext(name) {
|
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
// src/lib/validation.ts
|
|
206
|
+
var SQL_RESERVED_WORDS = [
|
|
207
|
+
// SQL keywords
|
|
208
|
+
"select",
|
|
209
|
+
"from",
|
|
210
|
+
"where",
|
|
211
|
+
"insert",
|
|
212
|
+
"update",
|
|
213
|
+
"delete",
|
|
214
|
+
"drop",
|
|
215
|
+
"create",
|
|
216
|
+
"alter",
|
|
217
|
+
"index",
|
|
218
|
+
"table",
|
|
219
|
+
"column",
|
|
220
|
+
"database",
|
|
221
|
+
"schema",
|
|
222
|
+
"and",
|
|
223
|
+
"or",
|
|
224
|
+
"not",
|
|
225
|
+
"null",
|
|
226
|
+
"true",
|
|
227
|
+
"false",
|
|
228
|
+
"order",
|
|
229
|
+
"by",
|
|
230
|
+
"group",
|
|
231
|
+
"having",
|
|
232
|
+
"limit",
|
|
233
|
+
"offset",
|
|
234
|
+
"join",
|
|
235
|
+
"left",
|
|
236
|
+
"right",
|
|
237
|
+
"inner",
|
|
238
|
+
"outer",
|
|
239
|
+
"on",
|
|
240
|
+
"as",
|
|
241
|
+
"in",
|
|
242
|
+
"between",
|
|
243
|
+
"like",
|
|
244
|
+
"is",
|
|
245
|
+
"case",
|
|
246
|
+
"when",
|
|
247
|
+
"then",
|
|
248
|
+
"else",
|
|
249
|
+
"end",
|
|
250
|
+
"exists",
|
|
251
|
+
"distinct",
|
|
252
|
+
"all",
|
|
253
|
+
"any",
|
|
254
|
+
"union",
|
|
255
|
+
"intersect",
|
|
256
|
+
"except",
|
|
257
|
+
"primary",
|
|
258
|
+
"foreign",
|
|
259
|
+
"key",
|
|
260
|
+
"references",
|
|
261
|
+
"unique",
|
|
262
|
+
"default",
|
|
263
|
+
"check",
|
|
264
|
+
"constraint",
|
|
265
|
+
// Common type names that might conflict
|
|
266
|
+
"int",
|
|
267
|
+
"integer",
|
|
268
|
+
"float",
|
|
269
|
+
"double",
|
|
270
|
+
"decimal",
|
|
271
|
+
"numeric",
|
|
272
|
+
"boolean",
|
|
273
|
+
"bool",
|
|
274
|
+
"text",
|
|
275
|
+
"varchar",
|
|
276
|
+
"char",
|
|
277
|
+
"date",
|
|
278
|
+
"time",
|
|
279
|
+
"timestamp",
|
|
280
|
+
"datetime"
|
|
281
|
+
];
|
|
203
282
|
function validateModelName(name) {
|
|
204
283
|
if (!name) {
|
|
205
284
|
throw new Error("Model name is required");
|
|
@@ -232,6 +311,11 @@ function validateFieldDefinition(fieldDef) {
|
|
|
232
311
|
`Invalid field name "${name}". Must be camelCase (start with lowercase letter).`
|
|
233
312
|
);
|
|
234
313
|
}
|
|
314
|
+
if (SQL_RESERVED_WORDS.includes(name.toLowerCase())) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Field name "${name}" is a SQL reserved word. Consider renaming to "${name}Value" or "${name}Field".`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
235
319
|
if (type && !type.startsWith("references") && type !== "enum" && type !== "unique") {
|
|
236
320
|
if (!VALID_FIELD_TYPES.includes(type)) {
|
|
237
321
|
throw new Error(
|
|
@@ -567,7 +651,7 @@ function generateEnumDefinitions(fields, dialect) {
|
|
|
567
651
|
}
|
|
568
652
|
return enumFields.map((field) => {
|
|
569
653
|
const enumName = `${field.name}Enum`;
|
|
570
|
-
const values = field.enumValues.map((v) => `"${v}"`).join(", ");
|
|
654
|
+
const values = field.enumValues.map((v) => `"${escapeString(v)}"`).join(", ");
|
|
571
655
|
return `
|
|
572
656
|
export const ${enumName} = pgEnum("${toSnakeCase(field.name)}", [${values}]);`;
|
|
573
657
|
}).join("\n");
|
|
@@ -629,10 +713,10 @@ function generateEnumField(field, columnName, dialect) {
|
|
|
629
713
|
case "postgresql":
|
|
630
714
|
return ` ${field.name}: ${field.name}Enum("${columnName}")${modifiers},`;
|
|
631
715
|
case "mysql":
|
|
632
|
-
const mysqlValues = values.map((v) => `"${v}"`).join(", ");
|
|
716
|
+
const mysqlValues = values.map((v) => `"${escapeString(v)}"`).join(", ");
|
|
633
717
|
return ` ${field.name}: mysqlEnum("${columnName}", [${mysqlValues}])${modifiers},`;
|
|
634
718
|
default:
|
|
635
|
-
const sqliteValues = values.map((v) => `"${v}"`).join(", ");
|
|
719
|
+
const sqliteValues = values.map((v) => `"${escapeString(v)}"`).join(", ");
|
|
636
720
|
return ` ${field.name}: text("${columnName}", { enum: [${sqliteValues}] })${modifiers},`;
|
|
637
721
|
}
|
|
638
722
|
}
|
|
@@ -682,12 +766,15 @@ export async function get${pascalName}(id: ${idType}) {
|
|
|
682
766
|
.from(${camelPlural})
|
|
683
767
|
.where(eq(${camelPlural}.id, id))
|
|
684
768
|
.limit(1);
|
|
769
|
+
|
|
685
770
|
return result[0] ?? null;
|
|
686
771
|
}
|
|
687
772
|
|
|
688
773
|
export async function create${pascalName}(data: Omit<New${pascalName}, "id" | "createdAt" | "updatedAt">) {
|
|
689
774
|
const result = await db.insert(${camelPlural}).values(data).returning();
|
|
775
|
+
|
|
690
776
|
revalidatePath("/${kebabPlural}");
|
|
777
|
+
|
|
691
778
|
return result[0];
|
|
692
779
|
}
|
|
693
780
|
|
|
@@ -700,12 +787,15 @@ export async function update${pascalName}(
|
|
|
700
787
|
.set({ ...data, updatedAt: new Date() })
|
|
701
788
|
.where(eq(${camelPlural}.id, id))
|
|
702
789
|
.returning();
|
|
790
|
+
|
|
703
791
|
revalidatePath("/${kebabPlural}");
|
|
792
|
+
|
|
704
793
|
return result[0];
|
|
705
794
|
}
|
|
706
795
|
|
|
707
796
|
export async function delete${pascalName}(id: ${idType}) {
|
|
708
797
|
await db.delete(${camelPlural}).where(eq(${camelPlural}.id, id));
|
|
798
|
+
|
|
709
799
|
revalidatePath("/${kebabPlural}");
|
|
710
800
|
}
|
|
711
801
|
`;
|
|
@@ -821,9 +911,11 @@ import { create${pascalName} } from "../actions";
|
|
|
821
911
|
export default function New${pascalName}Page() {
|
|
822
912
|
async function handleCreate(formData: FormData) {
|
|
823
913
|
"use server";
|
|
914
|
+
|
|
824
915
|
await create${pascalName}({
|
|
825
916
|
${fields.map((f) => ` ${f.name}: ${formDataValue(f)},`).join("\n")}
|
|
826
917
|
});
|
|
918
|
+
|
|
827
919
|
redirect("/${kebabPlural}");
|
|
828
920
|
}
|
|
829
921
|
|
|
@@ -856,9 +948,11 @@ ${fields.map((f) => generateFormField(f, camelName)).join("\n\n")}
|
|
|
856
948
|
}
|
|
857
949
|
function generateShowPage(pascalName, _pascalPlural, camelName, kebabPlural, fields, options = {}) {
|
|
858
950
|
const idHandling = options.uuid ? `const ${camelName} = await get${pascalName}(id);` : `const numericId = Number(id);
|
|
951
|
+
|
|
859
952
|
if (isNaN(numericId)) {
|
|
860
953
|
notFound();
|
|
861
954
|
}
|
|
955
|
+
|
|
862
956
|
const ${camelName} = await get${pascalName}(numericId);`;
|
|
863
957
|
return `import { notFound } from "next/navigation";
|
|
864
958
|
import Link from "next/link";
|
|
@@ -915,9 +1009,11 @@ ${fields.map(
|
|
|
915
1009
|
}
|
|
916
1010
|
function generateEditPage(pascalName, camelName, kebabPlural, fields, options = {}) {
|
|
917
1011
|
const idHandling = options.uuid ? `const ${camelName} = await get${pascalName}(id);` : `const numericId = Number(id);
|
|
1012
|
+
|
|
918
1013
|
if (isNaN(numericId)) {
|
|
919
1014
|
notFound();
|
|
920
1015
|
}
|
|
1016
|
+
|
|
921
1017
|
const ${camelName} = await get${pascalName}(numericId);`;
|
|
922
1018
|
const updateId = options.uuid ? "id" : "numericId";
|
|
923
1019
|
return `import { notFound, redirect } from "next/navigation";
|
|
@@ -938,9 +1034,11 @@ export default async function Edit${pascalName}Page({
|
|
|
938
1034
|
|
|
939
1035
|
async function handleUpdate(formData: FormData) {
|
|
940
1036
|
"use server";
|
|
1037
|
+
|
|
941
1038
|
await update${pascalName}(${updateId}, {
|
|
942
1039
|
${fields.map((f) => ` ${f.name}: ${formDataValue(f)},`).join("\n")}
|
|
943
1040
|
});
|
|
1041
|
+
|
|
944
1042
|
redirect("/${kebabPlural}");
|
|
945
1043
|
}
|
|
946
1044
|
|
|
@@ -971,17 +1069,20 @@ ${fields.map((f) => generateFormField(f, camelName, true)).join("\n\n")}
|
|
|
971
1069
|
}
|
|
972
1070
|
`;
|
|
973
1071
|
}
|
|
974
|
-
function
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1072
|
+
function createFieldContext(field, camelName, withDefault) {
|
|
1073
|
+
return {
|
|
1074
|
+
field,
|
|
1075
|
+
label: toPascalCase(field.name),
|
|
1076
|
+
optionalLabel: field.nullable ? ` <span className="text-gray-400">(optional)</span>` : "",
|
|
1077
|
+
required: field.nullable ? "" : " required",
|
|
1078
|
+
defaultValue: withDefault ? ` defaultValue={${camelName}.${field.name}}` : ""
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function generateTextareaField(ctx) {
|
|
1082
|
+
const { field, label, optionalLabel, required, defaultValue } = ctx;
|
|
1083
|
+
const rows = field.type === "json" ? 6 : 4;
|
|
1084
|
+
const placeholder = field.type === "json" ? ` placeholder="{}"` : "";
|
|
1085
|
+
return ` <div>
|
|
985
1086
|
<label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
|
|
986
1087
|
${label}${optionalLabel}
|
|
987
1088
|
</label>
|
|
@@ -989,13 +1090,14 @@ function generateFormField(field, camelName, withDefault = false) {
|
|
|
989
1090
|
id="${field.name}"
|
|
990
1091
|
name="${field.name}"
|
|
991
1092
|
rows={${rows}}
|
|
992
|
-
className="
|
|
1093
|
+
className="mt-1.5 block w-full rounded-lg border border-gray-200 px-3 py-2 text-gray-900 placeholder:text-gray-400 focus:border-gray-400 focus:outline-none focus:ring-0 resize-none"${defaultValue}${placeholder}${required}
|
|
993
1094
|
/>
|
|
994
1095
|
</div>`;
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1096
|
+
}
|
|
1097
|
+
function generateCheckboxField(ctx, camelName, withDefault) {
|
|
1098
|
+
const { field, label } = ctx;
|
|
1099
|
+
const defaultChecked = withDefault ? ` defaultChecked={${camelName}.${field.name}}` : "";
|
|
1100
|
+
return ` <div className="flex items-center gap-2">
|
|
999
1101
|
<input
|
|
1000
1102
|
type="checkbox"
|
|
1001
1103
|
id="${field.name}"
|
|
@@ -1006,38 +1108,27 @@ function generateFormField(field, camelName, withDefault = false) {
|
|
|
1006
1108
|
${label}
|
|
1007
1109
|
</label>
|
|
1008
1110
|
</div>`;
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
${
|
|
1014
|
-
|
|
1015
|
-
<input
|
|
1016
|
-
type="number"
|
|
1017
|
-
id="${field.name}"
|
|
1018
|
-
name="${field.name}"
|
|
1019
|
-
className="${inputClasses}"${defaultValue}${required}
|
|
1020
|
-
/>
|
|
1021
|
-
</div>`;
|
|
1022
|
-
}
|
|
1023
|
-
if (field.type === "float" || field.type === "decimal") {
|
|
1024
|
-
const step = field.type === "decimal" ? "0.01" : "any";
|
|
1025
|
-
return ` <div>
|
|
1111
|
+
}
|
|
1112
|
+
function generateNumberField(ctx, step) {
|
|
1113
|
+
const { field, label, optionalLabel, required, defaultValue } = ctx;
|
|
1114
|
+
const stepAttr = step ? `
|
|
1115
|
+
step="${step}"` : "";
|
|
1116
|
+
return ` <div>
|
|
1026
1117
|
<label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
|
|
1027
1118
|
${label}${optionalLabel}
|
|
1028
1119
|
</label>
|
|
1029
1120
|
<input
|
|
1030
|
-
type="number"
|
|
1031
|
-
step="${step}"
|
|
1121
|
+
type="number"${stepAttr}
|
|
1032
1122
|
id="${field.name}"
|
|
1033
1123
|
name="${field.name}"
|
|
1034
|
-
className="
|
|
1124
|
+
className="mt-1.5 block w-full rounded-lg border border-gray-200 px-3 py-2 text-gray-900 placeholder:text-gray-400 focus:border-gray-400 focus:outline-none focus:ring-0"${defaultValue}${required}
|
|
1035
1125
|
/>
|
|
1036
1126
|
</div>`;
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1127
|
+
}
|
|
1128
|
+
function generateDateField(ctx, camelName, withDefault) {
|
|
1129
|
+
const { field, label, optionalLabel, required } = ctx;
|
|
1130
|
+
const dateDefault = withDefault ? ` defaultValue={${camelName}.${field.name}?.toISOString().split("T")[0]}` : "";
|
|
1131
|
+
return ` <div>
|
|
1041
1132
|
<label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
|
|
1042
1133
|
${label}${optionalLabel}
|
|
1043
1134
|
</label>
|
|
@@ -1045,13 +1136,14 @@ function generateFormField(field, camelName, withDefault = false) {
|
|
|
1045
1136
|
type="date"
|
|
1046
1137
|
id="${field.name}"
|
|
1047
1138
|
name="${field.name}"
|
|
1048
|
-
className="
|
|
1139
|
+
className="mt-1.5 block w-full rounded-lg border border-gray-200 px-3 py-2 text-gray-900 placeholder:text-gray-400 focus:border-gray-400 focus:outline-none focus:ring-0"${dateDefault}${required}
|
|
1049
1140
|
/>
|
|
1050
1141
|
</div>`;
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1142
|
+
}
|
|
1143
|
+
function generateDatetimeField(ctx, camelName, withDefault) {
|
|
1144
|
+
const { field, label, optionalLabel, required } = ctx;
|
|
1145
|
+
const dateDefault = withDefault ? ` defaultValue={${camelName}.${field.name}?.toISOString().slice(0, 16)}` : "";
|
|
1146
|
+
return ` <div>
|
|
1055
1147
|
<label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
|
|
1056
1148
|
${label}${optionalLabel}
|
|
1057
1149
|
</label>
|
|
@@ -1059,25 +1151,28 @@ function generateFormField(field, camelName, withDefault = false) {
|
|
|
1059
1151
|
type="datetime-local"
|
|
1060
1152
|
id="${field.name}"
|
|
1061
1153
|
name="${field.name}"
|
|
1062
|
-
className="
|
|
1154
|
+
className="mt-1.5 block w-full rounded-lg border border-gray-200 px-3 py-2 text-gray-900 placeholder:text-gray-400 focus:border-gray-400 focus:outline-none focus:ring-0"${dateDefault}${required}
|
|
1063
1155
|
/>
|
|
1064
1156
|
</div>`;
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1157
|
+
}
|
|
1158
|
+
function generateSelectField(ctx) {
|
|
1159
|
+
const { field, label, optionalLabel, required, defaultValue } = ctx;
|
|
1160
|
+
const options = field.enumValues.map((v) => ` <option value="${escapeString(v)}">${toPascalCase(v)}</option>`).join("\n");
|
|
1161
|
+
return ` <div>
|
|
1069
1162
|
<label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
|
|
1070
1163
|
${label}${optionalLabel}
|
|
1071
1164
|
</label>
|
|
1072
1165
|
<select
|
|
1073
1166
|
id="${field.name}"
|
|
1074
1167
|
name="${field.name}"
|
|
1075
|
-
className="
|
|
1168
|
+
className="mt-1.5 block w-full rounded-lg border border-gray-200 px-3 py-2 text-gray-900 focus:border-gray-400 focus:outline-none focus:ring-0"${defaultValue}${required}
|
|
1076
1169
|
>
|
|
1077
1170
|
${options}
|
|
1078
1171
|
</select>
|
|
1079
1172
|
</div>`;
|
|
1080
|
-
|
|
1173
|
+
}
|
|
1174
|
+
function generateTextField(ctx) {
|
|
1175
|
+
const { field, label, optionalLabel, required, defaultValue } = ctx;
|
|
1081
1176
|
return ` <div>
|
|
1082
1177
|
<label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
|
|
1083
1178
|
${label}${optionalLabel}
|
|
@@ -1086,14 +1181,46 @@ ${options}
|
|
|
1086
1181
|
type="text"
|
|
1087
1182
|
id="${field.name}"
|
|
1088
1183
|
name="${field.name}"
|
|
1089
|
-
className="
|
|
1184
|
+
className="mt-1.5 block w-full rounded-lg border border-gray-200 px-3 py-2 text-gray-900 placeholder:text-gray-400 focus:border-gray-400 focus:outline-none focus:ring-0"${defaultValue}${required}
|
|
1090
1185
|
/>
|
|
1091
1186
|
</div>`;
|
|
1092
1187
|
}
|
|
1188
|
+
function generateFormField(field, camelName, withDefault = false) {
|
|
1189
|
+
const ctx = createFieldContext(field, camelName, withDefault);
|
|
1190
|
+
switch (field.type) {
|
|
1191
|
+
case "text":
|
|
1192
|
+
case "json":
|
|
1193
|
+
return generateTextareaField(ctx);
|
|
1194
|
+
case "boolean":
|
|
1195
|
+
case "bool":
|
|
1196
|
+
return generateCheckboxField(ctx, camelName, withDefault);
|
|
1197
|
+
case "integer":
|
|
1198
|
+
case "int":
|
|
1199
|
+
case "bigint":
|
|
1200
|
+
return generateNumberField(ctx);
|
|
1201
|
+
case "float":
|
|
1202
|
+
return generateNumberField(ctx, "any");
|
|
1203
|
+
case "decimal":
|
|
1204
|
+
return generateNumberField(ctx, "0.01");
|
|
1205
|
+
case "date":
|
|
1206
|
+
return generateDateField(ctx, camelName, withDefault);
|
|
1207
|
+
case "datetime":
|
|
1208
|
+
case "timestamp":
|
|
1209
|
+
return generateDatetimeField(ctx, camelName, withDefault);
|
|
1210
|
+
default:
|
|
1211
|
+
if (field.isEnum && field.enumValues) {
|
|
1212
|
+
return generateSelectField(ctx);
|
|
1213
|
+
}
|
|
1214
|
+
return generateTextField(ctx);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1093
1217
|
function formDataValue(field) {
|
|
1094
1218
|
const getValue = `formData.get("${field.name}")`;
|
|
1095
1219
|
const asString = `${getValue} as string`;
|
|
1096
1220
|
if (field.nullable) {
|
|
1221
|
+
if (field.type === "boolean" || field.type === "bool") {
|
|
1222
|
+
return `${getValue} === "on" ? true : null`;
|
|
1223
|
+
}
|
|
1097
1224
|
if (field.type === "integer" || field.type === "int" || field.type === "bigint") {
|
|
1098
1225
|
return `${getValue} ? parseInt(${asString}) : null`;
|
|
1099
1226
|
}
|
|
@@ -1120,6 +1247,9 @@ function formDataValue(field) {
|
|
|
1120
1247
|
if (field.type === "float") {
|
|
1121
1248
|
return `parseFloat(${asString})`;
|
|
1122
1249
|
}
|
|
1250
|
+
if (field.type === "decimal") {
|
|
1251
|
+
return asString;
|
|
1252
|
+
}
|
|
1123
1253
|
if (field.type === "datetime" || field.type === "timestamp" || field.type === "date") {
|
|
1124
1254
|
return `new Date(${asString})`;
|
|
1125
1255
|
}
|
|
@@ -1318,47 +1448,28 @@ export async function DELETE(request: Request, { params }: Params) {
|
|
|
1318
1448
|
|
|
1319
1449
|
// src/generators/destroy.ts
|
|
1320
1450
|
import * as path8 from "path";
|
|
1321
|
-
function
|
|
1451
|
+
function destroy(name, type, buildPath, options = {}) {
|
|
1322
1452
|
validateModelName(name);
|
|
1323
1453
|
const ctx = createModelContext(name);
|
|
1324
1454
|
const config = detectProjectConfig();
|
|
1325
1455
|
const prefix = options.dryRun ? "[dry-run] " : "";
|
|
1326
1456
|
log.info(`
|
|
1327
|
-
${prefix}Destroying
|
|
1457
|
+
${prefix}Destroying ${type} ${ctx.pascalName}...
|
|
1328
1458
|
`);
|
|
1329
|
-
const basePath =
|
|
1459
|
+
const basePath = buildPath(ctx);
|
|
1330
1460
|
deleteDirectory(basePath, options);
|
|
1331
1461
|
log.info(`
|
|
1332
1462
|
Note: Schema in ${config.dbPath}/schema.ts was not modified.`);
|
|
1333
1463
|
log.info(` Remove the table definition manually if needed.`);
|
|
1334
1464
|
}
|
|
1465
|
+
function destroyScaffold(name, options = {}) {
|
|
1466
|
+
destroy(name, "scaffold", (ctx) => path8.join(getAppPath(), ctx.kebabPlural), options);
|
|
1467
|
+
}
|
|
1335
1468
|
function destroyResource(name, options = {}) {
|
|
1336
|
-
|
|
1337
|
-
const ctx = createModelContext(name);
|
|
1338
|
-
const config = detectProjectConfig();
|
|
1339
|
-
const prefix = options.dryRun ? "[dry-run] " : "";
|
|
1340
|
-
log.info(`
|
|
1341
|
-
${prefix}Destroying resource ${ctx.pascalName}...
|
|
1342
|
-
`);
|
|
1343
|
-
const basePath = path8.join(getAppPath(), ctx.kebabPlural);
|
|
1344
|
-
deleteDirectory(basePath, options);
|
|
1345
|
-
log.info(`
|
|
1346
|
-
Note: Schema in ${config.dbPath}/schema.ts was not modified.`);
|
|
1347
|
-
log.info(` Remove the table definition manually if needed.`);
|
|
1469
|
+
destroy(name, "resource", (ctx) => path8.join(getAppPath(), ctx.kebabPlural), options);
|
|
1348
1470
|
}
|
|
1349
1471
|
function destroyApi(name, options = {}) {
|
|
1350
|
-
|
|
1351
|
-
const ctx = createModelContext(name);
|
|
1352
|
-
const config = detectProjectConfig();
|
|
1353
|
-
const prefix = options.dryRun ? "[dry-run] " : "";
|
|
1354
|
-
log.info(`
|
|
1355
|
-
${prefix}Destroying API ${ctx.pascalName}...
|
|
1356
|
-
`);
|
|
1357
|
-
const basePath = path8.join(getAppPath(), "api", ctx.kebabPlural);
|
|
1358
|
-
deleteDirectory(basePath, options);
|
|
1359
|
-
log.info(`
|
|
1360
|
-
Note: Schema in ${config.dbPath}/schema.ts was not modified.`);
|
|
1361
|
-
log.info(` Remove the table definition manually if needed.`);
|
|
1472
|
+
destroy(name, "API", (ctx) => path8.join(getAppPath(), "api", ctx.kebabPlural), options);
|
|
1362
1473
|
}
|
|
1363
1474
|
|
|
1364
1475
|
// src/index.ts
|