tsgonest 0.7.5 → 0.8.1
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/bin/migrate.cjs +283 -78
- package/package.json +9 -9
package/bin/migrate.cjs
CHANGED
|
@@ -175950,6 +175950,16 @@ function transformTypiaTags(file, report) {
|
|
|
175950
175950
|
count++;
|
|
175951
175951
|
}
|
|
175952
175952
|
}
|
|
175953
|
+
if (expr.startsWith("typia.json.assertParse") || expr.startsWith("typia.json.parse")) {
|
|
175954
|
+
const args = node.getArguments();
|
|
175955
|
+
if (args.length === 1) {
|
|
175956
|
+
const argText = args[0].getText();
|
|
175957
|
+
const line = node.getStartLineNumber();
|
|
175958
|
+
node.replaceWithText(`JSON.parse(${argText})`);
|
|
175959
|
+
report.todo(filePath, "typia", "Replaced typia.json.assertParse() with JSON.parse(). Add manual validation if needed.", line);
|
|
175960
|
+
count++;
|
|
175961
|
+
}
|
|
175962
|
+
}
|
|
175953
175963
|
});
|
|
175954
175964
|
for (const imp of file.getImportDeclarations()) {
|
|
175955
175965
|
if (imp.getModuleSpecifierValue() !== "typia") continue;
|
|
@@ -175979,19 +175989,41 @@ function ensureTagsImport(file) {
|
|
|
175979
175989
|
namedImports: ["tags"]
|
|
175980
175990
|
});
|
|
175981
175991
|
}
|
|
175992
|
+
/** Packages whose imports should be pruned after all transforms. */
|
|
175993
|
+
const PRUNE_PACKAGES = new Set([
|
|
175994
|
+
"class-validator",
|
|
175995
|
+
"class-transformer",
|
|
175996
|
+
"@nestjs/swagger"
|
|
175997
|
+
]);
|
|
175982
175998
|
/**
|
|
175983
175999
|
* Remove import declarations that have no named imports, no default import,
|
|
175984
176000
|
* and no namespace import (i.e., completely empty after transforms).
|
|
176001
|
+
* Also prune individual unused named imports from migration-target packages.
|
|
175985
176002
|
*/
|
|
175986
176003
|
function cleanupImports(file) {
|
|
175987
176004
|
const imports = file.getImportDeclarations();
|
|
175988
|
-
|
|
176005
|
+
const fullText = file.getFullText();
|
|
176006
|
+
for (const imp of [...imports]) {
|
|
175989
176007
|
const hasDefault = imp.getDefaultImport() !== void 0;
|
|
175990
176008
|
const hasNamespace = imp.getNamespaceImport() !== void 0;
|
|
175991
176009
|
const hasNamed = imp.getNamedImports().length > 0;
|
|
175992
|
-
imp.getModuleSpecifierValue();
|
|
175993
|
-
if (!hasDefault && !hasNamespace && !hasNamed && imp.getText().includes("{"))
|
|
176010
|
+
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
176011
|
+
if (!hasDefault && !hasNamespace && !hasNamed && imp.getText().includes("{")) {
|
|
176012
|
+
imp.remove();
|
|
176013
|
+
continue;
|
|
176014
|
+
}
|
|
176015
|
+
if (PRUNE_PACKAGES.has(moduleSpecifier) && hasNamed) {
|
|
176016
|
+
const importText = imp.getText();
|
|
176017
|
+
const withoutImport = fullText.replace(importText, "");
|
|
176018
|
+
const namedImports = imp.getNamedImports();
|
|
176019
|
+
const unused = namedImports.filter((n) => !withoutImport.includes(n.getName()));
|
|
176020
|
+
if (unused.length === namedImports.length) imp.remove();
|
|
176021
|
+
else if (unused.length > 0) for (const n of unused.reverse()) n.remove();
|
|
176022
|
+
}
|
|
175994
176023
|
}
|
|
176024
|
+
const text = file.getFullText();
|
|
176025
|
+
const collapsed = text.replace(/\n([ \t]*\n){2,}/g, "\n\n");
|
|
176026
|
+
if (collapsed !== text) file.replaceWithText(collapsed);
|
|
175995
176027
|
}
|
|
175996
176028
|
|
|
175997
176029
|
//#endregion
|
|
@@ -176051,46 +176083,48 @@ const NON_CLASS_VALIDATOR_DECORATORS = new Set([
|
|
|
176051
176083
|
"Default",
|
|
176052
176084
|
"ForeignKey",
|
|
176053
176085
|
"BelongsTo",
|
|
176054
|
-
"HasMany"
|
|
176086
|
+
"HasMany",
|
|
176087
|
+
"Prop",
|
|
176088
|
+
"prop"
|
|
176055
176089
|
]);
|
|
176056
|
-
/** Map class-validator decorator → branded type
|
|
176090
|
+
/** Map class-validator decorator → branded type expressions (or empty to just remove). */
|
|
176057
176091
|
function decoratorToBrandedType(decorator) {
|
|
176058
176092
|
const name = decorator.getName();
|
|
176059
176093
|
const args = decorator.getArguments().map((a) => a.getText());
|
|
176060
176094
|
switch (name) {
|
|
176061
176095
|
case "Min": return {
|
|
176062
|
-
|
|
176096
|
+
tags: args[0] ? [`tags.Minimum<${args[0]}>`] : [],
|
|
176063
176097
|
makeOptional: false,
|
|
176064
176098
|
known: true
|
|
176065
176099
|
};
|
|
176066
176100
|
case "Max": return {
|
|
176067
|
-
|
|
176101
|
+
tags: args[0] ? [`tags.Maximum<${args[0]}>`] : [],
|
|
176068
176102
|
makeOptional: false,
|
|
176069
176103
|
known: true
|
|
176070
176104
|
};
|
|
176071
176105
|
case "MinLength": return {
|
|
176072
|
-
|
|
176106
|
+
tags: args[0] ? [`tags.MinLength<${args[0]}>`] : [],
|
|
176073
176107
|
makeOptional: false,
|
|
176074
176108
|
known: true
|
|
176075
176109
|
};
|
|
176076
176110
|
case "MaxLength": return {
|
|
176077
|
-
|
|
176111
|
+
tags: args[0] ? [`tags.MaxLength<${args[0]}>`] : [],
|
|
176078
176112
|
makeOptional: false,
|
|
176079
176113
|
known: true
|
|
176080
176114
|
};
|
|
176081
176115
|
case "IsEmail": return {
|
|
176082
|
-
|
|
176116
|
+
tags: ["tags.Email"],
|
|
176083
176117
|
makeOptional: false,
|
|
176084
176118
|
known: true
|
|
176085
176119
|
};
|
|
176086
176120
|
case "IsUUID": return {
|
|
176087
|
-
|
|
176121
|
+
tags: ["tags.Uuid"],
|
|
176088
176122
|
makeOptional: false,
|
|
176089
176123
|
known: true
|
|
176090
176124
|
};
|
|
176091
176125
|
case "IsUrl":
|
|
176092
176126
|
case "IsURL": return {
|
|
176093
|
-
|
|
176127
|
+
tags: ["tags.Url"],
|
|
176094
176128
|
makeOptional: false,
|
|
176095
176129
|
known: true
|
|
176096
176130
|
};
|
|
@@ -176098,193 +176132,198 @@ function decoratorToBrandedType(decorator) {
|
|
|
176098
176132
|
if (args[0]) {
|
|
176099
176133
|
const pattern = args[0].replace(/^\/|\/[gimsuy]*$/g, "");
|
|
176100
176134
|
return {
|
|
176101
|
-
|
|
176135
|
+
tags: [`tags.Pattern<${JSON.stringify(pattern)}>`],
|
|
176102
176136
|
makeOptional: false,
|
|
176103
176137
|
known: true
|
|
176104
176138
|
};
|
|
176105
176139
|
}
|
|
176106
176140
|
return {
|
|
176107
|
-
|
|
176141
|
+
tags: [],
|
|
176108
176142
|
makeOptional: false,
|
|
176109
176143
|
known: true
|
|
176110
176144
|
};
|
|
176111
176145
|
case "IsDateString": return {
|
|
176112
|
-
|
|
176146
|
+
tags: [`tags.Format<"date-time">`],
|
|
176113
176147
|
makeOptional: false,
|
|
176114
176148
|
known: true
|
|
176115
176149
|
};
|
|
176116
176150
|
case "IsNotEmpty": return {
|
|
176117
|
-
|
|
176151
|
+
tags: ["tags.MinLength<1>"],
|
|
176118
176152
|
makeOptional: false,
|
|
176119
176153
|
known: true
|
|
176120
176154
|
};
|
|
176121
176155
|
case "IsPositive": return {
|
|
176122
|
-
|
|
176156
|
+
tags: ["tags.Positive"],
|
|
176123
176157
|
makeOptional: false,
|
|
176124
176158
|
known: true
|
|
176125
176159
|
};
|
|
176126
176160
|
case "IsNegative": return {
|
|
176127
|
-
|
|
176161
|
+
tags: ["tags.Negative"],
|
|
176128
176162
|
makeOptional: false,
|
|
176129
176163
|
known: true
|
|
176130
176164
|
};
|
|
176131
176165
|
case "IsInt": return {
|
|
176132
|
-
|
|
176166
|
+
tags: ["tags.Int"],
|
|
176133
176167
|
makeOptional: false,
|
|
176134
176168
|
known: true
|
|
176135
176169
|
};
|
|
176136
176170
|
case "ArrayMinSize": return {
|
|
176137
|
-
|
|
176171
|
+
tags: args[0] ? [`tags.MinItems<${args[0]}>`] : [],
|
|
176138
176172
|
makeOptional: false,
|
|
176139
176173
|
known: true
|
|
176140
176174
|
};
|
|
176141
176175
|
case "ArrayMaxSize": return {
|
|
176142
|
-
|
|
176176
|
+
tags: args[0] ? [`tags.MaxItems<${args[0]}>`] : [],
|
|
176143
176177
|
makeOptional: false,
|
|
176144
176178
|
known: true
|
|
176145
176179
|
};
|
|
176146
176180
|
case "ArrayNotEmpty": return {
|
|
176147
|
-
|
|
176181
|
+
tags: ["tags.MinItems<1>"],
|
|
176148
176182
|
makeOptional: false,
|
|
176149
176183
|
known: true
|
|
176150
176184
|
};
|
|
176151
176185
|
case "IsDate": return {
|
|
176152
|
-
|
|
176186
|
+
tags: [`tags.Format<"date-time">`],
|
|
176153
176187
|
makeOptional: false,
|
|
176154
176188
|
known: true
|
|
176155
176189
|
};
|
|
176156
176190
|
case "IsISO8601": return {
|
|
176157
|
-
|
|
176191
|
+
tags: [`tags.Format<"date-time">`],
|
|
176158
176192
|
makeOptional: false,
|
|
176159
176193
|
known: true
|
|
176160
176194
|
};
|
|
176161
176195
|
case "IsJSON": return {
|
|
176162
|
-
|
|
176196
|
+
tags: [],
|
|
176163
176197
|
makeOptional: false,
|
|
176164
176198
|
known: true
|
|
176165
176199
|
};
|
|
176166
176200
|
case "IsIn": return {
|
|
176167
|
-
|
|
176201
|
+
tags: [],
|
|
176168
176202
|
makeOptional: false,
|
|
176169
176203
|
known: false
|
|
176170
176204
|
};
|
|
176171
176205
|
case "IsTimeZone": return {
|
|
176172
|
-
|
|
176206
|
+
tags: [],
|
|
176173
176207
|
makeOptional: false,
|
|
176174
176208
|
known: true
|
|
176175
176209
|
};
|
|
176176
176210
|
case "IsIP": return {
|
|
176177
|
-
|
|
176211
|
+
tags: [`tags.Format<"ipv4">`],
|
|
176178
176212
|
makeOptional: false,
|
|
176179
176213
|
known: true
|
|
176180
176214
|
};
|
|
176181
176215
|
case "IsCreditCard": return {
|
|
176182
|
-
|
|
176216
|
+
tags: [],
|
|
176183
176217
|
makeOptional: false,
|
|
176184
176218
|
known: true
|
|
176185
176219
|
};
|
|
176186
176220
|
case "IsPhoneNumber": return {
|
|
176187
|
-
|
|
176221
|
+
tags: [],
|
|
176188
176222
|
makeOptional: false,
|
|
176189
176223
|
known: true
|
|
176190
176224
|
};
|
|
176191
176225
|
case "IsHexColor": return {
|
|
176192
|
-
|
|
176226
|
+
tags: [],
|
|
176193
176227
|
makeOptional: false,
|
|
176194
176228
|
known: true
|
|
176195
176229
|
};
|
|
176196
176230
|
case "IsMACAddress": return {
|
|
176197
|
-
|
|
176231
|
+
tags: [],
|
|
176198
176232
|
makeOptional: false,
|
|
176199
176233
|
known: true
|
|
176200
176234
|
};
|
|
176201
176235
|
case "IsPort": return {
|
|
176202
|
-
|
|
176236
|
+
tags: [],
|
|
176203
176237
|
makeOptional: false,
|
|
176204
176238
|
known: true
|
|
176205
176239
|
};
|
|
176206
176240
|
case "IsMimeType": return {
|
|
176207
|
-
|
|
176241
|
+
tags: [],
|
|
176208
176242
|
makeOptional: false,
|
|
176209
176243
|
known: true
|
|
176210
176244
|
};
|
|
176211
176245
|
case "IsSemVer": return {
|
|
176212
|
-
|
|
176246
|
+
tags: [],
|
|
176213
176247
|
makeOptional: false,
|
|
176214
176248
|
known: true
|
|
176215
176249
|
};
|
|
176216
176250
|
case "IsAlpha": return {
|
|
176217
|
-
|
|
176251
|
+
tags: [],
|
|
176218
176252
|
makeOptional: false,
|
|
176219
176253
|
known: true
|
|
176220
176254
|
};
|
|
176221
176255
|
case "IsAlphanumeric": return {
|
|
176222
|
-
|
|
176256
|
+
tags: [],
|
|
176223
176257
|
makeOptional: false,
|
|
176224
176258
|
known: true
|
|
176225
176259
|
};
|
|
176226
176260
|
case "IsNumberString": return {
|
|
176227
|
-
|
|
176261
|
+
tags: [],
|
|
176228
176262
|
makeOptional: false,
|
|
176229
176263
|
known: true
|
|
176230
176264
|
};
|
|
176231
176265
|
case "IsBase64": return {
|
|
176232
|
-
|
|
176266
|
+
tags: [],
|
|
176233
176267
|
makeOptional: false,
|
|
176234
176268
|
known: true
|
|
176235
176269
|
};
|
|
176236
176270
|
case "IsMongoId": return {
|
|
176237
|
-
|
|
176271
|
+
tags: [],
|
|
176238
176272
|
makeOptional: false,
|
|
176239
176273
|
known: true
|
|
176240
176274
|
};
|
|
176241
176275
|
case "IsCurrency": return {
|
|
176242
|
-
|
|
176276
|
+
tags: [],
|
|
176243
176277
|
makeOptional: false,
|
|
176244
176278
|
known: true
|
|
176245
176279
|
};
|
|
176246
176280
|
case "Contains": return {
|
|
176247
|
-
|
|
176281
|
+
tags: [],
|
|
176248
176282
|
makeOptional: false,
|
|
176249
176283
|
known: true
|
|
176250
176284
|
};
|
|
176251
176285
|
case "NotContains": return {
|
|
176252
|
-
|
|
176286
|
+
tags: [],
|
|
176253
176287
|
makeOptional: false,
|
|
176254
176288
|
known: true
|
|
176255
176289
|
};
|
|
176256
176290
|
case "IsHash": return {
|
|
176257
|
-
|
|
176291
|
+
tags: [],
|
|
176258
176292
|
makeOptional: false,
|
|
176259
176293
|
known: true
|
|
176260
176294
|
};
|
|
176261
176295
|
case "IsLatitude": return {
|
|
176262
|
-
|
|
176296
|
+
tags: [],
|
|
176263
176297
|
makeOptional: false,
|
|
176264
176298
|
known: true
|
|
176265
176299
|
};
|
|
176266
176300
|
case "IsLongitude": return {
|
|
176267
|
-
|
|
176301
|
+
tags: [],
|
|
176268
176302
|
makeOptional: false,
|
|
176269
176303
|
known: true
|
|
176270
176304
|
};
|
|
176271
176305
|
case "IsLatLong": return {
|
|
176272
|
-
|
|
176273
|
-
makeOptional: false,
|
|
176274
|
-
known: true
|
|
176275
|
-
};
|
|
176276
|
-
case "Length": return {
|
|
176277
|
-
tag: args[0] ? `tags.MinLength<${args[0]}>` : null,
|
|
176306
|
+
tags: [],
|
|
176278
176307
|
makeOptional: false,
|
|
176279
176308
|
known: true
|
|
176280
176309
|
};
|
|
176310
|
+
case "Length": {
|
|
176311
|
+
const result = [];
|
|
176312
|
+
if (args[0]) result.push(`tags.MinLength<${args[0]}>`);
|
|
176313
|
+
if (args[1]) result.push(`tags.MaxLength<${args[1]}>`);
|
|
176314
|
+
return {
|
|
176315
|
+
tags: result,
|
|
176316
|
+
makeOptional: false,
|
|
176317
|
+
known: true
|
|
176318
|
+
};
|
|
176319
|
+
}
|
|
176281
176320
|
case "IsOptional": return {
|
|
176282
|
-
|
|
176321
|
+
tags: [],
|
|
176283
176322
|
makeOptional: true,
|
|
176284
176323
|
known: true
|
|
176285
176324
|
};
|
|
176286
176325
|
case "Allow": return {
|
|
176287
|
-
|
|
176326
|
+
tags: [],
|
|
176288
176327
|
makeOptional: false,
|
|
176289
176328
|
known: true
|
|
176290
176329
|
};
|
|
@@ -176303,12 +176342,12 @@ function decoratorToBrandedType(decorator) {
|
|
|
176303
176342
|
case "Equals":
|
|
176304
176343
|
case "NotEquals":
|
|
176305
176344
|
case "IsInstance": return {
|
|
176306
|
-
|
|
176345
|
+
tags: [],
|
|
176307
176346
|
makeOptional: false,
|
|
176308
176347
|
known: true
|
|
176309
176348
|
};
|
|
176310
176349
|
default: return {
|
|
176311
|
-
|
|
176350
|
+
tags: [],
|
|
176312
176351
|
makeOptional: false,
|
|
176313
176352
|
known: false
|
|
176314
176353
|
};
|
|
@@ -176316,16 +176355,22 @@ function decoratorToBrandedType(decorator) {
|
|
|
176316
176355
|
}
|
|
176317
176356
|
/**
|
|
176318
176357
|
* Transform class-validator DTOs to interfaces with branded types.
|
|
176319
|
-
*
|
|
176358
|
+
* Processes any file that imports from class-validator. Skips NestJS framework
|
|
176359
|
+
* classes and entity/ORM classes via decorator detection.
|
|
176320
176360
|
*/
|
|
176321
176361
|
function transformClassValidator(file, report) {
|
|
176322
|
-
if (!file.getFilePath().endsWith(".dto.ts")) return 0;
|
|
176323
176362
|
const cvImport = file.getImportDeclaration((d) => d.getModuleSpecifierValue() === "class-validator");
|
|
176324
176363
|
if (!cvImport) return 0;
|
|
176325
176364
|
let count = 0;
|
|
176326
176365
|
const filePath = file.getFilePath();
|
|
176327
176366
|
let needsTagsImport = false;
|
|
176328
176367
|
const unknownDecorators = /* @__PURE__ */ new Set();
|
|
176368
|
+
const MAPPED_TYPE_HELPERS = new Set([
|
|
176369
|
+
"OmitType",
|
|
176370
|
+
"PickType",
|
|
176371
|
+
"PartialType",
|
|
176372
|
+
"IntersectionType"
|
|
176373
|
+
]);
|
|
176329
176374
|
const classes = file.getClasses();
|
|
176330
176375
|
for (const cls of classes) {
|
|
176331
176376
|
if (cls.getDecorators().some((d) => [
|
|
@@ -176335,8 +176380,55 @@ function transformClassValidator(file, report) {
|
|
|
176335
176380
|
"Guard",
|
|
176336
176381
|
"Interceptor",
|
|
176337
176382
|
"Pipe",
|
|
176338
|
-
"Filter"
|
|
176383
|
+
"Filter",
|
|
176384
|
+
"Entity",
|
|
176385
|
+
"Schema",
|
|
176386
|
+
"Table",
|
|
176387
|
+
"Document",
|
|
176388
|
+
"Model",
|
|
176389
|
+
"ViewEntity",
|
|
176390
|
+
"ChildEntity",
|
|
176391
|
+
"Embeddable"
|
|
176339
176392
|
].includes(d.getName()))) continue;
|
|
176393
|
+
const extendsExpr = cls.getExtends();
|
|
176394
|
+
if (extendsExpr && MAPPED_TYPE_HELPERS.has(extendsExpr.getExpression().getText().split("(")[0].trim())) {
|
|
176395
|
+
for (const prop of cls.getProperties()) {
|
|
176396
|
+
const brandedTypes = [];
|
|
176397
|
+
let isOptional = prop.hasQuestionToken();
|
|
176398
|
+
const decoratorsToRemove = [];
|
|
176399
|
+
for (const dec of prop.getDecorators()) {
|
|
176400
|
+
const decName = dec.getName();
|
|
176401
|
+
if (decName.startsWith("Api")) continue;
|
|
176402
|
+
if (NON_CLASS_VALIDATOR_DECORATORS.has(decName)) continue;
|
|
176403
|
+
const result = decoratorToBrandedType(dec);
|
|
176404
|
+
if (result.tags.length > 0) {
|
|
176405
|
+
brandedTypes.push(...result.tags);
|
|
176406
|
+
needsTagsImport = true;
|
|
176407
|
+
}
|
|
176408
|
+
if (result.makeOptional) isOptional = true;
|
|
176409
|
+
if (!result.known) {
|
|
176410
|
+
unknownDecorators.add(decName);
|
|
176411
|
+
continue;
|
|
176412
|
+
}
|
|
176413
|
+
decoratorsToRemove.push(dec);
|
|
176414
|
+
}
|
|
176415
|
+
for (const dec of decoratorsToRemove.reverse()) {
|
|
176416
|
+
dec.remove();
|
|
176417
|
+
count++;
|
|
176418
|
+
}
|
|
176419
|
+
if (brandedTypes.length > 0) {
|
|
176420
|
+
const currentType = prop.getTypeNode()?.getText() ?? "any";
|
|
176421
|
+
prop.setType(`${currentType} & ${brandedTypes.join(" & ")}`);
|
|
176422
|
+
}
|
|
176423
|
+
if (isOptional && !prop.hasQuestionToken()) prop.setHasQuestionToken(true);
|
|
176424
|
+
}
|
|
176425
|
+
const className = cls.getName();
|
|
176426
|
+
if (className) {
|
|
176427
|
+
report.info(filePath, "class-validator", `Stripped decorators from class ${className} (kept as class — extends mapped type)`);
|
|
176428
|
+
count++;
|
|
176429
|
+
}
|
|
176430
|
+
continue;
|
|
176431
|
+
}
|
|
176340
176432
|
const properties = cls.getProperties();
|
|
176341
176433
|
const interfaceProps = [];
|
|
176342
176434
|
for (const prop of properties) {
|
|
@@ -176350,8 +176442,8 @@ function transformClassValidator(file, report) {
|
|
|
176350
176442
|
if (decName.startsWith("Api")) continue;
|
|
176351
176443
|
if (NON_CLASS_VALIDATOR_DECORATORS.has(decName)) continue;
|
|
176352
176444
|
const result = decoratorToBrandedType(dec);
|
|
176353
|
-
if (result.
|
|
176354
|
-
brandedTypes.push(result.
|
|
176445
|
+
if (result.tags.length > 0) {
|
|
176446
|
+
brandedTypes.push(...result.tags);
|
|
176355
176447
|
needsTagsImport = true;
|
|
176356
176448
|
}
|
|
176357
176449
|
if (result.makeOptional) isOptional = true;
|
|
@@ -176374,7 +176466,6 @@ function transformClassValidator(file, report) {
|
|
|
176374
176466
|
if (isExported && isDefault) interfaceText = `export default interface ${className}`;
|
|
176375
176467
|
else if (isExported) interfaceText = `export interface ${className}`;
|
|
176376
176468
|
else interfaceText = `interface ${className}`;
|
|
176377
|
-
const extendsExpr = cls.getExtends();
|
|
176378
176469
|
if (extendsExpr) interfaceText += ` extends ${extendsExpr.getText()}`;
|
|
176379
176470
|
interfaceText += ` {\n${interfaceProps.join("\n")}\n}`;
|
|
176380
176471
|
cls.replaceWithText(interfaceText);
|
|
@@ -176572,28 +176663,101 @@ function transformSwagger(file, report) {
|
|
|
176572
176663
|
const fullText = file.getFullText();
|
|
176573
176664
|
if (fullText.includes("SwaggerModule") || fullText.includes("DocumentBuilder")) report.todo(filePath, "swagger", "Remove SwaggerModule.setup() and DocumentBuilder — tsgonest generates OpenAPI at build time. Configure in tsgonest.config.ts → openapi.");
|
|
176574
176665
|
if (fullText.includes("NestiaSwaggerComposer")) report.todo(filePath, "swagger", "Remove NestiaSwaggerComposer — tsgonest generates OpenAPI at build time. Configure in tsgonest.config.ts → openapi.");
|
|
176666
|
+
const decoratorsToRemove = [];
|
|
176575
176667
|
file.forEachDescendant((node) => {
|
|
176576
176668
|
if (!import_ts_morph.Node.isDecorator(node)) return;
|
|
176577
176669
|
const name = node.getName();
|
|
176578
176670
|
if (REMOVABLE_DECORATORS.has(name)) {
|
|
176579
|
-
|
|
176580
|
-
count++;
|
|
176671
|
+
decoratorsToRemove.push(node);
|
|
176581
176672
|
return;
|
|
176582
176673
|
}
|
|
176583
176674
|
if (name in SECURITY_DECORATORS) {
|
|
176584
176675
|
const schemeInfo = SECURITY_DECORATORS[name];
|
|
176585
176676
|
report.detectedSecuritySchemes.set(schemeInfo.name, schemeInfo.config);
|
|
176586
176677
|
report.info(filePath, "swagger", `Auto-detected ${name} → securitySchemes.${schemeInfo.name} (will be added to tsgonest.config.ts)`, node.getStartLineNumber());
|
|
176587
|
-
|
|
176588
|
-
count++;
|
|
176678
|
+
decoratorsToRemove.push(node);
|
|
176589
176679
|
}
|
|
176590
176680
|
});
|
|
176591
|
-
|
|
176681
|
+
for (const node of decoratorsToRemove.reverse()) {
|
|
176682
|
+
node.remove();
|
|
176683
|
+
count++;
|
|
176684
|
+
}
|
|
176685
|
+
const MAPPED_TYPE_HELPERS = new Set([
|
|
176686
|
+
"OmitType",
|
|
176687
|
+
"PickType",
|
|
176688
|
+
"PartialType",
|
|
176689
|
+
"IntersectionType"
|
|
176690
|
+
]);
|
|
176691
|
+
{
|
|
176592
176692
|
const remainingText = file.getFullText();
|
|
176593
|
-
const namedImports = swaggerImport.getNamedImports().map((n) => n.getName());
|
|
176594
176693
|
const importText = swaggerImport.getText();
|
|
176595
176694
|
const withoutImport = remainingText.replace(importText, "");
|
|
176596
|
-
|
|
176695
|
+
const namedImports = swaggerImport.getNamedImports();
|
|
176696
|
+
const toRemove = namedImports.filter((n) => !withoutImport.includes(n.getName()));
|
|
176697
|
+
if (toRemove.length === namedImports.length) {
|
|
176698
|
+
swaggerImport.remove();
|
|
176699
|
+
count++;
|
|
176700
|
+
} else if (toRemove.length > 0) {
|
|
176701
|
+
for (const imp of toRemove.reverse()) {
|
|
176702
|
+
imp.remove();
|
|
176703
|
+
count++;
|
|
176704
|
+
}
|
|
176705
|
+
const remaining = swaggerImport.getNamedImports();
|
|
176706
|
+
if (remaining.length > 0 && remaining.every((n) => MAPPED_TYPE_HELPERS.has(n.getName()))) {
|
|
176707
|
+
swaggerImport.setModuleSpecifier("@nestjs/mapped-types");
|
|
176708
|
+
report.info(filePath, "swagger", `Rewrote mapped-type import from @nestjs/swagger → @nestjs/mapped-types`);
|
|
176709
|
+
count++;
|
|
176710
|
+
}
|
|
176711
|
+
}
|
|
176712
|
+
}
|
|
176713
|
+
return count;
|
|
176714
|
+
}
|
|
176715
|
+
|
|
176716
|
+
//#endregion
|
|
176717
|
+
//#region src/migrate/transforms/baseurl-imports.ts
|
|
176718
|
+
const TS_EXTENSIONS = [
|
|
176719
|
+
".ts",
|
|
176720
|
+
".tsx",
|
|
176721
|
+
"/index.ts",
|
|
176722
|
+
"/index.tsx"
|
|
176723
|
+
];
|
|
176724
|
+
/**
|
|
176725
|
+
* Rewrite baseUrl-relative imports to relative paths.
|
|
176726
|
+
* Must run BEFORE other transforms — operates on raw import specifiers.
|
|
176727
|
+
*
|
|
176728
|
+
* @param file The source file to transform
|
|
176729
|
+
* @param baseDir The absolute directory that baseUrl resolves to (e.g., project root)
|
|
176730
|
+
* @param report Migration report for tracking changes
|
|
176731
|
+
* @param fileExists Optional file existence checker (defaults to fs.existsSync, injectable for tests)
|
|
176732
|
+
* @returns Number of rewrites applied
|
|
176733
|
+
*/
|
|
176734
|
+
function rewriteBaseUrlImports(file, baseDir, report, fileExists = fs.existsSync) {
|
|
176735
|
+
let count = 0;
|
|
176736
|
+
const filePath = file.getFilePath();
|
|
176737
|
+
const fileDir = (0, path.dirname)(filePath);
|
|
176738
|
+
for (const imp of file.getImportDeclarations()) {
|
|
176739
|
+
const specifier = imp.getModuleSpecifierValue();
|
|
176740
|
+
if (specifier.startsWith(".")) continue;
|
|
176741
|
+
if (specifier.startsWith("@")) continue;
|
|
176742
|
+
const candidate = (0, path.resolve)(baseDir, specifier);
|
|
176743
|
+
let resolvedPath = null;
|
|
176744
|
+
for (const ext of TS_EXTENSIONS) {
|
|
176745
|
+
const tryPath = candidate + ext;
|
|
176746
|
+
if (fileExists(tryPath)) {
|
|
176747
|
+
resolvedPath = tryPath;
|
|
176748
|
+
break;
|
|
176749
|
+
}
|
|
176750
|
+
}
|
|
176751
|
+
if (!resolvedPath && fileExists(candidate)) resolvedPath = candidate;
|
|
176752
|
+
if (!resolvedPath) continue;
|
|
176753
|
+
let relativePath = (0, path.relative)(fileDir, resolvedPath);
|
|
176754
|
+
relativePath = relativePath.split(path.sep).join("/");
|
|
176755
|
+
relativePath = relativePath.replace(/\.tsx?$/, "");
|
|
176756
|
+
relativePath = relativePath.replace(/\/index$/, "");
|
|
176757
|
+
if (!relativePath.startsWith(".")) relativePath = "./" + relativePath;
|
|
176758
|
+
imp.setModuleSpecifier(relativePath);
|
|
176759
|
+
count++;
|
|
176760
|
+
report.info(filePath, "general", `Rewrote baseUrl import '${specifier}' → '${relativePath}'`);
|
|
176597
176761
|
}
|
|
176598
176762
|
return count;
|
|
176599
176763
|
}
|
|
@@ -176624,7 +176788,8 @@ var MigrateReport = class {
|
|
|
176624
176788
|
typia: 0,
|
|
176625
176789
|
classValidator: 0,
|
|
176626
176790
|
classTransformer: 0,
|
|
176627
|
-
swagger: 0
|
|
176791
|
+
swagger: 0,
|
|
176792
|
+
baseUrlRewrites: 0
|
|
176628
176793
|
};
|
|
176629
176794
|
filesChanged = /* @__PURE__ */ new Set();
|
|
176630
176795
|
packageJsonChanges = {
|
|
@@ -176670,7 +176835,7 @@ var MigrateReport = class {
|
|
|
176670
176835
|
});
|
|
176671
176836
|
}
|
|
176672
176837
|
get totalTransforms() {
|
|
176673
|
-
return this.stats.nestia + this.stats.typia + this.stats.classValidator + this.stats.classTransformer + this.stats.swagger;
|
|
176838
|
+
return this.stats.nestia + this.stats.typia + this.stats.classValidator + this.stats.classTransformer + this.stats.swagger + this.stats.baseUrlRewrites;
|
|
176674
176839
|
}
|
|
176675
176840
|
get todos() {
|
|
176676
176841
|
return this.items.filter((i) => i.severity === "todo");
|
|
@@ -176687,6 +176852,7 @@ var MigrateReport = class {
|
|
|
176687
176852
|
if (this.stats.classValidator > 0) console.log(` class-validator → tsgonest: ${this.stats.classValidator}`);
|
|
176688
176853
|
if (this.stats.classTransformer > 0) console.log(` class-transformer → tsgonest: ${this.stats.classTransformer}`);
|
|
176689
176854
|
if (this.stats.swagger > 0) console.log(` @nestjs/swagger cleanup: ${this.stats.swagger}`);
|
|
176855
|
+
if (this.stats.baseUrlRewrites > 0) console.log(` baseUrl import rewrites: ${this.stats.baseUrlRewrites}`);
|
|
176690
176856
|
console.log(` total: ${this.totalTransforms}`);
|
|
176691
176857
|
console.log(` files changed: ${this.filesChanged.size}`);
|
|
176692
176858
|
if (this.configFileGenerated) console.log(`\n Generated: tsgonest.config.ts`);
|
|
@@ -176726,6 +176892,7 @@ var MigrateReport = class {
|
|
|
176726
176892
|
if (this.stats.classValidator > 0) lines.push(`| class-validator → tsgonest | ${this.stats.classValidator} |`);
|
|
176727
176893
|
if (this.stats.classTransformer > 0) lines.push(`| class-transformer → tsgonest | ${this.stats.classTransformer} |`);
|
|
176728
176894
|
if (this.stats.swagger > 0) lines.push(`| @nestjs/swagger cleanup | ${this.stats.swagger} |`);
|
|
176895
|
+
if (this.stats.baseUrlRewrites > 0) lines.push(`| baseUrl import rewrites | ${this.stats.baseUrlRewrites} |`);
|
|
176729
176896
|
lines.push(`| **Total** | **${this.totalTransforms}** |`);
|
|
176730
176897
|
lines.push(`| Files changed | ${this.filesChanged.size} |`);
|
|
176731
176898
|
lines.push("");
|
|
@@ -177350,13 +177517,51 @@ async function main() {
|
|
|
177350
177517
|
const snapshots = /* @__PURE__ */ new Map();
|
|
177351
177518
|
for (const file of files) snapshots.set(file, file.getFullText());
|
|
177352
177519
|
const report = new MigrateReport();
|
|
177353
|
-
if (plan.transformSources && sources.length > 0)
|
|
177354
|
-
|
|
177355
|
-
|
|
177356
|
-
|
|
177357
|
-
|
|
177358
|
-
|
|
177359
|
-
|
|
177520
|
+
if (plan.transformSources && sources.length > 0) {
|
|
177521
|
+
const tsconfigForBaseUrl = (0, path.resolve)(cwd, opts.tsconfig);
|
|
177522
|
+
let baseUrl;
|
|
177523
|
+
try {
|
|
177524
|
+
const tsc = (0, jsonc_parser.parse)((0, fs.readFileSync)(tsconfigForBaseUrl, "utf-8"), [], { allowTrailingComma: true });
|
|
177525
|
+
if (tsc?.compilerOptions?.baseUrl) baseUrl = tsc.compilerOptions.baseUrl;
|
|
177526
|
+
} catch {}
|
|
177527
|
+
if (baseUrl) {
|
|
177528
|
+
const baseDir = (0, path.resolve)((0, path.dirname)(tsconfigForBaseUrl), baseUrl);
|
|
177529
|
+
for (const file of files) report.stats.baseUrlRewrites += rewriteBaseUrlImports(file, baseDir, report);
|
|
177530
|
+
}
|
|
177531
|
+
for (const file of files) {
|
|
177532
|
+
if (detected.hasNestia) report.stats.nestia += transformNestia(file, report);
|
|
177533
|
+
if (detected.hasTypia) report.stats.typia += transformTypiaTags(file, report);
|
|
177534
|
+
if (detected.hasClassValidator) report.stats.classValidator += transformClassValidator(file, report);
|
|
177535
|
+
if (detected.hasClassTransformer) report.stats.classTransformer += transformClassTransformer(file, report);
|
|
177536
|
+
if (detected.hasSwagger) report.stats.swagger += transformSwagger(file, report);
|
|
177537
|
+
cleanupImports(file);
|
|
177538
|
+
}
|
|
177539
|
+
}
|
|
177540
|
+
const stillImported = /* @__PURE__ */ new Set();
|
|
177541
|
+
for (const file of files) for (const imp of file.getImportDeclarations()) stillImported.add(imp.getModuleSpecifierValue());
|
|
177542
|
+
if (plan.removeClassDeps) {
|
|
177543
|
+
const stillUsed = pkgInfo.classDeps.filter((d) => stillImported.has(d));
|
|
177544
|
+
if (stillUsed.length > 0) {
|
|
177545
|
+
pkgInfo.classDeps = pkgInfo.classDeps.filter((d) => !stillUsed.includes(d));
|
|
177546
|
+
plan.removeClassDeps = pkgInfo.classDeps.length > 0;
|
|
177547
|
+
report.warn("package.json", "general", `Kept ${stillUsed.join(", ")} in package.json — still imported in source files.`);
|
|
177548
|
+
}
|
|
177549
|
+
}
|
|
177550
|
+
if (plan.removeSwaggerDeps) {
|
|
177551
|
+
const stillUsed = pkgInfo.swaggerDeps.filter((d) => stillImported.has(d));
|
|
177552
|
+
if (stillUsed.length > 0) {
|
|
177553
|
+
pkgInfo.swaggerDeps = pkgInfo.swaggerDeps.filter((d) => !stillUsed.includes(d));
|
|
177554
|
+
plan.removeSwaggerDeps = pkgInfo.swaggerDeps.length > 0;
|
|
177555
|
+
report.warn("package.json", "general", `Kept ${stillUsed.join(", ")} in package.json — still imported in source files (e.g., custom decorators).`);
|
|
177556
|
+
}
|
|
177557
|
+
}
|
|
177558
|
+
if (plan.removeNestiaDeps) {
|
|
177559
|
+
const stillUsed = pkgInfo.nestiaDeps.filter((d) => stillImported.has(d));
|
|
177560
|
+
if (stillUsed.length > 0) {
|
|
177561
|
+
pkgInfo.nestiaDeps = pkgInfo.nestiaDeps.filter((d) => !stillUsed.includes(d));
|
|
177562
|
+
plan.removeNestiaDeps = pkgInfo.nestiaDeps.length > 0;
|
|
177563
|
+
report.warn("package.json", "general", `Kept ${stillUsed.join(", ")} in package.json — still imported in source files.`);
|
|
177564
|
+
}
|
|
177360
177565
|
}
|
|
177361
177566
|
for (const file of files) {
|
|
177362
177567
|
const original = snapshots.get(file);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsgonest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "TypeScript compiler with runtime validation, serialization, and OpenAPI generation for NestJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"jsonc-parser": "^3.3.1",
|
|
35
|
-
"@tsgonest/
|
|
36
|
-
"@tsgonest/
|
|
35
|
+
"@tsgonest/runtime": "0.8.1",
|
|
36
|
+
"@tsgonest/types": "0.8.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"ts-morph": "^25.0.0",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"vitest": "^4.0.0"
|
|
43
43
|
},
|
|
44
44
|
"optionalDependencies": {
|
|
45
|
-
"@tsgonest/cli-darwin-
|
|
46
|
-
"@tsgonest/cli-
|
|
47
|
-
"@tsgonest/cli-
|
|
48
|
-
"@tsgonest/cli-
|
|
49
|
-
"@tsgonest/cli-win32-arm64": "0.
|
|
50
|
-
"@tsgonest/cli-
|
|
45
|
+
"@tsgonest/cli-darwin-x64": "0.8.1",
|
|
46
|
+
"@tsgonest/cli-linux-arm64": "0.8.1",
|
|
47
|
+
"@tsgonest/cli-darwin-arm64": "0.8.1",
|
|
48
|
+
"@tsgonest/cli-win32-x64": "0.8.1",
|
|
49
|
+
"@tsgonest/cli-win32-arm64": "0.8.1",
|
|
50
|
+
"@tsgonest/cli-linux-x64": "0.8.1"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "tsdown",
|