@farming-labs/orm 0.0.5 → 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.cjs
CHANGED
|
@@ -187,6 +187,38 @@ function datetime() {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// src/manifest.ts
|
|
190
|
+
function createConstraintName(table, columns, suffix) {
|
|
191
|
+
const base = [table, ...columns].join("_").replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
192
|
+
return `${base}_${suffix}`;
|
|
193
|
+
}
|
|
194
|
+
function normalizeConstraints(modelName, table, fields, constraints) {
|
|
195
|
+
const normalize = (entries, unique) => (entries ?? []).map((entry) => {
|
|
196
|
+
if (!entry.length) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Model "${modelName}" defines an empty ${unique ? "unique" : "index"} constraint.`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const columns = entry.map((fieldName) => {
|
|
202
|
+
const field = fields[fieldName];
|
|
203
|
+
if (!field) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
`Model "${modelName}" defines a ${unique ? "unique" : "index"} constraint on unknown field "${fieldName}".`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return field.column;
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
name: createConstraintName(table, columns, unique ? "unique" : "idx"),
|
|
212
|
+
fields: [...entry],
|
|
213
|
+
columns,
|
|
214
|
+
unique
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
unique: normalize(constraints.unique, true),
|
|
219
|
+
indexes: normalize(constraints.indexes, false)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
190
222
|
function createManifest(schema) {
|
|
191
223
|
const models = Object.fromEntries(
|
|
192
224
|
Object.entries(schema.models).map(
|
|
@@ -214,7 +246,13 @@ function createManifest(schema) {
|
|
|
214
246
|
table: definition.table,
|
|
215
247
|
description: definition.description,
|
|
216
248
|
fields,
|
|
217
|
-
relations: definition.relations
|
|
249
|
+
relations: definition.relations,
|
|
250
|
+
constraints: normalizeConstraints(
|
|
251
|
+
name,
|
|
252
|
+
definition.table,
|
|
253
|
+
fields,
|
|
254
|
+
definition.constraints
|
|
255
|
+
)
|
|
218
256
|
}
|
|
219
257
|
];
|
|
220
258
|
}
|
|
@@ -226,6 +264,7 @@ function createManifest(schema) {
|
|
|
226
264
|
// src/generators.ts
|
|
227
265
|
var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
|
|
228
266
|
var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
|
|
267
|
+
var camelize = (value) => value.replace(/[^a-zA-Z0-9]+(.)/g, (_, char) => char.toUpperCase()).replace(/^[^a-zA-Z]+/, "").replace(/^[A-Z]/, (char) => char.toLowerCase());
|
|
229
268
|
function resolveReferenceTarget(manifest, model2, foreignKey, fallbackTarget) {
|
|
230
269
|
const reference = model2.fields[foreignKey]?.references;
|
|
231
270
|
if (!reference) {
|
|
@@ -260,6 +299,16 @@ function prismaType(field) {
|
|
|
260
299
|
return "DateTime";
|
|
261
300
|
}
|
|
262
301
|
}
|
|
302
|
+
function drizzleConstraintProperty(constraint) {
|
|
303
|
+
return camelize(constraint.name) || "constraint";
|
|
304
|
+
}
|
|
305
|
+
function constrainedFields(model2) {
|
|
306
|
+
return new Set(
|
|
307
|
+
[...model2.constraints.unique, ...model2.constraints.indexes].flatMap(
|
|
308
|
+
(constraint) => constraint.fields
|
|
309
|
+
)
|
|
310
|
+
);
|
|
311
|
+
}
|
|
263
312
|
function drizzleImports(dialect, manifest) {
|
|
264
313
|
const models = Object.values(manifest.models);
|
|
265
314
|
const needsBoolean = models.some(
|
|
@@ -268,12 +317,17 @@ function drizzleImports(dialect, manifest) {
|
|
|
268
317
|
const needsDate = models.some(
|
|
269
318
|
(model2) => Object.values(model2.fields).some((field) => field.kind === "datetime")
|
|
270
319
|
);
|
|
320
|
+
const needsIndexes = models.some(
|
|
321
|
+
(model2) => model2.constraints.indexes.length || model2.constraints.unique.length
|
|
322
|
+
);
|
|
271
323
|
if (dialect === "pg") {
|
|
272
324
|
return [
|
|
273
325
|
"pgTable",
|
|
274
326
|
"text",
|
|
275
327
|
needsBoolean ? "boolean" : null,
|
|
276
|
-
needsDate ? "timestamp" : null
|
|
328
|
+
needsDate ? "timestamp" : null,
|
|
329
|
+
needsIndexes ? "index" : null,
|
|
330
|
+
needsIndexes ? "uniqueIndex" : null
|
|
277
331
|
].filter(Boolean);
|
|
278
332
|
}
|
|
279
333
|
if (dialect === "mysql") {
|
|
@@ -282,12 +336,20 @@ function drizzleImports(dialect, manifest) {
|
|
|
282
336
|
"varchar",
|
|
283
337
|
"text",
|
|
284
338
|
needsBoolean ? "boolean" : null,
|
|
285
|
-
needsDate ? "datetime" : null
|
|
339
|
+
needsDate ? "datetime" : null,
|
|
340
|
+
needsIndexes ? "index" : null,
|
|
341
|
+
needsIndexes ? "uniqueIndex" : null
|
|
286
342
|
].filter(Boolean);
|
|
287
343
|
}
|
|
288
|
-
return [
|
|
344
|
+
return [
|
|
345
|
+
"sqliteTable",
|
|
346
|
+
"text",
|
|
347
|
+
"integer",
|
|
348
|
+
needsIndexes ? "index" : null,
|
|
349
|
+
needsIndexes ? "uniqueIndex" : null
|
|
350
|
+
].filter(Boolean);
|
|
289
351
|
}
|
|
290
|
-
function drizzleColumn(field, dialect) {
|
|
352
|
+
function drizzleColumn(field, dialect, options = {}) {
|
|
291
353
|
if (field.kind === "id") {
|
|
292
354
|
if (dialect === "mysql") {
|
|
293
355
|
return `varchar("${field.column}", { length: 191 }).primaryKey()`;
|
|
@@ -296,7 +358,7 @@ function drizzleColumn(field, dialect) {
|
|
|
296
358
|
}
|
|
297
359
|
if (field.kind === "string") {
|
|
298
360
|
if (dialect === "mysql") {
|
|
299
|
-
const base = field.unique || field.references ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
|
|
361
|
+
const base = field.unique || field.references || options.indexed ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
|
|
300
362
|
return `${base}${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
|
|
301
363
|
}
|
|
302
364
|
return `text("${field.column}")${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
|
|
@@ -313,14 +375,14 @@ function drizzleColumn(field, dialect) {
|
|
|
313
375
|
if (dialect === "sqlite") {
|
|
314
376
|
return `integer("${field.column}", { mode: "timestamp" })${field.nullable ? "" : ".notNull()"}`;
|
|
315
377
|
}
|
|
316
|
-
return `timestamp("${field.column}")${field.nullable ? "" : ".notNull()"}`;
|
|
378
|
+
return `timestamp("${field.column}", { withTimezone: true, mode: "date" })${field.nullable ? "" : ".notNull()"}`;
|
|
317
379
|
}
|
|
318
|
-
function sqlType(field, dialect) {
|
|
380
|
+
function sqlType(field, dialect, options = {}) {
|
|
319
381
|
if (field.kind === "id") {
|
|
320
382
|
return dialect === "mysql" ? "varchar(191)" : "text";
|
|
321
383
|
}
|
|
322
384
|
if (field.kind === "string") {
|
|
323
|
-
return dialect === "mysql" && (field.unique || field.references) ? "varchar(191)" : "text";
|
|
385
|
+
return dialect === "mysql" && (field.unique || field.references || options.indexed) ? "varchar(191)" : "text";
|
|
324
386
|
}
|
|
325
387
|
if (field.kind === "boolean") {
|
|
326
388
|
return dialect === "sqlite" ? "integer" : "boolean";
|
|
@@ -331,7 +393,7 @@ function sqlType(field, dialect) {
|
|
|
331
393
|
if (dialect === "sqlite") {
|
|
332
394
|
return "text";
|
|
333
395
|
}
|
|
334
|
-
return "
|
|
396
|
+
return "timestamptz";
|
|
335
397
|
}
|
|
336
398
|
function sqlIdentifier(dialect, value) {
|
|
337
399
|
if (dialect === "mysql") {
|
|
@@ -339,6 +401,14 @@ function sqlIdentifier(dialect, value) {
|
|
|
339
401
|
}
|
|
340
402
|
return `"${value}"`;
|
|
341
403
|
}
|
|
404
|
+
function sqlCreateIndexStatement(dialect, table, constraint) {
|
|
405
|
+
const indexName = sqlIdentifier(dialect, constraint.name);
|
|
406
|
+
const tableName = sqlIdentifier(dialect, table);
|
|
407
|
+
const columns = constraint.columns.map((column) => sqlIdentifier(dialect, column)).join(", ");
|
|
408
|
+
const createKeyword = constraint.unique ? "create unique index" : "create index";
|
|
409
|
+
const ifNotExists = dialect === "mysql" ? "" : " if not exists";
|
|
410
|
+
return `${createKeyword}${ifNotExists} ${indexName} on ${tableName}(${columns});`;
|
|
411
|
+
}
|
|
342
412
|
function renderPrismaSchema(schema, options = {}) {
|
|
343
413
|
const manifest = createManifest(schema);
|
|
344
414
|
const provider = options.provider ?? "postgresql";
|
|
@@ -421,10 +491,18 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
421
491
|
relation.many ? ` ${relationName} ${capitalize(relation.sourceModel)}[]` : ` ${relationName} ${capitalize(relation.sourceModel)}?`
|
|
422
492
|
);
|
|
423
493
|
}
|
|
424
|
-
const
|
|
425
|
-
|
|
494
|
+
const modelLines = [
|
|
495
|
+
...lines,
|
|
496
|
+
...model2.constraints.unique.map(
|
|
497
|
+
(constraint) => ` @@unique([${constraint.fields.join(", ")}])`
|
|
498
|
+
),
|
|
499
|
+
...model2.constraints.indexes.map(
|
|
500
|
+
(constraint) => ` @@index([${constraint.fields.join(", ")}])`
|
|
501
|
+
),
|
|
502
|
+
...model2.table !== modelName ? [` @@map("${model2.table}")`] : []
|
|
503
|
+
];
|
|
426
504
|
return `model ${modelName} {
|
|
427
|
-
${
|
|
505
|
+
${modelLines.join("\n")}
|
|
428
506
|
}`;
|
|
429
507
|
});
|
|
430
508
|
return `generator ${generatorName} {
|
|
@@ -444,17 +522,33 @@ function renderDrizzleSchema(schema, options) {
|
|
|
444
522
|
const coreImports = drizzleImports(options.dialect, manifest).join(", ");
|
|
445
523
|
const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
|
|
446
524
|
const modelBlocks = Object.values(manifest.models).map((model2) => {
|
|
525
|
+
const indexedFields = constrainedFields(model2);
|
|
447
526
|
const lines = Object.values(model2.fields).map((field) => {
|
|
448
|
-
let value = drizzleColumn(field, options.dialect);
|
|
527
|
+
let value = drizzleColumn(field, options.dialect, { indexed: indexedFields.has(field.name) });
|
|
449
528
|
if (field.references) {
|
|
450
529
|
const [targetModel, targetField] = field.references.split(".");
|
|
451
530
|
value += `.references(() => ${targetModel}.${targetField})`;
|
|
452
531
|
}
|
|
453
532
|
return ` ${field.name}: ${value}`;
|
|
454
533
|
});
|
|
455
|
-
|
|
534
|
+
const constraintLines = [
|
|
535
|
+
...model2.constraints.unique.map(
|
|
536
|
+
(constraint) => ` ${drizzleConstraintProperty(constraint)}: uniqueIndex("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
|
|
537
|
+
),
|
|
538
|
+
...model2.constraints.indexes.map(
|
|
539
|
+
(constraint) => ` ${drizzleConstraintProperty(constraint)}: index("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
|
|
540
|
+
)
|
|
541
|
+
];
|
|
542
|
+
if (!constraintLines.length) {
|
|
543
|
+
return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
|
|
456
544
|
${lines.join(",\n")}
|
|
457
545
|
});`;
|
|
546
|
+
}
|
|
547
|
+
return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
|
|
548
|
+
${lines.join(",\n")}
|
|
549
|
+
}, (table) => ({
|
|
550
|
+
${constraintLines.join(",\n")}
|
|
551
|
+
}));`;
|
|
458
552
|
});
|
|
459
553
|
const relationBlocks = Object.values(manifest.models).map((model2) => {
|
|
460
554
|
const lines = Object.entries(model2.relations).flatMap(([relationName, relation]) => {
|
|
@@ -493,10 +587,13 @@ ${[...modelBlocks, ...relationBlocks].join("\n\n")}
|
|
|
493
587
|
}
|
|
494
588
|
function renderSafeSql(schema, options) {
|
|
495
589
|
const manifest = createManifest(schema);
|
|
496
|
-
const statements = Object.values(manifest.models).
|
|
590
|
+
const statements = Object.values(manifest.models).flatMap((model2) => {
|
|
591
|
+
const indexedFields = constrainedFields(model2);
|
|
497
592
|
const columns = Object.values(model2.fields).map((field) => {
|
|
498
593
|
const parts = [
|
|
499
|
-
`${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect
|
|
594
|
+
`${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect, {
|
|
595
|
+
indexed: indexedFields.has(field.name)
|
|
596
|
+
})}`
|
|
500
597
|
];
|
|
501
598
|
if (field.kind === "id") parts.push("primary key");
|
|
502
599
|
if (!field.nullable) parts.push("not null");
|
|
@@ -519,9 +616,17 @@ function renderSafeSql(schema, options) {
|
|
|
519
616
|
}
|
|
520
617
|
return ` ${parts.join(" ")}`;
|
|
521
618
|
});
|
|
522
|
-
return
|
|
619
|
+
return [
|
|
620
|
+
`create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
|
|
523
621
|
${columns.join(",\n")}
|
|
524
|
-
)
|
|
622
|
+
);`,
|
|
623
|
+
...model2.constraints.unique.map(
|
|
624
|
+
(constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
|
|
625
|
+
),
|
|
626
|
+
...model2.constraints.indexes.map(
|
|
627
|
+
(constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
|
|
628
|
+
)
|
|
629
|
+
];
|
|
525
630
|
});
|
|
526
631
|
return `${statements.join("\n\n")}
|
|
527
632
|
`;
|
|
@@ -845,6 +950,7 @@ function model(config) {
|
|
|
845
950
|
table: config.table,
|
|
846
951
|
fields: config.fields,
|
|
847
952
|
relations: config.relations ?? {},
|
|
953
|
+
constraints: config.constraints ?? {},
|
|
848
954
|
description: config.description
|
|
849
955
|
};
|
|
850
956
|
}
|