@farming-labs/orm 0.0.8 → 0.0.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.
- package/dist/index.cjs +125 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +125 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -144,6 +144,38 @@ function datetime() {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// src/manifest.ts
|
|
147
|
+
function createConstraintName(table, columns, suffix) {
|
|
148
|
+
const base = [table, ...columns].join("_").replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
149
|
+
return `${base}_${suffix}`;
|
|
150
|
+
}
|
|
151
|
+
function normalizeConstraints(modelName, table, fields, constraints) {
|
|
152
|
+
const normalize = (entries, unique) => (entries ?? []).map((entry) => {
|
|
153
|
+
if (!entry.length) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Model "${modelName}" defines an empty ${unique ? "unique" : "index"} constraint.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const columns = entry.map((fieldName) => {
|
|
159
|
+
const field = fields[fieldName];
|
|
160
|
+
if (!field) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Model "${modelName}" defines a ${unique ? "unique" : "index"} constraint on unknown field "${fieldName}".`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return field.column;
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
name: createConstraintName(table, columns, unique ? "unique" : "idx"),
|
|
169
|
+
fields: [...entry],
|
|
170
|
+
columns,
|
|
171
|
+
unique
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
unique: normalize(constraints.unique, true),
|
|
176
|
+
indexes: normalize(constraints.indexes, false)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
147
179
|
function createManifest(schema) {
|
|
148
180
|
const models = Object.fromEntries(
|
|
149
181
|
Object.entries(schema.models).map(
|
|
@@ -171,7 +203,13 @@ function createManifest(schema) {
|
|
|
171
203
|
table: definition.table,
|
|
172
204
|
description: definition.description,
|
|
173
205
|
fields,
|
|
174
|
-
relations: definition.relations
|
|
206
|
+
relations: definition.relations,
|
|
207
|
+
constraints: normalizeConstraints(
|
|
208
|
+
name,
|
|
209
|
+
definition.table,
|
|
210
|
+
fields,
|
|
211
|
+
definition.constraints
|
|
212
|
+
)
|
|
175
213
|
}
|
|
176
214
|
];
|
|
177
215
|
}
|
|
@@ -183,6 +221,7 @@ function createManifest(schema) {
|
|
|
183
221
|
// src/generators.ts
|
|
184
222
|
var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
|
|
185
223
|
var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
|
|
224
|
+
var camelize = (value) => value.replace(/[^a-zA-Z0-9]+(.)/g, (_, char) => char.toUpperCase()).replace(/^[^a-zA-Z]+/, "").replace(/^[A-Z]/, (char) => char.toLowerCase());
|
|
186
225
|
function resolveReferenceTarget(manifest, model2, foreignKey, fallbackTarget) {
|
|
187
226
|
const reference = model2.fields[foreignKey]?.references;
|
|
188
227
|
if (!reference) {
|
|
@@ -217,6 +256,16 @@ function prismaType(field) {
|
|
|
217
256
|
return "DateTime";
|
|
218
257
|
}
|
|
219
258
|
}
|
|
259
|
+
function drizzleConstraintProperty(constraint) {
|
|
260
|
+
return camelize(constraint.name) || "constraint";
|
|
261
|
+
}
|
|
262
|
+
function constrainedFields(model2) {
|
|
263
|
+
return new Set(
|
|
264
|
+
[...model2.constraints.unique, ...model2.constraints.indexes].flatMap(
|
|
265
|
+
(constraint) => constraint.fields
|
|
266
|
+
)
|
|
267
|
+
);
|
|
268
|
+
}
|
|
220
269
|
function drizzleImports(dialect, manifest) {
|
|
221
270
|
const models = Object.values(manifest.models);
|
|
222
271
|
const needsBoolean = models.some(
|
|
@@ -225,12 +274,17 @@ function drizzleImports(dialect, manifest) {
|
|
|
225
274
|
const needsDate = models.some(
|
|
226
275
|
(model2) => Object.values(model2.fields).some((field) => field.kind === "datetime")
|
|
227
276
|
);
|
|
277
|
+
const needsIndexes = models.some(
|
|
278
|
+
(model2) => model2.constraints.indexes.length || model2.constraints.unique.length
|
|
279
|
+
);
|
|
228
280
|
if (dialect === "pg") {
|
|
229
281
|
return [
|
|
230
282
|
"pgTable",
|
|
231
283
|
"text",
|
|
232
284
|
needsBoolean ? "boolean" : null,
|
|
233
|
-
needsDate ? "timestamp" : null
|
|
285
|
+
needsDate ? "timestamp" : null,
|
|
286
|
+
needsIndexes ? "index" : null,
|
|
287
|
+
needsIndexes ? "uniqueIndex" : null
|
|
234
288
|
].filter(Boolean);
|
|
235
289
|
}
|
|
236
290
|
if (dialect === "mysql") {
|
|
@@ -239,12 +293,20 @@ function drizzleImports(dialect, manifest) {
|
|
|
239
293
|
"varchar",
|
|
240
294
|
"text",
|
|
241
295
|
needsBoolean ? "boolean" : null,
|
|
242
|
-
needsDate ? "datetime" : null
|
|
296
|
+
needsDate ? "datetime" : null,
|
|
297
|
+
needsIndexes ? "index" : null,
|
|
298
|
+
needsIndexes ? "uniqueIndex" : null
|
|
243
299
|
].filter(Boolean);
|
|
244
300
|
}
|
|
245
|
-
return [
|
|
301
|
+
return [
|
|
302
|
+
"sqliteTable",
|
|
303
|
+
"text",
|
|
304
|
+
"integer",
|
|
305
|
+
needsIndexes ? "index" : null,
|
|
306
|
+
needsIndexes ? "uniqueIndex" : null
|
|
307
|
+
].filter(Boolean);
|
|
246
308
|
}
|
|
247
|
-
function drizzleColumn(field, dialect) {
|
|
309
|
+
function drizzleColumn(field, dialect, options = {}) {
|
|
248
310
|
if (field.kind === "id") {
|
|
249
311
|
if (dialect === "mysql") {
|
|
250
312
|
return `varchar("${field.column}", { length: 191 }).primaryKey()`;
|
|
@@ -253,7 +315,7 @@ function drizzleColumn(field, dialect) {
|
|
|
253
315
|
}
|
|
254
316
|
if (field.kind === "string") {
|
|
255
317
|
if (dialect === "mysql") {
|
|
256
|
-
const base = field.unique || field.references ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
|
|
318
|
+
const base = field.unique || field.references || options.indexed ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
|
|
257
319
|
return `${base}${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
|
|
258
320
|
}
|
|
259
321
|
return `text("${field.column}")${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
|
|
@@ -270,14 +332,14 @@ function drizzleColumn(field, dialect) {
|
|
|
270
332
|
if (dialect === "sqlite") {
|
|
271
333
|
return `integer("${field.column}", { mode: "timestamp" })${field.nullable ? "" : ".notNull()"}`;
|
|
272
334
|
}
|
|
273
|
-
return `timestamp("${field.column}")${field.nullable ? "" : ".notNull()"}`;
|
|
335
|
+
return `timestamp("${field.column}", { withTimezone: true, mode: "date" })${field.nullable ? "" : ".notNull()"}`;
|
|
274
336
|
}
|
|
275
|
-
function sqlType(field, dialect) {
|
|
337
|
+
function sqlType(field, dialect, options = {}) {
|
|
276
338
|
if (field.kind === "id") {
|
|
277
339
|
return dialect === "mysql" ? "varchar(191)" : "text";
|
|
278
340
|
}
|
|
279
341
|
if (field.kind === "string") {
|
|
280
|
-
return dialect === "mysql" && (field.unique || field.references) ? "varchar(191)" : "text";
|
|
342
|
+
return dialect === "mysql" && (field.unique || field.references || options.indexed) ? "varchar(191)" : "text";
|
|
281
343
|
}
|
|
282
344
|
if (field.kind === "boolean") {
|
|
283
345
|
return dialect === "sqlite" ? "integer" : "boolean";
|
|
@@ -288,7 +350,7 @@ function sqlType(field, dialect) {
|
|
|
288
350
|
if (dialect === "sqlite") {
|
|
289
351
|
return "text";
|
|
290
352
|
}
|
|
291
|
-
return "
|
|
353
|
+
return "timestamptz";
|
|
292
354
|
}
|
|
293
355
|
function sqlIdentifier(dialect, value) {
|
|
294
356
|
if (dialect === "mysql") {
|
|
@@ -296,6 +358,14 @@ function sqlIdentifier(dialect, value) {
|
|
|
296
358
|
}
|
|
297
359
|
return `"${value}"`;
|
|
298
360
|
}
|
|
361
|
+
function sqlCreateIndexStatement(dialect, table, constraint) {
|
|
362
|
+
const indexName = sqlIdentifier(dialect, constraint.name);
|
|
363
|
+
const tableName = sqlIdentifier(dialect, table);
|
|
364
|
+
const columns = constraint.columns.map((column) => sqlIdentifier(dialect, column)).join(", ");
|
|
365
|
+
const createKeyword = constraint.unique ? "create unique index" : "create index";
|
|
366
|
+
const ifNotExists = dialect === "mysql" ? "" : " if not exists";
|
|
367
|
+
return `${createKeyword}${ifNotExists} ${indexName} on ${tableName}(${columns});`;
|
|
368
|
+
}
|
|
299
369
|
function renderPrismaSchema(schema, options = {}) {
|
|
300
370
|
const manifest = createManifest(schema);
|
|
301
371
|
const provider = options.provider ?? "postgresql";
|
|
@@ -378,10 +448,18 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
378
448
|
relation.many ? ` ${relationName} ${capitalize(relation.sourceModel)}[]` : ` ${relationName} ${capitalize(relation.sourceModel)}?`
|
|
379
449
|
);
|
|
380
450
|
}
|
|
381
|
-
const
|
|
382
|
-
|
|
451
|
+
const modelLines = [
|
|
452
|
+
...lines,
|
|
453
|
+
...model2.constraints.unique.map(
|
|
454
|
+
(constraint) => ` @@unique([${constraint.fields.join(", ")}])`
|
|
455
|
+
),
|
|
456
|
+
...model2.constraints.indexes.map(
|
|
457
|
+
(constraint) => ` @@index([${constraint.fields.join(", ")}])`
|
|
458
|
+
),
|
|
459
|
+
...model2.table !== modelName ? [` @@map("${model2.table}")`] : []
|
|
460
|
+
];
|
|
383
461
|
return `model ${modelName} {
|
|
384
|
-
${
|
|
462
|
+
${modelLines.join("\n")}
|
|
385
463
|
}`;
|
|
386
464
|
});
|
|
387
465
|
return `generator ${generatorName} {
|
|
@@ -401,17 +479,33 @@ function renderDrizzleSchema(schema, options) {
|
|
|
401
479
|
const coreImports = drizzleImports(options.dialect, manifest).join(", ");
|
|
402
480
|
const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
|
|
403
481
|
const modelBlocks = Object.values(manifest.models).map((model2) => {
|
|
482
|
+
const indexedFields = constrainedFields(model2);
|
|
404
483
|
const lines = Object.values(model2.fields).map((field) => {
|
|
405
|
-
let value = drizzleColumn(field, options.dialect);
|
|
484
|
+
let value = drizzleColumn(field, options.dialect, { indexed: indexedFields.has(field.name) });
|
|
406
485
|
if (field.references) {
|
|
407
486
|
const [targetModel, targetField] = field.references.split(".");
|
|
408
487
|
value += `.references(() => ${targetModel}.${targetField})`;
|
|
409
488
|
}
|
|
410
489
|
return ` ${field.name}: ${value}`;
|
|
411
490
|
});
|
|
412
|
-
|
|
491
|
+
const constraintLines = [
|
|
492
|
+
...model2.constraints.unique.map(
|
|
493
|
+
(constraint) => ` ${drizzleConstraintProperty(constraint)}: uniqueIndex("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
|
|
494
|
+
),
|
|
495
|
+
...model2.constraints.indexes.map(
|
|
496
|
+
(constraint) => ` ${drizzleConstraintProperty(constraint)}: index("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
|
|
497
|
+
)
|
|
498
|
+
];
|
|
499
|
+
if (!constraintLines.length) {
|
|
500
|
+
return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
|
|
413
501
|
${lines.join(",\n")}
|
|
414
502
|
});`;
|
|
503
|
+
}
|
|
504
|
+
return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
|
|
505
|
+
${lines.join(",\n")}
|
|
506
|
+
}, (table) => ({
|
|
507
|
+
${constraintLines.join(",\n")}
|
|
508
|
+
}));`;
|
|
415
509
|
});
|
|
416
510
|
const relationBlocks = Object.values(manifest.models).map((model2) => {
|
|
417
511
|
const lines = Object.entries(model2.relations).flatMap(([relationName, relation]) => {
|
|
@@ -450,10 +544,13 @@ ${[...modelBlocks, ...relationBlocks].join("\n\n")}
|
|
|
450
544
|
}
|
|
451
545
|
function renderSafeSql(schema, options) {
|
|
452
546
|
const manifest = createManifest(schema);
|
|
453
|
-
const statements = Object.values(manifest.models).
|
|
547
|
+
const statements = Object.values(manifest.models).flatMap((model2) => {
|
|
548
|
+
const indexedFields = constrainedFields(model2);
|
|
454
549
|
const columns = Object.values(model2.fields).map((field) => {
|
|
455
550
|
const parts = [
|
|
456
|
-
`${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect
|
|
551
|
+
`${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect, {
|
|
552
|
+
indexed: indexedFields.has(field.name)
|
|
553
|
+
})}`
|
|
457
554
|
];
|
|
458
555
|
if (field.kind === "id") parts.push("primary key");
|
|
459
556
|
if (!field.nullable) parts.push("not null");
|
|
@@ -476,9 +573,17 @@ function renderSafeSql(schema, options) {
|
|
|
476
573
|
}
|
|
477
574
|
return ` ${parts.join(" ")}`;
|
|
478
575
|
});
|
|
479
|
-
return
|
|
576
|
+
return [
|
|
577
|
+
`create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
|
|
480
578
|
${columns.join(",\n")}
|
|
481
|
-
)
|
|
579
|
+
);`,
|
|
580
|
+
...model2.constraints.unique.map(
|
|
581
|
+
(constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
|
|
582
|
+
),
|
|
583
|
+
...model2.constraints.indexes.map(
|
|
584
|
+
(constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
|
|
585
|
+
)
|
|
586
|
+
];
|
|
482
587
|
});
|
|
483
588
|
return `${statements.join("\n\n")}
|
|
484
589
|
`;
|
|
@@ -802,6 +907,7 @@ function model(config) {
|
|
|
802
907
|
table: config.table,
|
|
803
908
|
fields: config.fields,
|
|
804
909
|
relations: config.relations ?? {},
|
|
910
|
+
constraints: config.constraints ?? {},
|
|
805
911
|
description: config.description
|
|
806
912
|
};
|
|
807
913
|
}
|