brizzle 0.2.8 → 0.2.9

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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/dist/index.js +36 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -181,6 +181,24 @@ Next steps:
181
181
  - Next.js project with App Router
182
182
  - Drizzle ORM configured
183
183
 
184
+ ## Roadmap
185
+
186
+ - [ ] **Drizzle init** - `brizzle init` to set up Drizzle ORM, database config, and db connection
187
+ - [ ] **Authentication** - `brizzle auth` to generate [better-auth](https://www.better-auth.com/) setup with user model and sign-in/sign-up pages
188
+ - [ ] **Zod schemas** - Generate validation schemas for forms and API routes
189
+ - [ ] **Indexes** - Support for `name:string:index` field modifier
190
+ - [ ] **Default values** - Support for `status:string:default:active`
191
+ - [ ] **Soft deletes** - Add `--soft-delete` flag for `deletedAt` timestamp
192
+ - [ ] **Pagination** - Add pagination to list pages and API routes
193
+ - [ ] **Search & filtering** - Generate search/filter UI for list pages
194
+ - [ ] **Seed generator** - `brizzle seed <model>` to generate seed data files
195
+ - [ ] **Relations helper** - Better syntax for has-many/belongs-to relationships
196
+ - [ ] **Custom templates** - Allow overriding templates via `.brizzle/` directory
197
+ - [ ] **Interactive mode** - `brizzle new` wizard for step-by-step model creation
198
+ - [ ] **Import cleanup** - Remove unused imports when destroying models
199
+
200
+ Have a feature request? [Open an issue](https://github.com/mantaskaveckas/brizzle/issues)
201
+
184
202
  ## License
185
203
 
186
204
  MIT
package/dist/index.js CHANGED
@@ -945,31 +945,31 @@ export default async function ${pascalPlural}Page() {
945
945
  return (
946
946
  <div className="mx-auto max-w-3xl px-6 py-12">
947
947
  <div className="mb-10 flex items-center justify-between">
948
- <h1 className="text-2xl font-semibold text-gray-900">${pascalPlural}</h1>
948
+ <h1 className="text-2xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">${pascalPlural}</h1>
949
949
  <Link
950
950
  href="/${kebabPlural}/new"
951
- className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800"
951
+ className="flex h-10 items-center rounded-full bg-zinc-900 px-4 text-sm font-medium text-white transition-colors hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
952
952
  >
953
953
  New ${pascalName}
954
954
  </Link>
955
955
  </div>
956
956
 
957
957
  {${camelName}s.length === 0 ? (
958
- <p className="text-gray-500">No ${camelName}s yet.</p>
958
+ <p className="text-zinc-500 dark:text-zinc-400">No ${camelName}s yet.</p>
959
959
  ) : (
960
- <div className="divide-y divide-gray-100">
960
+ <div className="divide-y divide-zinc-100 dark:divide-zinc-800">
961
961
  {${camelName}s.map((${camelName}) => (
962
962
  <div
963
963
  key={${camelName}.id}
964
964
  className="flex items-center justify-between py-4"
965
965
  >
966
- <Link href={\`/${kebabPlural}/\${${camelName}.id}\`} className="font-medium text-gray-900 hover:text-gray-600">
966
+ <Link href={\`/${kebabPlural}/\${${camelName}.id}\`} className="font-medium text-zinc-900 hover:text-zinc-600 dark:text-zinc-50 dark:hover:text-zinc-300">
967
967
  {${camelName}.${displayField}}
968
968
  </Link>
969
969
  <div className="flex gap-4 text-sm">
970
970
  <Link
971
971
  href={\`/${kebabPlural}/\${${camelName}.id}/edit\`}
972
- className="text-gray-500 hover:text-gray-900"
972
+ className="text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-50"
973
973
  >
974
974
  Edit
975
975
  </Link>
@@ -979,7 +979,7 @@ export default async function ${pascalPlural}Page() {
979
979
  await delete${pascalName}(${camelName}.id);
980
980
  }}
981
981
  >
982
- <button type="submit" className="text-gray-500 hover:text-red-600">
982
+ <button type="submit" className="text-zinc-500 hover:text-red-600 dark:text-zinc-400 dark:hover:text-red-400">
983
983
  Delete
984
984
  </button>
985
985
  </form>
@@ -1011,7 +1011,7 @@ ${fields.map((f) => ` ${f.name}: ${formDataValue(f)},`).join("\n")}
1011
1011
 
1012
1012
  return (
1013
1013
  <div className="mx-auto max-w-xl px-6 py-12">
1014
- <h1 className="mb-8 text-2xl font-semibold text-gray-900">New ${pascalName}</h1>
1014
+ <h1 className="mb-8 text-2xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">New ${pascalName}</h1>
1015
1015
 
1016
1016
  <form action={handleCreate} className="space-y-5">
1017
1017
  ${fields.map((f) => generateFormField(f, camelName)).join("\n\n")}
@@ -1019,13 +1019,13 @@ ${fields.map((f) => generateFormField(f, camelName)).join("\n\n")}
1019
1019
  <div className="flex gap-3 pt-4">
1020
1020
  <button
1021
1021
  type="submit"
1022
- className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800"
1022
+ className="flex h-10 items-center rounded-full bg-zinc-900 px-4 text-sm font-medium text-white transition-colors hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
1023
1023
  >
1024
1024
  Create ${pascalName}
1025
1025
  </button>
1026
1026
  <Link
1027
1027
  href="/${kebabPlural}"
1028
- className="rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-50"
1028
+ className="flex h-10 items-center rounded-full border border-zinc-200 px-4 text-sm font-medium text-zinc-600 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
1029
1029
  >
1030
1030
  Cancel
1031
1031
  </Link>
@@ -1063,33 +1063,33 @@ export default async function ${pascalName}Page({
1063
1063
  return (
1064
1064
  <div className="mx-auto max-w-xl px-6 py-12">
1065
1065
  <div className="mb-8 flex items-center justify-between">
1066
- <h1 className="text-2xl font-semibold text-gray-900">${pascalName}</h1>
1066
+ <h1 className="text-2xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">${pascalName}</h1>
1067
1067
  <div className="flex gap-3">
1068
1068
  <Link
1069
1069
  href={\`/${kebabPlural}/\${${camelName}.id}/edit\`}
1070
- className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800"
1070
+ className="flex h-10 items-center rounded-full bg-zinc-900 px-4 text-sm font-medium text-white transition-colors hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
1071
1071
  >
1072
1072
  Edit
1073
1073
  </Link>
1074
1074
  <Link
1075
1075
  href="/${kebabPlural}"
1076
- className="rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-50"
1076
+ className="flex h-10 items-center rounded-full border border-zinc-200 px-4 text-sm font-medium text-zinc-600 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
1077
1077
  >
1078
1078
  Back
1079
1079
  </Link>
1080
1080
  </div>
1081
1081
  </div>
1082
1082
 
1083
- <dl className="divide-y divide-gray-100">
1083
+ <dl className="divide-y divide-zinc-100 dark:divide-zinc-800">
1084
1084
  ${fields.map(
1085
1085
  (f) => ` <div className="py-3">
1086
- <dt className="text-sm text-gray-500">${toPascalCase(f.name)}</dt>
1087
- <dd className="mt-1 text-gray-900">{${camelName}.${f.name}}</dd>
1086
+ <dt className="text-sm text-zinc-500 dark:text-zinc-400">${toPascalCase(f.name)}</dt>
1087
+ <dd className="mt-1 text-zinc-900 dark:text-zinc-50">{${camelName}.${f.name}}</dd>
1088
1088
  </div>`
1089
1089
  ).join("\n")}${options.noTimestamps ? "" : `
1090
1090
  <div className="py-3">
1091
- <dt className="text-sm text-gray-500">Created At</dt>
1092
- <dd className="mt-1 text-gray-900">{${camelName}.createdAt.toLocaleString()}</dd>
1091
+ <dt className="text-sm text-zinc-500 dark:text-zinc-400">Created At</dt>
1092
+ <dd className="mt-1 text-zinc-900 dark:text-zinc-50">{${camelName}.createdAt.toLocaleString()}</dd>
1093
1093
  </div>`}
1094
1094
  </dl>
1095
1095
  </div>
@@ -1134,7 +1134,7 @@ ${fields.map((f) => ` ${f.name}: ${formDataValue(f)},`).join("\n")}
1134
1134
 
1135
1135
  return (
1136
1136
  <div className="mx-auto max-w-xl px-6 py-12">
1137
- <h1 className="mb-8 text-2xl font-semibold text-gray-900">Edit ${pascalName}</h1>
1137
+ <h1 className="mb-8 text-2xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">Edit ${pascalName}</h1>
1138
1138
 
1139
1139
  <form action={handleUpdate} className="space-y-5">
1140
1140
  ${fields.map((f) => generateFormField(f, camelName, true)).join("\n\n")}
@@ -1142,13 +1142,13 @@ ${fields.map((f) => generateFormField(f, camelName, true)).join("\n\n")}
1142
1142
  <div className="flex gap-3 pt-4">
1143
1143
  <button
1144
1144
  type="submit"
1145
- className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800"
1145
+ className="flex h-10 items-center rounded-full bg-zinc-900 px-4 text-sm font-medium text-white transition-colors hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
1146
1146
  >
1147
1147
  Update ${pascalName}
1148
1148
  </button>
1149
1149
  <Link
1150
1150
  href="/${kebabPlural}"
1151
- className="rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-50"
1151
+ className="flex h-10 items-center rounded-full border border-zinc-200 px-4 text-sm font-medium text-zinc-600 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
1152
1152
  >
1153
1153
  Cancel
1154
1154
  </Link>
@@ -1163,7 +1163,7 @@ function createFieldContext(field, camelName, withDefault) {
1163
1163
  return {
1164
1164
  field,
1165
1165
  label: toPascalCase(field.name),
1166
- optionalLabel: field.nullable ? ` <span className="text-gray-400">(optional)</span>` : "",
1166
+ optionalLabel: field.nullable ? ` <span className="text-zinc-400 dark:text-zinc-500">(optional)</span>` : "",
1167
1167
  required: field.nullable ? "" : " required",
1168
1168
  defaultValue: withDefault ? ` defaultValue={${camelName}.${field.name}}` : ""
1169
1169
  };
@@ -1173,14 +1173,14 @@ function generateTextareaField(ctx) {
1173
1173
  const rows = field.type === "json" ? 6 : 4;
1174
1174
  const placeholder = field.type === "json" ? ` placeholder="{}"` : "";
1175
1175
  return ` <div>
1176
- <label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
1176
+ <label htmlFor="${field.name}" className="block text-sm font-medium text-zinc-700 dark:text-zinc-300">
1177
1177
  ${label}${optionalLabel}
1178
1178
  </label>
1179
1179
  <textarea
1180
1180
  id="${field.name}"
1181
1181
  name="${field.name}"
1182
1182
  rows={${rows}}
1183
- 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}
1183
+ className="mt-1.5 block w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-zinc-900 placeholder:text-zinc-400 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-50 dark:placeholder:text-zinc-500 dark:focus:border-zinc-500 resize-none"${defaultValue}${placeholder}${required}
1184
1184
  />
1185
1185
  </div>`;
1186
1186
  }
@@ -1192,9 +1192,9 @@ function generateCheckboxField(ctx, camelName, withDefault) {
1192
1192
  type="checkbox"
1193
1193
  id="${field.name}"
1194
1194
  name="${field.name}"
1195
- className="h-4 w-4 rounded border-gray-300 text-gray-900 focus:ring-0 focus:ring-offset-0"${defaultChecked}
1195
+ className="h-4 w-4 rounded border-zinc-300 text-zinc-900 focus:ring-0 focus:ring-offset-0 dark:border-zinc-600 dark:bg-zinc-800 dark:text-zinc-50"${defaultChecked}
1196
1196
  />
1197
- <label htmlFor="${field.name}" className="text-sm font-medium text-gray-700">
1197
+ <label htmlFor="${field.name}" className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
1198
1198
  ${label}
1199
1199
  </label>
1200
1200
  </div>`;
@@ -1204,14 +1204,14 @@ function generateNumberField(ctx, step) {
1204
1204
  const stepAttr = step ? `
1205
1205
  step="${step}"` : "";
1206
1206
  return ` <div>
1207
- <label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
1207
+ <label htmlFor="${field.name}" className="block text-sm font-medium text-zinc-700 dark:text-zinc-300">
1208
1208
  ${label}${optionalLabel}
1209
1209
  </label>
1210
1210
  <input
1211
1211
  type="number"${stepAttr}
1212
1212
  id="${field.name}"
1213
1213
  name="${field.name}"
1214
- 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}
1214
+ className="mt-1.5 block w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-zinc-900 placeholder:text-zinc-400 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-50 dark:placeholder:text-zinc-500 dark:focus:border-zinc-500"${defaultValue}${required}
1215
1215
  />
1216
1216
  </div>`;
1217
1217
  }
@@ -1219,14 +1219,14 @@ function generateDateField(ctx, camelName, withDefault) {
1219
1219
  const { field, label, optionalLabel, required } = ctx;
1220
1220
  const dateDefault = withDefault ? ` defaultValue={${camelName}.${field.name}?.toISOString().split("T")[0]}` : "";
1221
1221
  return ` <div>
1222
- <label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
1222
+ <label htmlFor="${field.name}" className="block text-sm font-medium text-zinc-700 dark:text-zinc-300">
1223
1223
  ${label}${optionalLabel}
1224
1224
  </label>
1225
1225
  <input
1226
1226
  type="date"
1227
1227
  id="${field.name}"
1228
1228
  name="${field.name}"
1229
- 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}
1229
+ className="mt-1.5 block w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-zinc-900 placeholder:text-zinc-400 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-50 dark:placeholder:text-zinc-500 dark:focus:border-zinc-500"${dateDefault}${required}
1230
1230
  />
1231
1231
  </div>`;
1232
1232
  }
@@ -1234,14 +1234,14 @@ function generateDatetimeField(ctx, camelName, withDefault) {
1234
1234
  const { field, label, optionalLabel, required } = ctx;
1235
1235
  const dateDefault = withDefault ? ` defaultValue={${camelName}.${field.name}?.toISOString().slice(0, 16)}` : "";
1236
1236
  return ` <div>
1237
- <label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
1237
+ <label htmlFor="${field.name}" className="block text-sm font-medium text-zinc-700 dark:text-zinc-300">
1238
1238
  ${label}${optionalLabel}
1239
1239
  </label>
1240
1240
  <input
1241
1241
  type="datetime-local"
1242
1242
  id="${field.name}"
1243
1243
  name="${field.name}"
1244
- 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}
1244
+ className="mt-1.5 block w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-zinc-900 placeholder:text-zinc-400 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-50 dark:placeholder:text-zinc-500 dark:focus:border-zinc-500"${dateDefault}${required}
1245
1245
  />
1246
1246
  </div>`;
1247
1247
  }
@@ -1249,13 +1249,13 @@ function generateSelectField(ctx) {
1249
1249
  const { field, label, optionalLabel, required, defaultValue } = ctx;
1250
1250
  const options = field.enumValues.map((v) => ` <option value="${escapeString(v)}">${toPascalCase(v)}</option>`).join("\n");
1251
1251
  return ` <div>
1252
- <label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
1252
+ <label htmlFor="${field.name}" className="block text-sm font-medium text-zinc-700 dark:text-zinc-300">
1253
1253
  ${label}${optionalLabel}
1254
1254
  </label>
1255
1255
  <select
1256
1256
  id="${field.name}"
1257
1257
  name="${field.name}"
1258
- 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}
1258
+ className="mt-1.5 block w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-zinc-900 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-50 dark:focus:border-zinc-500"${defaultValue}${required}
1259
1259
  >
1260
1260
  ${options}
1261
1261
  </select>
@@ -1264,14 +1264,14 @@ ${options}
1264
1264
  function generateTextField(ctx) {
1265
1265
  const { field, label, optionalLabel, required, defaultValue } = ctx;
1266
1266
  return ` <div>
1267
- <label htmlFor="${field.name}" className="block text-sm font-medium text-gray-700">
1267
+ <label htmlFor="${field.name}" className="block text-sm font-medium text-zinc-700 dark:text-zinc-300">
1268
1268
  ${label}${optionalLabel}
1269
1269
  </label>
1270
1270
  <input
1271
1271
  type="text"
1272
1272
  id="${field.name}"
1273
1273
  name="${field.name}"
1274
- 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}
1274
+ className="mt-1.5 block w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-zinc-900 placeholder:text-zinc-400 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-50 dark:placeholder:text-zinc-500 dark:focus:border-zinc-500"${defaultValue}${required}
1275
1275
  />
1276
1276
  </div>`;
1277
1277
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brizzle",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Rails-like generators for Next.js + Drizzle ORM projects",
5
5
  "type": "module",
6
6
  "bin": {