hekireki 0.2.1 โ 0.2.2
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/README.md +43 -4
- package/dist/generator/ecto/generator/ecto.d.ts +3 -0
- package/dist/generator/ecto/generator/ecto.js +61 -0
- package/dist/generator/ecto/index.d.ts +3 -0
- package/dist/generator/ecto/index.js +18 -0
- package/dist/generator/ecto/utils/index.d.ts +1 -0
- package/dist/generator/ecto/utils/index.js +1 -0
- package/dist/generator/ecto/utils/prisma-type-to-ecto-type.d.ts +1 -0
- package/dist/generator/ecto/utils/prisma-type-to-ecto-type.js +11 -0
- package/dist/shared/utils/decapitalize.d.ts +15 -0
- package/dist/shared/utils/decapitalize.js +17 -0
- package/dist/shared/utils/index.d.ts +2 -0
- package/dist/shared/utils/index.js +2 -0
- package/dist/shared/utils/snake-case.d.ts +1 -0
- package/dist/shared/utils/snake-case.js +3 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
- ๐ Automatically generates [Zod](https://zod.dev/) schemas from your Prisma schema
|
|
8
8
|
- ๐ค Automatically generates [Valibot](https://valibot.dev/) schemas from your Prisma schema
|
|
9
9
|
- ๐ Creates [Mermaid](https://mermaid.js.org/) ER diagrams
|
|
10
|
+
- ๐งช Generates [Ecto](https://hexdocs.pm/ecto/Ecto.Schema.html) schemas for Elixir projects
|
|
11
|
+
โ ๏ธ Foreign key constraints are **not** included โ manage relationships in your application logic
|
|
10
12
|
|
|
11
13
|
## Installation
|
|
12
14
|
|
|
@@ -35,13 +37,19 @@ generator Hekireki-ER {
|
|
|
35
37
|
generator Hekireki-Zod {
|
|
36
38
|
provider = "hekireki-zod"
|
|
37
39
|
type = true
|
|
38
|
-
comment =
|
|
40
|
+
comment = true
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
generator Hekireki-Valibot {
|
|
42
44
|
provider = "hekireki-valibot"
|
|
43
45
|
type = true
|
|
44
|
-
comment =
|
|
46
|
+
comment = true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
generator Hekireki-Ecto {
|
|
50
|
+
provider = "hekireki-ecto"
|
|
51
|
+
output = "schema"
|
|
52
|
+
app = "DBSchema"
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
model User {
|
|
@@ -63,12 +71,10 @@ model Post {
|
|
|
63
71
|
/// @z.uuid()
|
|
64
72
|
/// @v.pipe(v.string(), v.uuid())
|
|
65
73
|
id String @id @default(uuid())
|
|
66
|
-
|
|
67
74
|
/// Article title
|
|
68
75
|
/// @z.string().min(1).max(100)
|
|
69
76
|
/// @v.pipe(v.string(), v.minLength(1), v.maxLength(100))
|
|
70
77
|
title String
|
|
71
|
-
|
|
72
78
|
/// Body content (no length limit)
|
|
73
79
|
/// @z.string()
|
|
74
80
|
/// @v.string()
|
|
@@ -180,6 +186,32 @@ erDiagram
|
|
|
180
186
|
}
|
|
181
187
|
```
|
|
182
188
|
|
|
189
|
+
## Ecto
|
|
190
|
+
|
|
191
|
+
```elixir
|
|
192
|
+
defmodule DBSchema.User do
|
|
193
|
+
use Ecto.Schema
|
|
194
|
+
@primary_key false
|
|
195
|
+
schema "user" do
|
|
196
|
+
field(:id, :binary_id, primary_key: true)
|
|
197
|
+
field(:name, :string)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```elixir
|
|
203
|
+
defmodule DBSchema.Post do
|
|
204
|
+
use Ecto.Schema
|
|
205
|
+
@primary_key false
|
|
206
|
+
schema "post" do
|
|
207
|
+
field(:id, :binary_id, primary_key: true)
|
|
208
|
+
field(:title, :string)
|
|
209
|
+
field(:content, :string)
|
|
210
|
+
field(:userId, :string)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
```
|
|
214
|
+
|
|
183
215
|
## Configuration
|
|
184
216
|
|
|
185
217
|
### Zod Generator Options
|
|
@@ -208,6 +240,13 @@ erDiagram
|
|
|
208
240
|
| `output` | `string` | `./mermaid-er` | Output directory |
|
|
209
241
|
| `file` | `string` | `ER.md` | File Name |
|
|
210
242
|
|
|
243
|
+
### Ecto Generator Options
|
|
244
|
+
|
|
245
|
+
| Option | Type | Default | Description |
|
|
246
|
+
|--------------|-----------|-------------------------------------|--------------------------------------------------|
|
|
247
|
+
| `output` | `string` | `./ecto` | Output directory |
|
|
248
|
+
| `app` | `string` | `MyApp` | App Name |
|
|
249
|
+
|
|
211
250
|
โ ๏ธ WARNING: Potential Breaking Changes Without Notice
|
|
212
251
|
|
|
213
252
|
This project is in **early development** and being maintained by a developer with about 2 years of experience. While I'm doing my best to create a useful tool:
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DMMF } from '@prisma/generator-helper';
|
|
2
|
+
export declare function ectoSchemas(models: readonly DMMF.Model[], app: string | string[]): string;
|
|
3
|
+
export declare function writeEctoSchemasToFiles(models: readonly DMMF.Model[], app: string | string[], outDir: string): Promise<void>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { snakeCase } from '../../../shared/utils/index.js';
|
|
4
|
+
import { prismaTypeToEctoType } from '../utils/prisma-type-to-ecto-type.js';
|
|
5
|
+
function getPrimaryKeyType(field) {
|
|
6
|
+
const def = field.default;
|
|
7
|
+
return def && typeof def === 'object' && 'name' in def && def.name === 'uuid' ? 'binary_id' : 'id';
|
|
8
|
+
}
|
|
9
|
+
function makeMutable(models) {
|
|
10
|
+
return models.map((m) => ({
|
|
11
|
+
...m,
|
|
12
|
+
fields: m.fields?.map((f) => ({ ...f })) ?? [],
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
export function ectoSchemas(models, app) {
|
|
16
|
+
const mutableModels = makeMutable(models);
|
|
17
|
+
return mutableModels
|
|
18
|
+
.map((model) => {
|
|
19
|
+
const idFields = model.fields.filter((f) => f.isId);
|
|
20
|
+
const isCompositePK = model.primaryKey && model.primaryKey.fields.length > 1;
|
|
21
|
+
if (!(idFields.length || isCompositePK)) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
const pkField = idFields[0];
|
|
25
|
+
const pkType = pkField ? getPrimaryKeyType(pkField) : ':id';
|
|
26
|
+
const excludedFieldNames = ['inserted_at', 'updated_at'];
|
|
27
|
+
const fields = model.fields.filter((f) => !(f.relationName || excludedFieldNames.includes(f.name)));
|
|
28
|
+
const hasInsertedAt = model.fields.some((f) => f.name === 'inserted_at');
|
|
29
|
+
const hasUpdatedAt = model.fields.some((f) => f.name === 'updated_at');
|
|
30
|
+
const lines = [
|
|
31
|
+
`defmodule ${app}.${model.name} do`,
|
|
32
|
+
' use Ecto.Schema',
|
|
33
|
+
' @primary_key false',
|
|
34
|
+
` schema "${snakeCase(model.name)}" do`,
|
|
35
|
+
...fields.map((f) => {
|
|
36
|
+
const type = f.isId ? pkType : prismaTypeToEctoType(f.type);
|
|
37
|
+
const primary = f.isId && !isCompositePK ? ', primary_key: true' : '';
|
|
38
|
+
return ` field(:${f.name}, :${type}${primary})`;
|
|
39
|
+
}),
|
|
40
|
+
...(hasInsertedAt ? [' field :inserted_at, :utc_datetime'] : []),
|
|
41
|
+
...(hasUpdatedAt ? [' field :updated_at, :utc_datetime'] : []),
|
|
42
|
+
' end',
|
|
43
|
+
'end',
|
|
44
|
+
];
|
|
45
|
+
return lines.join('\n');
|
|
46
|
+
})
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.join('\n\n');
|
|
49
|
+
}
|
|
50
|
+
export async function writeEctoSchemasToFiles(models, app, outDir) {
|
|
51
|
+
const mutableModels = makeMutable(models);
|
|
52
|
+
await fsp.mkdir(outDir, { recursive: true });
|
|
53
|
+
for (const model of mutableModels) {
|
|
54
|
+
const code = ectoSchemas([model], app);
|
|
55
|
+
if (!code.trim())
|
|
56
|
+
continue;
|
|
57
|
+
const filePath = join(outDir, `${snakeCase(model.name)}.ex`);
|
|
58
|
+
await fsp.writeFile(filePath, code, 'utf8');
|
|
59
|
+
console.log(`โ
wrote ${filePath}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import pkg from '@prisma/generator-helper';
|
|
3
|
+
import { writeEctoSchemasToFiles } from './generator/ecto.js';
|
|
4
|
+
const { generatorHandler } = pkg;
|
|
5
|
+
export async function main(options) {
|
|
6
|
+
const output = options.generator.output?.value ?? './ecto';
|
|
7
|
+
const app = options.generator.config?.app ?? 'MyApp';
|
|
8
|
+
await writeEctoSchemasToFiles(options.dmmf.datamodel.models, app, output);
|
|
9
|
+
}
|
|
10
|
+
generatorHandler({
|
|
11
|
+
onManifest() {
|
|
12
|
+
return {
|
|
13
|
+
defaultOutput: './ecto/',
|
|
14
|
+
prettyName: 'Hekireki-Ecto',
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
onGenerate: main,
|
|
18
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { prismaTypeToEctoType } from './prisma-type-to-ecto-type.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { prismaTypeToEctoType } from './prisma-type-to-ecto-type.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function prismaTypeToEctoType(type: string): 'integer' | 'string' | 'boolean' | 'utc_datetime';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decapitalize the first letter of a string
|
|
3
|
+
* @param { string } str - String to decapitalize
|
|
4
|
+
* @returns { string } String with the first letter in lowercase
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* decapitalize('Posts') // Returns: 'posts'
|
|
8
|
+
* decapitalize('User') // Returns: 'user'
|
|
9
|
+
* decapitalize('Api') // Returns: 'api'
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* - Leaves the rest of the string unchanged
|
|
13
|
+
* - Returns an empty string if the input is empty
|
|
14
|
+
*/
|
|
15
|
+
export declare function decapitalize(str: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decapitalize the first letter of a string
|
|
3
|
+
* @param { string } str - String to decapitalize
|
|
4
|
+
* @returns { string } String with the first letter in lowercase
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* decapitalize('Posts') // Returns: 'posts'
|
|
8
|
+
* decapitalize('User') // Returns: 'user'
|
|
9
|
+
* decapitalize('Api') // Returns: 'api'
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* - Leaves the rest of the string unchanged
|
|
13
|
+
* - Returns an empty string if the input is empty
|
|
14
|
+
*/
|
|
15
|
+
export function decapitalize(str) {
|
|
16
|
+
return `${str.charAt(0).toLowerCase()}${str.slice(1)}`;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function snakeCase(name: string): string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hekireki",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
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": [
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"bin": {
|
|
30
30
|
"hekireki-zod": "dist/generator/zod/index.js",
|
|
31
31
|
"hekireki-valibot": "dist/generator/valibot/index.js",
|
|
32
|
-
"hekireki-mermaid-er": "dist/generator/mermaid-er/index.js"
|
|
32
|
+
"hekireki-mermaid-er": "dist/generator/mermaid-er/index.js",
|
|
33
|
+
"hekireki-ecto": "dist/generator/ecto/index.js"
|
|
33
34
|
},
|
|
34
35
|
"scripts": {
|
|
35
36
|
"generate": "prisma generate",
|