prisma-entity-gen 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.
@@ -0,0 +1,711 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var commander = require('commander');
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+ var zod = require('zod');
8
+ var prismaAst = require('@mrleebo/prisma-ast');
9
+
10
+ var ConfigSchema = zod.z.object({
11
+ /**
12
+ * Caminho para o schema.prisma
13
+ * @default "prisma/schema.prisma"
14
+ */
15
+ schemaPath: zod.z.string().default("prisma/schema.prisma"),
16
+ /**
17
+ * Diretório de saída das entities geradas
18
+ * @default "src/entities"
19
+ */
20
+ outputDir: zod.z.string().default("src/entities"),
21
+ /**
22
+ * Modo de geração:
23
+ * - "type" → type aliases e interfaces puras (melhor performance TS)
24
+ * - "class" → classes com decorators (NestJS / class-validator)
25
+ * - "both" → gera os dois em subpastas /types e /classes
26
+ */
27
+ mode: zod.z.enum(["type", "class", "both"]).default("type"),
28
+ /**
29
+ * Quando mode="class", inclui decorators do class-validator
30
+ * @default false
31
+ */
32
+ classValidator: zod.z.boolean().default(false),
33
+ /**
34
+ * Quando mode="class", inclui decorators do class-transformer
35
+ * @default false
36
+ */
37
+ classTransformer: zod.z.boolean().default(false),
38
+ /**
39
+ * Gera barrel index.ts na raiz do outputDir
40
+ * @default true
41
+ */
42
+ barrel: zod.z.boolean().default(true),
43
+ /**
44
+ * Modelos a ignorar na geração
45
+ * @default []
46
+ */
47
+ exclude: zod.z.array(zod.z.string()).default([]),
48
+ /**
49
+ * Apenas esses modelos serão gerados (vazio = todos)
50
+ * @default []
51
+ */
52
+ include: zod.z.array(zod.z.string()).default([]),
53
+ /**
54
+ * Formato de saída dos enums:
55
+ * - "enum" → export enum Role { ADMIN = "ADMIN" }
56
+ * - "union" → export type Role = "ADMIN" | "USER"
57
+ * Use "union" para evitar conflitos com enums do @prisma/client
58
+ * @default "enum"
59
+ */
60
+ enumOutput: zod.z.enum(["enum", "union"]).default("enum"),
61
+ /**
62
+ * Define se campos de relação são opcionais ou obrigatórios na entity gerada:
63
+ * - "optional" → relações sempre com ? (compatível com queries sem include)
64
+ * - "required" → relações sem ? (requer include em toda query que retornar o tipo)
65
+ * @default "optional"
66
+ */
67
+ relations: zod.z.enum(["optional", "required"]).default("optional"),
68
+ /**
69
+ * Sufixo dos arquivos gerados
70
+ * @default "entity"
71
+ * Resulta em: user.entity.ts
72
+ */
73
+ suffix: zod.z.string().default("entity"),
74
+ /**
75
+ * Simula a geração sem escrever arquivos
76
+ * @default false
77
+ */
78
+ dryRun: zod.z.boolean().default(false),
79
+ /**
80
+ * Nível de log: "silent" | "info" | "verbose"
81
+ * @default "info"
82
+ */
83
+ logLevel: zod.z.enum(["silent", "info", "verbose"]).default("info")
84
+ });
85
+ ConfigSchema.parse({});
86
+
87
+ // src/config/loader.ts
88
+ var RC_FILES = [
89
+ ".entitygenrc.json",
90
+ "entitygen.config.json",
91
+ ".entitygenrc"
92
+ ];
93
+ var ConfigError = class extends Error {
94
+ constructor(message, cause) {
95
+ super(message, { cause });
96
+ this.name = "ConfigError";
97
+ }
98
+ };
99
+ function loadConfig(cwd = process.cwd()) {
100
+ const rcFile = RC_FILES.map((f) => path.resolve(cwd, f)).find(fs.existsSync);
101
+ if (!rcFile) {
102
+ return ConfigSchema.parse({});
103
+ }
104
+ let raw;
105
+ try {
106
+ raw = JSON.parse(fs.readFileSync(rcFile, "utf-8"));
107
+ } catch (err) {
108
+ throw new ConfigError(`Falha ao ler ${rcFile}: arquivo JSON inv\xE1lido`, err);
109
+ }
110
+ try {
111
+ return ConfigSchema.parse(raw);
112
+ } catch (err) {
113
+ if (err instanceof zod.ZodError) {
114
+ const messages = err.errors.map((e) => ` \u2022 ${e.path.join(".")} \u2014 ${e.message}`).join("\n");
115
+ throw new ConfigError(
116
+ `Config inv\xE1lido em ${rcFile}:
117
+ ${messages}`,
118
+ err
119
+ );
120
+ }
121
+ throw err;
122
+ }
123
+ }
124
+
125
+ // src/parser/types.ts
126
+ var PRISMA_TO_TS = {
127
+ String: "string",
128
+ Boolean: "boolean",
129
+ Int: "number",
130
+ BigInt: "bigint",
131
+ Float: "number",
132
+ Decimal: "string",
133
+ // Decimal não tem tipo nativo TS — string é mais seguro que number
134
+ DateTime: "Date",
135
+ Json: "Record<string, unknown>",
136
+ Bytes: "Buffer"
137
+ };
138
+ var SCALARS = new Set(Object.keys(PRISMA_TO_TS));
139
+ function isScalar(type) {
140
+ return SCALARS.has(type);
141
+ }
142
+ function resolveTsType(prismaType, knownEnums) {
143
+ if (isScalar(prismaType)) return PRISMA_TO_TS[prismaType];
144
+ if (knownEnums.has(prismaType)) return prismaType;
145
+ return prismaType;
146
+ }
147
+
148
+ // src/parser/relations.ts
149
+ function detectCircularRefs(graph) {
150
+ const color = /* @__PURE__ */ new Map();
151
+ const circular = /* @__PURE__ */ new Map();
152
+ for (const node of graph.keys()) {
153
+ color.set(node, "white");
154
+ circular.set(node, /* @__PURE__ */ new Set());
155
+ }
156
+ function addCircular(a, b) {
157
+ circular.get(a)?.add(b);
158
+ circular.get(b)?.add(a);
159
+ }
160
+ function dfs(node, ancestors) {
161
+ color.set(node, "gray");
162
+ for (const neighbor of graph.get(node) ?? []) {
163
+ if (!color.has(neighbor)) continue;
164
+ if (color.get(neighbor) === "gray") {
165
+ addCircular(node, neighbor);
166
+ for (const anc of ancestors) {
167
+ if (color.get(anc) === "gray") {
168
+ addCircular(anc, neighbor);
169
+ addCircular(node, anc);
170
+ }
171
+ }
172
+ } else if (color.get(neighbor) === "white") {
173
+ dfs(neighbor, [...ancestors, node]);
174
+ }
175
+ }
176
+ color.set(node, "black");
177
+ }
178
+ for (const node of graph.keys()) {
179
+ if (color.get(node) === "white") {
180
+ dfs(node, []);
181
+ }
182
+ }
183
+ return circular;
184
+ }
185
+
186
+ // src/parser/index.ts
187
+ function hasAttribute(field, name) {
188
+ return field.attributes?.some((a) => a.name === name) ?? false;
189
+ }
190
+ function hasDefault(field) {
191
+ return hasAttribute(field, "default") || hasAttribute(field, "updatedAt");
192
+ }
193
+ function parseEnum(node) {
194
+ const values = node.enumerators.filter((e) => e.type === "enumerator").map((e) => e.name);
195
+ return { name: node.name, values, documentation: node.comment };
196
+ }
197
+ function parseField(field, knownModels, knownEnums) {
198
+ return {
199
+ name: field.name,
200
+ prismaType: field.fieldType,
201
+ tsType: resolveTsType(field.fieldType, knownEnums),
202
+ isOptional: field.optional,
203
+ isArray: field.array,
204
+ isRelation: knownModels.has(field.fieldType),
205
+ isCircular: false,
206
+ hasDefault: hasDefault(field),
207
+ isId: hasAttribute(field, "id"),
208
+ isUnique: hasAttribute(field, "unique"),
209
+ isUpdatedAt: hasAttribute(field, "updatedAt"),
210
+ decorators: []
211
+ };
212
+ }
213
+ function parseModel(node, knownModels, knownEnums) {
214
+ const fields = node.properties.filter((p) => p.type === "field").map((f) => parseField(f, knownModels, knownEnums));
215
+ const relations = [...new Set(
216
+ fields.filter((f) => f.isRelation).map((f) => f.prismaType)
217
+ )];
218
+ return {
219
+ name: node.name,
220
+ fields,
221
+ relations,
222
+ circularRefs: [],
223
+ documentation: node.comment
224
+ };
225
+ }
226
+ var ParseError = class extends Error {
227
+ constructor(message, cause) {
228
+ super(message, { cause });
229
+ this.name = "ParseError";
230
+ }
231
+ };
232
+ function parseSchema(schemaPath) {
233
+ let raw;
234
+ try {
235
+ raw = fs.readFileSync(schemaPath, "utf-8");
236
+ } catch (err) {
237
+ throw new ParseError(`N\xE3o foi poss\xEDvel ler o schema em "${schemaPath}"`, err);
238
+ }
239
+ return parseSchemaString(raw);
240
+ }
241
+ function parseSchemaString(schemaContent) {
242
+ let ast;
243
+ try {
244
+ ast = prismaAst.getSchema(schemaContent);
245
+ } catch (err) {
246
+ throw new ParseError("Falha ao fazer parse do schema Prisma", err);
247
+ }
248
+ const rawNodes = ast.list;
249
+ const knownModels = new Set(
250
+ rawNodes.filter((n) => n.type === "model").map((n) => n.name)
251
+ );
252
+ const knownEnums = new Set(
253
+ rawNodes.filter((n) => n.type === "enum").map((n) => n.name)
254
+ );
255
+ const enums = rawNodes.filter((n) => n.type === "enum").map((n) => parseEnum(n));
256
+ const models = rawNodes.filter((n) => n.type === "model").map((n) => parseModel(n, knownModels, knownEnums));
257
+ const graph = new Map(models.map((m) => [m.name, m.relations]));
258
+ const circularMap = detectCircularRefs(graph);
259
+ for (const model of models) {
260
+ const circulars = circularMap.get(model.name) ?? /* @__PURE__ */ new Set();
261
+ model.circularRefs = [...circulars];
262
+ for (const field of model.fields) {
263
+ if (field.isRelation && circulars.has(field.prismaType)) {
264
+ field.isCircular = true;
265
+ }
266
+ }
267
+ }
268
+ return { models, enums };
269
+ }
270
+
271
+ // src/emitters/base.ts
272
+ function fileHeader() {
273
+ return [
274
+ "/**",
275
+ " * ARQUIVO GERADO AUTOMATICAMENTE \u2014 N\xC3O EDITE MANUALMENTE.",
276
+ " * Gerado por prisma-entity-gen. Rode o gerador para atualizar.",
277
+ " */",
278
+ ""
279
+ ].join("\n");
280
+ }
281
+ function formatFieldType(tsType, isArray, isOptional, isCircular) {
282
+ const base = isArray ? `${tsType}[]` : tsType;
283
+ const nullable = isOptional ? ` | null` : "";
284
+ return `${base}${nullable}`;
285
+ }
286
+ function formatJsDoc(doc) {
287
+ if (!doc) return "";
288
+ const lines = doc.split("\n").map((l) => ` * ${l.trim()}`).join("\n");
289
+ return `/**
290
+ ${lines}
291
+ */
292
+ `;
293
+ }
294
+ var BaseEmitter = class {
295
+ };
296
+
297
+ // src/emitters/type.emitter.ts
298
+ function toKebab(name) {
299
+ return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
300
+ }
301
+ var TypeEmitter = class extends BaseEmitter {
302
+ constructor(options = {}) {
303
+ super();
304
+ this.options = options;
305
+ }
306
+ options;
307
+ emit(model, enums) {
308
+ const enumNames = new Set(enums.map((e) => e.name));
309
+ const lines = [fileHeader()];
310
+ const relationImports = this.collectRelationImports(model, enumNames);
311
+ if (relationImports.length > 0) {
312
+ for (const imp of relationImports) lines.push(imp);
313
+ lines.push("");
314
+ }
315
+ const enumImports = this.collectEnumImports(model, enumNames);
316
+ if (enumImports.length > 0) {
317
+ for (const imp of enumImports) lines.push(imp);
318
+ lines.push("");
319
+ }
320
+ if (model.documentation) lines.push(formatJsDoc(model.documentation));
321
+ lines.push(`export interface ${model.name}Entity {`);
322
+ for (const field of model.fields) {
323
+ lines.push(` ${this.formatField(field)}`);
324
+ }
325
+ lines.push("}");
326
+ lines.push("");
327
+ return lines.join("\n");
328
+ }
329
+ formatField(field) {
330
+ const relationsOptional = this.options.relations !== "required";
331
+ const optional = field.isRelation && relationsOptional || field.isOptional ? "?" : "";
332
+ const tsType = field.isRelation ? `${field.prismaType}Entity` : field.tsType;
333
+ const type = formatFieldType(tsType, field.isArray, field.isOptional, field.isCircular);
334
+ return `${field.name}${optional}: ${type};`;
335
+ }
336
+ collectRelationImports(model, enumNames) {
337
+ const seen = /* @__PURE__ */ new Set();
338
+ const imports = [];
339
+ for (const field of model.fields) {
340
+ if (field.isRelation && !enumNames.has(field.prismaType) && !seen.has(field.prismaType) && field.prismaType !== model.name) {
341
+ seen.add(field.prismaType);
342
+ imports.push(
343
+ `import type { ${field.prismaType}Entity } from "./${toKebab(field.prismaType)}.entity.js";`
344
+ );
345
+ }
346
+ }
347
+ return imports;
348
+ }
349
+ collectEnumImports(model, enumNames) {
350
+ const seen = /* @__PURE__ */ new Set();
351
+ const imports = [];
352
+ for (const field of model.fields) {
353
+ if (enumNames.has(field.prismaType) && !seen.has(field.prismaType)) {
354
+ seen.add(field.prismaType);
355
+ imports.push(
356
+ `import { ${field.prismaType} } from "./${toKebab(field.prismaType)}.enum.js";`
357
+ );
358
+ }
359
+ }
360
+ return imports;
361
+ }
362
+ };
363
+
364
+ // src/emitters/class.emitter.ts
365
+ var ClassEmitter = class extends BaseEmitter {
366
+ constructor(options = {}) {
367
+ super();
368
+ this.options = options;
369
+ }
370
+ options;
371
+ emit(model, enums) {
372
+ const enumNames = new Set(enums.map((e) => e.name));
373
+ const lines = [fileHeader()];
374
+ const cvImports = this.collectCvImports(model, enumNames);
375
+ if (this.options.classValidator && cvImports.length > 0) {
376
+ lines.push(`import { ${cvImports.join(", ")} } from "class-validator";`);
377
+ }
378
+ if (this.options.classTransformer) {
379
+ lines.push(`import { Type } from "class-transformer";`);
380
+ }
381
+ if (this.options.classValidator && cvImports.length > 0 || this.options.classTransformer) {
382
+ lines.push("");
383
+ }
384
+ const relationImports = this.collectRelationImports(model, enumNames);
385
+ if (relationImports.length > 0) {
386
+ for (const imp of relationImports) lines.push(imp);
387
+ lines.push("");
388
+ }
389
+ const enumImports = this.collectEnumImports(model, enumNames);
390
+ if (enumImports.length > 0) {
391
+ for (const imp of enumImports) lines.push(imp);
392
+ lines.push("");
393
+ }
394
+ if (model.documentation) lines.push(formatJsDoc(model.documentation));
395
+ lines.push(`export class ${model.name}Entity {`);
396
+ for (const field of model.fields) {
397
+ const fieldLines = this.formatField(field, enumNames);
398
+ for (const l of fieldLines) lines.push(` ${l}`);
399
+ }
400
+ lines.push("}");
401
+ lines.push("");
402
+ return lines.join("\n");
403
+ }
404
+ formatField(field, enumNames) {
405
+ const lines = [];
406
+ if (this.options.classValidator) {
407
+ const decorators = this.getCvDecorators(field, enumNames);
408
+ for (const d of decorators) lines.push(d);
409
+ }
410
+ if (this.options.classTransformer && field.isRelation && !field.isCircular) {
411
+ lines.push(`@Type(() => ${field.tsType}Entity)`);
412
+ }
413
+ const optional = field.isOptional || field.hasDefault ? "?" : "!";
414
+ const type = formatFieldType(
415
+ field.isRelation && !field.isCircular ? `${field.tsType}Entity` : field.tsType,
416
+ field.isArray,
417
+ field.isOptional,
418
+ field.isCircular
419
+ );
420
+ const comment = field.isCircular ? " // circular ref \u2014 usar Ref" : "";
421
+ lines.push(`${field.name}${optional}: ${type};${comment}`);
422
+ lines.push("");
423
+ return lines;
424
+ }
425
+ getCvDecorators(field, enumNames) {
426
+ const decorators = [];
427
+ if (field.isOptional || field.hasDefault) {
428
+ decorators.push("@IsOptional()");
429
+ }
430
+ if (field.isArray) {
431
+ decorators.push("@IsArray()");
432
+ }
433
+ if (field.isRelation || enumNames.has(field.prismaType)) {
434
+ return decorators;
435
+ }
436
+ switch (field.prismaType) {
437
+ case "String":
438
+ decorators.push("@IsString()");
439
+ break;
440
+ case "Boolean":
441
+ decorators.push("@IsBoolean()");
442
+ break;
443
+ case "Int":
444
+ case "Float":
445
+ case "Decimal":
446
+ decorators.push("@IsNumber()");
447
+ break;
448
+ case "BigInt":
449
+ decorators.push("@IsInt()");
450
+ break;
451
+ case "DateTime":
452
+ decorators.push("@IsDate()");
453
+ break;
454
+ }
455
+ return decorators;
456
+ }
457
+ collectCvImports(model, enumNames) {
458
+ const imports = /* @__PURE__ */ new Set();
459
+ for (const field of model.fields) {
460
+ if (field.isOptional || field.hasDefault) imports.add("IsOptional");
461
+ if (field.isArray) imports.add("IsArray");
462
+ if (field.isRelation || enumNames.has(field.prismaType)) continue;
463
+ switch (field.prismaType) {
464
+ case "String":
465
+ imports.add("IsString");
466
+ break;
467
+ case "Boolean":
468
+ imports.add("IsBoolean");
469
+ break;
470
+ case "Int":
471
+ case "Float":
472
+ case "Decimal":
473
+ imports.add("IsNumber");
474
+ break;
475
+ case "BigInt":
476
+ imports.add("IsInt");
477
+ break;
478
+ case "DateTime":
479
+ imports.add("IsDate");
480
+ break;
481
+ }
482
+ }
483
+ return [...imports].sort();
484
+ }
485
+ collectRelationImports(model, enumNames) {
486
+ const seen = /* @__PURE__ */ new Set();
487
+ const imports = [];
488
+ for (const field of model.fields) {
489
+ if (field.isRelation && !field.isCircular && !enumNames.has(field.prismaType) && !seen.has(field.prismaType)) {
490
+ seen.add(field.prismaType);
491
+ const fileName = field.prismaType.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
492
+ imports.push(
493
+ `import { ${field.prismaType}Entity } from "./${fileName}.entity.js";`
494
+ );
495
+ }
496
+ }
497
+ return imports;
498
+ }
499
+ collectEnumImports(model, enumNames) {
500
+ const seen = /* @__PURE__ */ new Set();
501
+ const imports = [];
502
+ for (const field of model.fields) {
503
+ if (enumNames.has(field.prismaType) && !seen.has(field.prismaType)) {
504
+ seen.add(field.prismaType);
505
+ const fileName = field.prismaType.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
506
+ imports.push(
507
+ `import { ${field.prismaType} } from "./${fileName}.enum.js";`
508
+ );
509
+ }
510
+ }
511
+ return imports;
512
+ }
513
+ };
514
+ var FileWriter = class {
515
+ constructor(outputDir, dryRun, logger) {
516
+ this.outputDir = outputDir;
517
+ this.dryRun = dryRun;
518
+ this.logger = logger;
519
+ }
520
+ outputDir;
521
+ dryRun;
522
+ logger;
523
+ writeAll(files) {
524
+ const result = { written: [], skipped: [] };
525
+ if (!this.dryRun) {
526
+ fs.mkdirSync(path.resolve(this.outputDir), { recursive: true });
527
+ }
528
+ for (const file of files) {
529
+ const absPath = path.resolve(this.outputDir, file.relativePath);
530
+ if (this.dryRun) {
531
+ this.logger.info(`[dry-run] ${file.relativePath}`);
532
+ result.skipped.push(absPath);
533
+ continue;
534
+ }
535
+ fs.mkdirSync(path.dirname(absPath), { recursive: true });
536
+ fs.writeFileSync(absPath, file.content, "utf-8");
537
+ this.logger.success(file.relativePath);
538
+ result.written.push(absPath);
539
+ }
540
+ return result;
541
+ }
542
+ };
543
+
544
+ // src/writer/barrel.ts
545
+ function generateBarrel(files) {
546
+ const exports$1 = files.filter((f) => !f.relativePath.endsWith("index.ts")).map((f) => {
547
+ const path = f.relativePath.replace(/\.ts$/, ".js");
548
+ return `export * from "./${path}";`;
549
+ }).sort();
550
+ return [fileHeader(), ...exports$1, ""].join("\n");
551
+ }
552
+
553
+ // src/logger.ts
554
+ var LEVELS = {
555
+ silent: 0,
556
+ info: 1,
557
+ verbose: 2
558
+ };
559
+ var Logger = class {
560
+ level;
561
+ constructor(logLevel = "info") {
562
+ this.level = LEVELS[logLevel];
563
+ }
564
+ info(msg) {
565
+ if (this.level >= LEVELS.info) {
566
+ console.log(` ${msg}`);
567
+ }
568
+ }
569
+ verbose(msg) {
570
+ if (this.level >= LEVELS.verbose) {
571
+ console.log(` [verbose] ${msg}`);
572
+ }
573
+ }
574
+ success(msg) {
575
+ if (this.level >= LEVELS.info) {
576
+ console.log(` \u2713 ${msg}`);
577
+ }
578
+ }
579
+ warn(msg) {
580
+ if (this.level >= LEVELS.info) {
581
+ console.warn(` \u26A0 ${msg}`);
582
+ }
583
+ }
584
+ error(msg) {
585
+ console.error(` \u2717 ${msg}`);
586
+ }
587
+ };
588
+
589
+ // src/generator.ts
590
+ function toKebabCase(name) {
591
+ return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
592
+ }
593
+ function shouldInclude(name, config) {
594
+ if (config.include.length > 0 && !config.include.includes(name)) return false;
595
+ if (config.exclude.includes(name)) return false;
596
+ return true;
597
+ }
598
+ function buildFiles(models, enums, config) {
599
+ const files = [];
600
+ const useType = config.mode === "type" || config.mode === "both";
601
+ const useClass = config.mode === "class" || config.mode === "both";
602
+ const typeEmitter = useType ? new TypeEmitter({ relations: config.relations }) : null;
603
+ const classEmitter = useClass ? new ClassEmitter({
604
+ classValidator: config.classValidator,
605
+ classTransformer: config.classTransformer
606
+ }) : null;
607
+ const typeDir = config.mode === "both" ? "types/" : "";
608
+ const classDir = config.mode === "both" ? "classes/" : "";
609
+ for (const model of models) {
610
+ if (!shouldInclude(model.name, config)) continue;
611
+ const fileName = `${toKebabCase(model.name)}.${config.suffix}.ts`;
612
+ if (typeEmitter) {
613
+ files.push({
614
+ relativePath: `${typeDir}${fileName}`,
615
+ content: typeEmitter.emit(model, enums),
616
+ sourceName: model.name
617
+ });
618
+ }
619
+ if (classEmitter) {
620
+ files.push({
621
+ relativePath: `${classDir}${fileName}`,
622
+ content: classEmitter.emit(model, enums),
623
+ sourceName: model.name
624
+ });
625
+ }
626
+ }
627
+ for (const enumDef of enums) {
628
+ if (!shouldInclude(enumDef.name, config)) continue;
629
+ const fileName = `${toKebabCase(enumDef.name)}.enum.ts`;
630
+ const content = buildEnumFile(enumDef, config.enumOutput);
631
+ if (typeDir) files.push({ relativePath: `${typeDir}${fileName}`, content, sourceName: enumDef.name });
632
+ else files.push({ relativePath: fileName, content, sourceName: enumDef.name });
633
+ if (classDir) files.push({ relativePath: `${classDir}${fileName}`, content, sourceName: enumDef.name });
634
+ }
635
+ return files;
636
+ }
637
+ function buildEnumFile(enumDef, enumOutput) {
638
+ const header = [
639
+ "/**",
640
+ " * ARQUIVO GERADO AUTOMATICAMENTE \u2014 N\xC3O EDITE MANUALMENTE.",
641
+ " * Gerado por prisma-entity-gen. Rode o gerador para atualizar.",
642
+ " */",
643
+ ""
644
+ ];
645
+ if (enumOutput === "union") {
646
+ const union = enumDef.values.map((v) => `"${v}"`).join(" | ");
647
+ return [...header, `export type ${enumDef.name} = ${union};`, ""].join("\n");
648
+ }
649
+ return [
650
+ ...header,
651
+ `export enum ${enumDef.name} {`,
652
+ ...enumDef.values.map((v) => ` ${v} = "${v}",`),
653
+ "}",
654
+ ""
655
+ ].join("\n");
656
+ }
657
+ async function generate(config) {
658
+ const logger = new Logger(config.logLevel);
659
+ logger.verbose(`Lendo schema: ${config.schemaPath}`);
660
+ const { models, enums } = parseSchema(config.schemaPath);
661
+ logger.info(`${models.length} models encontrados`);
662
+ logger.info(`${enums.length} enums encontrados`);
663
+ const files = buildFiles(models, enums, config);
664
+ logger.verbose(`${files.length} arquivos a gerar`);
665
+ if (config.barrel) {
666
+ files.push({
667
+ relativePath: "index.ts",
668
+ content: generateBarrel(files),
669
+ sourceName: "barrel"
670
+ });
671
+ }
672
+ const writer = new FileWriter(config.outputDir, config.dryRun, logger);
673
+ const result = writer.writeAll(files);
674
+ if (config.dryRun) {
675
+ logger.info(`${result.skipped.length} arquivos seriam gerados`);
676
+ } else {
677
+ logger.success(`${result.written.length} arquivos gerados em ${config.outputDir}`);
678
+ }
679
+ }
680
+
681
+ // src/cli/index.ts
682
+ var program = new commander.Command();
683
+ program.name("prisma-entity-gen").description("Gera entities TypeScript a partir do schema.prisma via AST").version("0.1.0");
684
+ program.command("generate", { isDefault: true }).description("Gera as entities do schema.prisma").option("-s, --schema <path>", "Caminho para o schema.prisma").option("-o, --output <dir>", "Diret\xF3rio de sa\xEDda").option("-m, --mode <mode>", "Modo: type | class | both").option("--relations <mode>", "Rela\xE7\xF5es nas entities: optional | required").option("--enum-output <format>", "Formato dos enums: enum | union").option("--dry-run", "Simula sem escrever arquivos").option("--log-level <level>", "silent | info | verbose").action(async (opts) => {
685
+ let config;
686
+ try {
687
+ config = loadConfig();
688
+ } catch (err) {
689
+ if (err instanceof ConfigError) {
690
+ console.error(err.message);
691
+ process.exit(1);
692
+ }
693
+ throw err;
694
+ }
695
+ if (opts.schema) config = { ...config, schemaPath: opts.schema };
696
+ if (opts.output) config = { ...config, outputDir: opts.output };
697
+ if (opts.mode) config = { ...config, mode: opts.mode };
698
+ if (opts.relations) config = { ...config, relations: opts.relations };
699
+ if (opts.enumOutput) config = { ...config, enumOutput: opts.enumOutput };
700
+ if (opts.dryRun) config = { ...config, dryRun: true };
701
+ if (opts.logLevel) config = { ...config, logLevel: opts.logLevel };
702
+ try {
703
+ await generate(config);
704
+ } catch (err) {
705
+ console.error(err.message);
706
+ process.exit(1);
707
+ }
708
+ });
709
+ program.parse();
710
+ //# sourceMappingURL=index.cjs.map
711
+ //# sourceMappingURL=index.cjs.map