hekireki 0.2.2 → 0.2.4
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.
|
@@ -2,31 +2,58 @@ import fsp from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { snakeCase } from '../../../shared/utils/index.js';
|
|
4
4
|
import { prismaTypeToEctoType } from '../utils/prisma-type-to-ecto-type.js';
|
|
5
|
+
/* ───────── Utilities ────────────────────────── */
|
|
6
|
+
/** UUID PK → :binary_id / otherwise :id */
|
|
5
7
|
function getPrimaryKeyType(field) {
|
|
6
8
|
const def = field.default;
|
|
7
9
|
return def && typeof def === 'object' && 'name' in def && def.name === 'uuid' ? 'binary_id' : 'id';
|
|
8
10
|
}
|
|
11
|
+
/** Convert readonly models to mutable copies */
|
|
9
12
|
function makeMutable(models) {
|
|
10
13
|
return models.map((m) => ({
|
|
11
14
|
...m,
|
|
12
15
|
fields: m.fields?.map((f) => ({ ...f })) ?? [],
|
|
13
16
|
}));
|
|
14
17
|
}
|
|
18
|
+
/* ───────── Main generator ───────────────────── */
|
|
15
19
|
export function ectoSchemas(models, app) {
|
|
16
20
|
const mutableModels = makeMutable(models);
|
|
21
|
+
/** Timestamp column aliases (snake_case & camelCase) */
|
|
22
|
+
const insertedAliases = ['inserted_at', 'created_at', 'createdAt'];
|
|
23
|
+
const updatedAliases = ['updated_at', 'modified_at', 'updatedAt', 'modifiedAt'];
|
|
17
24
|
return mutableModels
|
|
18
25
|
.map((model) => {
|
|
26
|
+
/* ── Primary-key handling ─────────────────── */
|
|
19
27
|
const idFields = model.fields.filter((f) => f.isId);
|
|
20
28
|
const isCompositePK = model.primaryKey && model.primaryKey.fields.length > 1;
|
|
21
|
-
if (!(idFields.length || isCompositePK))
|
|
29
|
+
if (!(idFields.length || isCompositePK))
|
|
22
30
|
return '';
|
|
23
|
-
}
|
|
24
31
|
const pkField = idFields[0];
|
|
25
|
-
const pkType = pkField ? getPrimaryKeyType(pkField) : '
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
32
|
+
const pkType = pkField ? getPrimaryKeyType(pkField) : 'id';
|
|
33
|
+
/* ── Timestamp field detection ────────────── */
|
|
34
|
+
const insertedField = model.fields.find((f) => insertedAliases.includes(f.name));
|
|
35
|
+
const updatedField = model.fields.find((f) => updatedAliases.includes(f.name));
|
|
36
|
+
/** Columns removed from explicit `field/3` declarations */
|
|
37
|
+
const excludedNames = [
|
|
38
|
+
...(insertedField ? [insertedField.name] : []),
|
|
39
|
+
...(updatedField ? [updatedField.name] : []),
|
|
40
|
+
];
|
|
41
|
+
/* ── Plain fields (no relations / no timestamps) ─ */
|
|
42
|
+
const fields = model.fields.filter((f) => !(f.relationName || excludedNames.includes(f.name)));
|
|
43
|
+
/* ── Build timestamps() line (const-only) ─────── */
|
|
44
|
+
const timestampsLine = (() => {
|
|
45
|
+
if (!(insertedField || updatedField))
|
|
46
|
+
return '';
|
|
47
|
+
const hasCustom = (insertedField && insertedField.name !== 'inserted_at') ||
|
|
48
|
+
(updatedField && updatedField.name !== 'updated_at');
|
|
49
|
+
if (!hasCustom)
|
|
50
|
+
return ' timestamps()'; // both defaults → short form
|
|
51
|
+
// Always include both keys when custom names are involved
|
|
52
|
+
const insertedName = insertedField ? insertedField.name : 'inserted_at';
|
|
53
|
+
const updatedName = updatedField ? updatedField.name : 'updated_at';
|
|
54
|
+
return ` timestamps(inserted_at: :${insertedName}, updated_at: :${updatedName})`;
|
|
55
|
+
})();
|
|
56
|
+
/* ── Assemble final module code ───────────── */
|
|
30
57
|
const lines = [
|
|
31
58
|
`defmodule ${app}.${model.name} do`,
|
|
32
59
|
' use Ecto.Schema',
|
|
@@ -37,8 +64,7 @@ export function ectoSchemas(models, app) {
|
|
|
37
64
|
const primary = f.isId && !isCompositePK ? ', primary_key: true' : '';
|
|
38
65
|
return ` field(:${f.name}, :${type}${primary})`;
|
|
39
66
|
}),
|
|
40
|
-
...(
|
|
41
|
-
...(hasUpdatedAt ? [' field :updated_at, :utc_datetime'] : []),
|
|
67
|
+
...(timestampsLine ? [timestampsLine] : []),
|
|
42
68
|
' end',
|
|
43
69
|
'end',
|
|
44
70
|
];
|
|
@@ -47,6 +73,7 @@ export function ectoSchemas(models, app) {
|
|
|
47
73
|
.filter(Boolean)
|
|
48
74
|
.join('\n\n');
|
|
49
75
|
}
|
|
76
|
+
/* ───────── File writer ───────────────────────── */
|
|
50
77
|
export async function writeEctoSchemasToFiles(models, app, outDir) {
|
|
51
78
|
const mutableModels = makeMutable(models);
|
|
52
79
|
await fsp.mkdir(outDir, { recursive: true });
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hekireki",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Hekireki is a tool that generates validation schemas for Zod and Valibot, as well as ER diagrams, from Prisma schemas annotated with comments.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"prisma",
|
|
9
9
|
"zod",
|
|
10
10
|
"valibot",
|
|
11
|
-
"mermaid"
|
|
11
|
+
"mermaid",
|
|
12
|
+
"ecto"
|
|
12
13
|
],
|
|
13
14
|
"homepage": "https://github.com/nakita628/hekireki",
|
|
14
15
|
"publishConfig": {
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"tsx": "^4.20.3",
|
|
50
51
|
"valibot": "1.1.0",
|
|
51
52
|
"vitest": "^3.2.4",
|
|
52
|
-
"zod": "^3.25.
|
|
53
|
+
"zod": "^3.25.76"
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
56
|
"@prisma/generator-helper": "^6.10.1",
|