@tommasomeli/prisma-generator-nestjs-dto 0.1.3
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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +552 -0
- package/dist/bin.cjs +1469 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/index.cjs +1539 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +562 -0
- package/dist/index.d.ts +562 -0
- package/dist/index.mjs +1486 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +90 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1539 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var path = require('path');
|
|
4
|
+
var fs3 = require('fs');
|
|
5
|
+
var chalk = require('chalk');
|
|
6
|
+
var prettier = require('prettier');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
11
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
12
|
+
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
13
|
+
var prettier__default = /*#__PURE__*/_interopDefault(prettier);
|
|
14
|
+
|
|
15
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
|
+
}) : x)(function(x) {
|
|
18
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
19
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// src/annotations.ts
|
|
23
|
+
var DTO_IGNORE_MODEL = "DtoIgnoreModel";
|
|
24
|
+
var DTO_API_EXTRA_MODELS = "DtoApiExtraModels";
|
|
25
|
+
var DTO_READ_ONLY = "DtoReadOnly";
|
|
26
|
+
var DTO_CREATE_HIDDEN = "DtoCreateHidden";
|
|
27
|
+
var DTO_UPDATE_HIDDEN = "DtoUpdateHidden";
|
|
28
|
+
var DTO_ENTITY_HIDDEN = "DtoEntityHidden";
|
|
29
|
+
var DTO_API_HIDDEN = "DtoApiHidden";
|
|
30
|
+
var DTO_HIDDEN = "DtoHidden";
|
|
31
|
+
var DTO_CREATED_AT = "createdAt";
|
|
32
|
+
var DTO_CREATE_OPTIONAL = "DtoCreateOptional";
|
|
33
|
+
var DTO_CREATE_REQUIRED = "DtoCreateRequired";
|
|
34
|
+
var DTO_UPDATE_OPTIONAL = "DtoUpdateOptional";
|
|
35
|
+
var DTO_UPDATE_REQUIRED = "DtoUpdateRequired";
|
|
36
|
+
var DTO_OVERRIDE_TYPE = "DtoOverrideType";
|
|
37
|
+
var DTO_OVERRIDE_API_PROPERTY_TYPE = "DtoOverrideApiPropertyType";
|
|
38
|
+
var DTO_CREATE_VALIDATE_IF = "DtoCreateValidateIf";
|
|
39
|
+
var DTO_UPDATE_VALIDATE_IF = "DtoUpdateValidateIf";
|
|
40
|
+
var ENTITY_HIDDEN_ANNOTATIONS = [DTO_HIDDEN, DTO_ENTITY_HIDDEN, DTO_API_HIDDEN];
|
|
41
|
+
var ENTITY_HIDDEN_SET = new Set(ENTITY_HIDDEN_ANNOTATIONS);
|
|
42
|
+
function isEntityHidden(field) {
|
|
43
|
+
if (field.annotations) return field.annotations.some((a) => ENTITY_HIDDEN_SET.has(a.name));
|
|
44
|
+
if (!field.documentation) return false;
|
|
45
|
+
return ENTITY_HIDDEN_ANNOTATIONS.some((name) => new RegExp(`@${name}\\b`).test(field.documentation));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/constants.ts
|
|
49
|
+
var ANNOTATION_NAME_REGEX = /@([A-Za-z][A-Za-z0-9_]*)/g;
|
|
50
|
+
var ANNOTATION_PARAMS_REGEX = /\(\s*((?:[^()]*|\([^()]*\))*)\s*\)/;
|
|
51
|
+
var ANNOTATION_PARAMS_SPLIT_REGEX = /,(?![^{]*})/;
|
|
52
|
+
var PRISMA_SCALAR = {
|
|
53
|
+
String: "string",
|
|
54
|
+
Boolean: "boolean",
|
|
55
|
+
Int: "number",
|
|
56
|
+
BigInt: "bigint",
|
|
57
|
+
Float: "number",
|
|
58
|
+
Decimal: "Prisma.Decimal",
|
|
59
|
+
DateTime: "Date",
|
|
60
|
+
Bytes: "Buffer",
|
|
61
|
+
Json: "Prisma.JsonValue",
|
|
62
|
+
Object: "{ [key: string]: any }"
|
|
63
|
+
};
|
|
64
|
+
var PRISMA_SCALAR_TYPE = {
|
|
65
|
+
...PRISMA_SCALAR,
|
|
66
|
+
Decimal: "number",
|
|
67
|
+
DateTime: "() => Date",
|
|
68
|
+
Bytes: "() => Buffer",
|
|
69
|
+
Json: "() => Object",
|
|
70
|
+
Object: "() => Object"
|
|
71
|
+
};
|
|
72
|
+
var PRISMA_SCALAR_FORMAT = {
|
|
73
|
+
String: { type: "string" },
|
|
74
|
+
Boolean: { type: "boolean" },
|
|
75
|
+
Int: { type: "integer", format: "int32" },
|
|
76
|
+
BigInt: { type: "integer", format: "int64" },
|
|
77
|
+
Float: { type: "number", format: "float" },
|
|
78
|
+
Decimal: { type: "string", format: "Decimal.js" },
|
|
79
|
+
DateTime: { type: "string", format: "date-time" }
|
|
80
|
+
};
|
|
81
|
+
var SWAGGER_DECORATORS = ["ApiHideProperty", "ApiProperty", "ApiPropertyOptional", "ApiPropertyRequired", "ApiExtraModels"];
|
|
82
|
+
var SWAGGER_API_PROPERTY_DECORATORS = ["description", "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "minLength", "maxLength", "minItems", "maxItems", "example"];
|
|
83
|
+
var CLASS_TRANSFORMER_DECORATORS = ["Type", "Transform"];
|
|
84
|
+
var CLASS_VALIDATOR_DECORATORS = [
|
|
85
|
+
"Allow",
|
|
86
|
+
"ArrayContains",
|
|
87
|
+
"ArrayMaxSize",
|
|
88
|
+
"ArrayMinSize",
|
|
89
|
+
"ArrayNotContains",
|
|
90
|
+
"ArrayNotEmpty",
|
|
91
|
+
"ArrayUnique",
|
|
92
|
+
"Contains",
|
|
93
|
+
"Equals",
|
|
94
|
+
"IsAlpha",
|
|
95
|
+
"IsAlphanumeric",
|
|
96
|
+
"IsArray",
|
|
97
|
+
"IsAscii",
|
|
98
|
+
"IsBIC",
|
|
99
|
+
"IsBase32",
|
|
100
|
+
"IsBase58",
|
|
101
|
+
"IsBase64",
|
|
102
|
+
"IsBoolean",
|
|
103
|
+
"IsBooleanString",
|
|
104
|
+
"IsBtcAddress",
|
|
105
|
+
"IsByteLength",
|
|
106
|
+
"IsCreditCard",
|
|
107
|
+
"IsCurrency",
|
|
108
|
+
"IsDataURI",
|
|
109
|
+
"IsDate",
|
|
110
|
+
"IsDateString",
|
|
111
|
+
"IsDecimal",
|
|
112
|
+
"IsDefined",
|
|
113
|
+
"IsDivisibleBy",
|
|
114
|
+
"IsEAN",
|
|
115
|
+
"IsEmail",
|
|
116
|
+
"IsEmpty",
|
|
117
|
+
"IsEthereumAddress",
|
|
118
|
+
"IsFQDN",
|
|
119
|
+
"IsFirebasePushId",
|
|
120
|
+
"IsFullWidth",
|
|
121
|
+
"IsHash",
|
|
122
|
+
"IsHSLColor",
|
|
123
|
+
"IsHexadecimal",
|
|
124
|
+
"IsIBAN",
|
|
125
|
+
"IsISIN",
|
|
126
|
+
"IsISO31661Alpha2",
|
|
127
|
+
"IsISO31661Alpha3",
|
|
128
|
+
"IsISO4217CurrencyCode",
|
|
129
|
+
"IsISO8601",
|
|
130
|
+
"IsISRC",
|
|
131
|
+
"IsISSN",
|
|
132
|
+
"IsIdentityCard",
|
|
133
|
+
"IsImage",
|
|
134
|
+
"IsIn",
|
|
135
|
+
"IsInstance",
|
|
136
|
+
"IsInt",
|
|
137
|
+
"IsIP",
|
|
138
|
+
"IsISBN",
|
|
139
|
+
"IsIsHalfWidth",
|
|
140
|
+
"IsIsHexColor",
|
|
141
|
+
"IsIsVariableWidth",
|
|
142
|
+
"IsJWT",
|
|
143
|
+
"IsJSON",
|
|
144
|
+
"IsLatLong",
|
|
145
|
+
"IsLatitude",
|
|
146
|
+
"IsLocale",
|
|
147
|
+
"IsLongitude",
|
|
148
|
+
"IsLowercase",
|
|
149
|
+
"IsMACAddress",
|
|
150
|
+
"IsMagnetURI",
|
|
151
|
+
"IsMilitaryTime",
|
|
152
|
+
"IsMimeType",
|
|
153
|
+
"IsMobilePhone",
|
|
154
|
+
"IsMongoId",
|
|
155
|
+
"IsMultiByte",
|
|
156
|
+
"IsNegative",
|
|
157
|
+
"IsNotEmpty",
|
|
158
|
+
"IsNotEmptyObject",
|
|
159
|
+
"IsNotIn",
|
|
160
|
+
"IsNumber",
|
|
161
|
+
"IsNumberString",
|
|
162
|
+
"IsObject",
|
|
163
|
+
"IsOctal",
|
|
164
|
+
"IsOptional",
|
|
165
|
+
"IsPassportNumber",
|
|
166
|
+
"IsPhoneNumber",
|
|
167
|
+
"IsPort",
|
|
168
|
+
"IsPositive",
|
|
169
|
+
"IsPostalCode",
|
|
170
|
+
"IsRFC3339",
|
|
171
|
+
"IsRgbColor",
|
|
172
|
+
"IsSemVer",
|
|
173
|
+
"IsString",
|
|
174
|
+
"IsStrongPassword",
|
|
175
|
+
"IsSurrogatePair",
|
|
176
|
+
"IsTaxId",
|
|
177
|
+
"IsTimeZone",
|
|
178
|
+
"IsUppercase",
|
|
179
|
+
"IsUrl",
|
|
180
|
+
"IsUUID",
|
|
181
|
+
"Length",
|
|
182
|
+
"Max",
|
|
183
|
+
"MaxDate",
|
|
184
|
+
"MaxLength",
|
|
185
|
+
"Matches",
|
|
186
|
+
"Min",
|
|
187
|
+
"MinDate",
|
|
188
|
+
"MinLength",
|
|
189
|
+
"NotContains",
|
|
190
|
+
"NotEquals",
|
|
191
|
+
"ValidateIf",
|
|
192
|
+
"ValidateNested"
|
|
193
|
+
];
|
|
194
|
+
var Utility = class _Utility {
|
|
195
|
+
static prefix = "prisma-generator-nestjs-dto";
|
|
196
|
+
/** Override the global log prefix (defaults to the package name). */
|
|
197
|
+
static setLogPrefix(prefix) {
|
|
198
|
+
this.prefix = prefix;
|
|
199
|
+
}
|
|
200
|
+
static log(message, ...data) {
|
|
201
|
+
console.info(`${chalk__default.default.cyan(this.prefix)} ${message}`, ...data);
|
|
202
|
+
}
|
|
203
|
+
static warn(message, ...data) {
|
|
204
|
+
console.info(chalk__default.default.yellowBright(`${this.prefix} ${message}`), ...data);
|
|
205
|
+
}
|
|
206
|
+
static error(message, ...data) {
|
|
207
|
+
console.error(chalk__default.default.redBright(`${this.prefix} ${message}`), ...data);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Parses a string into a boolean. Accepts native booleans and the Prisma-style
|
|
211
|
+
* stringified values that come from `schema.prisma` config options.
|
|
212
|
+
*/
|
|
213
|
+
static parseBoolean(value) {
|
|
214
|
+
if (typeof value === "boolean") return value;
|
|
215
|
+
if (typeof value === "string") return value === "true" || value === "1" || value === "yes" || value === "on";
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Parses one or more import declarations from the value of an `extra*` config option.
|
|
220
|
+
* The only supported syntax is the compact inline form:
|
|
221
|
+
*
|
|
222
|
+
* ```prisma
|
|
223
|
+
* extraValidators = "IsUnique,IsBool:src/common/validators|IsAdult:src/common/validators/is-adult"
|
|
224
|
+
* ```
|
|
225
|
+
*
|
|
226
|
+
* - `Foo,Bar:path` — named imports.
|
|
227
|
+
* - `* as Foo:path` — namespace import.
|
|
228
|
+
* - `default as Foo:path` — default import.
|
|
229
|
+
* - `|` — separator for multiple groups (different paths).
|
|
230
|
+
*
|
|
231
|
+
* For anything more elaborate (rename, default + named, type-safe paths, ...) use a
|
|
232
|
+
* `configFile` with `ImportDescriptor` or the `from()` / `fromNamespace()` / `fromDefault()`
|
|
233
|
+
* helpers.
|
|
234
|
+
*
|
|
235
|
+
* Also accepts an array of strings, with each entry already containing one or more
|
|
236
|
+
* `|`-separated groups.
|
|
237
|
+
*/
|
|
238
|
+
static parseImports(value) {
|
|
239
|
+
if (value === void 0 || value === null) return [];
|
|
240
|
+
const items = Array.isArray(value) ? value : [value];
|
|
241
|
+
const out = [];
|
|
242
|
+
for (const item of items) {
|
|
243
|
+
const trimmed = item.trim();
|
|
244
|
+
if (!trimmed) continue;
|
|
245
|
+
if (trimmed.startsWith("import")) {
|
|
246
|
+
this.warn(
|
|
247
|
+
`ES import syntax is no longer supported in extra* options. Use "Name:path|Other:path2" or move to a configFile with ImportDescriptor / from() helpers. Got: ${trimmed}`
|
|
248
|
+
);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
for (const entry of this.parseInlineImport(trimmed)) this.mergeInto(out, entry);
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Parses `extraAnnotations` into a deduplicated list of annotation names.
|
|
257
|
+
* Accepts a single comma/whitespace-separated string, a string array, or a mix:
|
|
258
|
+
* `"@DtoFoo, DtoBar"`, `["DtoFoo", "DtoBar"]`, `"DtoFoo DtoBar"`. Leading `@`
|
|
259
|
+
* characters are stripped so plugin authors can refer to them either way.
|
|
260
|
+
*/
|
|
261
|
+
static parseAnnotationNames(value) {
|
|
262
|
+
if (value === void 0 || value === null) return [];
|
|
263
|
+
const raw = Array.isArray(value) ? value : [value];
|
|
264
|
+
const seen = /* @__PURE__ */ new Set();
|
|
265
|
+
const out = [];
|
|
266
|
+
for (const entry of raw) {
|
|
267
|
+
if (typeof entry !== "string") continue;
|
|
268
|
+
for (const piece of entry.split(/[,\s]+/)) {
|
|
269
|
+
const name = piece.replace(/^@/, "").trim();
|
|
270
|
+
if (!name || seen.has(name)) continue;
|
|
271
|
+
seen.add(name);
|
|
272
|
+
out.push(name);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Parses the `extraScalars` config value. Accepts a JSON string (the form emitted by
|
|
279
|
+
* {@link applyConfigFile}) or an already-parsed `Record<string, ScalarOverride>` object.
|
|
280
|
+
* Invalid entries are skipped with a warning rather than throwing.
|
|
281
|
+
*/
|
|
282
|
+
static parseExtraScalars(value) {
|
|
283
|
+
if (!value) return {};
|
|
284
|
+
let parsed = value;
|
|
285
|
+
if (typeof value === "string") {
|
|
286
|
+
try {
|
|
287
|
+
parsed = JSON.parse(value);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
_Utility.warn("Failed to parse extraScalars JSON, ignoring.", error);
|
|
290
|
+
return {};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
294
|
+
const out = {};
|
|
295
|
+
for (const [scalar, raw] of Object.entries(parsed)) {
|
|
296
|
+
if (!raw || typeof raw !== "object") continue;
|
|
297
|
+
const override = raw;
|
|
298
|
+
if (typeof override.ts !== "string") {
|
|
299
|
+
_Utility.warn(`extraScalars["${scalar}"] is missing a string \`ts\` field, ignoring.`);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
out[scalar] = {
|
|
303
|
+
ts: override.ts,
|
|
304
|
+
from: typeof override.from === "string" ? override.from : void 0,
|
|
305
|
+
apiType: typeof override.apiType === "string" ? override.apiType : void 0,
|
|
306
|
+
format: typeof override.format === "string" ? override.format : void 0
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* @deprecated Use {@link parseImports} instead. Kept for sub-generators that
|
|
313
|
+
* relied on the public name in earlier releases.
|
|
314
|
+
*/
|
|
315
|
+
static stringToImports(value) {
|
|
316
|
+
return this.parseImports(value);
|
|
317
|
+
}
|
|
318
|
+
/** Parses the inline `Name1,Name2:path|Other:path2` syntax. */
|
|
319
|
+
static parseInlineImport(value) {
|
|
320
|
+
const out = [];
|
|
321
|
+
for (const group of value.split("|")) {
|
|
322
|
+
const lastColonIndex = group.lastIndexOf(":");
|
|
323
|
+
if (lastColonIndex === -1) {
|
|
324
|
+
this.warn(`Invalid import format: ${group}. Missing path after colon.`);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const from2 = group.substring(lastColonIndex + 1).trim();
|
|
328
|
+
const names = group.substring(0, lastColonIndex).trim();
|
|
329
|
+
if (names.includes("* as ") || names.includes("default as ")) {
|
|
330
|
+
out.push({ from: from2, alias: names });
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const destruct = names.split(",").map((n) => n.trim()).filter(Boolean);
|
|
334
|
+
if (destruct.length === 0) {
|
|
335
|
+
this.warn(`Invalid import format: ${group}. No import names found.`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
out.push({ from: from2, destruct });
|
|
339
|
+
}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
/** Merges a new entry into an existing array, deduplicating named imports on the same path. */
|
|
343
|
+
static mergeInto(imports, next) {
|
|
344
|
+
if (next.alias) {
|
|
345
|
+
const existing = imports.find((i) => i.from === next.from && i.alias);
|
|
346
|
+
if (existing) {
|
|
347
|
+
existing.alias = next.alias;
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
imports.push({ from: next.from, alias: next.alias });
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (next.destruct && next.destruct.length) {
|
|
354
|
+
const existing = imports.find((i) => i.from === next.from && i.destruct);
|
|
355
|
+
if (existing) {
|
|
356
|
+
existing.destruct = Array.from(/* @__PURE__ */ new Set([...existing.destruct || [], ...next.destruct]));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
imports.push({ from: next.from, destruct: [...next.destruct] });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/base-generator.ts
|
|
365
|
+
var BaseGenerator = class {
|
|
366
|
+
options;
|
|
367
|
+
config;
|
|
368
|
+
models;
|
|
369
|
+
constructor(options) {
|
|
370
|
+
this.options = options;
|
|
371
|
+
const raw = options.generator.config;
|
|
372
|
+
const asString = (value) => typeof value === "string" ? value : void 0;
|
|
373
|
+
this.config = {
|
|
374
|
+
provider: options.generator.provider?.value || "",
|
|
375
|
+
output: options.generator.output?.value || "",
|
|
376
|
+
outputType: asString(raw.outputType) || "class",
|
|
377
|
+
outputStructure: asString(raw.outputStructure) || "nestjs",
|
|
378
|
+
reExport: Utility.parseBoolean(asString(raw.reExport)),
|
|
379
|
+
fileNamingStrategy: asString(raw.fileNamingStrategy) || "camel",
|
|
380
|
+
classValidator: Utility.parseBoolean(asString(raw.classValidator)),
|
|
381
|
+
swaggerDocs: Utility.parseBoolean(asString(raw.swaggerDocs)),
|
|
382
|
+
prettier: Utility.parseBoolean(asString(raw.prettier)),
|
|
383
|
+
schemaDir: asString(raw.schemaDir),
|
|
384
|
+
emitManifest: Utility.parseBoolean(asString(raw.emitManifest)),
|
|
385
|
+
extraDecorators: Utility.parseImports(raw.extraDecorators),
|
|
386
|
+
extraValidators: Utility.parseImports(raw.extraValidators),
|
|
387
|
+
extraImports: Utility.parseImports(raw.extraImports),
|
|
388
|
+
extraGenerators: raw.extraGenerators,
|
|
389
|
+
extraScalars: Utility.parseExtraScalars(raw.extraScalars),
|
|
390
|
+
extraAnnotations: Utility.parseAnnotationNames(raw.extraAnnotations)
|
|
391
|
+
};
|
|
392
|
+
this.models = this.options.dmmf.datamodel.models.map((model) => ({
|
|
393
|
+
name: model.name,
|
|
394
|
+
dbName: model.dbName,
|
|
395
|
+
documentation: model.documentation,
|
|
396
|
+
primaryKey: model.primaryKey,
|
|
397
|
+
uniqueFields: model.uniqueFields,
|
|
398
|
+
uniqueIndexes: model.uniqueIndexes,
|
|
399
|
+
isGenerated: model.isGenerated,
|
|
400
|
+
annotations: this.extractAnnotations(model.documentation || ""),
|
|
401
|
+
outputType: this.config.outputType,
|
|
402
|
+
fields: [
|
|
403
|
+
...model.fields.map((field) => ({
|
|
404
|
+
name: field.name,
|
|
405
|
+
type: field.type,
|
|
406
|
+
kind: field.kind,
|
|
407
|
+
isList: field.isList,
|
|
408
|
+
isRequired: field.isRequired,
|
|
409
|
+
isUnique: field.isUnique,
|
|
410
|
+
isId: field.isId,
|
|
411
|
+
isReadOnly: field.isReadOnly,
|
|
412
|
+
isGenerated: field.isGenerated,
|
|
413
|
+
isUpdatedAt: field.isUpdatedAt,
|
|
414
|
+
hasDefaultValue: field.hasDefaultValue,
|
|
415
|
+
default: field.default,
|
|
416
|
+
relationName: field.relationName,
|
|
417
|
+
relationFromFields: field.relationFromFields,
|
|
418
|
+
relationToFields: field.relationToFields,
|
|
419
|
+
relationOnDelete: field.relationOnDelete,
|
|
420
|
+
documentation: field.documentation,
|
|
421
|
+
isNullable: !field.isRequired || field.isList,
|
|
422
|
+
annotations: this.extractAnnotations(field.documentation || "")
|
|
423
|
+
})),
|
|
424
|
+
...this.getIgnoredFields(model.name)
|
|
425
|
+
]
|
|
426
|
+
})).filter((model) => !model.annotations.some((a) => a.name === DTO_IGNORE_MODEL));
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Optional pre-pass invoked once before any built-in or plugin `generate()` runs.
|
|
430
|
+
* Receives the **shared** `Model[]` array (already filtered for `@DtoIgnoreModel`);
|
|
431
|
+
* mutating it (e.g. injecting synthetic fields, rewriting annotations) is supported
|
|
432
|
+
* and visible to every generator that runs afterwards. Defaults to no-op.
|
|
433
|
+
*/
|
|
434
|
+
async beforeAll(_models) {
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Optional post-pass invoked once after every generator's `generate()` completes,
|
|
438
|
+
* receiving the **complete** list of files produced in the run (built-ins + plugins).
|
|
439
|
+
* Return a non-empty `File[]` to append extra files (e.g. an aggregated barrel,
|
|
440
|
+
* audit report, schema export); return `void` to leave the run untouched. Defaults to no-op.
|
|
441
|
+
*/
|
|
442
|
+
async afterAll(_files) {
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Reads `@ignore` fields from `.prisma` files inside `schemaDir`. Prisma
|
|
446
|
+
* strips ignored fields from the DMMF but we still want them visible to
|
|
447
|
+
* annotations like `@DtoApiHidden`, so we re-parse the raw schema.
|
|
448
|
+
*/
|
|
449
|
+
getIgnoredFields(modelName) {
|
|
450
|
+
try {
|
|
451
|
+
if (!this.config.schemaDir) return [];
|
|
452
|
+
const schemaDir = path__default.default.resolve(process.cwd(), this.config.schemaDir);
|
|
453
|
+
if (!fs3__default.default.existsSync(schemaDir)) {
|
|
454
|
+
Utility.warn(`Configured schemaDir does not exist: ${schemaDir}`);
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
const findPrismaFilesRecursively = (dir) => {
|
|
458
|
+
const files = [];
|
|
459
|
+
try {
|
|
460
|
+
const entries = fs3__default.default.readdirSync(dir, { withFileTypes: true });
|
|
461
|
+
for (const entry of entries) {
|
|
462
|
+
const fullPath = path__default.default.join(dir, entry.name);
|
|
463
|
+
if (entry.isDirectory()) files.push(...findPrismaFilesRecursively(fullPath));
|
|
464
|
+
else if (entry.isFile() && entry.name.endsWith(".prisma") && entry.name !== "schema.prisma") files.push(fullPath);
|
|
465
|
+
}
|
|
466
|
+
} catch (error) {
|
|
467
|
+
Utility.warn(`Could not read directory ${dir}:`, error);
|
|
468
|
+
}
|
|
469
|
+
return files;
|
|
470
|
+
};
|
|
471
|
+
const schemaFiles = findPrismaFilesRecursively(schemaDir);
|
|
472
|
+
const ignoredFields = [];
|
|
473
|
+
for (const schemaFile of schemaFiles) {
|
|
474
|
+
const schemaContent = fs3__default.default.readFileSync(schemaFile, "utf8");
|
|
475
|
+
const modelRegex = new RegExp(`model\\s+${modelName}\\s*\\{([\\s\\S]*?)\\}`, "g");
|
|
476
|
+
const modelMatch = modelRegex.exec(schemaContent);
|
|
477
|
+
if (!modelMatch) continue;
|
|
478
|
+
const modelContent = modelMatch[1];
|
|
479
|
+
const fieldRegex = /((?:[ \t]*\/\/\/[^\n]*\n)*)([ \t]*)([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z0-9\[\]?]+)([^@\n]*@ignore)/g;
|
|
480
|
+
let fieldMatch;
|
|
481
|
+
while ((fieldMatch = fieldRegex.exec(modelContent)) !== null) {
|
|
482
|
+
const documentation = fieldMatch[1]?.trim() || "";
|
|
483
|
+
const fieldName = fieldMatch[3];
|
|
484
|
+
const fieldType = fieldMatch[4];
|
|
485
|
+
const isOptional = fieldType.includes("?");
|
|
486
|
+
const isList = fieldType.includes("[]");
|
|
487
|
+
const cleanType = fieldType.replace(/[\[\]?]/g, "");
|
|
488
|
+
const kind = ["String", "Int", "Float", "Boolean", "DateTime", "Json", "Decimal", "BigInt", "Bytes"].includes(cleanType) ? "scalar" : "object";
|
|
489
|
+
ignoredFields.push({
|
|
490
|
+
name: fieldName,
|
|
491
|
+
type: cleanType,
|
|
492
|
+
kind,
|
|
493
|
+
isList,
|
|
494
|
+
isRequired: !isOptional,
|
|
495
|
+
isNullable: isOptional || isList,
|
|
496
|
+
annotations: this.extractAnnotations(documentation),
|
|
497
|
+
isId: false,
|
|
498
|
+
isUnique: false,
|
|
499
|
+
isReadOnly: false,
|
|
500
|
+
hasDefaultValue: false
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return ignoredFields;
|
|
505
|
+
} catch (error) {
|
|
506
|
+
Utility.warn(`Could not read ignored fields for model ${modelName}:`, error);
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/** Parses `/// @Foo(arg, {object})` documentation strings into structured annotations. */
|
|
511
|
+
extractAnnotations(documentation) {
|
|
512
|
+
const annotations = [];
|
|
513
|
+
let nameMatch;
|
|
514
|
+
while ((nameMatch = ANNOTATION_NAME_REGEX.exec(documentation)) !== null) {
|
|
515
|
+
const annotationName = nameMatch[1];
|
|
516
|
+
const startPosition = nameMatch.index + ("@" + annotationName).length;
|
|
517
|
+
const remainingText = documentation.substring(startPosition);
|
|
518
|
+
if (!remainingText.startsWith("(")) {
|
|
519
|
+
annotations.push({ name: annotationName, params: [] });
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
ANNOTATION_PARAMS_REGEX.lastIndex = 0;
|
|
523
|
+
const paramsMatch = ANNOTATION_PARAMS_REGEX.exec(remainingText);
|
|
524
|
+
let params = [];
|
|
525
|
+
if (paramsMatch && paramsMatch[1]) {
|
|
526
|
+
const paramsString = paramsMatch[1].trim();
|
|
527
|
+
params = paramsString.split(ANNOTATION_PARAMS_SPLIT_REGEX).map((param) => param.trim()).map((param) => {
|
|
528
|
+
if (param.startsWith("{") && param.endsWith("}")) return param;
|
|
529
|
+
return isNaN(Number(param)) ? param : Number(param);
|
|
530
|
+
}).filter(Boolean);
|
|
531
|
+
}
|
|
532
|
+
annotations.push({ name: annotationName, params });
|
|
533
|
+
}
|
|
534
|
+
return annotations;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Returns the first annotation matching `name` on a field or model, or `undefined`.
|
|
538
|
+
* Use from custom sub-generators to react to user-defined annotations declared via
|
|
539
|
+
* `extraAnnotations` (or any built-in `@Dto*` name).
|
|
540
|
+
*/
|
|
541
|
+
getAnnotation(target, name) {
|
|
542
|
+
return target.annotations.find((a) => a.name === name);
|
|
543
|
+
}
|
|
544
|
+
/** Convenience boolean form of {@link getAnnotation}. */
|
|
545
|
+
hasAnnotation(target, name) {
|
|
546
|
+
return this.getAnnotation(target, name) !== void 0;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Adds (and merges) an import descriptor into an `ImportType[]` array, honouring
|
|
550
|
+
* the same dedup/merge rules used by the built-in pipeline. Repeated calls with
|
|
551
|
+
* the same `from` collapse named imports together; `alias` entries are stored
|
|
552
|
+
* separately from `destruct` entries on the same path.
|
|
553
|
+
*/
|
|
554
|
+
addImport(imports, next) {
|
|
555
|
+
this.mergeImport(imports, next);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Renders an `ImportType[]` array as a multi-line `import ... from '...'` block.
|
|
559
|
+
* Absolute paths (e.g. produced by `getPath(relatedModel, outputPath)`) are rewritten
|
|
560
|
+
* as POSIX-style relative paths anchored at `outputPath`; TypeScript file extensions
|
|
561
|
+
* (`.ts` / `.mts` / `.cts` / `.tsx`) on the `from` path are stripped automatically
|
|
562
|
+
* since they are never desired in import statements.
|
|
563
|
+
*/
|
|
564
|
+
formatImports(imports, outputPath) {
|
|
565
|
+
return imports.map((i) => {
|
|
566
|
+
const from2 = this.stripTsExtension(this.rewriteImportPath(i.from, outputPath));
|
|
567
|
+
return `import ${i.alias ? i.alias : `{ ${(i.destruct || []).join(", ")} }`} from '${from2}';`;
|
|
568
|
+
}).join("\n");
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Returns the output path of a `model` formatted for use as an `import` specifier:
|
|
572
|
+
* same as {@link getPath} but with the TypeScript file extension stripped and a
|
|
573
|
+
* `./` prefix added when the result would otherwise look like a bare module specifier
|
|
574
|
+
* (`flat` outputStructure case). Use this when a custom sub-generator needs to
|
|
575
|
+
* reference a peer-generated file (e.g. for relations between models) and you would
|
|
576
|
+
* otherwise hand-strip `.ts` from `getPath`.
|
|
577
|
+
*/
|
|
578
|
+
getImportPath(model, fromOutputPath) {
|
|
579
|
+
const stripped = this.stripTsExtension(this.getPath(model, fromOutputPath));
|
|
580
|
+
if (fromOutputPath && !stripped.startsWith(".") && !path__default.default.isAbsolute(stripped)) return `./${stripped}`;
|
|
581
|
+
return stripped;
|
|
582
|
+
}
|
|
583
|
+
/** Strips `.ts` / `.mts` / `.cts` / `.tsx` suffixes from an `import` specifier. */
|
|
584
|
+
stripTsExtension(from2) {
|
|
585
|
+
return from2.replace(/\.(ts|mts|cts|tsx)$/, "");
|
|
586
|
+
}
|
|
587
|
+
/** File / class name transformer. Defaults to camelCase. */
|
|
588
|
+
getModelName(name) {
|
|
589
|
+
switch (this.config.fileNamingStrategy) {
|
|
590
|
+
case "snake":
|
|
591
|
+
return name.charAt(0).toLowerCase() + name.slice(1).replace(/[A-Z]/g, (char) => "_" + char.toLowerCase());
|
|
592
|
+
case "kebab":
|
|
593
|
+
return name.charAt(0).toLowerCase() + name.slice(1).replace(/[A-Z]/g, (char) => "-" + char.toLowerCase()).replace(/_/g, "-");
|
|
594
|
+
default:
|
|
595
|
+
case "camel":
|
|
596
|
+
return name.charAt(0).toLowerCase() + name.slice(1).replace(/_(\w)/g, (_, char) => char.toUpperCase());
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Applies `@DtoOverrideType(<type>)` non-destructively. The original `model`
|
|
601
|
+
* is left untouched; callers receive a model with cloned fields whose `kind`
|
|
602
|
+
* and `type` reflect the override.
|
|
603
|
+
*/
|
|
604
|
+
applyOverrides(model) {
|
|
605
|
+
const fields = model.fields.map((field) => {
|
|
606
|
+
const overrideType = field.annotations.find((a) => a.name === DTO_OVERRIDE_TYPE);
|
|
607
|
+
if (overrideType && overrideType.params?.[0]) {
|
|
608
|
+
const clone = { ...field };
|
|
609
|
+
delete clone.kind;
|
|
610
|
+
clone.type = String(overrideType.params[0]);
|
|
611
|
+
return clone;
|
|
612
|
+
}
|
|
613
|
+
return field;
|
|
614
|
+
});
|
|
615
|
+
return { ...model, fields };
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Merges a new `ImportType` into the existing array. Distinct concerns are kept
|
|
619
|
+
* as separate entries (i.e. an `alias` import and a `destruct` import from the
|
|
620
|
+
* same path will not collide).
|
|
621
|
+
*/
|
|
622
|
+
mergeImport(imports, next) {
|
|
623
|
+
if (next.alias) {
|
|
624
|
+
const existing = imports.find((i) => i.from === next.from && i.alias);
|
|
625
|
+
if (existing) {
|
|
626
|
+
existing.alias = next.alias;
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
imports.push({ from: next.from, alias: next.alias });
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
if (next.destruct && next.destruct.length) {
|
|
633
|
+
const existing = imports.find((i) => i.from === next.from && i.destruct);
|
|
634
|
+
if (existing) {
|
|
635
|
+
existing.destruct = Array.from(/* @__PURE__ */ new Set([...existing.destruct || [], ...next.destruct]));
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
imports.push({ from: next.from, destruct: [...next.destruct] });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
/** Collects every import the rendered class will need. */
|
|
642
|
+
getImports(args) {
|
|
643
|
+
const { model, classValidator = Utility.parseBoolean(this.config.classValidator) || false, swaggerDocs = Utility.parseBoolean(this.config.swaggerDocs) || false } = args;
|
|
644
|
+
const imports = [];
|
|
645
|
+
const findOverride = (name) => [...this.config.extraValidators, ...this.config.extraDecorators, ...this.config.extraImports].find((v) => v.destruct?.includes(name));
|
|
646
|
+
const addImport = (importParams) => {
|
|
647
|
+
if (!importParams.destruct?.length || importParams.alias) {
|
|
648
|
+
this.mergeImport(imports, importParams);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
652
|
+
importParams.destruct.forEach((name) => {
|
|
653
|
+
const from2 = findOverride(name)?.from ?? importParams.from;
|
|
654
|
+
grouped.set(from2, [...grouped.get(from2) ?? [], name]);
|
|
655
|
+
});
|
|
656
|
+
grouped.forEach((destruct, from2) => this.mergeImport(imports, { from: from2, destruct }));
|
|
657
|
+
};
|
|
658
|
+
const scalarOverrides = this.config.extraScalars ?? {};
|
|
659
|
+
model.fields.forEach((field) => {
|
|
660
|
+
if (field.kind === "object" && field.type && field.type !== `${this.classPrefix}${model.name}${this.classSuffix}`) {
|
|
661
|
+
const relationModel = this.models.find((m) => m.name === field.type);
|
|
662
|
+
if (relationModel) {
|
|
663
|
+
const importPath = this.getPath(relationModel, this.getPath(model));
|
|
664
|
+
addImport({ from: Utility.parseBoolean(this.config.reExport) ? path__default.default.dirname(importPath) : importPath, destruct: [relationModel.name] });
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (field.kind === "enum") addImport({ from: "@prisma/client", destruct: [field.type] });
|
|
668
|
+
const override = scalarOverrides[field.type];
|
|
669
|
+
if (override && override.from) addImport({ from: override.from, destruct: [override.ts] });
|
|
670
|
+
else if (field.type === "Json" || field.type === "Decimal") addImport({ from: "@prisma/client", destruct: ["Prisma"] });
|
|
671
|
+
});
|
|
672
|
+
if (this.config.outputType === "class" && swaggerDocs) {
|
|
673
|
+
const addSwaggerDecorator = (decorator) => addImport({ from: "@nestjs/swagger", destruct: [decorator] });
|
|
674
|
+
addSwaggerDecorator("ApiProperty");
|
|
675
|
+
const extraModelsAnnotation = model.annotations.find((a) => a.name === DTO_API_EXTRA_MODELS);
|
|
676
|
+
if (extraModelsAnnotation?.params?.length) addSwaggerDecorator("ApiExtraModels");
|
|
677
|
+
model.fields.forEach((field) => {
|
|
678
|
+
field.annotations.forEach((annotation) => {
|
|
679
|
+
if (SWAGGER_DECORATORS.includes(annotation.name)) addSwaggerDecorator(annotation.name);
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
if (this.config.outputType === "class" && classValidator) {
|
|
684
|
+
const addValidator = (decorator) => addImport({ from: "class-validator", destruct: [decorator] });
|
|
685
|
+
model.fields.forEach((field) => {
|
|
686
|
+
if (!field.isRequired) addValidator("IsOptional");
|
|
687
|
+
if (!field.isNullable) addValidator("IsNotEmpty");
|
|
688
|
+
if (field.isList) addValidator("IsArray");
|
|
689
|
+
switch (field.type) {
|
|
690
|
+
case "String":
|
|
691
|
+
addValidator("IsString");
|
|
692
|
+
break;
|
|
693
|
+
case "Int":
|
|
694
|
+
addValidator("IsInt");
|
|
695
|
+
break;
|
|
696
|
+
case "Float":
|
|
697
|
+
addValidator("IsNumber");
|
|
698
|
+
break;
|
|
699
|
+
case "Boolean":
|
|
700
|
+
addValidator("IsBoolean");
|
|
701
|
+
break;
|
|
702
|
+
case "DateTime":
|
|
703
|
+
addValidator("IsDateString");
|
|
704
|
+
break;
|
|
705
|
+
case "Json":
|
|
706
|
+
addValidator("IsObject");
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
if (field.kind === "object") {
|
|
710
|
+
addValidator("ValidateNested");
|
|
711
|
+
addImport({ from: "class-transformer", destruct: ["Type"] });
|
|
712
|
+
}
|
|
713
|
+
field.annotations.forEach((annotation) => {
|
|
714
|
+
if (CLASS_VALIDATOR_DECORATORS.includes(annotation.name)) addValidator(annotation.name);
|
|
715
|
+
});
|
|
716
|
+
field.annotations.forEach((annotation) => {
|
|
717
|
+
if (CLASS_TRANSFORMER_DECORATORS.includes(annotation.name)) addImport({ from: "class-transformer", destruct: [annotation.name] });
|
|
718
|
+
});
|
|
719
|
+
if (this.config.extraValidators.length) {
|
|
720
|
+
field.annotations.forEach((annotation) => {
|
|
721
|
+
this.config.extraValidators.forEach((validatorImport) => {
|
|
722
|
+
if (annotation.name === validatorImport.alias || validatorImport.destruct && validatorImport.destruct.includes(annotation.name)) {
|
|
723
|
+
if (validatorImport.alias) addImport({ from: validatorImport.from, alias: validatorImport.alias });
|
|
724
|
+
else if (validatorImport.destruct) addImport({ from: validatorImport.from, destruct: [annotation.name] });
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
if (this.config.extraDecorators.length) {
|
|
732
|
+
model.fields.forEach((field) => {
|
|
733
|
+
field.annotations.forEach((annotation) => {
|
|
734
|
+
this.config.extraDecorators.forEach((decoratorImport) => {
|
|
735
|
+
if (annotation.name === decoratorImport.alias || decoratorImport.destruct && decoratorImport.destruct.includes(annotation.name)) {
|
|
736
|
+
if (decoratorImport.alias) addImport({ from: decoratorImport.from, alias: decoratorImport.alias });
|
|
737
|
+
else if (decoratorImport.destruct) addImport({ from: decoratorImport.from, destruct: [annotation.name] });
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
if (this.config.extraImports.length) {
|
|
744
|
+
model.fields.forEach((field) => {
|
|
745
|
+
field.annotations.forEach((annotation) => {
|
|
746
|
+
const isValidatorAnnotation = CLASS_VALIDATOR_DECORATORS.includes(annotation.name) || CLASS_TRANSFORMER_DECORATORS.includes(annotation.name) || this.config.extraValidators.some((v) => annotation.name === v.alias || v.destruct && v.destruct.includes(annotation.name));
|
|
747
|
+
const isSwaggerAnnotation = SWAGGER_DECORATORS.includes(annotation.name) || SWAGGER_API_PROPERTY_DECORATORS.includes(annotation.name);
|
|
748
|
+
if (isValidatorAnnotation && !classValidator || isSwaggerAnnotation && !swaggerDocs) return;
|
|
749
|
+
if (!annotation.params) return;
|
|
750
|
+
this.config.extraImports.forEach((importConfig) => {
|
|
751
|
+
if (importConfig.alias) {
|
|
752
|
+
const aliasName = importConfig.alias.split(" as ")[1] || importConfig.alias;
|
|
753
|
+
const isUsed = annotation.params.some((param) => typeof param === "string" && param.includes(aliasName));
|
|
754
|
+
if (isUsed) addImport({ from: importConfig.from, alias: importConfig.alias });
|
|
755
|
+
} else if (importConfig.destruct) {
|
|
756
|
+
importConfig.destruct.forEach((item) => {
|
|
757
|
+
const isUsed = annotation.params.some((param) => typeof param === "string" && param.includes(item));
|
|
758
|
+
if (isUsed) addImport({ from: importConfig.from, destruct: [item] });
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
const extraModelsAnnotation = model.annotations.find((a) => a.name === DTO_API_EXTRA_MODELS);
|
|
765
|
+
if (extraModelsAnnotation?.params?.length) {
|
|
766
|
+
extraModelsAnnotation.params.forEach((param) => {
|
|
767
|
+
if (typeof param !== "string") return;
|
|
768
|
+
const typeName = param.trim();
|
|
769
|
+
this.config.extraImports.forEach((importConfig) => {
|
|
770
|
+
if (importConfig.destruct && importConfig.destruct.includes(typeName)) addImport({ from: importConfig.from, destruct: [typeName] });
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
model.fields.forEach((field) => {
|
|
776
|
+
const overrideType = field.annotations.find((a) => a.name === DTO_OVERRIDE_TYPE);
|
|
777
|
+
if (overrideType && overrideType.params?.[0]) {
|
|
778
|
+
const overrideModel = this.models.find((m) => m.name === String(overrideType.params[0]));
|
|
779
|
+
if (overrideModel) {
|
|
780
|
+
const importPath = this.getPath(overrideModel, this.getPath(model));
|
|
781
|
+
addImport({ from: Utility.parseBoolean(this.config.reExport) ? path__default.default.dirname(importPath) : importPath, destruct: [overrideModel.name] });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
const overrideApiPropertyType = field.annotations.find((a) => a.name === DTO_OVERRIDE_API_PROPERTY_TYPE);
|
|
785
|
+
if (overrideApiPropertyType && overrideApiPropertyType.params?.[0]) {
|
|
786
|
+
const overrideModel = this.models.find((m) => m.name === String(overrideApiPropertyType.params[0]));
|
|
787
|
+
if (overrideModel) {
|
|
788
|
+
const importPath = this.getPath(overrideModel, this.getPath(model));
|
|
789
|
+
addImport({ from: Utility.parseBoolean(this.config.reExport) ? path__default.default.dirname(importPath) : importPath, destruct: [overrideModel.name] });
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
return imports;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Resolves an import `from` value for the file being emitted. Absolute paths
|
|
797
|
+
* (typically produced when a relative `from` was declared in the external config file)
|
|
798
|
+
* are rewritten as POSIX-style relative paths anchored at the output file's directory.
|
|
799
|
+
*/
|
|
800
|
+
rewriteImportPath(from2, outputPath) {
|
|
801
|
+
if (!outputPath || !path__default.default.isAbsolute(from2)) return from2;
|
|
802
|
+
const absoluteOutput = path__default.default.resolve(outputPath);
|
|
803
|
+
let relative = path__default.default.relative(path__default.default.dirname(absoluteOutput), from2);
|
|
804
|
+
if (!relative.startsWith(".") && !path__default.default.isAbsolute(relative)) relative = `./${relative}`;
|
|
805
|
+
return relative.split(path__default.default.sep).join("/");
|
|
806
|
+
}
|
|
807
|
+
/** Renders the `import ... from ...` block. */
|
|
808
|
+
getImportsTemplate(args) {
|
|
809
|
+
return this.formatImports(this.getImports(args), args.outputPath);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Renders the full class body (imports + class declaration with decorators and fields).
|
|
813
|
+
* Sub-generators usually feed it a `model` they already pre-filtered.
|
|
814
|
+
*/
|
|
815
|
+
getTemplate(args) {
|
|
816
|
+
const classValidator = Utility.parseBoolean(args.classValidator ?? this.config.classValidator) || false;
|
|
817
|
+
const swaggerDocs = Utility.parseBoolean(args.swaggerDocs ?? this.config.swaggerDocs) || false;
|
|
818
|
+
const model = this.applyOverrides(args.model);
|
|
819
|
+
const scalarOverrides = this.config.extraScalars ?? {};
|
|
820
|
+
const getPropertyType = (field, isDecorator = false) => {
|
|
821
|
+
const override = field.kind === "scalar" ? scalarOverrides[field.type] : void 0;
|
|
822
|
+
if (override) {
|
|
823
|
+
if (!isDecorator) return override.ts;
|
|
824
|
+
if (override.apiType) return override.apiType;
|
|
825
|
+
return override.from ? `() => ${override.ts}` : `'${override.ts}'`;
|
|
826
|
+
}
|
|
827
|
+
const SCALAR_TYPE = isDecorator ? Object.fromEntries(Object.entries(PRISMA_SCALAR_TYPE).map(([key, value]) => [key, value.startsWith("() =>") ? value : `'${value}'`])) : PRISMA_SCALAR;
|
|
828
|
+
if (field.kind === "scalar") return SCALAR_TYPE[field.type] || "any";
|
|
829
|
+
else if ((field.kind === "enum" || field.kind === "object") && isDecorator) return `() => ${field.type}`;
|
|
830
|
+
return field.type;
|
|
831
|
+
};
|
|
832
|
+
const getDefaultValue = (field) => {
|
|
833
|
+
if (!field.hasDefaultValue) return void 0;
|
|
834
|
+
if (Array.isArray(field.default)) return JSON.stringify(field.default);
|
|
835
|
+
switch (typeof field.default) {
|
|
836
|
+
case "string":
|
|
837
|
+
case "number":
|
|
838
|
+
case "boolean":
|
|
839
|
+
if (field.type === "Decimal" && typeof field.default === "number") return `new Prisma.Decimal(${field.default})`;
|
|
840
|
+
return field.default;
|
|
841
|
+
case "object":
|
|
842
|
+
if (field.default && typeof field.default === "object" && "name" in field.default) {
|
|
843
|
+
if (field.default.name === "now" && field.type === "DateTime") return void 0;
|
|
844
|
+
if (field.default.name === "autoincrement") return void 0;
|
|
845
|
+
return field.default.name;
|
|
846
|
+
}
|
|
847
|
+
return void 0;
|
|
848
|
+
default:
|
|
849
|
+
return void 0;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
const encapsulateString = (value) => {
|
|
853
|
+
if (value === "true" || value === "false" || value === "null" || /^-?\d+(?:\.\d+)?$/.test(value) || /^\[.*]$/.test(value)) return value;
|
|
854
|
+
return `'${value.replace(/'/g, "\\'")}'`;
|
|
855
|
+
};
|
|
856
|
+
const fields = model.fields.map((field) => {
|
|
857
|
+
const decorators = [];
|
|
858
|
+
if (this.config.outputType === "class" && swaggerDocs) {
|
|
859
|
+
if (field.annotations.some((a) => a.name === DTO_API_HIDDEN)) {
|
|
860
|
+
decorators.push("@ApiHideProperty()");
|
|
861
|
+
} else {
|
|
862
|
+
const apiPropertyParams = [];
|
|
863
|
+
if (field.kind === "enum") {
|
|
864
|
+
apiPropertyParams.push(`enum: ${field.type}`);
|
|
865
|
+
apiPropertyParams.push(`enumName: ${encapsulateString(field.type)}`);
|
|
866
|
+
} else {
|
|
867
|
+
const overrideApiPropertyType = field.annotations.find((a) => a.name === DTO_OVERRIDE_API_PROPERTY_TYPE);
|
|
868
|
+
if (overrideApiPropertyType && overrideApiPropertyType.params && overrideApiPropertyType.params.length > 0) {
|
|
869
|
+
apiPropertyParams.push(`type: () => ${overrideApiPropertyType.params[0]}`);
|
|
870
|
+
} else {
|
|
871
|
+
apiPropertyParams.push(`type: ${getPropertyType(field, true)}`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (field.isList) apiPropertyParams.push(`isArray: true`);
|
|
875
|
+
apiPropertyParams.push(`required: ${!field.isList && field.isRequired}`);
|
|
876
|
+
if (field.isNullable) apiPropertyParams.push(`nullable: ${field.isNullable}`);
|
|
877
|
+
const formatOverride = scalarOverrides[field.type]?.format;
|
|
878
|
+
const scalarFormat = formatOverride ?? PRISMA_SCALAR_FORMAT[field.type]?.format;
|
|
879
|
+
if (scalarFormat) apiPropertyParams.push(`format: ${encapsulateString(scalarFormat)}`);
|
|
880
|
+
const defaultValue2 = getDefaultValue(field);
|
|
881
|
+
if (defaultValue2 !== void 0) apiPropertyParams.push(`default: ${typeof defaultValue2 === "string" ? encapsulateString(defaultValue2) : defaultValue2}`);
|
|
882
|
+
SWAGGER_API_PROPERTY_DECORATORS.forEach((prop) => {
|
|
883
|
+
const annotation = field.annotations.find((a) => a.name === prop);
|
|
884
|
+
if (annotation) {
|
|
885
|
+
const value = annotation.params ? annotation.params[0] : "";
|
|
886
|
+
apiPropertyParams.push(`${prop}: ${encapsulateString(String(value))}`);
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
decorators.push(`@ApiProperty({ ${apiPropertyParams.join(", ")} })`);
|
|
890
|
+
field.annotations.filter((annotation) => SWAGGER_DECORATORS.includes(annotation.name)).forEach((annotation) => {
|
|
891
|
+
const params = annotation.params ? `(${annotation.params.join(", ")})` : "";
|
|
892
|
+
decorators.push(`@${annotation.name}${params}`);
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (this.config.outputType === "class" && classValidator) {
|
|
897
|
+
if (field.isList || !field.isRequired) decorators.push("@IsOptional()");
|
|
898
|
+
if (!field.isNullable) decorators.push("@IsNotEmpty()");
|
|
899
|
+
if (field.isList) decorators.push("@IsArray()");
|
|
900
|
+
switch (field.type) {
|
|
901
|
+
case "String":
|
|
902
|
+
decorators.push(field.isList ? "@IsString({ each: true })" : "@IsString()");
|
|
903
|
+
break;
|
|
904
|
+
case "Int":
|
|
905
|
+
decorators.push(field.isList ? "@IsInt({ each: true })" : "@IsInt()");
|
|
906
|
+
break;
|
|
907
|
+
case "Float":
|
|
908
|
+
decorators.push(field.isList ? "@IsNumber({}, { each: true })" : "@IsNumber()");
|
|
909
|
+
break;
|
|
910
|
+
case "Boolean":
|
|
911
|
+
decorators.push(field.isList ? "@IsBoolean({ each: true })" : "@IsBoolean()");
|
|
912
|
+
break;
|
|
913
|
+
case "DateTime":
|
|
914
|
+
decorators.push(field.isList ? "@IsDateString({}, { each: true })" : "@IsDateString()");
|
|
915
|
+
break;
|
|
916
|
+
case "Json":
|
|
917
|
+
decorators.push(field.isList ? "@IsObject({ each: true })" : "@IsObject()");
|
|
918
|
+
break;
|
|
919
|
+
}
|
|
920
|
+
if (field.kind === "object") {
|
|
921
|
+
decorators.push(field.isList ? "@ValidateNested({ each: true })" : "@ValidateNested()");
|
|
922
|
+
decorators.push(`@Type(() => ${field.type})`);
|
|
923
|
+
}
|
|
924
|
+
field.annotations.filter((annotation) => CLASS_VALIDATOR_DECORATORS.includes(annotation.name) || CLASS_TRANSFORMER_DECORATORS.includes(annotation.name)).forEach((annotation) => {
|
|
925
|
+
const params = annotation.params ? `(${annotation.params.join(", ")})` : "";
|
|
926
|
+
decorators.push(`@${annotation.name}${params}`);
|
|
927
|
+
});
|
|
928
|
+
if (this.config.extraValidators.length) {
|
|
929
|
+
field.annotations.forEach((annotation) => {
|
|
930
|
+
const validator = this.config.extraValidators.find((d) => annotation.name === d.alias || (d.destruct ? d.destruct.includes(annotation.name) : false));
|
|
931
|
+
if (validator) decorators.push(`@${annotation.name}(${annotation.params})`);
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (this.config.extraDecorators.length) {
|
|
936
|
+
field.annotations.forEach((annotation) => {
|
|
937
|
+
const decorator = this.config.extraDecorators.find((d) => annotation.name === d.alias || (d.destruct ? d.destruct.includes(annotation.name) : false));
|
|
938
|
+
if (decorator) decorators.push(`@${annotation.name}(${annotation.params})`);
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
const isReadOnly = field.annotations.some((a) => a.name === DTO_READ_ONLY);
|
|
942
|
+
const defaultValue = getDefaultValue(field);
|
|
943
|
+
const hasDefault = defaultValue !== void 0 && this.config.outputType === "class";
|
|
944
|
+
let fieldType = getPropertyType(field);
|
|
945
|
+
if (field.isList) fieldType += "[]";
|
|
946
|
+
if (field.isNullable && !hasDefault) fieldType += " | null";
|
|
947
|
+
return ` ${decorators.length ? decorators.join("\n ") + "\n" : ""} ${isReadOnly ? " readonly" : ""} ${field.name}${!field.isRequired || field.isNullable && !hasDefault || hasDefault ? "?" : this.config.outputType === "class" ? "!" : ""}: ${fieldType}${hasDefault ? ` = ${typeof defaultValue === "string" && !defaultValue.startsWith("new") ? `'${defaultValue}'` : defaultValue}` : ""};
|
|
948
|
+
`;
|
|
949
|
+
}).join("\n");
|
|
950
|
+
const className = `${this.classPrefix}${model.name}${this.classSuffix}`;
|
|
951
|
+
const classDecorators = [];
|
|
952
|
+
if (swaggerDocs) {
|
|
953
|
+
const extraModelsAnnotation = model.annotations.find((a) => a.name === DTO_API_EXTRA_MODELS);
|
|
954
|
+
if (extraModelsAnnotation?.params?.length) classDecorators.push(`@ApiExtraModels(${extraModelsAnnotation.params.join(", ")})`);
|
|
955
|
+
}
|
|
956
|
+
const decoratorsString = classDecorators.length ? `${classDecorators.join("\n")}
|
|
957
|
+
` : "";
|
|
958
|
+
return `${this.getImportsTemplate({ model, classValidator, swaggerDocs, outputPath: args.outputPath })}
|
|
959
|
+
|
|
960
|
+
${decoratorsString}export ${this.config.outputType} ${className.charAt(0).toUpperCase() + className.slice(1).replace(/_(\w)/g, (_, char) => char.toUpperCase())} {
|
|
961
|
+
${fields}}`;
|
|
962
|
+
}
|
|
963
|
+
/** Returns the absolute (or relative-to-`relativeFrom`) output path for `model`. */
|
|
964
|
+
getPath(model, relativeFrom) {
|
|
965
|
+
const modelName = this.getModelName(model.name);
|
|
966
|
+
const fileName = `${this.filePrefix}${modelName}${this.fileSuffix}`;
|
|
967
|
+
let outputPath = "";
|
|
968
|
+
switch (this.config.outputStructure) {
|
|
969
|
+
case "nestjs":
|
|
970
|
+
outputPath = `${this.config.output}/${modelName}/${fileName}.ts`;
|
|
971
|
+
break;
|
|
972
|
+
default:
|
|
973
|
+
case "flat":
|
|
974
|
+
outputPath = `${this.config.output}/${fileName}.ts`;
|
|
975
|
+
break;
|
|
976
|
+
}
|
|
977
|
+
return relativeFrom ? path__default.default.relative(path__default.default.dirname(relativeFrom), outputPath) : outputPath;
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
var EXTRA_IMPORT_KEYS = /* @__PURE__ */ new Set(["extraDecorators", "extraValidators", "extraImports", "extraGenerators"]);
|
|
981
|
+
var NATIVE_EXTS = /* @__PURE__ */ new Set([".js", ".cjs", ".mjs", ".json"]);
|
|
982
|
+
var TS_EXTS = /* @__PURE__ */ new Set([".ts", ".cts", ".mts"]);
|
|
983
|
+
function resolveConfigFilePath(value, options) {
|
|
984
|
+
if (path__default.default.isAbsolute(value)) return fs3__default.default.existsSync(value) ? value : null;
|
|
985
|
+
const candidates = [];
|
|
986
|
+
const schemaPath = options.schemaPath;
|
|
987
|
+
if (schemaPath) {
|
|
988
|
+
let schemaDir = path__default.default.dirname(schemaPath);
|
|
989
|
+
try {
|
|
990
|
+
if (fs3__default.default.statSync(schemaPath).isDirectory()) schemaDir = schemaPath;
|
|
991
|
+
} catch {
|
|
992
|
+
}
|
|
993
|
+
candidates.push(path__default.default.resolve(schemaDir, value));
|
|
994
|
+
}
|
|
995
|
+
candidates.push(path__default.default.resolve(process.cwd(), value));
|
|
996
|
+
for (const candidate of candidates) {
|
|
997
|
+
if (fs3__default.default.existsSync(candidate)) return candidate;
|
|
998
|
+
}
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
async function importWithJiti(filePath) {
|
|
1002
|
+
const { createJiti } = await import('jiti');
|
|
1003
|
+
const jiti = createJiti(filePath, { interopDefault: true, moduleCache: false });
|
|
1004
|
+
return jiti.import(filePath, { default: true });
|
|
1005
|
+
}
|
|
1006
|
+
function pickConfig(mod) {
|
|
1007
|
+
if (!mod || typeof mod !== "object") return null;
|
|
1008
|
+
const candidate = mod.default ?? mod.config ?? mod;
|
|
1009
|
+
if (!candidate || typeof candidate !== "object") return null;
|
|
1010
|
+
return candidate;
|
|
1011
|
+
}
|
|
1012
|
+
async function loadConfigFile(options) {
|
|
1013
|
+
const rawConfig = options.generator.config;
|
|
1014
|
+
const configFile = typeof rawConfig.configFile === "string" ? rawConfig.configFile : void 0;
|
|
1015
|
+
if (!configFile) return null;
|
|
1016
|
+
const resolved = resolveConfigFilePath(configFile, options);
|
|
1017
|
+
if (!resolved) {
|
|
1018
|
+
Utility.error(`configFile not found: ${configFile}`);
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
const ext = path__default.default.extname(resolved).toLowerCase();
|
|
1022
|
+
try {
|
|
1023
|
+
let mod;
|
|
1024
|
+
if (TS_EXTS.has(ext)) {
|
|
1025
|
+
mod = await importWithJiti(resolved);
|
|
1026
|
+
} else if (NATIVE_EXTS.has(ext)) {
|
|
1027
|
+
delete __require.cache[resolved];
|
|
1028
|
+
mod = __require(resolved);
|
|
1029
|
+
} else {
|
|
1030
|
+
Utility.error(`Unsupported configFile extension: ${ext}`);
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
const config = pickConfig(mod);
|
|
1034
|
+
if (!config) {
|
|
1035
|
+
Utility.error(`configFile ${resolved} did not export a configuration object`);
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
Utility.log(`Loaded configFile from ${resolved}`);
|
|
1039
|
+
return { config, dir: path__default.default.dirname(resolved), file: resolved };
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
Utility.error(`Failed to load configFile ${resolved}:`, error);
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
function isImportDescriptor(value) {
|
|
1046
|
+
return typeof value === "object" && value !== null && typeof value.from === "string";
|
|
1047
|
+
}
|
|
1048
|
+
function isRelativePath(value) {
|
|
1049
|
+
return value.startsWith("./") || value.startsWith("../");
|
|
1050
|
+
}
|
|
1051
|
+
function resolveFrom(from2, configFileDir) {
|
|
1052
|
+
if (!configFileDir || !isRelativePath(from2)) return from2;
|
|
1053
|
+
return path__default.default.resolve(configFileDir, from2);
|
|
1054
|
+
}
|
|
1055
|
+
function rewriteRelativeInline(statement, configFileDir) {
|
|
1056
|
+
if (!configFileDir) return statement;
|
|
1057
|
+
return statement.split("|").map((group) => {
|
|
1058
|
+
const idx = group.lastIndexOf(":");
|
|
1059
|
+
if (idx === -1) return group;
|
|
1060
|
+
const fromPath = group.substring(idx + 1).trim();
|
|
1061
|
+
if (!isRelativePath(fromPath)) return group;
|
|
1062
|
+
return `${group.substring(0, idx)}:${path__default.default.resolve(configFileDir, fromPath)}`;
|
|
1063
|
+
}).join("|");
|
|
1064
|
+
}
|
|
1065
|
+
function descriptorToImportStatement(descriptor, configFileDir) {
|
|
1066
|
+
const fromPath = resolveFrom(descriptor.from, configFileDir);
|
|
1067
|
+
const groups = [];
|
|
1068
|
+
if (descriptor.default) groups.push(`default as ${descriptor.default}:${fromPath}`);
|
|
1069
|
+
if (descriptor.namespace) groups.push(`* as ${descriptor.namespace}:${fromPath}`);
|
|
1070
|
+
if (descriptor.names && descriptor.names.length > 0) {
|
|
1071
|
+
const named = descriptor.names.map((entry) => typeof entry === "string" ? entry : entry.as).join(",");
|
|
1072
|
+
groups.push(`${named}:${fromPath}`);
|
|
1073
|
+
}
|
|
1074
|
+
if (groups.length === 0) {
|
|
1075
|
+
Utility.warn(`Empty ImportDescriptor for '${descriptor.from}' \u2014 no names/default/namespace provided.`);
|
|
1076
|
+
return "";
|
|
1077
|
+
}
|
|
1078
|
+
return groups.join("|");
|
|
1079
|
+
}
|
|
1080
|
+
function serializeExtraImport(value, configFileDir) {
|
|
1081
|
+
if (value === void 0 || value === null) return void 0;
|
|
1082
|
+
if (typeof value === "string") return rewriteRelativeInline(value, configFileDir);
|
|
1083
|
+
if (isImportDescriptor(value)) return descriptorToImportStatement(value, configFileDir);
|
|
1084
|
+
if (Array.isArray(value)) {
|
|
1085
|
+
const out = [];
|
|
1086
|
+
for (const entry of value) {
|
|
1087
|
+
if (typeof entry === "string") out.push(rewriteRelativeInline(entry, configFileDir));
|
|
1088
|
+
else if (isImportDescriptor(entry)) out.push(descriptorToImportStatement(entry, configFileDir));
|
|
1089
|
+
}
|
|
1090
|
+
return out;
|
|
1091
|
+
}
|
|
1092
|
+
return void 0;
|
|
1093
|
+
}
|
|
1094
|
+
function applyConfigFile(rawConfig, configFile, configFileDir) {
|
|
1095
|
+
if (!configFile) return rawConfig;
|
|
1096
|
+
const merged = { ...rawConfig };
|
|
1097
|
+
const stringify = (value) => {
|
|
1098
|
+
if (value === void 0 || value === null) return void 0;
|
|
1099
|
+
if (typeof value === "string") return value;
|
|
1100
|
+
if (typeof value === "boolean") return String(value);
|
|
1101
|
+
if (Array.isArray(value)) return value.map(String);
|
|
1102
|
+
return String(value);
|
|
1103
|
+
};
|
|
1104
|
+
for (const [key, value] of Object.entries(configFile)) {
|
|
1105
|
+
let coerced;
|
|
1106
|
+
if (EXTRA_IMPORT_KEYS.has(key)) coerced = serializeExtraImport(value, configFileDir);
|
|
1107
|
+
else if (key === "extraScalars" && value && typeof value === "object") coerced = serializeExtraScalars(value, configFileDir);
|
|
1108
|
+
else coerced = stringify(value);
|
|
1109
|
+
if (coerced !== void 0) merged[key] = coerced;
|
|
1110
|
+
}
|
|
1111
|
+
return merged;
|
|
1112
|
+
}
|
|
1113
|
+
function serializeExtraScalars(value, configFileDir) {
|
|
1114
|
+
const out = {};
|
|
1115
|
+
for (const [scalar, override] of Object.entries(value)) {
|
|
1116
|
+
if (!override || typeof override !== "object") continue;
|
|
1117
|
+
out[scalar] = { ...override, from: override.from ? resolveFrom(override.from, configFileDir) : void 0 };
|
|
1118
|
+
}
|
|
1119
|
+
return JSON.stringify(out);
|
|
1120
|
+
}
|
|
1121
|
+
function fromModule(from2, symbols) {
|
|
1122
|
+
return { from: from2, names: Object.keys(symbols) };
|
|
1123
|
+
}
|
|
1124
|
+
var MODULE_PATH_REGEX = /(?:\bimport|\brequire|\bjitiImport|\b__webpack_require__|\b__nccwpck_require__|\b_?_importDefault)\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/;
|
|
1125
|
+
function extractModulePath(importer) {
|
|
1126
|
+
const source = importer.toString();
|
|
1127
|
+
const match = MODULE_PATH_REGEX.exec(source);
|
|
1128
|
+
if (match && match[1]) return match[1];
|
|
1129
|
+
const fallback = source.match(/['"`]([^'"`]+)['"`]/);
|
|
1130
|
+
if (fallback && fallback[1]) return fallback[1];
|
|
1131
|
+
throw new Error(`Could not extract module path from importer function. Source: ${source.slice(0, 200)}`);
|
|
1132
|
+
}
|
|
1133
|
+
function resolveSource(source) {
|
|
1134
|
+
return typeof source === "function" ? extractModulePath(source) : source;
|
|
1135
|
+
}
|
|
1136
|
+
function toArray(value) {
|
|
1137
|
+
return Array.isArray(value) ? [...value] : [value];
|
|
1138
|
+
}
|
|
1139
|
+
function from(source, names) {
|
|
1140
|
+
return { from: resolveSource(source), names: toArray(names) };
|
|
1141
|
+
}
|
|
1142
|
+
function fromNamespace(source, alias) {
|
|
1143
|
+
return { from: resolveSource(source), namespace: alias };
|
|
1144
|
+
}
|
|
1145
|
+
function fromDefault(source, defaultName, names) {
|
|
1146
|
+
return {
|
|
1147
|
+
from: resolveSource(source),
|
|
1148
|
+
default: defaultName,
|
|
1149
|
+
names: names === void 0 ? void 0 : toArray(names)
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
function namespaceFrom(from2, namespaceAlias) {
|
|
1153
|
+
return { from: from2, namespace: namespaceAlias };
|
|
1154
|
+
}
|
|
1155
|
+
function defaultFrom(from2, defaultName, namedSymbols) {
|
|
1156
|
+
return { from: from2, default: defaultName, names: namedSymbols ? Object.keys(namedSymbols) : void 0 };
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// src/generators/entity-generator.ts
|
|
1160
|
+
var EntityDtoGenerator = class extends BaseGenerator {
|
|
1161
|
+
filePrefix = "";
|
|
1162
|
+
fileSuffix = ".entity";
|
|
1163
|
+
classPrefix = "";
|
|
1164
|
+
classSuffix = "";
|
|
1165
|
+
async generate() {
|
|
1166
|
+
return this.models.map((model) => {
|
|
1167
|
+
const filteredFields = model.fields.filter((field) => !isEntityHidden(field));
|
|
1168
|
+
const processedModel = { ...model, fields: filteredFields };
|
|
1169
|
+
const outputPath = this.getPath(model);
|
|
1170
|
+
return {
|
|
1171
|
+
path: outputPath,
|
|
1172
|
+
content: this.getTemplate({ model: processedModel, classValidator: false, outputPath })
|
|
1173
|
+
};
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
// src/generators/create-dto-generator.ts
|
|
1179
|
+
var CreateDtoGenerator = class extends BaseGenerator {
|
|
1180
|
+
filePrefix = "create-";
|
|
1181
|
+
fileSuffix = ".dto";
|
|
1182
|
+
classPrefix = "Create";
|
|
1183
|
+
classSuffix = "Dto";
|
|
1184
|
+
async generate() {
|
|
1185
|
+
return this.models.map((model) => {
|
|
1186
|
+
const filteredFields = model.fields.filter((field) => {
|
|
1187
|
+
if (field.annotations.some((a) => a.name === DTO_HIDDEN)) return false;
|
|
1188
|
+
if (field.annotations.some((a) => a.name === DTO_CREATE_HIDDEN)) return false;
|
|
1189
|
+
if (field.annotations.some((a) => a.name === DTO_CREATED_AT)) return false;
|
|
1190
|
+
const isRelationField = model.fields.some((f) => f.relationFromFields?.includes?.(field.name));
|
|
1191
|
+
if (isRelationField) return false;
|
|
1192
|
+
if (field.isId) return false;
|
|
1193
|
+
if (field.isUpdatedAt) return false;
|
|
1194
|
+
return true;
|
|
1195
|
+
});
|
|
1196
|
+
const processedFields = filteredFields.map((f) => {
|
|
1197
|
+
const field = { ...f, annotations: [...f.annotations] };
|
|
1198
|
+
if (field.annotations.some((a) => a.name === DTO_CREATE_OPTIONAL)) {
|
|
1199
|
+
field.isRequired = false;
|
|
1200
|
+
field.isNullable = true;
|
|
1201
|
+
}
|
|
1202
|
+
if (field.annotations.some((a) => a.name === DTO_CREATE_REQUIRED)) field.isRequired = true;
|
|
1203
|
+
const validateIfAnnotation = field.annotations.find((a) => a.name === DTO_CREATE_VALIDATE_IF);
|
|
1204
|
+
if (validateIfAnnotation && validateIfAnnotation.params && validateIfAnnotation.params.length > 0) {
|
|
1205
|
+
const validateIfParams = validateIfAnnotation.params.join(", ");
|
|
1206
|
+
field.annotations.push({ name: "ValidateIf", params: [validateIfParams] });
|
|
1207
|
+
}
|
|
1208
|
+
return field;
|
|
1209
|
+
});
|
|
1210
|
+
const processedModel = { ...model, fields: processedFields };
|
|
1211
|
+
const outputPath = this.getPath(model);
|
|
1212
|
+
return {
|
|
1213
|
+
path: outputPath,
|
|
1214
|
+
content: this.getTemplate({ model: processedModel, outputPath })
|
|
1215
|
+
};
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// src/generators/update-dto-generator.ts
|
|
1221
|
+
var UpdateDtoGenerator = class extends BaseGenerator {
|
|
1222
|
+
filePrefix = "update-";
|
|
1223
|
+
fileSuffix = ".dto";
|
|
1224
|
+
classPrefix = "Update";
|
|
1225
|
+
classSuffix = "Dto";
|
|
1226
|
+
async generate() {
|
|
1227
|
+
return this.models.map((model) => {
|
|
1228
|
+
const filteredFields = model.fields.filter((field) => {
|
|
1229
|
+
if (field.annotations.some((a) => a.name === DTO_HIDDEN)) return false;
|
|
1230
|
+
if (field.annotations.some((a) => a.name === DTO_UPDATE_HIDDEN)) return false;
|
|
1231
|
+
if (field.annotations.some((a) => a.name === DTO_CREATED_AT)) return false;
|
|
1232
|
+
const isRelationField = model.fields.some((f) => f.relationFromFields?.includes?.(field.name));
|
|
1233
|
+
if (isRelationField) return false;
|
|
1234
|
+
if (field.isId) return true;
|
|
1235
|
+
if (field.isUpdatedAt) return false;
|
|
1236
|
+
return true;
|
|
1237
|
+
});
|
|
1238
|
+
const processedFields = filteredFields.map((f) => {
|
|
1239
|
+
const field = { ...f, annotations: [...f.annotations] };
|
|
1240
|
+
field.isRequired = field.isId ? true : false;
|
|
1241
|
+
if (field.annotations.some((a) => a.name === DTO_UPDATE_OPTIONAL)) {
|
|
1242
|
+
field.isRequired = false;
|
|
1243
|
+
field.isNullable = true;
|
|
1244
|
+
}
|
|
1245
|
+
if (field.annotations.some((a) => a.name === DTO_UPDATE_REQUIRED)) field.isRequired = true;
|
|
1246
|
+
const validateIfAnnotation = field.annotations.find((a) => a.name === DTO_UPDATE_VALIDATE_IF);
|
|
1247
|
+
if (validateIfAnnotation && validateIfAnnotation.params && validateIfAnnotation.params.length > 0) {
|
|
1248
|
+
const validateIfParams = validateIfAnnotation.params.join(", ");
|
|
1249
|
+
field.annotations.push({ name: "ValidateIf", params: [validateIfParams] });
|
|
1250
|
+
}
|
|
1251
|
+
return field;
|
|
1252
|
+
});
|
|
1253
|
+
const processedModel = { ...model, fields: processedFields };
|
|
1254
|
+
const outputPath = this.getPath(model);
|
|
1255
|
+
return {
|
|
1256
|
+
path: outputPath,
|
|
1257
|
+
content: this.getTemplate({ model: processedModel, outputPath })
|
|
1258
|
+
};
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
// src/generators/manifest-generator.ts
|
|
1264
|
+
var ManifestGenerator = class extends BaseGenerator {
|
|
1265
|
+
filePrefix = "";
|
|
1266
|
+
fileSuffix = "";
|
|
1267
|
+
classPrefix = "";
|
|
1268
|
+
classSuffix = "";
|
|
1269
|
+
async generate() {
|
|
1270
|
+
const map = {};
|
|
1271
|
+
for (const model of this.models) {
|
|
1272
|
+
const relations = {};
|
|
1273
|
+
for (const f of model.fields) {
|
|
1274
|
+
if (f.kind !== "object") continue;
|
|
1275
|
+
relations[f.name] = { type: f.type, isList: Boolean(f.isList) };
|
|
1276
|
+
}
|
|
1277
|
+
map[model.name] = {
|
|
1278
|
+
primaryKey: model.fields.find((f) => f.isId)?.name || "id",
|
|
1279
|
+
entityFields: model.fields.filter((f) => !isEntityHidden(f)).map((f) => f.name),
|
|
1280
|
+
relations
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
const manifestContent = `import type { Prisma } from '@prisma/client';
|
|
1284
|
+
|
|
1285
|
+
export interface RelationDescriptor {
|
|
1286
|
+
type: string;
|
|
1287
|
+
isList: boolean;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
export interface ModelManifest {
|
|
1291
|
+
primaryKey: string;
|
|
1292
|
+
entityFields: string[];
|
|
1293
|
+
relations: Record<string, RelationDescriptor>;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
export const PrismaManifest: Record<Prisma.ModelName, ModelManifest> = ${JSON.stringify(map, null, 4)};
|
|
1297
|
+
`;
|
|
1298
|
+
const modelNames = this.models.map((m) => m.name);
|
|
1299
|
+
const isFlat = this.config.outputStructure === "flat";
|
|
1300
|
+
const importLines = modelNames.map((name) => {
|
|
1301
|
+
const fileName = this.getModelName(name);
|
|
1302
|
+
const importPath = isFlat ? `./${fileName}.entity` : `./${fileName}/${fileName}.entity`;
|
|
1303
|
+
return `import type { ${name} as ${name}Entity } from '${importPath}';`;
|
|
1304
|
+
}).join("\n");
|
|
1305
|
+
const mapLines = modelNames.map((name) => ` ${name}: ${name}Entity;`).join("\n");
|
|
1306
|
+
const entityMapContent = `${importLines}
|
|
1307
|
+
|
|
1308
|
+
export interface ModelEntityMap {
|
|
1309
|
+
${mapLines}
|
|
1310
|
+
}
|
|
1311
|
+
`;
|
|
1312
|
+
return [
|
|
1313
|
+
{ path: `${this.config.output}/manifest.ts`, content: manifestContent },
|
|
1314
|
+
{ path: `${this.config.output}/model-entity-map.ts`, content: entityMapContent }
|
|
1315
|
+
];
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
// src/generator.ts
|
|
1320
|
+
var BuiltInGenerators = { CreateDtoGenerator, EntityGenerator: EntityDtoGenerator, UpdateDtoGenerator };
|
|
1321
|
+
async function resolvePrettierConfig(enabled) {
|
|
1322
|
+
if (!enabled) return null;
|
|
1323
|
+
try {
|
|
1324
|
+
const prettierConfigFile = await prettier__default.default.resolveConfigFile();
|
|
1325
|
+
let config = {};
|
|
1326
|
+
if (!prettierConfigFile) {
|
|
1327
|
+
Utility.log("Stylizing output DTOs with the default Prettier config.");
|
|
1328
|
+
} else {
|
|
1329
|
+
Utility.log(`Stylizing output DTOs with found Prettier config. (${prettierConfigFile})`);
|
|
1330
|
+
const resolved = await prettier__default.default.resolveConfig(prettierConfigFile, { config: prettierConfigFile });
|
|
1331
|
+
if (resolved) config = resolved;
|
|
1332
|
+
}
|
|
1333
|
+
config.parser = "typescript";
|
|
1334
|
+
return config;
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
Utility.warn("Failed to resolve Prettier config:", error);
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
var TS_PLUGIN_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".cts", ".mts", ".tsx"]);
|
|
1341
|
+
async function importPluginModule(absolutePath) {
|
|
1342
|
+
const ext = path__default.default.extname(absolutePath).toLowerCase();
|
|
1343
|
+
if (TS_PLUGIN_EXTENSIONS.has(ext)) {
|
|
1344
|
+
const { createJiti } = await import('jiti');
|
|
1345
|
+
const jiti = createJiti(absolutePath, { interopDefault: true, moduleCache: false });
|
|
1346
|
+
const mod = await jiti.import(absolutePath);
|
|
1347
|
+
if (mod && typeof mod === "object" && "default" in mod && mod.default && typeof mod.default === "object") {
|
|
1348
|
+
return { ...mod.default, ...mod };
|
|
1349
|
+
}
|
|
1350
|
+
return mod;
|
|
1351
|
+
}
|
|
1352
|
+
return __require(absolutePath);
|
|
1353
|
+
}
|
|
1354
|
+
async function loadExtraGenerators(options) {
|
|
1355
|
+
const extra = {};
|
|
1356
|
+
const raw = options.generator.config.extraGenerators;
|
|
1357
|
+
if (!raw) return extra;
|
|
1358
|
+
try {
|
|
1359
|
+
const imports = Utility.parseImports(raw);
|
|
1360
|
+
for (const importInfo of imports) {
|
|
1361
|
+
try {
|
|
1362
|
+
const modulePath = path__default.default.isAbsolute(importInfo.from) ? importInfo.from : path__default.default.resolve(process.cwd(), importInfo.from);
|
|
1363
|
+
const importedModule = await importPluginModule(modulePath);
|
|
1364
|
+
if (importInfo.destruct && importInfo.destruct.length > 0) {
|
|
1365
|
+
for (const generatorName of importInfo.destruct) {
|
|
1366
|
+
if (importedModule[generatorName]) {
|
|
1367
|
+
extra[generatorName] = importedModule[generatorName];
|
|
1368
|
+
Utility.log(`Loaded generator ${generatorName} from ${importInfo.from}`);
|
|
1369
|
+
} else {
|
|
1370
|
+
Utility.warn(`Generator ${generatorName} not found in ${importInfo.from}`);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
} else if (importInfo.alias) {
|
|
1374
|
+
extra[importInfo.alias] = importedModule;
|
|
1375
|
+
Utility.log(`Loaded generator ${importInfo.alias} from ${importInfo.from}`);
|
|
1376
|
+
} else {
|
|
1377
|
+
Object.entries(importedModule).forEach(([name, exported]) => {
|
|
1378
|
+
extra[name] = exported;
|
|
1379
|
+
Utility.log(`Loaded generator ${name} from ${importInfo.from}`);
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
Utility.error(`Failed to load generators from ${importInfo.from}:`, error);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
Utility.error(`Failed to parse extraGenerators:`, error);
|
|
1388
|
+
}
|
|
1389
|
+
return extra;
|
|
1390
|
+
}
|
|
1391
|
+
function instantiateGenerators(registry, options) {
|
|
1392
|
+
return Object.entries(registry).map(([name, Ctor]) => {
|
|
1393
|
+
try {
|
|
1394
|
+
if (typeof Ctor !== "function") {
|
|
1395
|
+
Utility.warn(`Skipping ${name}: not a constructor function`);
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
const instance = new Ctor(options);
|
|
1399
|
+
if (typeof instance.generate === "function" && typeof instance.getPath === "function") return instance;
|
|
1400
|
+
Utility.warn(`Skipping ${name}: not a BaseGenerator-shaped object (missing generate/getPath)`);
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
Utility.error(`Error initializing generator ${name}:`, error);
|
|
1403
|
+
}
|
|
1404
|
+
return null;
|
|
1405
|
+
}).filter((instance) => instance !== null);
|
|
1406
|
+
}
|
|
1407
|
+
async function maybeFormat(content, config, label) {
|
|
1408
|
+
if (!config) return content;
|
|
1409
|
+
try {
|
|
1410
|
+
return await prettier__default.default.format(content, config);
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
Utility.warn(`Failed to format ${label} with Prettier:`, error);
|
|
1413
|
+
return content;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
async function generate(options) {
|
|
1417
|
+
const outputDir = options.generator.output?.value;
|
|
1418
|
+
if (!outputDir) {
|
|
1419
|
+
Utility.error('Output directory is not set. Add `output = "..."` to the generator block in schema.prisma.');
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
const externalConfig = await loadConfigFile(options);
|
|
1423
|
+
const mergedConfig = applyConfigFile(options.generator.config, externalConfig?.config ?? null, externalConfig?.dir);
|
|
1424
|
+
options.generator.config = mergedConfig;
|
|
1425
|
+
const rawConfig = mergedConfig;
|
|
1426
|
+
const extraGenerators = await loadExtraGenerators(options);
|
|
1427
|
+
const allGenerators = { ...BuiltInGenerators, ...extraGenerators };
|
|
1428
|
+
const generators = instantiateGenerators(allGenerators, options);
|
|
1429
|
+
const sharedModels = generators[0]?.models ?? [];
|
|
1430
|
+
for (const generator of generators) generator.models = sharedModels;
|
|
1431
|
+
const applyPrettier = Utility.parseBoolean(typeof rawConfig.prettier === "string" ? rawConfig.prettier : void 0);
|
|
1432
|
+
const prettierConfig = await resolvePrettierConfig(applyPrettier);
|
|
1433
|
+
const outputStructure = typeof rawConfig.outputStructure === "string" ? rawConfig.outputStructure : "nestjs";
|
|
1434
|
+
for (const generator of generators) {
|
|
1435
|
+
try {
|
|
1436
|
+
await generator.beforeAll(sharedModels);
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
Utility.error(`Error in ${generator.constructor.name}#beforeAll:`, error);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
const exports = {};
|
|
1442
|
+
const emittedFiles = [];
|
|
1443
|
+
const persistFile = async (file, label) => {
|
|
1444
|
+
const content = await maybeFormat(file.content, prettierConfig, label);
|
|
1445
|
+
fs3__default.default.mkdirSync(path__default.default.dirname(file.path), { recursive: true });
|
|
1446
|
+
fs3__default.default.writeFileSync(file.path, content);
|
|
1447
|
+
const exportPath = outputStructure === "flat" ? file.path : path__default.default.dirname(file.path);
|
|
1448
|
+
const exportLine = `export * from './${path__default.default.basename(file.path).replace(".ts", "")}';`;
|
|
1449
|
+
exports[exportPath] = exports[exportPath] ? `${exports[exportPath]}
|
|
1450
|
+
${exportLine}` : exportLine;
|
|
1451
|
+
emittedFiles.push(file);
|
|
1452
|
+
};
|
|
1453
|
+
for (const generator of generators) {
|
|
1454
|
+
try {
|
|
1455
|
+
Utility.log(`Executing generator: ${generator.constructor.name}`);
|
|
1456
|
+
const files = await generator.generate();
|
|
1457
|
+
for (const file of files) await persistFile(file, path__default.default.basename(file.path));
|
|
1458
|
+
} catch (error) {
|
|
1459
|
+
Utility.error(`Error while executing ${generator.constructor.name}:`, error);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
for (const generator of generators) {
|
|
1463
|
+
try {
|
|
1464
|
+
const extra = await generator.afterAll(emittedFiles);
|
|
1465
|
+
if (Array.isArray(extra)) for (const file of extra) await persistFile(file, path__default.default.basename(file.path));
|
|
1466
|
+
} catch (error) {
|
|
1467
|
+
Utility.error(`Error in ${generator.constructor.name}#afterAll:`, error);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (Utility.parseBoolean(typeof rawConfig.reExport === "string" ? rawConfig.reExport : void 0)) {
|
|
1471
|
+
if (outputStructure !== "flat") {
|
|
1472
|
+
for (const [exportPath, content] of Object.entries(exports)) {
|
|
1473
|
+
const indexContent = await maybeFormat(content, prettierConfig, "index file");
|
|
1474
|
+
fs3__default.default.writeFileSync(`${exportPath}/index.ts`, indexContent);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
const mainIndexContent = await maybeFormat(
|
|
1478
|
+
Object.keys(exports).map((exportPath) => `export * from './${path__default.default.basename(exportPath).replace(".ts", "")}';`).join("\n"),
|
|
1479
|
+
prettierConfig,
|
|
1480
|
+
"main index file"
|
|
1481
|
+
);
|
|
1482
|
+
fs3__default.default.writeFileSync(`${outputDir}/index.ts`, mainIndexContent);
|
|
1483
|
+
}
|
|
1484
|
+
if (Utility.parseBoolean(typeof rawConfig.emitManifest === "string" ? rawConfig.emitManifest : void 0)) {
|
|
1485
|
+
const manifestFiles = await new ManifestGenerator(options).generate();
|
|
1486
|
+
for (const file of manifestFiles) {
|
|
1487
|
+
const content = await maybeFormat(file.content, prettierConfig, path__default.default.basename(file.path));
|
|
1488
|
+
fs3__default.default.writeFileSync(file.path, content);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
exports.ANNOTATION_NAME_REGEX = ANNOTATION_NAME_REGEX;
|
|
1494
|
+
exports.ANNOTATION_PARAMS_REGEX = ANNOTATION_PARAMS_REGEX;
|
|
1495
|
+
exports.ANNOTATION_PARAMS_SPLIT_REGEX = ANNOTATION_PARAMS_SPLIT_REGEX;
|
|
1496
|
+
exports.BaseGenerator = BaseGenerator;
|
|
1497
|
+
exports.CLASS_TRANSFORMER_DECORATORS = CLASS_TRANSFORMER_DECORATORS;
|
|
1498
|
+
exports.CLASS_VALIDATOR_DECORATORS = CLASS_VALIDATOR_DECORATORS;
|
|
1499
|
+
exports.CreateDtoGenerator = CreateDtoGenerator;
|
|
1500
|
+
exports.DTO_API_EXTRA_MODELS = DTO_API_EXTRA_MODELS;
|
|
1501
|
+
exports.DTO_API_HIDDEN = DTO_API_HIDDEN;
|
|
1502
|
+
exports.DTO_CREATED_AT = DTO_CREATED_AT;
|
|
1503
|
+
exports.DTO_CREATE_HIDDEN = DTO_CREATE_HIDDEN;
|
|
1504
|
+
exports.DTO_CREATE_OPTIONAL = DTO_CREATE_OPTIONAL;
|
|
1505
|
+
exports.DTO_CREATE_REQUIRED = DTO_CREATE_REQUIRED;
|
|
1506
|
+
exports.DTO_CREATE_VALIDATE_IF = DTO_CREATE_VALIDATE_IF;
|
|
1507
|
+
exports.DTO_ENTITY_HIDDEN = DTO_ENTITY_HIDDEN;
|
|
1508
|
+
exports.DTO_HIDDEN = DTO_HIDDEN;
|
|
1509
|
+
exports.DTO_IGNORE_MODEL = DTO_IGNORE_MODEL;
|
|
1510
|
+
exports.DTO_OVERRIDE_API_PROPERTY_TYPE = DTO_OVERRIDE_API_PROPERTY_TYPE;
|
|
1511
|
+
exports.DTO_OVERRIDE_TYPE = DTO_OVERRIDE_TYPE;
|
|
1512
|
+
exports.DTO_READ_ONLY = DTO_READ_ONLY;
|
|
1513
|
+
exports.DTO_UPDATE_HIDDEN = DTO_UPDATE_HIDDEN;
|
|
1514
|
+
exports.DTO_UPDATE_OPTIONAL = DTO_UPDATE_OPTIONAL;
|
|
1515
|
+
exports.DTO_UPDATE_REQUIRED = DTO_UPDATE_REQUIRED;
|
|
1516
|
+
exports.DTO_UPDATE_VALIDATE_IF = DTO_UPDATE_VALIDATE_IF;
|
|
1517
|
+
exports.ENTITY_HIDDEN_ANNOTATIONS = ENTITY_HIDDEN_ANNOTATIONS;
|
|
1518
|
+
exports.EntityGenerator = EntityDtoGenerator;
|
|
1519
|
+
exports.ManifestGenerator = ManifestGenerator;
|
|
1520
|
+
exports.PRISMA_SCALAR = PRISMA_SCALAR;
|
|
1521
|
+
exports.PRISMA_SCALAR_FORMAT = PRISMA_SCALAR_FORMAT;
|
|
1522
|
+
exports.PRISMA_SCALAR_TYPE = PRISMA_SCALAR_TYPE;
|
|
1523
|
+
exports.SWAGGER_API_PROPERTY_DECORATORS = SWAGGER_API_PROPERTY_DECORATORS;
|
|
1524
|
+
exports.SWAGGER_DECORATORS = SWAGGER_DECORATORS;
|
|
1525
|
+
exports.UpdateDtoGenerator = UpdateDtoGenerator;
|
|
1526
|
+
exports.Utility = Utility;
|
|
1527
|
+
exports.applyConfigFile = applyConfigFile;
|
|
1528
|
+
exports.defaultFrom = defaultFrom;
|
|
1529
|
+
exports.descriptorToImportStatement = descriptorToImportStatement;
|
|
1530
|
+
exports.from = from;
|
|
1531
|
+
exports.fromDefault = fromDefault;
|
|
1532
|
+
exports.fromModule = fromModule;
|
|
1533
|
+
exports.fromNamespace = fromNamespace;
|
|
1534
|
+
exports.generate = generate;
|
|
1535
|
+
exports.isEntityHidden = isEntityHidden;
|
|
1536
|
+
exports.loadConfigFile = loadConfigFile;
|
|
1537
|
+
exports.namespaceFrom = namespaceFrom;
|
|
1538
|
+
//# sourceMappingURL=index.cjs.map
|
|
1539
|
+
//# sourceMappingURL=index.cjs.map
|