@lexmata/prisma-ent-generator 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lexmata LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # prisma-ent-generator
2
+
3
+ A [Prisma](https://www.prisma.io/) generator that produces a complete [Go Ent](https://entgo.io/) installation from your Prisma schema.
4
+
5
+ Define your data model once in Prisma and generate fully working Ent schemas — including fields, edges, enums, and the `generate.go` / `entc.go` scaffolding needed to run `go generate`.
6
+
7
+ ## Features
8
+
9
+ - **Full Ent install** — generates `generate.go`, `entc.go`, and `schema/*.go` so you can immediately run `go generate ./ent`
10
+ - **Environment variable toggle** — controlled by `GENERATE_ENT`; skips when unset or `false`, runs when set to `true`
11
+ - **Scalar type mapping** — `String`, `Int`, `BigInt`, `Float`, `Decimal`, `Boolean`, `DateTime`, `Json`, `Bytes`
12
+ - **Enum support** — Prisma enums map to inline `field.Enum(...).Values(...)` with defaults
13
+ - **Relationship edges** — O2O, O2M, and M2M relations are translated to `edge.To` / `edge.From` with correct ownership, `.Ref()`, `.Unique()`, and `.Required()`
14
+ - **FK edge fields** — foreign key scalars are included in `Fields()` and bound to edges via `.Field()`
15
+ - **Defaults** — `@default(now())`, `@default(uuid())`, `@default(autoincrement())`, `@default(false)`, `@default(0)`, `@default("value")`, and enum defaults
16
+ - **@updatedAt** — maps to `.Default(time.Now).UpdateDefault(time.Now)`
17
+ - **UUID IDs** — `@id @default(uuid())` generates `field.UUID("id", uuid.UUID{}).Default(uuid.New)`
18
+ - **Optional / Nillable** — optional fields get `.Optional().Nillable()` (except JSON, which only gets `.Optional()`)
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @lexmata/prisma-ent-generator
24
+ # or
25
+ pnpm add @lexmata/prisma-ent-generator
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### 1. Add the generator to your Prisma schema
31
+
32
+ ```prisma
33
+ generator ent {
34
+ provider = "@lexmata/prisma-ent-generator"
35
+ output = "./ent"
36
+ }
37
+ ```
38
+
39
+ ### 2. Run Prisma generate with the environment variable
40
+
41
+ ```bash
42
+ GENERATE_ENT=true npx prisma generate
43
+ ```
44
+
45
+ Without `GENERATE_ENT=true`, the generator prints a skip message and produces no output:
46
+
47
+ ```
48
+ prisma-ent-generator: Skipping — set GENERATE_ENT=true to enable.
49
+ ```
50
+
51
+ ### 3. Run Ent code generation
52
+
53
+ ```bash
54
+ cd your-go-project
55
+ go generate ./ent
56
+ ```
57
+
58
+ This triggers Ent's own pipeline via the generated `generate.go`, producing the full client, queries, mutations, migrations, and predicates.
59
+
60
+ ## Output Structure
61
+
62
+ ```
63
+ ent/
64
+ ├── generate.go # go:generate directive for Ent codegen
65
+ ├── entc.go # Ent codegen configuration (build-tag guarded)
66
+ └── schema/
67
+ ├── user.go # One file per Prisma model
68
+ ├── post.go
69
+ ├── profile.go
70
+ └── tag.go
71
+ ```
72
+
73
+ `generate.go` and `entc.go` are only written if they don't already exist, so your customizations are preserved across re-runs. Schema files are always overwritten.
74
+
75
+ ## Example
76
+
77
+ Given this Prisma schema:
78
+
79
+ ```prisma
80
+ enum Role {
81
+ USER
82
+ ADMIN
83
+ MODERATOR
84
+ }
85
+
86
+ model User {
87
+ id Int @id @default(autoincrement())
88
+ email String @unique
89
+ name String?
90
+ role Role @default(USER)
91
+ posts Post[]
92
+ createdAt DateTime @default(now())
93
+ updatedAt DateTime @updatedAt
94
+ }
95
+
96
+ model Post {
97
+ id Int @id @default(autoincrement())
98
+ title String
99
+ author User @relation(fields: [authorId], references: [id])
100
+ authorId Int
101
+ tags Tag[]
102
+ }
103
+
104
+ model Tag {
105
+ id Int @id @default(autoincrement())
106
+ name String @unique
107
+ posts Post[]
108
+ }
109
+ ```
110
+
111
+ The generator produces:
112
+
113
+ **`ent/schema/user.go`**
114
+
115
+ ```go
116
+ package schema
117
+
118
+ import (
119
+ "entgo.io/ent"
120
+ "entgo.io/ent/schema/field"
121
+ "entgo.io/ent/schema/edge"
122
+
123
+ "time"
124
+ )
125
+
126
+ type User struct {
127
+ ent.Schema
128
+ }
129
+
130
+ func (User) Fields() []ent.Field {
131
+ return []ent.Field{
132
+ field.String("email").Unique(),
133
+ field.String("name").Optional().Nillable(),
134
+ field.Enum("role").Values("USER", "ADMIN", "MODERATOR").Default("USER"),
135
+ field.Time("created_at").Default(time.Now),
136
+ field.Time("updated_at").Default(time.Now).UpdateDefault(time.Now),
137
+ }
138
+ }
139
+
140
+ func (User) Edges() []ent.Edge {
141
+ return []ent.Edge{
142
+ edge.To("posts", Post.Type),
143
+ }
144
+ }
145
+ ```
146
+
147
+ **`ent/schema/post.go`**
148
+
149
+ ```go
150
+ package schema
151
+
152
+ import (
153
+ "entgo.io/ent"
154
+ "entgo.io/ent/schema/field"
155
+ "entgo.io/ent/schema/edge"
156
+ )
157
+
158
+ type Post struct {
159
+ ent.Schema
160
+ }
161
+
162
+ func (Post) Fields() []ent.Field {
163
+ return []ent.Field{
164
+ field.String("title"),
165
+ field.Int("author_id"),
166
+ }
167
+ }
168
+
169
+ func (Post) Edges() []ent.Edge {
170
+ return []ent.Edge{
171
+ edge.From("author", User.Type).Ref("posts").Unique().Field("author_id").Required(),
172
+ edge.To("tags", Tag.Type),
173
+ }
174
+ }
175
+ ```
176
+
177
+ ## Type Mapping
178
+
179
+ | Prisma Type | Ent Field | Notes |
180
+ |---|---|---|
181
+ | `String` | `field.String` | |
182
+ | `Boolean` | `field.Bool` | |
183
+ | `Int` | `field.Int` | |
184
+ | `BigInt` | `field.Int` | int64 in Go |
185
+ | `Float` | `field.Float` | |
186
+ | `Decimal` | `field.Float` | float64 in Go |
187
+ | `DateTime` | `field.Time` | Imports `"time"` |
188
+ | `Json` | `field.JSON` | Second arg: `map[string]interface{}{}` |
189
+ | `Bytes` | `field.Bytes` | |
190
+ | Enums | `field.Enum` | Inline `.Values(...)` |
191
+
192
+ ## Edge Mapping
193
+
194
+ | Prisma Relation | Ent Edge |
195
+ |---|---|
196
+ | O2O (owner side) | `edge.To("name", Type.Type).Unique()` |
197
+ | O2O (FK side) | `edge.From("name", Type.Type).Ref("...").Unique().Field("fk")` |
198
+ | O2M (owner side) | `edge.To("name", Type.Type)` |
199
+ | O2M (FK side) | `edge.From("name", Type.Type).Ref("...").Unique().Field("fk")` |
200
+ | M2M (owner side) | `edge.To("name", Type.Type)` |
201
+ | M2M (inverse side) | `edge.From("name", Type.Type).Ref("...")` |
202
+
203
+ M2M ownership is determined alphabetically by model name when neither side holds a FK.
204
+
205
+ ## Development
206
+
207
+ ```bash
208
+ pnpm install
209
+ pnpm build
210
+ pnpm test
211
+ ```
212
+
213
+ To test generation locally:
214
+
215
+ ```bash
216
+ pnpm build
217
+ GENERATE_ENT=true npx prisma generate --schema=prisma/schema.prisma
218
+ ```
219
+
220
+ ## License
221
+
222
+ MIT - [Lexmata LLC](mailto:jquinn@lexmata.ai)
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const generator_helper_1 = require("@prisma/generator-helper");
5
+ const generator_1 = require("./generator");
6
+ (0, generator_helper_1.generatorHandler)({
7
+ onManifest() {
8
+ return {
9
+ prettyName: 'Prisma Ent Generator',
10
+ defaultOutput: './ent',
11
+ };
12
+ },
13
+ onGenerate: generator_1.generate,
14
+ });
15
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";;;AACA,+DAA4D;AAC5D,2CAAuC;AAEvC,IAAA,mCAAgB,EAAC;IACf,UAAU;QACR,OAAO;YACL,UAAU,EAAE,sBAAsB;YAClC,aAAa,EAAE,OAAO;SACvB,CAAC;IACJ,CAAC;IACD,UAAU,EAAE,oBAAQ;CACrB,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { GeneratorOptions } from '@prisma/generator-helper';
2
+ /**
3
+ * Check whether the generator is enabled via the GENERATE_ENT environment variable.
4
+ * Defaults to disabled if the variable is absent or not set to "true".
5
+ */
6
+ export declare function isEnabled(): boolean;
7
+ /**
8
+ * Main generator function — receives the full Prisma DMMF and generator config,
9
+ * then writes a complete Go Ent installation to the output directory.
10
+ *
11
+ * Output structure:
12
+ * <outputDir>/
13
+ * generate.go — go:generate directive to run ent codegen
14
+ * entc.go — ent codegen configuration (build-tag guarded)
15
+ * schema/
16
+ * user.go — one schema file per Prisma model
17
+ * post.go
18
+ * ...
19
+ */
20
+ export declare function generate(options: GeneratorOptions): Promise<void>;
21
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAWvE;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDvE"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isEnabled = isEnabled;
7
+ exports.generate = generate;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const schema_1 = require("./helpers/schema");
11
+ const entfiles_1 = require("./helpers/entfiles");
12
+ const utils_1 = require("./utils");
13
+ /**
14
+ * The environment variable that controls whether the generator runs.
15
+ * Set to "true" to enable generation; any other value or absence disables it.
16
+ */
17
+ const ENV_VAR = 'GENERATE_ENT';
18
+ /**
19
+ * Check whether the generator is enabled via the GENERATE_ENT environment variable.
20
+ * Defaults to disabled if the variable is absent or not set to "true".
21
+ */
22
+ function isEnabled() {
23
+ return process.env[ENV_VAR]?.toLowerCase() === 'true';
24
+ }
25
+ /**
26
+ * Main generator function — receives the full Prisma DMMF and generator config,
27
+ * then writes a complete Go Ent installation to the output directory.
28
+ *
29
+ * Output structure:
30
+ * <outputDir>/
31
+ * generate.go — go:generate directive to run ent codegen
32
+ * entc.go — ent codegen configuration (build-tag guarded)
33
+ * schema/
34
+ * user.go — one schema file per Prisma model
35
+ * post.go
36
+ * ...
37
+ */
38
+ async function generate(options) {
39
+ if (!isEnabled()) {
40
+ console.log(`prisma-ent-generator: Skipping — set ${ENV_VAR}=true to enable.`);
41
+ return;
42
+ }
43
+ const outputDir = options.generator.output?.value;
44
+ if (!outputDir) {
45
+ throw new Error('No output directory specified for prisma-ent-generator');
46
+ }
47
+ // The output dir is the ent root (e.g. ./ent).
48
+ // Schema files go into <outputDir>/schema/
49
+ const schemaDir = path_1.default.join(outputDir, 'schema');
50
+ fs_1.default.mkdirSync(schemaDir, { recursive: true });
51
+ const { datamodel } = options.dmmf;
52
+ const models = [...datamodel.models];
53
+ const enums = [...datamodel.enums];
54
+ // Build enum lookup map
55
+ const enumDefs = new Map();
56
+ for (const e of enums) {
57
+ enumDefs.set(e.name, e);
58
+ }
59
+ // --- generate.go (go:generate directive) ---
60
+ const generateGoPath = path_1.default.join(outputDir, 'generate.go');
61
+ if (!fs_1.default.existsSync(generateGoPath)) {
62
+ fs_1.default.writeFileSync(generateGoPath, (0, entfiles_1.generateGenerateGo)(), 'utf-8');
63
+ }
64
+ // --- entc.go (ent codegen config, build-tag guarded) ---
65
+ const entcGoPath = path_1.default.join(outputDir, 'entc.go');
66
+ if (!fs_1.default.existsSync(entcGoPath)) {
67
+ fs_1.default.writeFileSync(entcGoPath, (0, entfiles_1.generateEntcGo)(), 'utf-8');
68
+ }
69
+ // --- schema/*.go (one per Prisma model, always overwritten) ---
70
+ for (const model of models) {
71
+ const content = (0, schema_1.generateSchema)(model, models, enumDefs);
72
+ const fileName = (0, utils_1.modelToFileName)(model.name);
73
+ const filePath = path_1.default.join(schemaDir, `${fileName}.go`);
74
+ fs_1.default.writeFileSync(filePath, content, 'utf-8');
75
+ }
76
+ const modelNames = models.map((m) => m.name).join(', ');
77
+ console.log(`prisma-ent-generator: Generated ${models.length} schema(s) → ${schemaDir} [${modelNames}]`);
78
+ console.log(`prisma-ent-generator: Ent root → ${outputDir} (generate.go, entc.go)`);
79
+ }
80
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { DMMF } from '@prisma/generator-helper';
2
+ import type { GoImports } from './type-map';
3
+ /**
4
+ * Information about a generated Ent edge.
5
+ */
6
+ export interface EdgeDef {
7
+ /** The full Go code for the edge definition (e.g., `edge.To("posts", Post.Type)`). */
8
+ code: string;
9
+ }
10
+ /**
11
+ * Generate the Ent edge definition lines for a given model.
12
+ *
13
+ * Analyses all relation (object) fields on the model, determines edge ownership
14
+ * (edge.To vs edge.From) based on FK presence, and generates the correct Ent
15
+ * edge builder calls.
16
+ *
17
+ * Ownership rules:
18
+ * - Model with FK fields (relationFromFields non-empty) → edge.From (back-reference)
19
+ * - Model without FK fields, but inverse has FK → edge.To (owns the relation)
20
+ * - Neither side has FK (M2M implicit join table) → alphabetical model name tiebreaker
21
+ *
22
+ * @returns An array of Go code strings, each representing one edge definition.
23
+ */
24
+ export declare function generateEdges(model: DMMF.Model, allModels: readonly DMMF.Model[], imports: GoImports): string[];
25
+ //# sourceMappingURL=edge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../../src/helpers/edge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,SAAS,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,EAChC,OAAO,EAAE,SAAS,GACjB,MAAM,EAAE,CAcV"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateEdges = generateEdges;
4
+ const utils_1 = require("../utils");
5
+ /**
6
+ * Generate the Ent edge definition lines for a given model.
7
+ *
8
+ * Analyses all relation (object) fields on the model, determines edge ownership
9
+ * (edge.To vs edge.From) based on FK presence, and generates the correct Ent
10
+ * edge builder calls.
11
+ *
12
+ * Ownership rules:
13
+ * - Model with FK fields (relationFromFields non-empty) → edge.From (back-reference)
14
+ * - Model without FK fields, but inverse has FK → edge.To (owns the relation)
15
+ * - Neither side has FK (M2M implicit join table) → alphabetical model name tiebreaker
16
+ *
17
+ * @returns An array of Go code strings, each representing one edge definition.
18
+ */
19
+ function generateEdges(model, allModels, imports) {
20
+ const edges = [];
21
+ for (const field of model.fields) {
22
+ if (field.kind !== 'object')
23
+ continue;
24
+ const edge = generateEdge(field, model, allModels);
25
+ if (edge) {
26
+ imports.edge = true;
27
+ edges.push(edge);
28
+ }
29
+ }
30
+ return edges;
31
+ }
32
+ /**
33
+ * Generate a single Ent edge definition from a Prisma relation field.
34
+ */
35
+ function generateEdge(field, currentModel, allModels) {
36
+ const targetModelName = field.type;
37
+ const targetModel = allModels.find((m) => m.name === targetModelName);
38
+ if (!targetModel)
39
+ return null;
40
+ // Find the inverse relation field on the target model
41
+ const inverseField = targetModel.fields.find((f) => f.kind === 'object' && f.relationName === field.relationName);
42
+ const hasFK = field.relationFromFields !== undefined && field.relationFromFields.length > 0;
43
+ const inverseHasFK = inverseField?.relationFromFields !== undefined &&
44
+ inverseField.relationFromFields.length > 0;
45
+ const edgeName = (0, utils_1.toSnakeCase)(field.name);
46
+ if (hasFK) {
47
+ // This model holds the FK → edge.From (back-reference)
48
+ return buildEdgeFrom(field, edgeName, targetModelName, inverseField);
49
+ }
50
+ if (inverseHasFK) {
51
+ // Other model holds the FK → edge.To (this model owns)
52
+ return buildEdgeTo(field, edgeName, targetModelName);
53
+ }
54
+ // Neither side has FK — M2M implicit or O2O bidirectional
55
+ return buildImplicitEdge(field, edgeName, targetModelName, currentModel.name, inverseField);
56
+ }
57
+ /**
58
+ * Build an edge.From definition (this model holds the FK).
59
+ */
60
+ function buildEdgeFrom(field, edgeName, targetModelName, inverseField) {
61
+ const refName = inverseField ? (0, utils_1.toSnakeCase)(inverseField.name) : (0, utils_1.toSnakeCase)(targetModelName);
62
+ const parts = [];
63
+ parts.push(`edge.From("${edgeName}", ${targetModelName}.Type)`);
64
+ parts.push(`.Ref("${refName}")`);
65
+ parts.push('.Unique()');
66
+ // Bind the FK field to this edge
67
+ if (field.relationFromFields && field.relationFromFields.length > 0) {
68
+ const fkFieldName = (0, utils_1.toSnakeCase)(field.relationFromFields[0]);
69
+ parts.push(`.Field("${fkFieldName}")`);
70
+ }
71
+ if (field.isRequired) {
72
+ parts.push('.Required()');
73
+ }
74
+ return parts.join('');
75
+ }
76
+ /**
77
+ * Build an edge.To definition (this model owns the relation).
78
+ */
79
+ function buildEdgeTo(field, edgeName, targetModelName) {
80
+ const parts = [];
81
+ parts.push(`edge.To("${edgeName}", ${targetModelName}.Type)`);
82
+ if (!field.isList) {
83
+ parts.push('.Unique()');
84
+ }
85
+ return parts.join('');
86
+ }
87
+ /**
88
+ * Build an edge for an implicit M2M or bidirectional relation
89
+ * where neither side holds a FK.
90
+ *
91
+ * Uses alphabetical model name ordering to determine ownership:
92
+ * - Model with earlier name → edge.To (owner)
93
+ * - Model with later name → edge.From (back-reference)
94
+ *
95
+ * For self-referential relations on the same model, the field that appears
96
+ * first (alphabetically by field name) becomes edge.To.
97
+ */
98
+ function buildImplicitEdge(field, edgeName, targetModelName, currentModelName, inverseField) {
99
+ const isSelfRef = currentModelName === targetModelName;
100
+ let isOwner;
101
+ if (isSelfRef) {
102
+ // Self-referential: use field name ordering
103
+ isOwner = field.name <= (inverseField?.name ?? field.name);
104
+ }
105
+ else {
106
+ // Cross-model: use model name ordering
107
+ isOwner = currentModelName <= targetModelName;
108
+ }
109
+ if (isOwner) {
110
+ return buildEdgeTo(field, edgeName, targetModelName);
111
+ }
112
+ // Back-reference
113
+ const refName = inverseField ? (0, utils_1.toSnakeCase)(inverseField.name) : (0, utils_1.toSnakeCase)(targetModelName);
114
+ const parts = [];
115
+ parts.push(`edge.From("${edgeName}", ${targetModelName}.Type)`);
116
+ parts.push(`.Ref("${refName}")`);
117
+ if (!field.isList) {
118
+ parts.push('.Unique()');
119
+ }
120
+ return parts.join('');
121
+ }
122
+ //# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.js","sourceRoot":"","sources":["../../src/helpers/edge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Generate the contents of `generate.go` — the standard Ent entry point
3
+ * that enables `go generate ./ent` to trigger Ent's own code generation.
4
+ *
5
+ * This file is placed in the ent root directory (e.g. `./ent/generate.go`).
6
+ */
7
+ export declare function generateGenerateGo(): string;
8
+ /**
9
+ * Generate the contents of `entc.go` — the Ent codegen configuration file.
10
+ *
11
+ * This file is guarded by the `ignore` build tag so it is only executed
12
+ * during code generation (via `go generate`) and not during normal builds.
13
+ *
14
+ * Users can customise this file to add features, hooks, or extensions
15
+ * to the Ent code generation pipeline.
16
+ */
17
+ export declare function generateEntcGo(): string;
18
+ //# sourceMappingURL=entfiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entfiles.d.ts","sourceRoot":"","sources":["../../src/helpers/entfiles.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAU3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAwBvC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateGenerateGo = generateGenerateGo;
4
+ exports.generateEntcGo = generateEntcGo;
5
+ const utils_1 = require("../utils");
6
+ /**
7
+ * Generate the contents of `generate.go` — the standard Ent entry point
8
+ * that enables `go generate ./ent` to trigger Ent's own code generation.
9
+ *
10
+ * This file is placed in the ent root directory (e.g. `./ent/generate.go`).
11
+ */
12
+ function generateGenerateGo() {
13
+ const lines = [];
14
+ lines.push((0, utils_1.fileHeader)());
15
+ lines.push('package ent');
16
+ lines.push('');
17
+ lines.push('//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema');
18
+ lines.push('');
19
+ return lines.join('\n');
20
+ }
21
+ /**
22
+ * Generate the contents of `entc.go` — the Ent codegen configuration file.
23
+ *
24
+ * This file is guarded by the `ignore` build tag so it is only executed
25
+ * during code generation (via `go generate`) and not during normal builds.
26
+ *
27
+ * Users can customise this file to add features, hooks, or extensions
28
+ * to the Ent code generation pipeline.
29
+ */
30
+ function generateEntcGo() {
31
+ const lines = [];
32
+ lines.push((0, utils_1.fileHeader)());
33
+ lines.push('//go:build ignore');
34
+ lines.push('');
35
+ lines.push('package main');
36
+ lines.push('');
37
+ lines.push('import (');
38
+ lines.push('\t"log"');
39
+ lines.push('');
40
+ lines.push('\t"entgo.io/ent/entc"');
41
+ lines.push('\t"entgo.io/ent/entc/gen"');
42
+ lines.push(')');
43
+ lines.push('');
44
+ lines.push('func main() {');
45
+ lines.push('\terr := entc.Generate("./schema", &gen.Config{})');
46
+ lines.push('\tif err != nil {');
47
+ lines.push('\t\tlog.Fatalf("running ent codegen: %v", err)');
48
+ lines.push('\t}');
49
+ lines.push('}');
50
+ lines.push('');
51
+ return lines.join('\n');
52
+ }
53
+ //# sourceMappingURL=entfiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entfiles.js","sourceRoot":"","sources":["../../src/helpers/entfiles.ts"],"names":[],"mappings":";;AAQA,gDAUC;AAWD,wCAwBC;AArDD,oCAAsC;AAEtC;;;;;GAKG;AACH,SAAgB,kBAAkB;IAChC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,IAAA,kBAAU,GAAE,CAAC,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;IACnF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,cAAc;IAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,IAAA,kBAAU,GAAE,CAAC,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACpC,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAChE,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAChC,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAC7D,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { DMMF } from '@prisma/generator-helper';
2
+ import { type GoImports } from './type-map';
3
+ /**
4
+ * Determine the set of field names that serve as foreign keys for relations.
5
+ * These are the scalar fields referenced in `relationFromFields` of relation fields.
6
+ */
7
+ export declare function collectFKFieldNames(fields: readonly DMMF.Field[]): Set<string>;
8
+ /**
9
+ * Generate the Ent field definition lines for a Prisma model.
10
+ *
11
+ * - Skips relation (object) fields — those become edges.
12
+ * - Includes FK scalar fields so they can be bound to edges via `.Field()`.
13
+ * - Handles ID fields: skips default Int autoincrement IDs (Ent default),
14
+ * but generates UUID ID fields when needed.
15
+ *
16
+ * @returns An array of Go code strings, each representing one `field.X(...)` call.
17
+ */
18
+ export declare function generateFields(fields: readonly DMMF.Field[], fkFieldNames: Set<string>, enumDefs: Map<string, DMMF.DatamodelEnum>, imports: GoImports): string[];
19
+ //# sourceMappingURL=field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/helpers/field.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAA0C,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAGpF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAU9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE,EAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EACzB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,EACzC,OAAO,EAAE,SAAS,GACjB,MAAM,EAAE,CAcV"}