typeorm-dto-generator 1.0.1 → 1.0.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/dist/index.d.mts +156 -0
- package/dist/index.d.ts +133 -52
- package/dist/index.js +833 -768
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +803 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +27 -9
- package/dist/.tsbuildinfo +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types.d.ts +0 -43
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/dist/index.js
CHANGED
|
@@ -1,772 +1,837 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
TORMDTOGenerator: () => TORMDTOGenerator
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
var import_fs = __toESM(require("fs"));
|
|
36
|
+
var import_path = __toESM(require("path"));
|
|
37
|
+
var import_ts_morph = require("ts-morph");
|
|
38
|
+
var TORMDTOGenerator = class {
|
|
39
|
+
config;
|
|
40
|
+
project;
|
|
41
|
+
sourceInfoByClassName = /* @__PURE__ */ new Map();
|
|
42
|
+
constructor(config) {
|
|
43
|
+
this.config = this.createConfig(config);
|
|
44
|
+
this.project = new import_ts_morph.Project({
|
|
45
|
+
skipAddingFilesFromTsConfig: true
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async run() {
|
|
49
|
+
this.loadEntitySourceInfo();
|
|
50
|
+
const metas = await this.createEntityMetas();
|
|
51
|
+
import_fs.default.rmSync(this.config.dtoOutputDir, {
|
|
52
|
+
recursive: true,
|
|
53
|
+
force: true
|
|
54
|
+
});
|
|
55
|
+
import_fs.default.mkdirSync(this.config.dtoOutputDir, {
|
|
56
|
+
recursive: true
|
|
57
|
+
});
|
|
58
|
+
this.ensureCommonTypesFile(metas);
|
|
59
|
+
this.validateCommonTypeCoverage(metas);
|
|
60
|
+
for (const meta of metas) {
|
|
61
|
+
this.writeFile(
|
|
62
|
+
import_path.default.join(this.config.dtoOutputDir, meta.dtoFileName),
|
|
63
|
+
this.createDTOContent(meta, metas)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
this.writeFile(
|
|
67
|
+
import_path.default.join(this.config.dtoOutputDir, `index.ts`),
|
|
68
|
+
`${this.createDTOIndexContent(metas)}
|
|
69
|
+
`
|
|
70
|
+
);
|
|
71
|
+
this.writeFile(
|
|
72
|
+
this.config.mapperOutputFile,
|
|
73
|
+
this.createGeneratedMapperContent(metas)
|
|
74
|
+
);
|
|
75
|
+
this.log(`Generated DTO files: ${metas.length}`);
|
|
76
|
+
this.log(`Generated DTO directory: ${this.config.dtoOutputDir}`);
|
|
77
|
+
this.log(`Generated mapper file: ${this.config.mapperOutputFile}`);
|
|
78
|
+
return {
|
|
79
|
+
metas,
|
|
80
|
+
dtoOutputDir: this.config.dtoOutputDir,
|
|
81
|
+
mapperOutputFile: this.config.mapperOutputFile
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
log(message) {
|
|
85
|
+
if (this.config.debug)
|
|
86
|
+
console.log(message);
|
|
87
|
+
}
|
|
88
|
+
createConfig(config) {
|
|
89
|
+
if (!config.datasource)
|
|
90
|
+
throw new Error("datasource is required");
|
|
91
|
+
const root = process.cwd();
|
|
92
|
+
const dtoOutputDir = config.dtoOutputDir || import_path.default.join(root, "dto");
|
|
93
|
+
const mapperOutputFile = config.mapperOutputFile || import_path.default.join(root, "dto.mapper.ts");
|
|
94
|
+
const sharedDTOImportPath = config.sharedDTOImportPath || import_path.default.relative(import_path.default.parse(mapperOutputFile).dir, dtoOutputDir).replaceAll("\\", "/");
|
|
95
|
+
return {
|
|
96
|
+
debug: config.debug || false,
|
|
97
|
+
dtoOutputDir,
|
|
98
|
+
mapperOutputFile,
|
|
99
|
+
sharedDTOImportPath: sharedDTOImportPath.startsWith(".") ? sharedDTOImportPath : `./${sharedDTOImportPath}`,
|
|
100
|
+
includeExtensionOnImports: config.includeExtensionOnImports ?? true,
|
|
101
|
+
excludePropertyNames: config.excludePropertyNames ?? [
|
|
102
|
+
"password",
|
|
103
|
+
"passwordHash",
|
|
104
|
+
"secret",
|
|
105
|
+
"refreshToken",
|
|
106
|
+
"accessToken",
|
|
107
|
+
"keyHash",
|
|
108
|
+
"token",
|
|
109
|
+
"otp",
|
|
110
|
+
"salt"
|
|
111
|
+
],
|
|
112
|
+
customScalarTypeMap: config.customScalarTypeMap ?? {
|
|
113
|
+
BigInt: "string",
|
|
114
|
+
bigint: "string",
|
|
115
|
+
Buffer: "string",
|
|
116
|
+
Decimal: "string"
|
|
117
|
+
},
|
|
118
|
+
...config
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
writeFile(filePath, content) {
|
|
122
|
+
import_fs.default.mkdirSync(import_path.default.dirname(filePath), {
|
|
123
|
+
recursive: true
|
|
124
|
+
});
|
|
125
|
+
import_fs.default.writeFileSync(filePath, content);
|
|
126
|
+
}
|
|
127
|
+
toKebabCase(value) {
|
|
128
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
unique(items) {
|
|
131
|
+
return [...new Set(items)];
|
|
132
|
+
}
|
|
133
|
+
createRelativeJSImportPath(fromFile, targetFile) {
|
|
134
|
+
const relativePath = import_path.default.relative(import_path.default.dirname(fromFile), targetFile).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts)$/, this.config.includeExtensionOnImports ? ".ts" : "");
|
|
135
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
136
|
+
}
|
|
137
|
+
getCommonTypesFilePath() {
|
|
138
|
+
return import_path.default.join(this.config.dtoOutputDir, "common.dto.ts");
|
|
139
|
+
}
|
|
140
|
+
getCommonTypesImportSpecifier(dtoFilePath) {
|
|
141
|
+
return this.createRelativeJSImportPath(dtoFilePath, this.getCommonTypesFilePath());
|
|
142
|
+
}
|
|
143
|
+
getDatasourceEntities() {
|
|
144
|
+
const entities = this.config.datasource.options.entities ?? [];
|
|
145
|
+
if (Array.isArray(entities)) {
|
|
146
|
+
return entities;
|
|
147
|
+
}
|
|
148
|
+
return Object.values(entities);
|
|
149
|
+
}
|
|
150
|
+
normalizeEntityPattern(pattern) {
|
|
151
|
+
return pattern.replace(/\\/g, "/");
|
|
152
|
+
}
|
|
153
|
+
isSourceFilePath(filePath) {
|
|
154
|
+
return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/.test(filePath);
|
|
155
|
+
}
|
|
156
|
+
isEntitySourceFilePath(filePath) {
|
|
157
|
+
return /\.entity\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/.test(filePath);
|
|
158
|
+
}
|
|
159
|
+
normalizeAbsolutePath(filePath) {
|
|
160
|
+
return import_path.default.resolve(filePath).replace(/\\/g, "/").toLowerCase();
|
|
161
|
+
}
|
|
162
|
+
isIgnoredSourcePath(filePath) {
|
|
163
|
+
const normalized = this.normalizeAbsolutePath(filePath);
|
|
164
|
+
const dtoOutputDir = this.normalizeAbsolutePath(this.config.dtoOutputDir);
|
|
165
|
+
const mapperOutputFile = this.normalizeAbsolutePath(this.config.mapperOutputFile);
|
|
166
|
+
const ignoredSegments = [
|
|
167
|
+
"/node_modules/",
|
|
168
|
+
"/.git/",
|
|
169
|
+
"/.next/",
|
|
170
|
+
"/dist/",
|
|
171
|
+
"/build/",
|
|
172
|
+
"/coverage/"
|
|
173
|
+
];
|
|
174
|
+
if (ignoredSegments.some((segment) => normalized.includes(segment))) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (normalized === dtoOutputDir || normalized.startsWith(`${dtoOutputDir}/`)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return normalized === mapperOutputFile;
|
|
181
|
+
}
|
|
182
|
+
discoverProjectSourcePaths() {
|
|
183
|
+
const result = [];
|
|
184
|
+
const walk = (directory) => {
|
|
185
|
+
if (!import_fs.default.existsSync(directory)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const entry of import_fs.default.readdirSync(directory, { withFileTypes: true })) {
|
|
189
|
+
const fullPath = import_path.default.join(directory, entry.name);
|
|
190
|
+
const normalized = fullPath.replace(/\\/g, "/");
|
|
191
|
+
if (this.isIgnoredSourcePath(normalized)) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (entry.isDirectory()) {
|
|
195
|
+
walk(fullPath);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (entry.isFile() && this.isSourceFilePath(fullPath)) {
|
|
199
|
+
result.push(fullPath);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
walk(process.cwd());
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
addEntityOptionSourceFiles() {
|
|
207
|
+
const sourceFiles = [];
|
|
208
|
+
for (const entity of this.getDatasourceEntities()) {
|
|
209
|
+
if (typeof entity !== "string") {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
sourceFiles.push(...this.project.addSourceFilesAtPaths(this.normalizeEntityPattern(entity)));
|
|
213
|
+
}
|
|
214
|
+
return sourceFiles;
|
|
215
|
+
}
|
|
216
|
+
addDiscoveredSourceFiles() {
|
|
217
|
+
const paths = this.discoverProjectSourcePaths();
|
|
218
|
+
return paths.map((filePath) => {
|
|
219
|
+
return this.project.addSourceFileAtPathIfExists(filePath);
|
|
220
|
+
}).filter((sourceFile) => {
|
|
221
|
+
return Boolean(sourceFile);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
loadEntitySourceInfo() {
|
|
225
|
+
const fromEntityOptions = this.addEntityOptionSourceFiles();
|
|
226
|
+
this.addDiscoveredSourceFiles();
|
|
227
|
+
const candidateFiles = fromEntityOptions.length ? fromEntityOptions : this.project.getSourceFiles();
|
|
228
|
+
for (const sourceFile of candidateFiles) {
|
|
229
|
+
for (const classDeclaration of sourceFile.getClasses()) {
|
|
230
|
+
if (!this.isEntityClass(classDeclaration)) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
this.sourceInfoByClassName.set(classDeclaration.getNameOrThrow(), {
|
|
234
|
+
filePath: sourceFile.getFilePath(),
|
|
235
|
+
enumTypeByProperty: this.collectEnumTypesByProperty(classDeclaration),
|
|
236
|
+
typeByProperty: this.collectTypesByProperty(classDeclaration)
|
|
11
237
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
isEntityClass(classDeclaration) {
|
|
242
|
+
return classDeclaration.getDecorators().some((decorator) => {
|
|
243
|
+
return decorator.getName() === "Entity";
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
getDecoratorObjectLiteral(property) {
|
|
247
|
+
for (const decorator of property.getDecorators()) {
|
|
248
|
+
const objectLiteral = decorator.getArguments().find((argument) => {
|
|
249
|
+
return import_ts_morph.Node.isObjectLiteralExpression(argument);
|
|
250
|
+
});
|
|
251
|
+
if (objectLiteral && import_ts_morph.Node.isObjectLiteralExpression(objectLiteral)) {
|
|
252
|
+
return objectLiteral;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
getColumnEnumInitializer(property) {
|
|
258
|
+
var _a;
|
|
259
|
+
const objectLiteral = this.getDecoratorObjectLiteral(property);
|
|
260
|
+
const enumProperty = objectLiteral == null ? void 0 : objectLiteral.getProperty("enum");
|
|
261
|
+
if (!enumProperty || !import_ts_morph.Node.isPropertyAssignment(enumProperty)) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
return ((_a = enumProperty.getInitializer()) == null ? void 0 : _a.getText()) ?? null;
|
|
265
|
+
}
|
|
266
|
+
getEnumTypeNameFromProperty(property) {
|
|
267
|
+
var _a;
|
|
268
|
+
const typeText = (_a = property.getTypeNode()) == null ? void 0 : _a.getText();
|
|
269
|
+
if (typeText && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(typeText)) {
|
|
270
|
+
return typeText;
|
|
271
|
+
}
|
|
272
|
+
const enumInitializer = this.getColumnEnumInitializer(property);
|
|
273
|
+
if (enumInitializer && /^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(enumInitializer)) {
|
|
274
|
+
return enumInitializer.split(".").pop() ?? enumInitializer;
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
collectEnumTypesByProperty(classDeclaration) {
|
|
279
|
+
const enumTypeByProperty = /* @__PURE__ */ new Map();
|
|
280
|
+
for (const property of this.collectProperties(classDeclaration)) {
|
|
281
|
+
const enumTypeName = this.getEnumTypeNameFromProperty(property);
|
|
282
|
+
if (enumTypeName) {
|
|
283
|
+
enumTypeByProperty.set(property.getName(), enumTypeName);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return enumTypeByProperty;
|
|
287
|
+
}
|
|
288
|
+
cleanTypeText(value) {
|
|
289
|
+
return value.replace(/import\(".*?"\)\./g, "").replace(/\s+/g, " ").trim();
|
|
290
|
+
}
|
|
291
|
+
collectTypesByProperty(classDeclaration) {
|
|
292
|
+
var _a;
|
|
293
|
+
const typeByProperty = /* @__PURE__ */ new Map();
|
|
294
|
+
for (const property of this.collectProperties(classDeclaration)) {
|
|
295
|
+
const typeText = (_a = property.getTypeNode()) == null ? void 0 : _a.getText();
|
|
296
|
+
if (typeText) {
|
|
297
|
+
typeByProperty.set(property.getName(), this.cleanTypeText(typeText));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return typeByProperty;
|
|
301
|
+
}
|
|
302
|
+
collectProperties(classDeclaration, visitedClassNames = /* @__PURE__ */ new Set()) {
|
|
303
|
+
const className = classDeclaration.getName();
|
|
304
|
+
if (className && visitedClassNames.has(className)) {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
if (className) {
|
|
308
|
+
visitedClassNames.add(className);
|
|
309
|
+
}
|
|
310
|
+
const baseClass = classDeclaration.getBaseClass();
|
|
311
|
+
const baseProperties = baseClass ? this.collectProperties(baseClass, visitedClassNames) : [];
|
|
312
|
+
return [...baseProperties, ...classDeclaration.getProperties()];
|
|
313
|
+
}
|
|
314
|
+
getClassName(target, fallback) {
|
|
315
|
+
return typeof target === "function" ? target.name : fallback;
|
|
316
|
+
}
|
|
317
|
+
async ensureDatasourceMetadata() {
|
|
318
|
+
if (this.config.datasource.entityMetadatas.length) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const datasource = this.config.datasource;
|
|
322
|
+
if (typeof datasource.buildMetadatas !== "function") {
|
|
323
|
+
throw new Error("DTOGeneratorService config error: datasource cannot build entity metadata.");
|
|
324
|
+
}
|
|
325
|
+
await datasource.buildMetadatas();
|
|
326
|
+
}
|
|
327
|
+
isExcludedColumn(column) {
|
|
328
|
+
return this.config.excludePropertyNames.includes(column.propertyName) || column.isSelect === false || Boolean(column.relationMetadata);
|
|
329
|
+
}
|
|
330
|
+
isDateColumn(column) {
|
|
331
|
+
return column.type === Date || [
|
|
332
|
+
"date",
|
|
333
|
+
"datetime",
|
|
334
|
+
"datetime2",
|
|
335
|
+
"timestamp",
|
|
336
|
+
"timestamp with time zone",
|
|
337
|
+
"timestamp without time zone",
|
|
338
|
+
"time",
|
|
339
|
+
"time with time zone",
|
|
340
|
+
"time without time zone"
|
|
341
|
+
].includes(String(column.type));
|
|
342
|
+
}
|
|
343
|
+
isJsonColumn(column) {
|
|
344
|
+
return ["json", "jsonb", "simple-json"].includes(String(column.type));
|
|
345
|
+
}
|
|
346
|
+
resolveEnumTypeName(entityName, column) {
|
|
347
|
+
var _a;
|
|
348
|
+
const enumTypeName = (_a = this.sourceInfoByClassName.get(entityName)) == null ? void 0 : _a.enumTypeByProperty.get(column.propertyName);
|
|
349
|
+
if (enumTypeName) {
|
|
350
|
+
return enumTypeName;
|
|
351
|
+
}
|
|
352
|
+
if (column.enumName) {
|
|
353
|
+
return column.enumName;
|
|
354
|
+
}
|
|
355
|
+
return `${entityName}${column.propertyName.charAt(0).toUpperCase()}${column.propertyName.slice(1)}`;
|
|
356
|
+
}
|
|
357
|
+
resolveSourcePropertyType(entityName, propertyName) {
|
|
358
|
+
var _a;
|
|
359
|
+
const sourceType = (_a = this.sourceInfoByClassName.get(entityName)) == null ? void 0 : _a.typeByProperty.get(propertyName);
|
|
360
|
+
if (!sourceType) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
return sourceType.split("|").map((item) => item.trim()).map((item) => item === "Date" ? "string" : item).join(" | ");
|
|
364
|
+
}
|
|
365
|
+
resolveColumnType(entityName, column) {
|
|
366
|
+
var _a;
|
|
367
|
+
if ((_a = column.enum) == null ? void 0 : _a.length) {
|
|
368
|
+
return this.resolveEnumTypeName(entityName, column);
|
|
369
|
+
}
|
|
370
|
+
const sourceType = this.resolveSourcePropertyType(entityName, column.propertyName);
|
|
371
|
+
if (sourceType) {
|
|
372
|
+
return sourceType;
|
|
373
|
+
}
|
|
374
|
+
const typeText = typeof column.type === "function" ? column.type.name : String(column.type);
|
|
375
|
+
if (this.config.customScalarTypeMap[typeText]) {
|
|
376
|
+
return this.config.customScalarTypeMap[typeText];
|
|
377
|
+
}
|
|
378
|
+
if (this.isDateColumn(column)) {
|
|
379
|
+
return "string";
|
|
380
|
+
}
|
|
381
|
+
if (column.type === String || [
|
|
382
|
+
"char",
|
|
383
|
+
"varchar",
|
|
384
|
+
"nvarchar",
|
|
385
|
+
"text",
|
|
386
|
+
"tinytext",
|
|
387
|
+
"mediumtext",
|
|
388
|
+
"longtext",
|
|
389
|
+
"uuid",
|
|
390
|
+
"simple-enum"
|
|
391
|
+
].includes(typeText)) {
|
|
392
|
+
return "string";
|
|
393
|
+
}
|
|
394
|
+
if (column.type === Number || [
|
|
395
|
+
"int",
|
|
396
|
+
"integer",
|
|
397
|
+
"tinyint",
|
|
398
|
+
"smallint",
|
|
399
|
+
"mediumint",
|
|
400
|
+
"bigint",
|
|
401
|
+
"float",
|
|
402
|
+
"double",
|
|
403
|
+
"double precision",
|
|
404
|
+
"real",
|
|
405
|
+
"decimal",
|
|
406
|
+
"numeric"
|
|
407
|
+
].includes(typeText)) {
|
|
408
|
+
return "number";
|
|
409
|
+
}
|
|
410
|
+
if (column.type === Boolean || ["bool", "boolean"].includes(typeText)) {
|
|
411
|
+
return "boolean";
|
|
412
|
+
}
|
|
413
|
+
if (this.isJsonColumn(column)) {
|
|
414
|
+
return "any";
|
|
415
|
+
}
|
|
416
|
+
if (typeText === "object") {
|
|
417
|
+
return "Record<string, unknown>";
|
|
418
|
+
}
|
|
419
|
+
return typeText;
|
|
420
|
+
}
|
|
421
|
+
appendNullType(typeText) {
|
|
422
|
+
if (typeText.split("|").map((item) => item.trim()).includes("null")) {
|
|
423
|
+
return typeText;
|
|
424
|
+
}
|
|
425
|
+
return `${typeText} | null`;
|
|
426
|
+
}
|
|
427
|
+
columnToMeta(entityName, column) {
|
|
428
|
+
const resolvedType = this.resolveColumnType(entityName, column);
|
|
429
|
+
return {
|
|
430
|
+
name: column.propertyName,
|
|
431
|
+
optional: false,
|
|
432
|
+
type: column.isNullable ? this.appendNullType(resolvedType) : resolvedType,
|
|
433
|
+
isDate: this.isDateColumn(column),
|
|
434
|
+
mapAsNumber: resolvedType === "number",
|
|
435
|
+
enumValues: column.enum ? [...column.enum] : void 0
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
relationToMeta(relation) {
|
|
439
|
+
const targetClassName = this.getClassName(relation.inverseEntityMetadata.target, relation.inverseEntityMetadata.name);
|
|
440
|
+
return {
|
|
441
|
+
name: relation.propertyName,
|
|
442
|
+
targetClassName,
|
|
443
|
+
targetDTOName: `${targetClassName}DTO`,
|
|
444
|
+
isArray: relation.isOneToMany || relation.isManyToMany,
|
|
445
|
+
nullable: relation.isNullable
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
async createEntityMetas() {
|
|
449
|
+
await this.ensureDatasourceMetadata();
|
|
450
|
+
const metas = this.config.datasource.entityMetadatas.map((metadata) => {
|
|
451
|
+
var _a;
|
|
452
|
+
const className = this.getClassName(metadata.target, metadata.name);
|
|
453
|
+
const moduleName = this.toKebabCase(className);
|
|
454
|
+
const columns = metadata.columns.filter((column) => !this.isExcludedColumn(column)).map((column) => this.columnToMeta(className, column));
|
|
455
|
+
const relations = metadata.relations.map((relation) => this.relationToMeta(relation));
|
|
456
|
+
return {
|
|
457
|
+
className,
|
|
458
|
+
dtoName: `${className}DTO`,
|
|
459
|
+
moduleName,
|
|
460
|
+
dtoFileName: `${moduleName}.dto.ts`,
|
|
461
|
+
mapperName: `parse${className}DTO`,
|
|
462
|
+
filePath: (_a = this.sourceInfoByClassName.get(className)) == null ? void 0 : _a.filePath,
|
|
463
|
+
columns,
|
|
464
|
+
relations
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
return metas.sort((a, b) => {
|
|
468
|
+
return a.className.localeCompare(b.className);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
findMetaByClassName(metas, className) {
|
|
472
|
+
return metas.find((meta) => {
|
|
473
|
+
return meta.className === className;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
getValidRelations(meta, metas) {
|
|
477
|
+
return meta.relations.filter((relation) => {
|
|
478
|
+
return Boolean(this.findMetaByClassName(metas, relation.targetClassName));
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
createDTOContent(meta, metas) {
|
|
482
|
+
var _a;
|
|
483
|
+
const dtoFilePath = import_path.default.join(this.config.dtoOutputDir, meta.dtoFileName);
|
|
484
|
+
const validRelations = this.getValidRelations(meta, metas);
|
|
485
|
+
const relationImports = this.unique(
|
|
486
|
+
validRelations.filter((relation) => relation.targetClassName !== meta.className).map((relation) => relation.targetClassName)
|
|
487
|
+
).map((targetClassName) => {
|
|
488
|
+
const targetMeta = this.findMetaByClassName(metas, targetClassName);
|
|
489
|
+
if (!targetMeta) {
|
|
232
490
|
return null;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const arrowMatch = firstArgument.match(/=>\s*([A-Za-z0-9_]+)/);
|
|
430
|
-
const returnMatch = firstArgument.match(/return\s+([A-Za-z0-9_]+)/);
|
|
431
|
-
if (arrowMatch?.[1]) {
|
|
432
|
-
return arrowMatch[1];
|
|
433
|
-
}
|
|
434
|
-
if (returnMatch?.[1]) {
|
|
435
|
-
return returnMatch[1];
|
|
436
|
-
}
|
|
437
|
-
return this.unwrapRelationType(this.getPropertyTypeText(property));
|
|
438
|
-
}
|
|
439
|
-
isArrayRelation(property) {
|
|
440
|
-
const decorator = this.getDecorator(property, this.config.relationDecorators);
|
|
441
|
-
const decoratorName = decorator?.getName() ?? '';
|
|
442
|
-
const typeText = this.getPropertyTypeText(property);
|
|
443
|
-
return this.config.arrayRelationDecorators.includes(decoratorName)
|
|
444
|
-
|| typeText.includes('[]')
|
|
445
|
-
|| typeText.startsWith('Array<')
|
|
446
|
-
|| typeText.startsWith('Promise<Array<')
|
|
447
|
-
|| typeText.includes('Promise<') && typeText.includes('[]>');
|
|
448
|
-
}
|
|
449
|
-
isNullableRelation(property) {
|
|
450
|
-
const typeText = this.getPropertyTypeText(property);
|
|
451
|
-
return this.isNullableTypeText(typeText) || this.hasDecoratorOption(property, this.config.relationDecorators, 'nullable', 'true');
|
|
452
|
-
}
|
|
453
|
-
propertyToRelationMeta(property) {
|
|
454
|
-
const targetClassName = this.getRelationTargetClassName(property);
|
|
455
|
-
return {
|
|
456
|
-
name: property.getName(),
|
|
457
|
-
targetClassName,
|
|
458
|
-
targetDTOName: `${targetClassName}DTO`,
|
|
459
|
-
isArray: this.isArrayRelation(property),
|
|
460
|
-
nullable: this.isNullableRelation(property)
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
createEntityMetas(entitySourceFiles) {
|
|
464
|
-
const metas = [];
|
|
465
|
-
for (const sourceFile of entitySourceFiles) {
|
|
466
|
-
for (const classDeclaration of sourceFile.getClasses()) {
|
|
467
|
-
if (!this.isEntityClass(classDeclaration)) {
|
|
468
|
-
continue;
|
|
469
|
-
}
|
|
470
|
-
const className = classDeclaration.getNameOrThrow();
|
|
471
|
-
const moduleName = this.toKebabCase(className);
|
|
472
|
-
const properties = this.collectProperties(classDeclaration);
|
|
473
|
-
const columns = properties
|
|
474
|
-
.filter((property) => this.isColumnProperty(property))
|
|
475
|
-
.map((property) => this.propertyToColumnMeta(property));
|
|
476
|
-
const relations = properties
|
|
477
|
-
.filter((property) => this.isRelationProperty(property))
|
|
478
|
-
.map((property) => this.propertyToRelationMeta(property));
|
|
479
|
-
const takenNames = new Set([
|
|
480
|
-
...columns.map((column) => column.name),
|
|
481
|
-
...relations.map((relation) => relation.name)
|
|
482
|
-
]);
|
|
483
|
-
const getterColumns = this.config.includeGetters
|
|
484
|
-
? this.collectGetAccessors(classDeclaration)
|
|
485
|
-
.filter((getter) => getter.getParameters().length === 0)
|
|
486
|
-
.filter((getter) => !this.isExcludedGetter(getter))
|
|
487
|
-
.filter((getter) => !takenNames.has(getter.getName()))
|
|
488
|
-
.map((getter) => this.accessorToColumnMeta(getter))
|
|
489
|
-
: [];
|
|
490
|
-
metas.push({
|
|
491
|
-
className,
|
|
492
|
-
dtoName: `${className}DTO`,
|
|
493
|
-
moduleName,
|
|
494
|
-
filePath: classDeclaration.getSourceFile().getFilePath(),
|
|
495
|
-
sourceFilePath: sourceFile.getFilePath(),
|
|
496
|
-
dtoFileName: `${moduleName}.dto.ts`,
|
|
497
|
-
mapperName: `parse${className}DTO`,
|
|
498
|
-
columns: [...columns, ...getterColumns],
|
|
499
|
-
relations
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return metas.sort((a, b) => {
|
|
504
|
-
return a.className.localeCompare(b.className);
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
findMetaByClassName(metas, className) {
|
|
508
|
-
return metas.find((meta) => {
|
|
509
|
-
return meta.className === className;
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
getValidRelations(meta, metas) {
|
|
513
|
-
return meta.relations.filter((relation) => {
|
|
514
|
-
return Boolean(this.findMetaByClassName(metas, relation.targetClassName));
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
createDTOContent(meta, metas) {
|
|
518
|
-
const dtoFilePath = path.join(this.config.dtoOutputDir, meta.dtoFileName);
|
|
519
|
-
const validRelations = this.getValidRelations(meta, metas);
|
|
520
|
-
const relationImports = this.unique(validRelations
|
|
521
|
-
.filter((relation) => {
|
|
522
|
-
return relation.targetClassName !== meta.className;
|
|
523
|
-
})
|
|
524
|
-
.map((relation) => {
|
|
525
|
-
return relation.targetClassName;
|
|
526
|
-
})).map((targetClassName) => {
|
|
527
|
-
const targetMeta = this.findMetaByClassName(metas, targetClassName);
|
|
528
|
-
if (!targetMeta) {
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
const targetPath = path.join(this.config.dtoOutputDir, targetMeta.dtoFileName);
|
|
532
|
-
return [targetMeta.dtoName, this.createRelativeJSImportPath(dtoFilePath, targetPath)];
|
|
533
|
-
}).filter((item) => {
|
|
534
|
-
return item !== null;
|
|
535
|
-
});
|
|
536
|
-
const relationTypeNames = new Set(validRelations.map((relation) => relation.targetDTOName));
|
|
537
|
-
const externalTypeImports = this.collectExternalTypeImports(meta, dtoFilePath, relationTypeNames);
|
|
538
|
-
const importMap = new Map();
|
|
539
|
-
for (const [typeName, moduleSpecifier] of [...relationImports, ...externalTypeImports]) {
|
|
540
|
-
if (!importMap.has(moduleSpecifier)) {
|
|
541
|
-
importMap.set(moduleSpecifier, new Set());
|
|
542
|
-
}
|
|
543
|
-
importMap.get(moduleSpecifier)?.add(typeName);
|
|
544
|
-
}
|
|
545
|
-
const imports = [...importMap.entries()]
|
|
546
|
-
.sort((a, b) => {
|
|
547
|
-
return a[0].localeCompare(b[0]);
|
|
548
|
-
})
|
|
549
|
-
.map(([moduleSpecifier, typeNames]) => {
|
|
550
|
-
return `import type { ${[...typeNames].sort((a, b) => a.localeCompare(b)).join(', ')} } from '${moduleSpecifier}'`;
|
|
551
|
-
});
|
|
552
|
-
const columnLines = meta.columns.map((column) => {
|
|
553
|
-
const optional = column.optional ? '?' : '';
|
|
554
|
-
return ` ${column.name}${optional}: ${column.type}`;
|
|
555
|
-
});
|
|
556
|
-
const relationLines = validRelations.map((relation) => {
|
|
557
|
-
const type = relation.isArray
|
|
558
|
-
? `${relation.targetDTOName}[]`
|
|
559
|
-
: `${relation.targetDTOName}${relation.nullable ? ' | null' : ''}`;
|
|
560
|
-
return ` ${relation.name}?: ${type}`;
|
|
561
|
-
});
|
|
562
|
-
const importBlock = imports.length ? `${imports.join('\n')}\n\n` : '';
|
|
563
|
-
const body = [...columnLines, ...relationLines].join('\n');
|
|
564
|
-
return `${importBlock}export type ${meta.dtoName} = {\n${body}\n}\n`;
|
|
565
|
-
}
|
|
566
|
-
collectExternalTypeImports(meta, dtoFilePath, relationTypeNames) {
|
|
567
|
-
const symbols = this.collectExternalTypeSymbols(meta, relationTypeNames);
|
|
568
|
-
const importSpecifier = this.getCommonTypesImportSpecifier(dtoFilePath);
|
|
569
|
-
return [...symbols].map((symbol) => {
|
|
570
|
-
return [symbol, importSpecifier];
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
collectExternalTypeSymbols(meta, relationTypeNames) {
|
|
574
|
-
const symbols = new Set();
|
|
575
|
-
const ignoredNames = new Set([
|
|
576
|
-
'string',
|
|
577
|
-
'number',
|
|
578
|
-
'boolean',
|
|
579
|
-
'null',
|
|
580
|
-
'undefined',
|
|
581
|
-
'unknown',
|
|
582
|
-
'any',
|
|
583
|
-
'object',
|
|
584
|
-
'bigint',
|
|
585
|
-
'never',
|
|
586
|
-
'void',
|
|
587
|
-
'true',
|
|
588
|
-
'false',
|
|
589
|
-
'Record',
|
|
590
|
-
'Array',
|
|
591
|
-
'Promise',
|
|
592
|
-
'ReadonlyArray'
|
|
593
|
-
]);
|
|
594
|
-
for (const column of meta.columns) {
|
|
491
|
+
}
|
|
492
|
+
const targetPath = import_path.default.join(this.config.dtoOutputDir, targetMeta.dtoFileName);
|
|
493
|
+
return [targetMeta.dtoName, this.createRelativeJSImportPath(dtoFilePath, targetPath)];
|
|
494
|
+
}).filter((item) => {
|
|
495
|
+
return item !== null;
|
|
496
|
+
});
|
|
497
|
+
const relationTypeNames = new Set(validRelations.map((relation) => relation.targetDTOName));
|
|
498
|
+
const externalTypeImports = this.collectExternalTypeImports(meta, dtoFilePath, relationTypeNames);
|
|
499
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
500
|
+
for (const [typeName, moduleSpecifier] of [...relationImports, ...externalTypeImports]) {
|
|
501
|
+
if (!importMap.has(moduleSpecifier)) {
|
|
502
|
+
importMap.set(moduleSpecifier, /* @__PURE__ */ new Set());
|
|
503
|
+
}
|
|
504
|
+
(_a = importMap.get(moduleSpecifier)) == null ? void 0 : _a.add(typeName);
|
|
505
|
+
}
|
|
506
|
+
const imports = [...importMap.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([moduleSpecifier, typeNames]) => {
|
|
507
|
+
let newModuleSpecifier = moduleSpecifier;
|
|
508
|
+
if (!this.config.includeExtensionOnImports) {
|
|
509
|
+
newModuleSpecifier = newModuleSpecifier.replace(".ts", "");
|
|
510
|
+
}
|
|
511
|
+
return `import type { ${[...typeNames].sort((a, b) => a.localeCompare(b)).join(", ")} } from '${newModuleSpecifier}'`;
|
|
512
|
+
});
|
|
513
|
+
const columnLines = meta.columns.map((column) => {
|
|
514
|
+
const optional = column.optional ? "?" : "";
|
|
515
|
+
return ` ${column.name}${optional}: ${column.type}`;
|
|
516
|
+
});
|
|
517
|
+
const relationLines = validRelations.map((relation) => {
|
|
518
|
+
const type = relation.isArray ? `${relation.targetDTOName}[]` : `${relation.targetDTOName}${relation.nullable ? " | null" : ""}`;
|
|
519
|
+
return ` ${relation.name}?: ${type}`;
|
|
520
|
+
});
|
|
521
|
+
const importBlock = imports.length ? `${imports.join("\n")}
|
|
522
|
+
|
|
523
|
+
` : "";
|
|
524
|
+
const body = [...columnLines, ...relationLines].join("\n");
|
|
525
|
+
return `${importBlock}export type ${meta.dtoName} = {
|
|
526
|
+
${body}
|
|
527
|
+
}
|
|
528
|
+
`;
|
|
529
|
+
}
|
|
530
|
+
collectExternalTypeImports(meta, dtoFilePath, relationTypeNames) {
|
|
531
|
+
const symbols = this.collectExternalTypeSymbols(meta, relationTypeNames);
|
|
532
|
+
const importSpecifier = this.getCommonTypesImportSpecifier(dtoFilePath);
|
|
533
|
+
return [...symbols].map((symbol) => {
|
|
534
|
+
return [symbol, importSpecifier];
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
collectExternalTypeSymbols(meta, relationTypeNames) {
|
|
538
|
+
const symbols = /* @__PURE__ */ new Set();
|
|
539
|
+
const ignoredNames = /* @__PURE__ */ new Set([
|
|
540
|
+
"string",
|
|
541
|
+
"number",
|
|
542
|
+
"boolean",
|
|
543
|
+
"null",
|
|
544
|
+
"undefined",
|
|
545
|
+
"unknown",
|
|
546
|
+
"any",
|
|
547
|
+
"object",
|
|
548
|
+
"bigint",
|
|
549
|
+
"never",
|
|
550
|
+
"void",
|
|
551
|
+
"true",
|
|
552
|
+
"false",
|
|
553
|
+
"Record",
|
|
554
|
+
"Array",
|
|
555
|
+
"Promise",
|
|
556
|
+
"ReadonlyArray"
|
|
557
|
+
]);
|
|
558
|
+
for (const column of meta.columns) {
|
|
559
|
+
const identifiers = column.type.match(/\b[A-Za-z_][A-Za-z0-9_]*\b/g) ?? [];
|
|
560
|
+
for (const identifier of identifiers) {
|
|
561
|
+
if (ignoredNames.has(identifier)) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (relationTypeNames.has(identifier) || identifier === meta.dtoName) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
symbols.add(identifier);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return symbols;
|
|
571
|
+
}
|
|
572
|
+
collectAllExternalTypeSymbols(metas) {
|
|
573
|
+
const symbols = /* @__PURE__ */ new Set();
|
|
574
|
+
for (const meta of metas) {
|
|
575
|
+
const relationTypeNames = new Set(meta.relations.map((relation) => relation.targetDTOName));
|
|
576
|
+
const metaSymbols = this.collectExternalTypeSymbols(meta, relationTypeNames);
|
|
577
|
+
for (const symbol of metaSymbols) {
|
|
578
|
+
symbols.add(symbol);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return symbols;
|
|
582
|
+
}
|
|
583
|
+
findTypeDeclarationByName(typeName) {
|
|
584
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
585
|
+
if (this.isIgnoredSourcePath(sourceFile.getFilePath())) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
const enumDeclaration = sourceFile.getEnum(typeName);
|
|
589
|
+
if (enumDeclaration) {
|
|
590
|
+
return enumDeclaration;
|
|
591
|
+
}
|
|
592
|
+
const interfaceDeclaration = sourceFile.getInterface(typeName);
|
|
593
|
+
if (interfaceDeclaration) {
|
|
594
|
+
return interfaceDeclaration;
|
|
595
|
+
}
|
|
596
|
+
const typeAliasDeclaration = sourceFile.getTypeAlias(typeName);
|
|
597
|
+
if (typeAliasDeclaration) {
|
|
598
|
+
return typeAliasDeclaration;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
findEnumDeclarationByName(typeName) {
|
|
604
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
605
|
+
if (this.isIgnoredSourcePath(sourceFile.getFilePath())) {
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const enumDeclaration = sourceFile.getEnum(typeName);
|
|
609
|
+
if (enumDeclaration) {
|
|
610
|
+
return enumDeclaration;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
normalizeDeclarationToExportText(typeName, preferEnum = false) {
|
|
616
|
+
const declaration = preferEnum ? this.findEnumDeclarationByName(typeName) ?? this.findTypeDeclarationByName(typeName) : this.findTypeDeclarationByName(typeName);
|
|
617
|
+
if (!declaration) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
const declarationText = declaration.getText().trim();
|
|
621
|
+
if (declarationText.startsWith("export ")) {
|
|
622
|
+
return declarationText;
|
|
623
|
+
}
|
|
624
|
+
return `export ${declarationText}`;
|
|
625
|
+
}
|
|
626
|
+
formatEnumKey(value) {
|
|
627
|
+
const normalized = String(value).replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
628
|
+
return normalized || `VALUE_${String(value)}`;
|
|
629
|
+
}
|
|
630
|
+
formatEnumValue(value) {
|
|
631
|
+
return typeof value === "number" ? String(value) : `'${value.replace(/'/g, "\\'")}'`;
|
|
632
|
+
}
|
|
633
|
+
createCommonEnumDeclaration(typeName, values) {
|
|
634
|
+
const uniqueValues = this.unique(values);
|
|
635
|
+
const lines = uniqueValues.map((value) => {
|
|
636
|
+
return ` ${this.formatEnumKey(value)} = ${this.formatEnumValue(value)}`;
|
|
637
|
+
});
|
|
638
|
+
return `export enum ${typeName} {
|
|
639
|
+
${lines.join(",\n")}
|
|
640
|
+
}`;
|
|
641
|
+
}
|
|
642
|
+
ensureCommonTypesFile(metas) {
|
|
643
|
+
var _a;
|
|
644
|
+
const symbols = [...this.collectAllExternalTypeSymbols(metas)].sort((a, b) => a.localeCompare(b));
|
|
645
|
+
const enumBySymbol = /* @__PURE__ */ new Map();
|
|
646
|
+
for (const meta of metas) {
|
|
647
|
+
for (const column of meta.columns) {
|
|
648
|
+
if ((_a = column.enumValues) == null ? void 0 : _a.length) {
|
|
649
|
+
enumBySymbol.set(column.type, column.enumValues);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
const lines = symbols.map((symbol) => {
|
|
654
|
+
const enumValues = enumBySymbol.get(symbol);
|
|
655
|
+
const declarationText = this.normalizeDeclarationToExportText(symbol, Boolean(enumValues));
|
|
656
|
+
if (declarationText) {
|
|
657
|
+
return declarationText.replace(/^export declare /, "export ");
|
|
658
|
+
}
|
|
659
|
+
if (!enumValues) {
|
|
660
|
+
throw new Error(`DTOGeneratorService config error: could not generate common type '${symbol}'.`);
|
|
661
|
+
}
|
|
662
|
+
return this.createCommonEnumDeclaration(symbol, enumValues);
|
|
663
|
+
});
|
|
664
|
+
this.writeFile(this.getCommonTypesFilePath(), `${lines.join("\n\n")}
|
|
665
|
+
`);
|
|
666
|
+
}
|
|
667
|
+
resolveCommonTypesSourceContent() {
|
|
668
|
+
const commonTypesFilePath = this.getCommonTypesFilePath();
|
|
669
|
+
if (!import_fs.default.existsSync(commonTypesFilePath)) {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
return import_fs.default.readFileSync(commonTypesFilePath, "utf-8");
|
|
673
|
+
}
|
|
674
|
+
validateCommonTypeCoverage(metas) {
|
|
675
|
+
const commonTypesContent = this.resolveCommonTypesSourceContent();
|
|
676
|
+
if (!commonTypesContent) {
|
|
677
|
+
throw new Error(`DTOGeneratorService config error: common.dto.ts was not generated in '${this.config.dtoOutputDir}'.`);
|
|
678
|
+
}
|
|
679
|
+
const missing = [];
|
|
680
|
+
for (const meta of metas) {
|
|
681
|
+
const relationTypeNames = new Set(meta.relations.map((relation) => relation.targetDTOName));
|
|
682
|
+
const symbols = this.collectExternalTypeSymbols(meta, relationTypeNames);
|
|
683
|
+
for (const symbol of symbols) {
|
|
684
|
+
const pattern = new RegExp(`export\\s+(?:enum|type|interface)\\s+${symbol}\\b`);
|
|
685
|
+
if (!pattern.test(commonTypesContent)) {
|
|
686
|
+
const columns = meta.columns.filter((column) => {
|
|
595
687
|
const identifiers = column.type.match(/\b[A-Za-z_][A-Za-z0-9_]*\b/g) ?? [];
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
missing.push(`${symbol} (used in ${meta.dtoName}.${uniqueColumns.join(', ')})`);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
if (missing.length) {
|
|
721
|
-
const details = this.unique(missing).sort((a, b) => a.localeCompare(b)).join('\n- ');
|
|
722
|
-
throw new Error(`DTOGeneratorService validation error: generated/common.dto.ts is missing exports for:\n- ${details}`);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
createDTOIndexContent(metas) {
|
|
726
|
-
const dtoTypeExports = metas
|
|
727
|
-
.map((meta) => {
|
|
728
|
-
return `export type { ${meta.dtoName} } from './${meta.moduleName}.dto.ts'`;
|
|
729
|
-
})
|
|
730
|
-
.join('\n');
|
|
731
|
-
return `export * from './common.dto.ts'\n${dtoTypeExports}`;
|
|
732
|
-
}
|
|
733
|
-
createGeneratedMapperContent(metas) {
|
|
734
|
-
const hasDateColumn = metas.some((meta) => {
|
|
735
|
-
return meta.columns.some((column) => {
|
|
736
|
-
return column.isDate;
|
|
737
|
-
});
|
|
738
|
-
});
|
|
739
|
-
const helperImport = hasDateColumn ? `import { toDTODate } from './dto-date.util.ts'\n` : '';
|
|
740
|
-
const entityImports = metas.map((meta) => {
|
|
741
|
-
return `import { ${meta.className} } from '${this.createRelativeJSImportPath(this.config.mapperOutputFile, meta.filePath)}'`;
|
|
742
|
-
});
|
|
743
|
-
const dtoImports = `import type { ${metas.map((meta) => meta.dtoName).join(', ')} } from '${this.config.sharedDTOImportPath}'`;
|
|
744
|
-
const mapperFunctions = metas.map((meta) => {
|
|
745
|
-
return this.createMapperFunctionContent(meta, metas);
|
|
746
|
-
});
|
|
747
|
-
return `${helperImport}${entityImports.join('\n')}\n${dtoImports}\n\n${mapperFunctions.join('\n\n')}\n`;
|
|
748
|
-
}
|
|
749
|
-
createMapperFunctionContent(meta, metas) {
|
|
750
|
-
const validRelations = this.getValidRelations(meta, metas);
|
|
751
|
-
const columnLines = meta.columns.map((column) => {
|
|
752
|
-
const value = column.isDate ? `toDTODate(entity.${column.name})` : `entity.${column.name}`;
|
|
753
|
-
return ` ${column.name}: ${value}`;
|
|
754
|
-
});
|
|
755
|
-
const relationLines = validRelations.map((relation) => {
|
|
756
|
-
return this.createRelationMapperLine(relation, metas);
|
|
757
|
-
});
|
|
758
|
-
const relationBlock = relationLines.length ? `\n${relationLines.join('\n\n')}\n` : '';
|
|
759
|
-
return `export function ${meta.mapperName}(entity: ${meta.className}): ${meta.dtoName} {\n const dto: ${meta.dtoName} = {\n${columnLines.join(',\n')}\n }\n${relationBlock}\n return dto\n}\n\nexport function ${meta.mapperName}Array(entities: ${meta.className}[]): ${meta.dtoName}[] {\n return entities.map(${meta.mapperName})\n}`;
|
|
760
|
-
}
|
|
761
|
-
createRelationMapperLine(relation, metas) {
|
|
762
|
-
const targetMeta = this.findMetaByClassName(metas, relation.targetClassName);
|
|
763
|
-
const parserName = targetMeta?.mapperName;
|
|
764
|
-
if (relation.isArray) {
|
|
765
|
-
return ` if (Array.isArray(entity.${relation.name})) {\n dto.${relation.name} = entity.${relation.name}.map(${parserName})\n }`;
|
|
766
|
-
}
|
|
767
|
-
if (relation.nullable) {
|
|
768
|
-
return ` if (entity.${relation.name} !== undefined) {\n dto.${relation.name} = entity.${relation.name} === null ? null : ${parserName}(entity.${relation.name})\n }`;
|
|
769
|
-
}
|
|
770
|
-
return ` if (entity.${relation.name} !== undefined && entity.${relation.name} !== null) {\n dto.${relation.name} = ${parserName}(entity.${relation.name})\n }`;
|
|
771
|
-
}
|
|
688
|
+
return identifiers.includes(symbol);
|
|
689
|
+
}).map((column) => column.name);
|
|
690
|
+
const uniqueColumns = this.unique(columns);
|
|
691
|
+
missing.push(`${symbol} (used in ${meta.dtoName}.${uniqueColumns.join(", ")})`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (missing.length) {
|
|
696
|
+
const details = this.unique(missing).sort((a, b) => a.localeCompare(b)).join("\n- ");
|
|
697
|
+
throw new Error(
|
|
698
|
+
`DTOGeneratorService validation error: generated/common.dto.ts is missing exports for:
|
|
699
|
+
- ${details}`
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
createDTOIndexContent(metas) {
|
|
704
|
+
const dtoTypeExports = metas.map((meta) => {
|
|
705
|
+
return `export type { ${meta.dtoName} } from './${meta.moduleName}.dto${this.config.includeExtensionOnImports ? ".ts" : ""}'`;
|
|
706
|
+
}).join("\n");
|
|
707
|
+
return `export * from './common.dto${this.config.includeExtensionOnImports ? ".ts" : ""}'
|
|
708
|
+
${dtoTypeExports}`;
|
|
709
|
+
}
|
|
710
|
+
getMetasWithFilePath(metas) {
|
|
711
|
+
return metas.filter((meta) => {
|
|
712
|
+
return Boolean(meta.filePath);
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
createEntityImports(metas) {
|
|
716
|
+
var _a;
|
|
717
|
+
const metasWithFilePath = this.getMetasWithFilePath(metas);
|
|
718
|
+
if (metasWithFilePath.length !== metas.length) {
|
|
719
|
+
return "";
|
|
720
|
+
}
|
|
721
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
722
|
+
for (const meta of metasWithFilePath) {
|
|
723
|
+
if (!meta.filePath) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
const moduleSpecifier = this.createRelativeJSImportPath(this.config.mapperOutputFile, meta.filePath);
|
|
727
|
+
if (!importMap.has(moduleSpecifier)) {
|
|
728
|
+
importMap.set(moduleSpecifier, /* @__PURE__ */ new Set());
|
|
729
|
+
}
|
|
730
|
+
(_a = importMap.get(moduleSpecifier)) == null ? void 0 : _a.add(meta.className);
|
|
731
|
+
}
|
|
732
|
+
return [...importMap.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([moduleSpecifier, classNames]) => {
|
|
733
|
+
let newModuleSpecifier = moduleSpecifier;
|
|
734
|
+
if (!this.config.includeExtensionOnImports) {
|
|
735
|
+
newModuleSpecifier = newModuleSpecifier.replace(".ts", "");
|
|
736
|
+
}
|
|
737
|
+
return `import { ${[...classNames].sort((a, b) => a.localeCompare(b)).join(", ")} } from '${newModuleSpecifier}'`;
|
|
738
|
+
}).join("\n");
|
|
739
|
+
}
|
|
740
|
+
getMapperEntityTypeName(meta) {
|
|
741
|
+
return meta.filePath ? meta.className : `${meta.className}EntityLike`;
|
|
742
|
+
}
|
|
743
|
+
createStructuralEntityTypes(metas) {
|
|
744
|
+
if (this.getMetasWithFilePath(metas).length === metas.length) {
|
|
745
|
+
return "";
|
|
746
|
+
}
|
|
747
|
+
return metas.map((meta) => {
|
|
748
|
+
return `type ${meta.className}EntityLike = Record<string, any>`;
|
|
749
|
+
}).join("\n");
|
|
750
|
+
}
|
|
751
|
+
createGeneratedMapperContent(metas) {
|
|
752
|
+
const entityImports = this.createEntityImports(metas);
|
|
753
|
+
const dtoImports = `import type { ${metas.map((meta) => meta.dtoName).join(", ")} } from '${this.config.sharedDTOImportPath}'`;
|
|
754
|
+
const structuralTypes = this.createStructuralEntityTypes(metas);
|
|
755
|
+
const mapperFunctions = metas.map((meta) => {
|
|
756
|
+
return this.createMapperFunctionContent(meta, metas);
|
|
757
|
+
});
|
|
758
|
+
const blocks = [
|
|
759
|
+
entityImports,
|
|
760
|
+
dtoImports,
|
|
761
|
+
structuralTypes,
|
|
762
|
+
mapperFunctions.join("\n\n")
|
|
763
|
+
].filter(Boolean);
|
|
764
|
+
let contents = `${blocks.join("\n")}
|
|
765
|
+
|
|
766
|
+
`;
|
|
767
|
+
const dtoDateContent = `export function toDTODate(value: Date | string): string
|
|
768
|
+
export function toDTODate(value: Date | string | null | undefined): string | null
|
|
769
|
+
export function toDTODate(value: Date | string | null | undefined) {
|
|
770
|
+
if (!value) {
|
|
771
|
+
return null
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (value instanceof Date) {
|
|
775
|
+
return value.toISOString()
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return value
|
|
779
|
+
}
|
|
780
|
+
`;
|
|
781
|
+
contents += dtoDateContent;
|
|
782
|
+
return contents;
|
|
783
|
+
}
|
|
784
|
+
createMapperFunctionContent(meta, metas) {
|
|
785
|
+
const validRelations = this.getValidRelations(meta, metas);
|
|
786
|
+
const entityTypeName = this.getMapperEntityTypeName(meta);
|
|
787
|
+
const columnLines = meta.columns.map((column) => {
|
|
788
|
+
let value = `entity.${column.name}`;
|
|
789
|
+
if (column.isDate) {
|
|
790
|
+
value = `toDTODate(entity.${column.name})`;
|
|
791
|
+
} else if (column.mapAsNumber) {
|
|
792
|
+
value = column.type.includes("null") ? `entity.${column.name} === null || entity.${column.name} === undefined ? null : Number(entity.${column.name})` : `Number(entity.${column.name})`;
|
|
793
|
+
}
|
|
794
|
+
return ` ${column.name}: ${value}`;
|
|
795
|
+
});
|
|
796
|
+
const relationLines = validRelations.map((relation) => {
|
|
797
|
+
return this.createRelationMapperLine(relation, metas);
|
|
798
|
+
});
|
|
799
|
+
const relationBlock = relationLines.length ? `
|
|
800
|
+
${relationLines.join("\n\n")}
|
|
801
|
+
` : "";
|
|
802
|
+
return `export function ${meta.mapperName}(entity: ${entityTypeName}): ${meta.dtoName} {
|
|
803
|
+
const dto: ${meta.dtoName} = {
|
|
804
|
+
${columnLines.join(",\n")}
|
|
805
|
+
}
|
|
806
|
+
${relationBlock}
|
|
807
|
+
return dto
|
|
772
808
|
}
|
|
809
|
+
|
|
810
|
+
export function ${meta.mapperName}Array(entities: ${entityTypeName}[]): ${meta.dtoName}[] {
|
|
811
|
+
return entities.map(${meta.mapperName})
|
|
812
|
+
|
|
813
|
+
}`;
|
|
814
|
+
}
|
|
815
|
+
createRelationMapperLine(relation, metas) {
|
|
816
|
+
const targetMeta = this.findMetaByClassName(metas, relation.targetClassName);
|
|
817
|
+
const parserName = targetMeta == null ? void 0 : targetMeta.mapperName;
|
|
818
|
+
if (relation.isArray) {
|
|
819
|
+
return ` if (Array.isArray(entity.${relation.name})) {
|
|
820
|
+
dto.${relation.name} = entity.${relation.name}.map(${parserName})
|
|
821
|
+
}`;
|
|
822
|
+
}
|
|
823
|
+
if (relation.nullable) {
|
|
824
|
+
return ` if (entity.${relation.name} !== undefined) {
|
|
825
|
+
dto.${relation.name} = entity.${relation.name} === null ? null : ${parserName}(entity.${relation.name})
|
|
826
|
+
}`;
|
|
827
|
+
}
|
|
828
|
+
return ` if (entity.${relation.name} !== undefined && entity.${relation.name} !== null) {
|
|
829
|
+
dto.${relation.name} = ${parserName}(entity.${relation.name})
|
|
830
|
+
}`;
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
834
|
+
0 && (module.exports = {
|
|
835
|
+
TORMDTOGenerator
|
|
836
|
+
});
|
|
837
|
+
//# sourceMappingURL=index.js.map
|