@trapi/metadata 1.2.3 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -16
- package/dist/index.d.mts +1718 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3310 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +27 -15
- package/dist/cache/client.d.ts +0 -15
- package/dist/cache/client.d.ts.map +0 -1
- package/dist/cache/client.js +0 -98
- package/dist/cache/client.js.map +0 -1
- package/dist/cache/index.d.ts +0 -4
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/index.js +0 -26
- package/dist/cache/index.js.map +0 -1
- package/dist/cache/type.d.ts +0 -33
- package/dist/cache/type.d.ts.map +0 -1
- package/dist/cache/type.js +0 -9
- package/dist/cache/type.js.map +0 -1
- package/dist/cache/utils.d.ts +0 -4
- package/dist/cache/utils.d.ts.map +0 -1
- package/dist/cache/utils.js +0 -53
- package/dist/cache/utils.js.map +0 -1
- package/dist/config/index.d.ts +0 -2
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -24
- package/dist/config/index.js.map +0 -1
- package/dist/config/type.d.ts +0 -37
- package/dist/config/type.d.ts.map +0 -1
- package/dist/config/type.js +0 -9
- package/dist/config/type.js.map +0 -1
- package/dist/decorator/constants.d.ts +0 -41
- package/dist/decorator/constants.d.ts.map +0 -1
- package/dist/decorator/constants.js +0 -53
- package/dist/decorator/constants.js.map +0 -1
- package/dist/decorator/index.d.ts +0 -7
- package/dist/decorator/index.d.ts.map +0 -1
- package/dist/decorator/index.js +0 -29
- package/dist/decorator/index.js.map +0 -1
- package/dist/decorator/preset/index.d.ts +0 -3
- package/dist/decorator/preset/index.d.ts.map +0 -1
- package/dist/decorator/preset/index.js +0 -25
- package/dist/decorator/preset/index.js.map +0 -1
- package/dist/decorator/preset/type.d.ts +0 -6
- package/dist/decorator/preset/type.d.ts.map +0 -1
- package/dist/decorator/preset/type.js +0 -9
- package/dist/decorator/preset/type.js.map +0 -1
- package/dist/decorator/preset/utils/check.d.ts +0 -3
- package/dist/decorator/preset/utils/check.d.ts.map +0 -1
- package/dist/decorator/preset/utils/check.js +0 -18
- package/dist/decorator/preset/utils/check.js.map +0 -1
- package/dist/decorator/preset/utils/index.d.ts +0 -4
- package/dist/decorator/preset/utils/index.d.ts.map +0 -1
- package/dist/decorator/preset/utils/index.js +0 -26
- package/dist/decorator/preset/utils/index.js.map +0 -1
- package/dist/decorator/preset/utils/load.d.ts +0 -3
- package/dist/decorator/preset/utils/load.d.ts.map +0 -1
- package/dist/decorator/preset/utils/load.js +0 -51
- package/dist/decorator/preset/utils/load.js.map +0 -1
- package/dist/decorator/preset/utils/normalize.d.ts +0 -2
- package/dist/decorator/preset/utils/normalize.d.ts.map +0 -1
- package/dist/decorator/preset/utils/normalize.js +0 -27
- package/dist/decorator/preset/utils/normalize.js.map +0 -1
- package/dist/decorator/property-manager/index.d.ts +0 -3
- package/dist/decorator/property-manager/index.d.ts.map +0 -1
- package/dist/decorator/property-manager/index.js +0 -25
- package/dist/decorator/property-manager/index.js.map +0 -1
- package/dist/decorator/property-manager/module.d.ts +0 -17
- package/dist/decorator/property-manager/module.d.ts.map +0 -1
- package/dist/decorator/property-manager/module.js +0 -59
- package/dist/decorator/property-manager/module.js.map +0 -1
- package/dist/decorator/property-manager/utils.d.ts +0 -6
- package/dist/decorator/property-manager/utils.d.ts.map +0 -1
- package/dist/decorator/property-manager/utils.js +0 -92
- package/dist/decorator/property-manager/utils.js.map +0 -1
- package/dist/decorator/resolver/index.d.ts +0 -3
- package/dist/decorator/resolver/index.d.ts.map +0 -1
- package/dist/decorator/resolver/index.js +0 -25
- package/dist/decorator/resolver/index.js.map +0 -1
- package/dist/decorator/resolver/module.d.ts +0 -27
- package/dist/decorator/resolver/module.d.ts.map +0 -1
- package/dist/decorator/resolver/module.js +0 -72
- package/dist/decorator/resolver/module.js.map +0 -1
- package/dist/decorator/resolver/type.d.ts +0 -10
- package/dist/decorator/resolver/type.d.ts.map +0 -1
- package/dist/decorator/resolver/type.js +0 -3
- package/dist/decorator/resolver/type.js.map +0 -1
- package/dist/decorator/sets/class.d.ts +0 -7
- package/dist/decorator/sets/class.d.ts.map +0 -1
- package/dist/decorator/sets/class.js +0 -9
- package/dist/decorator/sets/class.js.map +0 -1
- package/dist/decorator/sets/index.d.ts +0 -8
- package/dist/decorator/sets/index.d.ts.map +0 -1
- package/dist/decorator/sets/index.js +0 -29
- package/dist/decorator/sets/index.js.map +0 -1
- package/dist/decorator/sets/method.d.ts +0 -14
- package/dist/decorator/sets/method.d.ts.map +0 -1
- package/dist/decorator/sets/method.js +0 -9
- package/dist/decorator/sets/method.js.map +0 -1
- package/dist/decorator/sets/mixed.d.ts +0 -12
- package/dist/decorator/sets/mixed.d.ts.map +0 -1
- package/dist/decorator/sets/mixed.js +0 -9
- package/dist/decorator/sets/mixed.js.map +0 -1
- package/dist/decorator/sets/parameter.d.ts +0 -21
- package/dist/decorator/sets/parameter.d.ts.map +0 -1
- package/dist/decorator/sets/parameter.js +0 -9
- package/dist/decorator/sets/parameter.js.map +0 -1
- package/dist/decorator/sets/request.d.ts +0 -6
- package/dist/decorator/sets/request.d.ts.map +0 -1
- package/dist/decorator/sets/request.js +0 -9
- package/dist/decorator/sets/request.js.map +0 -1
- package/dist/decorator/sets/response.d.ts +0 -17
- package/dist/decorator/sets/response.d.ts.map +0 -1
- package/dist/decorator/sets/response.js +0 -9
- package/dist/decorator/sets/response.js.map +0 -1
- package/dist/decorator/sets/type.d.ts +0 -4
- package/dist/decorator/sets/type.d.ts.map +0 -1
- package/dist/decorator/sets/type.js +0 -3
- package/dist/decorator/sets/type.js.map +0 -1
- package/dist/decorator/type.d.ts +0 -32
- package/dist/decorator/type.d.ts.map +0 -1
- package/dist/decorator/type.js +0 -9
- package/dist/decorator/type.js.map +0 -1
- package/dist/error/base.d.ts +0 -3
- package/dist/error/base.d.ts.map +0 -1
- package/dist/error/base.js +0 -13
- package/dist/error/base.js.map +0 -1
- package/dist/error/index.d.ts +0 -2
- package/dist/error/index.d.ts.map +0 -1
- package/dist/error/index.js +0 -24
- package/dist/error/index.js.map +0 -1
- package/dist/generator/abstract.d.ts +0 -19
- package/dist/generator/abstract.d.ts.map +0 -1
- package/dist/generator/abstract.js +0 -151
- package/dist/generator/abstract.js.map +0 -1
- package/dist/generator/controller/index.d.ts +0 -3
- package/dist/generator/controller/index.d.ts.map +0 -1
- package/dist/generator/controller/index.js +0 -25
- package/dist/generator/controller/index.js.map +0 -1
- package/dist/generator/controller/module.d.ts +0 -13
- package/dist/generator/controller/module.d.ts.map +0 -1
- package/dist/generator/controller/module.js +0 -68
- package/dist/generator/controller/module.js.map +0 -1
- package/dist/generator/controller/type.d.ts +0 -47
- package/dist/generator/controller/type.d.ts.map +0 -1
- package/dist/generator/controller/type.js +0 -3
- package/dist/generator/controller/type.js.map +0 -1
- package/dist/generator/index.d.ts +0 -7
- package/dist/generator/index.d.ts.map +0 -1
- package/dist/generator/index.js +0 -29
- package/dist/generator/index.js.map +0 -1
- package/dist/generator/metadata/index.d.ts +0 -4
- package/dist/generator/metadata/index.d.ts.map +0 -1
- package/dist/generator/metadata/index.js +0 -26
- package/dist/generator/metadata/index.js.map +0 -1
- package/dist/generator/metadata/module.d.ts +0 -39
- package/dist/generator/metadata/module.d.ts.map +0 -1
- package/dist/generator/metadata/module.js +0 -140
- package/dist/generator/metadata/module.js.map +0 -1
- package/dist/generator/metadata/type.d.ts +0 -25
- package/dist/generator/metadata/type.d.ts.map +0 -1
- package/dist/generator/metadata/type.js +0 -3
- package/dist/generator/metadata/type.js.map +0 -1
- package/dist/generator/metadata/utils.d.ts +0 -3
- package/dist/generator/metadata/utils.d.ts.map +0 -1
- package/dist/generator/metadata/utils.js +0 -19
- package/dist/generator/metadata/utils.js.map +0 -1
- package/dist/generator/method/constants.d.ts +0 -11
- package/dist/generator/method/constants.d.ts.map +0 -1
- package/dist/generator/method/constants.js +0 -21
- package/dist/generator/method/constants.js.map +0 -1
- package/dist/generator/method/index.d.ts +0 -4
- package/dist/generator/method/index.d.ts.map +0 -1
- package/dist/generator/method/index.js +0 -26
- package/dist/generator/method/index.js.map +0 -1
- package/dist/generator/method/module.d.ts +0 -19
- package/dist/generator/method/module.d.ts.map +0 -1
- package/dist/generator/method/module.js +0 -188
- package/dist/generator/method/module.js.map +0 -1
- package/dist/generator/method/type.d.ts +0 -23
- package/dist/generator/method/type.d.ts.map +0 -1
- package/dist/generator/method/type.js +0 -9
- package/dist/generator/method/type.js.map +0 -1
- package/dist/generator/parameter/constants.d.ts +0 -19
- package/dist/generator/parameter/constants.d.ts.map +0 -1
- package/dist/generator/parameter/constants.js +0 -30
- package/dist/generator/parameter/constants.js.map +0 -1
- package/dist/generator/parameter/error.d.ts +0 -35
- package/dist/generator/parameter/error.d.ts.map +0 -1
- package/dist/generator/parameter/error.js +0 -68
- package/dist/generator/parameter/error.js.map +0 -1
- package/dist/generator/parameter/index.d.ts +0 -5
- package/dist/generator/parameter/index.d.ts.map +0 -1
- package/dist/generator/parameter/index.js +0 -27
- package/dist/generator/parameter/index.js.map +0 -1
- package/dist/generator/parameter/module.d.ts +0 -29
- package/dist/generator/parameter/module.d.ts.map +0 -1
- package/dist/generator/parameter/module.js +0 -556
- package/dist/generator/parameter/module.js.map +0 -1
- package/dist/generator/parameter/type.d.ts +0 -25
- package/dist/generator/parameter/type.d.ts.map +0 -1
- package/dist/generator/parameter/type.js +0 -9
- package/dist/generator/parameter/type.js.map +0 -1
- package/dist/generator/type.d.ts +0 -20
- package/dist/generator/type.d.ts.map +0 -1
- package/dist/generator/type.js +0 -9
- package/dist/generator/type.js.map +0 -1
- package/dist/index.d.ts +0 -10
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -32
- package/dist/index.js.map +0 -1
- package/dist/module.d.ts +0 -5
- package/dist/module.d.ts.map +0 -1
- package/dist/module.js +0 -41
- package/dist/module.js.map +0 -1
- package/dist/resolver/constants.d.ts +0 -36
- package/dist/resolver/constants.d.ts.map +0 -1
- package/dist/resolver/constants.js +0 -47
- package/dist/resolver/constants.js.map +0 -1
- package/dist/resolver/error.d.ts +0 -8
- package/dist/resolver/error.d.ts.map +0 -1
- package/dist/resolver/error.js +0 -41
- package/dist/resolver/error.js.map +0 -1
- package/dist/resolver/extension/index.d.ts +0 -3
- package/dist/resolver/extension/index.d.ts.map +0 -1
- package/dist/resolver/extension/index.js +0 -25
- package/dist/resolver/extension/index.js.map +0 -1
- package/dist/resolver/extension/module.d.ts +0 -5
- package/dist/resolver/extension/module.d.ts.map +0 -1
- package/dist/resolver/extension/module.js +0 -27
- package/dist/resolver/extension/module.js.map +0 -1
- package/dist/resolver/extension/type.d.ts +0 -8
- package/dist/resolver/extension/type.d.ts.map +0 -1
- package/dist/resolver/extension/type.js +0 -9
- package/dist/resolver/extension/type.js.map +0 -1
- package/dist/resolver/index.d.ts +0 -6
- package/dist/resolver/index.d.ts.map +0 -1
- package/dist/resolver/index.js +0 -28
- package/dist/resolver/index.js.map +0 -1
- package/dist/resolver/module.d.ts +0 -57
- package/dist/resolver/module.d.ts.map +0 -1
- package/dist/resolver/module.js +0 -1109
- package/dist/resolver/module.js.map +0 -1
- package/dist/resolver/type.d.ts +0 -139
- package/dist/resolver/type.d.ts.map +0 -1
- package/dist/resolver/type.js +0 -130
- package/dist/resolver/type.js.map +0 -1
- package/dist/type.d.ts +0 -2
- package/dist/type.d.ts.map +0 -1
- package/dist/type.js +0 -9
- package/dist/type.js.map +0 -1
- package/dist/utils/array.d.ts +0 -2
- package/dist/utils/array.d.ts.map +0 -1
- package/dist/utils/array.js +0 -22
- package/dist/utils/array.js.map +0 -1
- package/dist/utils/index.d.ts +0 -10
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -32
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/initializer.d.ts +0 -5
- package/dist/utils/initializer.d.ts.map +0 -1
- package/dist/utils/initializer.js +0 -131
- package/dist/utils/initializer.js.map +0 -1
- package/dist/utils/js-doc/constants.d.ts +0 -19
- package/dist/utils/js-doc/constants.d.ts.map +0 -1
- package/dist/utils/js-doc/constants.js +0 -30
- package/dist/utils/js-doc/constants.js.map +0 -1
- package/dist/utils/js-doc/index.d.ts +0 -4
- package/dist/utils/js-doc/index.d.ts.map +0 -1
- package/dist/utils/js-doc/index.js +0 -26
- package/dist/utils/js-doc/index.js.map +0 -1
- package/dist/utils/js-doc/module.d.ts +0 -9
- package/dist/utils/js-doc/module.d.ts.map +0 -1
- package/dist/utils/js-doc/module.js +0 -98
- package/dist/utils/js-doc/module.js.map +0 -1
- package/dist/utils/js-doc/utils.d.ts +0 -3
- package/dist/utils/js-doc/utils.d.ts.map +0 -1
- package/dist/utils/js-doc/utils.js +0 -29
- package/dist/utils/js-doc/utils.js.map +0 -1
- package/dist/utils/node/index.d.ts +0 -3
- package/dist/utils/node/index.d.ts.map +0 -1
- package/dist/utils/node/index.js +0 -25
- package/dist/utils/node/index.js.map +0 -1
- package/dist/utils/node/module.d.ts +0 -10
- package/dist/utils/node/module.d.ts.map +0 -1
- package/dist/utils/node/module.js +0 -52
- package/dist/utils/node/module.js.map +0 -1
- package/dist/utils/node/type.d.ts +0 -6
- package/dist/utils/node/type.d.ts.map +0 -1
- package/dist/utils/node/type.js +0 -9
- package/dist/utils/node/type.js.map +0 -1
- package/dist/utils/object.d.ts +0 -2
- package/dist/utils/object.d.ts.map +0 -1
- package/dist/utils/object.js +0 -15
- package/dist/utils/object.js.map +0 -1
- package/dist/utils/path-normalize.d.ts +0 -2
- package/dist/utils/path-normalize.d.ts.map +0 -1
- package/dist/utils/path-normalize.js +0 -17
- package/dist/utils/path-normalize.js.map +0 -1
- package/dist/utils/source-files.d.ts +0 -3
- package/dist/utils/source-files.d.ts.map +0 -1
- package/dist/utils/source-files.js +0 -32
- package/dist/utils/source-files.js.map +0 -1
- package/dist/utils/tsconfig/index.d.ts +0 -3
- package/dist/utils/tsconfig/index.d.ts.map +0 -1
- package/dist/utils/tsconfig/index.js +0 -25
- package/dist/utils/tsconfig/index.js.map +0 -1
- package/dist/utils/tsconfig/module.d.ts +0 -4
- package/dist/utils/tsconfig/module.d.ts.map +0 -1
- package/dist/utils/tsconfig/module.js +0 -54
- package/dist/utils/tsconfig/module.js.map +0 -1
- package/dist/utils/tsconfig/type.d.ts +0 -11
- package/dist/utils/tsconfig/type.d.ts.map +0 -1
- package/dist/utils/tsconfig/type.js +0 -9
- package/dist/utils/tsconfig/type.js.map +0 -1
- package/dist/utils/validator/constants.d.ts +0 -13
- package/dist/utils/validator/constants.d.ts.map +0 -1
- package/dist/utils/validator/constants.js +0 -23
- package/dist/utils/validator/constants.js.map +0 -1
- package/dist/utils/validator/index.d.ts +0 -4
- package/dist/utils/validator/index.d.ts.map +0 -1
- package/dist/utils/validator/index.js +0 -26
- package/dist/utils/validator/index.js.map +0 -1
- package/dist/utils/validator/module.d.ts +0 -4
- package/dist/utils/validator/module.d.ts.map +0 -1
- package/dist/utils/validator/module.js +0 -142
- package/dist/utils/validator/module.js.map +0 -1
- package/dist/utils/validator/type.d.ts +0 -6
- package/dist/utils/validator/type.d.ts.map +0 -1
- package/dist/utils/validator/type.js +0 -9
- package/dist/utils/validator/type.js.map +0 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3310 @@
|
|
|
1
|
+
import path, { normalize } from "node:path";
|
|
2
|
+
import { BaseError, isBaseError } from "@ebec/core";
|
|
3
|
+
import * as ts from "typescript";
|
|
4
|
+
import { NodeBuilderFlags, NodeFlags, SymbolFlags, SyntaxKind, canHaveDecorators, canHaveModifiers, convertCompilerOptionsFromJson, createProgram, displayPartsToString, forEachChild, getDecorators, getModifiers, isCallExpression, isClassDeclaration, isEnumDeclaration, isEnumMember, isJSDocParameterTag, isMethodDeclaration, isModuleBlock, isModuleDeclaration, isNumericLiteral, isStringLiteral } from "typescript";
|
|
5
|
+
import { buildFilePath, hasOwnProperty as hasOwnProperty$1, isObject, load, locateMany } from "locter";
|
|
6
|
+
import { Container } from "validup";
|
|
7
|
+
import { createValidator } from "@validup/adapter-zod";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
import { parse, stringify } from "flatted";
|
|
11
|
+
import crypto from "node:crypto";
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { minimatch } from "minimatch";
|
|
15
|
+
//#region src/core/resolver/constants.ts
|
|
16
|
+
let TypeName = /* @__PURE__ */ function(TypeName) {
|
|
17
|
+
TypeName["STRING"] = "string";
|
|
18
|
+
TypeName["BOOLEAN"] = "boolean";
|
|
19
|
+
TypeName["BIGINT"] = "bigint";
|
|
20
|
+
TypeName["DOUBLE"] = "double";
|
|
21
|
+
TypeName["FLOAT"] = "float";
|
|
22
|
+
TypeName["FILE"] = "file";
|
|
23
|
+
TypeName["INTEGER"] = "integer";
|
|
24
|
+
TypeName["LONG"] = "long";
|
|
25
|
+
TypeName["ENUM"] = "enum";
|
|
26
|
+
TypeName["ARRAY"] = "array";
|
|
27
|
+
TypeName["DATETIME"] = "datetime";
|
|
28
|
+
TypeName["DATE"] = "date";
|
|
29
|
+
TypeName["BINARY"] = "binary";
|
|
30
|
+
TypeName["BUFFER"] = "buffer";
|
|
31
|
+
TypeName["BYTE"] = "byte";
|
|
32
|
+
TypeName["VOID"] = "void";
|
|
33
|
+
TypeName["OBJECT"] = "object";
|
|
34
|
+
TypeName["ANY"] = "any";
|
|
35
|
+
TypeName["UNDEFINED"] = "undefined";
|
|
36
|
+
TypeName["NEVER"] = "never";
|
|
37
|
+
TypeName["REF_ENUM"] = "refEnum";
|
|
38
|
+
TypeName["REF_OBJECT"] = "refObject";
|
|
39
|
+
TypeName["REF_ALIAS"] = "refAlias";
|
|
40
|
+
TypeName["NESTED_OBJECT_LITERAL"] = "nestedObjectLiteral";
|
|
41
|
+
TypeName["UNION"] = "union";
|
|
42
|
+
TypeName["INTERSECTION"] = "intersection";
|
|
43
|
+
TypeName["TUPLE"] = "tuple";
|
|
44
|
+
return TypeName;
|
|
45
|
+
}({});
|
|
46
|
+
let UtilityTypeName = /* @__PURE__ */ function(UtilityTypeName) {
|
|
47
|
+
UtilityTypeName["NON_NULLABLE"] = "NonNullable";
|
|
48
|
+
UtilityTypeName["OMIT"] = "Omit";
|
|
49
|
+
UtilityTypeName["PARTIAL"] = "Partial";
|
|
50
|
+
UtilityTypeName["READONLY"] = "Readonly";
|
|
51
|
+
UtilityTypeName["RECORD"] = "Record";
|
|
52
|
+
UtilityTypeName["REQUIRED"] = "Required";
|
|
53
|
+
UtilityTypeName["PICK"] = "Pick";
|
|
54
|
+
UtilityTypeName["EXTRACT"] = "Extract";
|
|
55
|
+
UtilityTypeName["EXCLUDE"] = "Exclude";
|
|
56
|
+
UtilityTypeName["RETURN_TYPE"] = "ReturnType";
|
|
57
|
+
UtilityTypeName["PARAMETERS"] = "Parameters";
|
|
58
|
+
UtilityTypeName["AWAITED"] = "Awaited";
|
|
59
|
+
UtilityTypeName["INSTANCE_TYPE"] = "InstanceType";
|
|
60
|
+
UtilityTypeName["CONSTRUCTOR_PARAMETERS"] = "ConstructorParameters";
|
|
61
|
+
return UtilityTypeName;
|
|
62
|
+
}({});
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/core/resolver/type-guards.ts
|
|
65
|
+
function isAnyType(param) {
|
|
66
|
+
return param.typeName === "any";
|
|
67
|
+
}
|
|
68
|
+
function isUndefinedType(param) {
|
|
69
|
+
return param.typeName === "undefined";
|
|
70
|
+
}
|
|
71
|
+
function isStringType(param) {
|
|
72
|
+
return param.typeName === "string";
|
|
73
|
+
}
|
|
74
|
+
function isBooleanType(param) {
|
|
75
|
+
return param.typeName === "boolean";
|
|
76
|
+
}
|
|
77
|
+
function isBigintType(param) {
|
|
78
|
+
return param.typeName === "bigint";
|
|
79
|
+
}
|
|
80
|
+
function isDoubleType(param) {
|
|
81
|
+
return param.typeName === "double";
|
|
82
|
+
}
|
|
83
|
+
function isFloatType(param) {
|
|
84
|
+
return param.typeName === "float";
|
|
85
|
+
}
|
|
86
|
+
function isIntegerType(param) {
|
|
87
|
+
return param.typeName === "integer";
|
|
88
|
+
}
|
|
89
|
+
function isLongType(param) {
|
|
90
|
+
return param.typeName === "long";
|
|
91
|
+
}
|
|
92
|
+
function isVoidType(param) {
|
|
93
|
+
return typeof param === "undefined" || param.typeName === "void";
|
|
94
|
+
}
|
|
95
|
+
function isNeverType(param) {
|
|
96
|
+
return param.typeName === "never";
|
|
97
|
+
}
|
|
98
|
+
function isDateType(param) {
|
|
99
|
+
return param.typeName === "date";
|
|
100
|
+
}
|
|
101
|
+
function isFileType(param) {
|
|
102
|
+
return param.typeName === "file";
|
|
103
|
+
}
|
|
104
|
+
function isDateTimeType(param) {
|
|
105
|
+
return param.typeName === "datetime";
|
|
106
|
+
}
|
|
107
|
+
function isBinaryType(param) {
|
|
108
|
+
return param.typeName === "binary";
|
|
109
|
+
}
|
|
110
|
+
function isBufferType(param) {
|
|
111
|
+
return param.typeName === "buffer";
|
|
112
|
+
}
|
|
113
|
+
function isByteType(param) {
|
|
114
|
+
return param.typeName === "byte";
|
|
115
|
+
}
|
|
116
|
+
function isObjectType(param) {
|
|
117
|
+
return param.typeName === "object";
|
|
118
|
+
}
|
|
119
|
+
function isEnumType(param) {
|
|
120
|
+
return param.typeName === "enum";
|
|
121
|
+
}
|
|
122
|
+
function isArrayType(param) {
|
|
123
|
+
return param.typeName === "array";
|
|
124
|
+
}
|
|
125
|
+
function isNestedObjectLiteralType(param) {
|
|
126
|
+
return param.typeName === "nestedObjectLiteral";
|
|
127
|
+
}
|
|
128
|
+
function isIntersectionType(param) {
|
|
129
|
+
return param.typeName === "intersection";
|
|
130
|
+
}
|
|
131
|
+
function isUnionType(param) {
|
|
132
|
+
return param.typeName === "union";
|
|
133
|
+
}
|
|
134
|
+
function isTupleType(param) {
|
|
135
|
+
return param.typeName === "tuple";
|
|
136
|
+
}
|
|
137
|
+
function isRefEnumType(param) {
|
|
138
|
+
return param.typeName === "refEnum";
|
|
139
|
+
}
|
|
140
|
+
function isRefObjectType(param) {
|
|
141
|
+
return param.typeName === "refObject";
|
|
142
|
+
}
|
|
143
|
+
function isRefAliasType(param) {
|
|
144
|
+
return param.typeName === "refAlias";
|
|
145
|
+
}
|
|
146
|
+
function isReferenceType(param) {
|
|
147
|
+
return param.typeName === "refAlias" || param.typeName === "refEnum" || param.typeName === "refObject";
|
|
148
|
+
}
|
|
149
|
+
function isPrimitiveType(type) {
|
|
150
|
+
return isAnyType(type) || isBinaryType(type) || isBooleanType(type) || isBufferType(type) || isByteType(type) || isDateType(type) || isDateTimeType(type) || isBigintType(type) || isDoubleType(type) || isFloatType(type) || isFileType(type) || isIntegerType(type) || isLongType(type) || isObjectType(type) || isStringType(type) || isUndefinedType(type);
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/core/error/base.ts
|
|
154
|
+
var MetadataError = class extends BaseError {};
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/core/resolver/error.ts
|
|
157
|
+
var ResolverError = class extends MetadataError {
|
|
158
|
+
file;
|
|
159
|
+
line;
|
|
160
|
+
constructor(message, node, options) {
|
|
161
|
+
const opts = typeof options === "boolean" ? { onlyCurrent: options } : options;
|
|
162
|
+
const onlyCurrent = opts?.onlyCurrent ?? false;
|
|
163
|
+
const parts = [message];
|
|
164
|
+
let file;
|
|
165
|
+
let line;
|
|
166
|
+
if (node) {
|
|
167
|
+
const location = prettyLocationOfNode(node);
|
|
168
|
+
if (location) {
|
|
169
|
+
parts.push(location.text);
|
|
170
|
+
file = location.file;
|
|
171
|
+
line = location.line;
|
|
172
|
+
}
|
|
173
|
+
parts.push(prettyTroubleCause(node, onlyCurrent));
|
|
174
|
+
}
|
|
175
|
+
super({
|
|
176
|
+
message: parts.join("\n"),
|
|
177
|
+
cause: opts?.cause
|
|
178
|
+
});
|
|
179
|
+
this.file = file;
|
|
180
|
+
this.line = line;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
function isResolverError(input) {
|
|
184
|
+
if (!isBaseError(input)) return false;
|
|
185
|
+
return "file" in input && "line" in input;
|
|
186
|
+
}
|
|
187
|
+
function prettyLocationOfNode(node) {
|
|
188
|
+
try {
|
|
189
|
+
const sourceFile = node.getSourceFile();
|
|
190
|
+
if (!sourceFile) return void 0;
|
|
191
|
+
const token = node.getFirstToken() || node.parent?.getFirstToken();
|
|
192
|
+
const start = token ? sourceFile.getLineAndCharacterOfPosition(token.getStart()).line + 1 : void 0;
|
|
193
|
+
const end = token ? sourceFile.getLineAndCharacterOfPosition(token.getEnd()).line + 1 : void 0;
|
|
194
|
+
return {
|
|
195
|
+
text: `At: ${normalize(sourceFile.fileName)}${start ? `:${start}` : ""}${end ? `:${end}` : ""}.`,
|
|
196
|
+
file: sourceFile.fileName,
|
|
197
|
+
line: start
|
|
198
|
+
};
|
|
199
|
+
} catch {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function prettyTroubleCause(node, onlyCurrent = false) {
|
|
204
|
+
try {
|
|
205
|
+
let name;
|
|
206
|
+
if (onlyCurrent || !node.parent) name = node.pos !== -1 ? node.getText() : node.name.text;
|
|
207
|
+
else name = node.parent.pos !== -1 ? node.parent.getText() : node.parent.name.text;
|
|
208
|
+
return `This was caused by '${name}'`;
|
|
209
|
+
} catch {
|
|
210
|
+
return "This was caused by an unknown node";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/core/method/constants.ts
|
|
215
|
+
const MethodName = {
|
|
216
|
+
ALL: "ALL",
|
|
217
|
+
GET: "GET",
|
|
218
|
+
POST: "POST",
|
|
219
|
+
PUT: "PUT",
|
|
220
|
+
DELETE: "DELETE",
|
|
221
|
+
PATCH: "PATCH",
|
|
222
|
+
OPTIONS: "OPTIONS",
|
|
223
|
+
HEAD: "HEAD"
|
|
224
|
+
};
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/core/parameter/constants.ts
|
|
227
|
+
const ParameterSource = {
|
|
228
|
+
BODY: "body",
|
|
229
|
+
BODY_PROP: "bodyProp",
|
|
230
|
+
CONTEXT: "context",
|
|
231
|
+
COOKIE: "cookie",
|
|
232
|
+
HEADER: "header",
|
|
233
|
+
FORM_DATA: "formData",
|
|
234
|
+
QUERY: "query",
|
|
235
|
+
QUERY_PROP: "queryProp",
|
|
236
|
+
PATH: "path"
|
|
237
|
+
};
|
|
238
|
+
const CollectionFormat = {
|
|
239
|
+
CSV: "csv",
|
|
240
|
+
SSV: "ssv",
|
|
241
|
+
TSV: "tsv",
|
|
242
|
+
PIPES: "pipes",
|
|
243
|
+
MULTI: "multi"
|
|
244
|
+
};
|
|
245
|
+
const ParameterErrorCode = {
|
|
246
|
+
TYPE_UNSUPPORTED: "PARAMETER_TYPE_UNSUPPORTED",
|
|
247
|
+
METHOD_UNSUPPORTED: "PARAMETER_METHOD_UNSUPPORTED",
|
|
248
|
+
PATH_MISMATCH: "PARAMETER_PATH_MISMATCH",
|
|
249
|
+
SCOPE_REQUIRED: "PARAMETER_SCOPE_REQUIRED",
|
|
250
|
+
INVALID_EXAMPLE: "PARAMETER_INVALID_EXAMPLE"
|
|
251
|
+
};
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/core/parameter/error.ts
|
|
254
|
+
var ParameterError = class ParameterError extends MetadataError {
|
|
255
|
+
static typeUnsupported(context) {
|
|
256
|
+
const location = context.node ? ParameterError.getCurrentLocation(context.node) : void 0;
|
|
257
|
+
return new ParameterError({
|
|
258
|
+
message: `@${context.decoratorName}('${context.propertyName}') does not support '${context.type.typeName}' type${location ? ` at ${location}` : ""}.`,
|
|
259
|
+
code: ParameterErrorCode.TYPE_UNSUPPORTED
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
static methodUnsupported(context) {
|
|
263
|
+
const location = context.node ? ParameterError.getCurrentLocation(context.node) : void 0;
|
|
264
|
+
return new ParameterError({
|
|
265
|
+
message: `@${context.decoratorName}('${context.propertyName}') does not support method '${context.method}'${location ? ` at ${location}` : ""}.`,
|
|
266
|
+
code: ParameterErrorCode.METHOD_UNSUPPORTED
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
static invalidPathMatch(context) {
|
|
270
|
+
const location = context.node ? ParameterError.getCurrentLocation(context.node) : void 0;
|
|
271
|
+
return new ParameterError({
|
|
272
|
+
message: `@${context.decoratorName}('${context.propertyName}') does not exist in path '${context.path}'${location ? ` at ${location}` : ""}.`,
|
|
273
|
+
code: ParameterErrorCode.PATH_MISMATCH
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
static scopeRequired(context) {
|
|
277
|
+
const location = context.node ? ParameterError.getCurrentLocation(context.node) : void 0;
|
|
278
|
+
return new ParameterError({
|
|
279
|
+
message: `@${context.decoratorName}() requires a scope argument${location ? ` at ${location}` : ""}.`,
|
|
280
|
+
code: ParameterErrorCode.SCOPE_REQUIRED
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
static invalidExampleSchema() {
|
|
284
|
+
return new ParameterError({
|
|
285
|
+
message: "The @example JSDoc tag contains invalid JSON.",
|
|
286
|
+
code: ParameterErrorCode.INVALID_EXAMPLE
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
static getCurrentLocation(node) {
|
|
290
|
+
const parts = [];
|
|
291
|
+
if (isMethodDeclaration(node.parent)) {
|
|
292
|
+
parts.push(node.parent.name.getText());
|
|
293
|
+
if (isClassDeclaration(node.parent.parent)) parts.unshift(node.parent.parent.name.text);
|
|
294
|
+
}
|
|
295
|
+
return parts.join(".");
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/core/generator/error.ts
|
|
300
|
+
var GeneratorError = class extends MetadataError {};
|
|
301
|
+
function isGeneratorError(input) {
|
|
302
|
+
if (!isBaseError(input)) return false;
|
|
303
|
+
return typeof input.code === "string";
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/core/generator/constants.ts
|
|
307
|
+
const GeneratorErrorCode = {
|
|
308
|
+
CONTROLLER_NO_SOURCE_FILE: "GENERATOR_CONTROLLER_NO_SOURCE_FILE",
|
|
309
|
+
CONTROLLER_NO_NAME: "GENERATOR_CONTROLLER_NO_NAME",
|
|
310
|
+
PARAMETER_GENERATION_FAILED: "GENERATOR_PARAMETER_GENERATION_FAILED",
|
|
311
|
+
BODY_PARAMETER_DUPLICATE: "GENERATOR_BODY_PARAMETER_DUPLICATE",
|
|
312
|
+
BODY_FORM_CONFLICT: "GENERATOR_BODY_FORM_CONFLICT"
|
|
313
|
+
};
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/core/validator/error.ts
|
|
316
|
+
var ValidatorError = class extends MetadataError {};
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/core/validator/constants.ts
|
|
319
|
+
let ValidatorName = /* @__PURE__ */ function(ValidatorName) {
|
|
320
|
+
ValidatorName["UNIQUE_ITEMS"] = "uniqueItems";
|
|
321
|
+
ValidatorName["MINIMUM"] = "minimum";
|
|
322
|
+
ValidatorName["MAXIMUM"] = "maximum";
|
|
323
|
+
ValidatorName["MIN_ITEMS"] = "minItems";
|
|
324
|
+
ValidatorName["MAX_ITEMS"] = "maxItems";
|
|
325
|
+
ValidatorName["MIN_LENGTH"] = "minLength";
|
|
326
|
+
ValidatorName["MAX_LENGTH"] = "maxLength";
|
|
327
|
+
ValidatorName["MIN_DATE"] = "minDate";
|
|
328
|
+
ValidatorName["MAX_DATE"] = "maxDate";
|
|
329
|
+
ValidatorName["PATTERN"] = "pattern";
|
|
330
|
+
return ValidatorName;
|
|
331
|
+
}({});
|
|
332
|
+
const ValidatorErrorCode = {
|
|
333
|
+
EXPECTED_NUMBER: "VALIDATOR_EXPECTED_NUMBER",
|
|
334
|
+
EXPECTED_DATE: "VALIDATOR_EXPECTED_DATE",
|
|
335
|
+
EXPECTED_STRING: "VALIDATOR_EXPECTED_STRING"
|
|
336
|
+
};
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/core/error/config.ts
|
|
339
|
+
var ConfigError = class extends MetadataError {};
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/core/error/config-codes.ts
|
|
342
|
+
const ConfigErrorCode = {
|
|
343
|
+
TSCONFIG_MALFORMED: "CONFIG_TSCONFIG_MALFORMED",
|
|
344
|
+
PRESET_NOT_FOUND: "CONFIG_PRESET_NOT_FOUND",
|
|
345
|
+
PRESET_MISSING: "CONFIG_PRESET_MISSING"
|
|
346
|
+
};
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/adapters/decorator/constants.ts
|
|
349
|
+
const ParamKind = {
|
|
350
|
+
Body: "body",
|
|
351
|
+
BodyProp: "bodyProp",
|
|
352
|
+
Context: "context",
|
|
353
|
+
Cookie: "cookie",
|
|
354
|
+
Header: "header",
|
|
355
|
+
FormData: "formData",
|
|
356
|
+
Query: "query",
|
|
357
|
+
QueryProp: "queryProp",
|
|
358
|
+
Path: "path"
|
|
359
|
+
};
|
|
360
|
+
const CollectionKind = {
|
|
361
|
+
Csv: "csv",
|
|
362
|
+
Ssv: "ssv",
|
|
363
|
+
Tsv: "tsv",
|
|
364
|
+
Pipes: "pipes",
|
|
365
|
+
Multi: "multi"
|
|
366
|
+
};
|
|
367
|
+
const DecoratorTargetKind = {
|
|
368
|
+
Class: "class",
|
|
369
|
+
Method: "method",
|
|
370
|
+
Parameter: "parameter",
|
|
371
|
+
Property: "property"
|
|
372
|
+
};
|
|
373
|
+
const DecoratorArgumentKindName = {
|
|
374
|
+
Literal: "literal",
|
|
375
|
+
Object: "object",
|
|
376
|
+
Array: "array",
|
|
377
|
+
Identifier: "identifier",
|
|
378
|
+
Unresolvable: "unresolvable"
|
|
379
|
+
};
|
|
380
|
+
const MarkerName = {
|
|
381
|
+
Hidden: "hidden",
|
|
382
|
+
Deprecated: "deprecated",
|
|
383
|
+
Extension: "extension"
|
|
384
|
+
};
|
|
385
|
+
const NumericKind = {
|
|
386
|
+
Int: "int",
|
|
387
|
+
Long: "long",
|
|
388
|
+
Float: "float",
|
|
389
|
+
Double: "double"
|
|
390
|
+
};
|
|
391
|
+
//#endregion
|
|
392
|
+
//#region src/adapters/decorator/utils.ts
|
|
393
|
+
function matches(match, source) {
|
|
394
|
+
if (match.name !== source.name) return false;
|
|
395
|
+
if (match.on && match.on !== source.target) return false;
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
function matchesJsDoc(match, source) {
|
|
399
|
+
if (match.tag !== source.tag) return false;
|
|
400
|
+
if (match.on && match.on !== source.target) return false;
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
function newControllerDraft(input) {
|
|
404
|
+
return {
|
|
405
|
+
name: input.name,
|
|
406
|
+
location: input.location,
|
|
407
|
+
hidden: false,
|
|
408
|
+
consumes: [],
|
|
409
|
+
produces: [],
|
|
410
|
+
tags: [],
|
|
411
|
+
responses: [],
|
|
412
|
+
security: [],
|
|
413
|
+
extensions: []
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function newMethodDraft(input) {
|
|
417
|
+
return {
|
|
418
|
+
name: input.name,
|
|
419
|
+
path: "",
|
|
420
|
+
description: "",
|
|
421
|
+
hidden: false,
|
|
422
|
+
consumes: [],
|
|
423
|
+
produces: [],
|
|
424
|
+
tags: [],
|
|
425
|
+
responses: [],
|
|
426
|
+
security: [],
|
|
427
|
+
extensions: [],
|
|
428
|
+
parameters: [],
|
|
429
|
+
defaultResponseExamples: []
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function newParameterDraft(input) {
|
|
433
|
+
return {
|
|
434
|
+
parameterName: input.parameterName,
|
|
435
|
+
name: input.parameterName,
|
|
436
|
+
description: "",
|
|
437
|
+
required: true,
|
|
438
|
+
examples: [],
|
|
439
|
+
exampleLabels: [],
|
|
440
|
+
extensions: [],
|
|
441
|
+
validators: {}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function createRegistry() {
|
|
445
|
+
return {
|
|
446
|
+
controllers: [],
|
|
447
|
+
methods: [],
|
|
448
|
+
parameters: [],
|
|
449
|
+
controllerJsDoc: [],
|
|
450
|
+
methodJsDoc: [],
|
|
451
|
+
parameterJsDoc: []
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function generatePresetLookupPaths(input) {
|
|
455
|
+
if (path.isAbsolute(input) || input.startsWith("./") || input.startsWith("../")) return [input];
|
|
456
|
+
if (input.startsWith("module:")) return [input.substring(7)];
|
|
457
|
+
if (!input.startsWith("@")) return [input, `@trapi/${input}`];
|
|
458
|
+
return [input];
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Collect the unique decorator names of every handler whose `marker` matches
|
|
462
|
+
* the given predicate. Lets the type resolver discover preset-renamed
|
|
463
|
+
* decorators without hard-coding canonical names.
|
|
464
|
+
*/
|
|
465
|
+
function namesForMarker(registry, predicate) {
|
|
466
|
+
const names = /* @__PURE__ */ new Set();
|
|
467
|
+
const all = [
|
|
468
|
+
...registry.controllers,
|
|
469
|
+
...registry.methods,
|
|
470
|
+
...registry.parameters
|
|
471
|
+
];
|
|
472
|
+
for (const handler of all) if (handler.marker !== void 0 && predicate(handler.marker)) names.add(handler.match.name);
|
|
473
|
+
return names;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Collect the unique JSDoc tag names of every JSDoc handler whose `marker`
|
|
477
|
+
* matches the given predicate. JSDoc analogue of `namesForMarker`.
|
|
478
|
+
*/
|
|
479
|
+
function tagsForMarker(registry, predicate) {
|
|
480
|
+
const tags = /* @__PURE__ */ new Set();
|
|
481
|
+
const all = [
|
|
482
|
+
...registry.controllerJsDoc,
|
|
483
|
+
...registry.methodJsDoc,
|
|
484
|
+
...registry.parameterJsDoc
|
|
485
|
+
];
|
|
486
|
+
for (const handler of all) if (handler.marker !== void 0 && predicate(handler.marker)) tags.add(handler.match.tag);
|
|
487
|
+
return tags;
|
|
488
|
+
}
|
|
489
|
+
function isHiddenMarker(marker) {
|
|
490
|
+
return marker === "hidden";
|
|
491
|
+
}
|
|
492
|
+
function isDeprecatedMarker(marker) {
|
|
493
|
+
return marker === "deprecated";
|
|
494
|
+
}
|
|
495
|
+
function isExtensionMarker(marker) {
|
|
496
|
+
return marker === "extension";
|
|
497
|
+
}
|
|
498
|
+
function numericMarkerKind(marker) {
|
|
499
|
+
if (typeof marker === "object" && marker !== null && "numeric" in marker) return marker.numeric;
|
|
500
|
+
}
|
|
501
|
+
const controller = (handler) => handler;
|
|
502
|
+
const method = (handler) => handler;
|
|
503
|
+
const parameter = (handler) => handler;
|
|
504
|
+
const controllerJsDoc = (handler) => handler;
|
|
505
|
+
const methodJsDoc = (handler) => handler;
|
|
506
|
+
const parameterJsDoc = (handler) => handler;
|
|
507
|
+
function isLiteralLike(kind) {
|
|
508
|
+
return kind === "literal" || kind === "identifier";
|
|
509
|
+
}
|
|
510
|
+
function readArray(draft, key) {
|
|
511
|
+
const record = draft;
|
|
512
|
+
const existing = record[key];
|
|
513
|
+
if (Array.isArray(existing)) return existing;
|
|
514
|
+
if (typeof existing !== "undefined") throw new TypeError(`append(): cannot append to draft key "${key}": existing value is not an array (got ${typeof existing}).`);
|
|
515
|
+
const created = [];
|
|
516
|
+
record[key] = created;
|
|
517
|
+
return created;
|
|
518
|
+
}
|
|
519
|
+
function into(key) {
|
|
520
|
+
return {
|
|
521
|
+
positional(index) {
|
|
522
|
+
return (ctx, draft) => {
|
|
523
|
+
const arg = ctx.argument(index);
|
|
524
|
+
if (arg && isLiteralLike(arg.kind)) draft[key] = arg.raw;
|
|
525
|
+
};
|
|
526
|
+
},
|
|
527
|
+
typeArgument(index = 0) {
|
|
528
|
+
return (ctx, draft) => {
|
|
529
|
+
const ta = ctx.typeArgument(index);
|
|
530
|
+
if (ta) draft[key] = ta.resolve();
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function append(key) {
|
|
536
|
+
return {
|
|
537
|
+
positional(index) {
|
|
538
|
+
return (ctx, draft) => {
|
|
539
|
+
const arg = ctx.argument(index);
|
|
540
|
+
if (!arg) return;
|
|
541
|
+
const target = readArray(draft, key);
|
|
542
|
+
if (arg.kind === "array" && Array.isArray(arg.raw)) target.push(...arg.raw);
|
|
543
|
+
else if (isLiteralLike(arg.kind)) target.push(arg.raw);
|
|
544
|
+
};
|
|
545
|
+
},
|
|
546
|
+
positionalAll() {
|
|
547
|
+
return (ctx, draft) => {
|
|
548
|
+
const target = readArray(draft, key);
|
|
549
|
+
for (const arg of ctx.arguments()) if (arg.kind === "array" && Array.isArray(arg.raw)) target.push(...arg.raw);
|
|
550
|
+
else if (isLiteralLike(arg.kind)) target.push(arg.raw);
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function flag(key, value = true) {
|
|
556
|
+
return (_ctx, draft) => {
|
|
557
|
+
draft[key] = value;
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/adapters/decorator/validation/module.ts
|
|
562
|
+
const decoratorTargetSchema = z.enum([
|
|
563
|
+
"class",
|
|
564
|
+
"method",
|
|
565
|
+
"parameter",
|
|
566
|
+
"property"
|
|
567
|
+
]);
|
|
568
|
+
const replacesPolicySchema = z.union([z.literal(true), z.string().min(1)]);
|
|
569
|
+
function matchSchemaForKind(allowedOn) {
|
|
570
|
+
return z.object({
|
|
571
|
+
name: z.string().min(1),
|
|
572
|
+
on: decoratorTargetSchema.optional()
|
|
573
|
+
}).refine((m) => m.on === void 0 || m.on === allowedOn, {
|
|
574
|
+
message: `match.on is incompatible with the handler array it lives in — expected '${allowedOn}' or omit 'on' entirely.`,
|
|
575
|
+
path: ["on"]
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
function jsDocMatchSchemaForKind(allowedOn) {
|
|
579
|
+
return z.object({
|
|
580
|
+
tag: z.string().min(1),
|
|
581
|
+
on: decoratorTargetSchema.optional()
|
|
582
|
+
}).refine((m) => m.on === void 0 || m.on === allowedOn, {
|
|
583
|
+
message: `match.on is incompatible with the JSDoc handler array it lives in — expected '${allowedOn}' or omit 'on' entirely.`,
|
|
584
|
+
path: ["on"]
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const matchSchema = z.object({
|
|
588
|
+
name: z.string().min(1),
|
|
589
|
+
on: decoratorTargetSchema.optional()
|
|
590
|
+
});
|
|
591
|
+
const jsDocMatchSchema = z.object({
|
|
592
|
+
tag: z.string().min(1),
|
|
593
|
+
on: decoratorTargetSchema.optional()
|
|
594
|
+
});
|
|
595
|
+
const numericMarkerSchema = z.object({ numeric: z.enum([
|
|
596
|
+
"int",
|
|
597
|
+
"long",
|
|
598
|
+
"float",
|
|
599
|
+
"double"
|
|
600
|
+
]) });
|
|
601
|
+
const resolverMarkerSchema = z.union([
|
|
602
|
+
z.literal("hidden"),
|
|
603
|
+
z.literal("deprecated"),
|
|
604
|
+
z.literal("extension"),
|
|
605
|
+
numericMarkerSchema
|
|
606
|
+
]);
|
|
607
|
+
const handlerBaseShape = {
|
|
608
|
+
replaces: replacesPolicySchema.optional(),
|
|
609
|
+
marker: resolverMarkerSchema.optional(),
|
|
610
|
+
apply: z.custom((value) => typeof value === "function", { message: "apply must be a function" })
|
|
611
|
+
};
|
|
612
|
+
const controllerHandlerSchema = z.object({
|
|
613
|
+
match: matchSchemaForKind("class"),
|
|
614
|
+
...handlerBaseShape
|
|
615
|
+
});
|
|
616
|
+
const methodHandlerSchema = z.object({
|
|
617
|
+
match: matchSchemaForKind("method"),
|
|
618
|
+
...handlerBaseShape
|
|
619
|
+
});
|
|
620
|
+
const parameterHandlerSchema = z.object({
|
|
621
|
+
match: matchSchemaForKind("parameter"),
|
|
622
|
+
...handlerBaseShape
|
|
623
|
+
});
|
|
624
|
+
const controllerJsDocHandlerSchema = z.object({
|
|
625
|
+
match: jsDocMatchSchemaForKind("class"),
|
|
626
|
+
...handlerBaseShape
|
|
627
|
+
});
|
|
628
|
+
const methodJsDocHandlerSchema = z.object({
|
|
629
|
+
match: jsDocMatchSchemaForKind("method"),
|
|
630
|
+
...handlerBaseShape
|
|
631
|
+
});
|
|
632
|
+
const parameterJsDocHandlerSchema = z.object({
|
|
633
|
+
match: jsDocMatchSchemaForKind("parameter"),
|
|
634
|
+
...handlerBaseShape
|
|
635
|
+
});
|
|
636
|
+
const presetSchema = z.object({
|
|
637
|
+
name: z.string().min(1),
|
|
638
|
+
extends: z.array(z.string().min(1)).optional(),
|
|
639
|
+
controllers: z.array(controllerHandlerSchema).optional(),
|
|
640
|
+
methods: z.array(methodHandlerSchema).optional(),
|
|
641
|
+
parameters: z.array(parameterHandlerSchema).optional(),
|
|
642
|
+
controllerJsDoc: z.array(controllerJsDocHandlerSchema).optional(),
|
|
643
|
+
methodJsDoc: z.array(methodJsDocHandlerSchema).optional(),
|
|
644
|
+
parameterJsDoc: z.array(parameterJsDocHandlerSchema).optional()
|
|
645
|
+
});
|
|
646
|
+
function createPresetContainer() {
|
|
647
|
+
const container = new Container();
|
|
648
|
+
container.mount("name", createValidator(presetSchema.shape.name));
|
|
649
|
+
container.mount("extends", { optional: true }, createValidator(presetSchema.shape.extends));
|
|
650
|
+
container.mount("controllers", { optional: true }, createValidator(presetSchema.shape.controllers));
|
|
651
|
+
container.mount("methods", { optional: true }, createValidator(presetSchema.shape.methods));
|
|
652
|
+
container.mount("parameters", { optional: true }, createValidator(presetSchema.shape.parameters));
|
|
653
|
+
container.mount("controllerJsDoc", { optional: true }, createValidator(presetSchema.shape.controllerJsDoc));
|
|
654
|
+
container.mount("methodJsDoc", { optional: true }, createValidator(presetSchema.shape.methodJsDoc));
|
|
655
|
+
container.mount("parameterJsDoc", { optional: true }, createValidator(presetSchema.shape.parameterJsDoc));
|
|
656
|
+
return container;
|
|
657
|
+
}
|
|
658
|
+
const sharedContainer = createPresetContainer();
|
|
659
|
+
async function validatePreset(input) {
|
|
660
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) throw new TypeError("preset input must be an object");
|
|
661
|
+
return sharedContainer.run(input);
|
|
662
|
+
}
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region src/adapters/decorator/module.ts
|
|
665
|
+
const decoratorKinds = [
|
|
666
|
+
"controllers",
|
|
667
|
+
"methods",
|
|
668
|
+
"parameters"
|
|
669
|
+
];
|
|
670
|
+
const jsDocKinds = [
|
|
671
|
+
"controllerJsDoc",
|
|
672
|
+
"methodJsDoc",
|
|
673
|
+
"parameterJsDoc"
|
|
674
|
+
];
|
|
675
|
+
function emptyTaggedRegistry() {
|
|
676
|
+
return {
|
|
677
|
+
controllers: [],
|
|
678
|
+
methods: [],
|
|
679
|
+
parameters: [],
|
|
680
|
+
controllerJsDoc: [],
|
|
681
|
+
methodJsDoc: [],
|
|
682
|
+
parameterJsDoc: []
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
async function loadRegistry(preset, options) {
|
|
686
|
+
return stripOrigins(await loadTaggedRegistry(preset, options, /* @__PURE__ */ new Set()));
|
|
687
|
+
}
|
|
688
|
+
async function loadTaggedRegistry(preset, options, visited) {
|
|
689
|
+
const validated = await validatePreset(preset);
|
|
690
|
+
if (visited.has(validated.name)) throw new MetadataError({ message: `Preset cycle detected: ${[...visited, validated.name].join(" -> ")}` });
|
|
691
|
+
const nextVisited = new Set(visited);
|
|
692
|
+
nextVisited.add(validated.name);
|
|
693
|
+
const merged = emptyTaggedRegistry();
|
|
694
|
+
for (const extendsName of validated.extends ?? []) appendTagged(merged, await loadTaggedRegistry(await options.resolver(extendsName), options, nextVisited));
|
|
695
|
+
applyOwnHandlers(merged, validated, !!options.strict);
|
|
696
|
+
return merged;
|
|
697
|
+
}
|
|
698
|
+
function applyOwnHandlers(merged, preset, strict) {
|
|
699
|
+
for (const kind of decoratorKinds) {
|
|
700
|
+
const handlers = preset[kind] ?? [];
|
|
701
|
+
for (const handler of handlers) {
|
|
702
|
+
if (handler.replaces) applyReplacesDecorator(merged, kind, handler, preset.name, strict);
|
|
703
|
+
merged[kind].push({
|
|
704
|
+
origin: preset.name,
|
|
705
|
+
handler
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
for (const kind of jsDocKinds) {
|
|
710
|
+
const handlers = preset[kind] ?? [];
|
|
711
|
+
for (const handler of handlers) {
|
|
712
|
+
if (handler.replaces) applyReplacesJsDoc(merged, kind, handler, preset.name, strict);
|
|
713
|
+
merged[kind].push({
|
|
714
|
+
origin: preset.name,
|
|
715
|
+
handler
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function applyReplacesDecorator(merged, kind, handler, presetName, strict) {
|
|
721
|
+
const list = merged[kind];
|
|
722
|
+
const remaining = [];
|
|
723
|
+
let removed = 0;
|
|
724
|
+
for (const entry of list) {
|
|
725
|
+
if (entry.origin !== presetName && handlerMatches(handler, entry.handler.match.name, entry.handler.match.on) && originMatches(handler.replaces, entry.origin)) {
|
|
726
|
+
removed += 1;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
remaining.push(entry);
|
|
730
|
+
}
|
|
731
|
+
if (removed === 0 && strict) throw new MetadataError({ message: `Preset '${presetName}': handler with replaces=${describeReplaces(handler.replaces)} on '${handler.match.name}' did not match any parent handler` });
|
|
732
|
+
merged[kind] = remaining;
|
|
733
|
+
}
|
|
734
|
+
function applyReplacesJsDoc(merged, kind, handler, presetName, strict) {
|
|
735
|
+
const list = merged[kind];
|
|
736
|
+
const remaining = [];
|
|
737
|
+
let removed = 0;
|
|
738
|
+
for (const entry of list) {
|
|
739
|
+
if (entry.origin !== presetName && handlerMatches(handler, entry.handler.match.tag, entry.handler.match.on) && originMatches(handler.replaces, entry.origin)) {
|
|
740
|
+
removed += 1;
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
remaining.push(entry);
|
|
744
|
+
}
|
|
745
|
+
if (removed === 0 && strict) throw new MetadataError({ message: `Preset '${presetName}': JSDoc handler with replaces=${describeReplaces(handler.replaces)} on '@${handler.match.tag}' did not match any parent handler` });
|
|
746
|
+
merged[kind] = remaining;
|
|
747
|
+
}
|
|
748
|
+
function handlerMatches(handler, targetName, targetOn) {
|
|
749
|
+
if (("name" in handler.match ? handler.match.name : handler.match.tag) !== targetName) return false;
|
|
750
|
+
if (handler.match.on && handler.match.on !== targetOn) return false;
|
|
751
|
+
return true;
|
|
752
|
+
}
|
|
753
|
+
function originMatches(replaces, origin) {
|
|
754
|
+
if (replaces === true) return true;
|
|
755
|
+
return replaces === origin;
|
|
756
|
+
}
|
|
757
|
+
function describeReplaces(replaces) {
|
|
758
|
+
return replaces === true ? "true" : `'${replaces}'`;
|
|
759
|
+
}
|
|
760
|
+
function appendTagged(target, source) {
|
|
761
|
+
for (const kind of decoratorKinds) target[kind].push(...source[kind]);
|
|
762
|
+
for (const kind of jsDocKinds) target[kind].push(...source[kind]);
|
|
763
|
+
}
|
|
764
|
+
function stripOrigins(tagged) {
|
|
765
|
+
const registry = createRegistry();
|
|
766
|
+
registry.controllers = tagged.controllers.map((e) => e.handler);
|
|
767
|
+
registry.methods = tagged.methods.map((e) => e.handler);
|
|
768
|
+
registry.parameters = tagged.parameters.map((e) => e.handler);
|
|
769
|
+
registry.controllerJsDoc = tagged.controllerJsDoc.map((e) => e.handler);
|
|
770
|
+
registry.methodJsDoc = tagged.methodJsDoc.map((e) => e.handler);
|
|
771
|
+
registry.parameterJsDoc = tagged.parameterJsDoc.map((e) => e.handler);
|
|
772
|
+
return registry;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Resolve a preset by string identifier (npm package, relative path, etc.) and
|
|
776
|
+
* return the v2 Preset object. Looks for a `preset` named export, then the
|
|
777
|
+
* default export, then the module itself.
|
|
778
|
+
*/
|
|
779
|
+
async function resolvePresetByName(input) {
|
|
780
|
+
const lookupPaths = generatePresetLookupPaths(input);
|
|
781
|
+
let lastError;
|
|
782
|
+
for (const lookupPath of lookupPaths) try {
|
|
783
|
+
const moduleExport = await load(lookupPath);
|
|
784
|
+
const candidates = [
|
|
785
|
+
moduleExport.preset,
|
|
786
|
+
moduleExport.default,
|
|
787
|
+
moduleExport
|
|
788
|
+
];
|
|
789
|
+
for (const candidate of candidates) if (isV2Preset(candidate)) return candidate;
|
|
790
|
+
} catch (e) {
|
|
791
|
+
lastError = e;
|
|
792
|
+
}
|
|
793
|
+
throw new ConfigError({
|
|
794
|
+
message: `Preset '${input}' could not be resolved.`,
|
|
795
|
+
code: ConfigErrorCode.PRESET_NOT_FOUND,
|
|
796
|
+
cause: lastError
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Resolve a preset by name and immediately materialize its registry, recursively
|
|
801
|
+
* loading `extends` parents through the same resolver.
|
|
802
|
+
*/
|
|
803
|
+
async function loadRegistryByName(input) {
|
|
804
|
+
return loadRegistry(await resolvePresetByName(input), { resolver: resolvePresetByName });
|
|
805
|
+
}
|
|
806
|
+
function isV2Preset(input) {
|
|
807
|
+
return typeof input === "object" && input !== null && typeof input.name === "string" && !("items" in input);
|
|
808
|
+
}
|
|
809
|
+
//#endregion
|
|
810
|
+
//#region src/adapters/typescript/js-doc/constants.ts
|
|
811
|
+
let JSDocTagName = /* @__PURE__ */ function(JSDocTagName) {
|
|
812
|
+
JSDocTagName["ABSTRACT"] = "abstract";
|
|
813
|
+
JSDocTagName["ACCESS"] = "access";
|
|
814
|
+
JSDocTagName["ALIAS"] = "alias";
|
|
815
|
+
JSDocTagName["ASYNC"] = "async";
|
|
816
|
+
/**
|
|
817
|
+
* Alias for @extends
|
|
818
|
+
*/
|
|
819
|
+
JSDocTagName["AUGMENTS"] = "augments";
|
|
820
|
+
JSDocTagName["AUTHOR"] = "author";
|
|
821
|
+
JSDocTagName["DEFAULT"] = "default";
|
|
822
|
+
JSDocTagName["DEPRECATED"] = "deprecated";
|
|
823
|
+
JSDocTagName["DESCRIPTION"] = "description";
|
|
824
|
+
JSDocTagName["EXAMPLE"] = "example";
|
|
825
|
+
JSDocTagName["FORMAT"] = "format";
|
|
826
|
+
JSDocTagName["IGNORE"] = "ignore";
|
|
827
|
+
JSDocTagName["SUMMARY"] = "summary";
|
|
828
|
+
return JSDocTagName;
|
|
829
|
+
}({});
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/core/utils/array.ts
|
|
832
|
+
function isStringArray(input) {
|
|
833
|
+
if (!Array.isArray(input)) return false;
|
|
834
|
+
for (const element of input) if (typeof element !== "string") return false;
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
//#endregion
|
|
838
|
+
//#region src/core/utils/object.ts
|
|
839
|
+
function hasOwnProperty(obj, prop) {
|
|
840
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
841
|
+
}
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/core/utils/path-normalize.ts
|
|
844
|
+
function normalizePath(str) {
|
|
845
|
+
str = str.replace(/^[/\\\s]+|[/\\\s]+$/g, "");
|
|
846
|
+
str = str.replace(/([^:]\/)\/+/g, "$1");
|
|
847
|
+
return str;
|
|
848
|
+
}
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region src/adapters/typescript/js-doc/utils.ts
|
|
851
|
+
function transformJSDocComment(input) {
|
|
852
|
+
if (typeof input === "string") return input;
|
|
853
|
+
if (!input || input.length === 0) return;
|
|
854
|
+
const comment = input[0];
|
|
855
|
+
if (typeof comment === "string") return comment;
|
|
856
|
+
if (isObject(comment) && typeof comment.text === "string") return comment.text;
|
|
857
|
+
}
|
|
858
|
+
//#endregion
|
|
859
|
+
//#region src/adapters/typescript/js-doc/module.ts
|
|
860
|
+
function getJSDocDescription(node, index) {
|
|
861
|
+
const jsDoc = getJSDoc(node, index);
|
|
862
|
+
if (!jsDoc) return;
|
|
863
|
+
return transformJSDocComment(jsDoc.comment);
|
|
864
|
+
}
|
|
865
|
+
function getJSDoc(node, index) {
|
|
866
|
+
if (!hasOwnProperty(node, "jsDoc")) return;
|
|
867
|
+
const jsDoc = node.jsDoc;
|
|
868
|
+
if (!jsDoc || !Array.isArray(jsDoc) || !jsDoc.length) return;
|
|
869
|
+
index = index ?? 0;
|
|
870
|
+
return jsDoc.length > index && index >= 0 ? jsDoc[index] : void 0;
|
|
871
|
+
}
|
|
872
|
+
function getJSDocTags(node, isMatching) {
|
|
873
|
+
const jsDoc = getJSDoc(node);
|
|
874
|
+
if (typeof jsDoc === "undefined") return [];
|
|
875
|
+
const jsDocTags = jsDoc.tags;
|
|
876
|
+
if (typeof jsDocTags === "undefined") return [];
|
|
877
|
+
if (typeof isMatching === "undefined") return jsDocTags;
|
|
878
|
+
if (typeof isMatching === "function") return jsDocTags.filter(isMatching);
|
|
879
|
+
const tagNames = Array.isArray(isMatching) ? isMatching : [isMatching];
|
|
880
|
+
return jsDocTags.filter((tag) => tagNames.includes(tag.tagName.text));
|
|
881
|
+
}
|
|
882
|
+
function hasJSDocTag(node, tagName) {
|
|
883
|
+
const tags = getJSDocTags(node, tagName);
|
|
884
|
+
return !(!tags || !tags.length);
|
|
885
|
+
}
|
|
886
|
+
function getJSDocTagComment(node, tagName) {
|
|
887
|
+
const tags = getJSDocTags(node, tagName);
|
|
888
|
+
if (!tags || !tags.length || typeof tags[0].comment !== "string") return;
|
|
889
|
+
return tags[0].comment;
|
|
890
|
+
}
|
|
891
|
+
function getJSDocTagNames(node, requireTagName = false) {
|
|
892
|
+
let tags;
|
|
893
|
+
/* istanbul ignore next */
|
|
894
|
+
if (node.kind === SyntaxKind.Parameter) {
|
|
895
|
+
const parameterName = node.name.text;
|
|
896
|
+
tags = getJSDocTags(node.parent, (tag) => {
|
|
897
|
+
if (isJSDocParameterTag(tag)) return false;
|
|
898
|
+
if (tag.comment === void 0) throw new MetadataError(`Orphan tag: @${String(tag.tagName.text || tag.tagName.escapedText)} must be followed by a parameter name.`);
|
|
899
|
+
return typeof tag.comment === "string" ? tag.comment.startsWith(parameterName) : false;
|
|
900
|
+
});
|
|
901
|
+
} else tags = getJSDocTags(node, (tag) => requireTagName ? tag.comment !== void 0 : true);
|
|
902
|
+
return tags.map((tag) => tag.tagName.text);
|
|
903
|
+
}
|
|
904
|
+
//#endregion
|
|
905
|
+
//#region src/adapters/typescript/initializer.ts
|
|
906
|
+
function getInitializerValue(initializer, typeChecker, type) {
|
|
907
|
+
if (!initializer) return;
|
|
908
|
+
switch (initializer.kind) {
|
|
909
|
+
case ts.SyntaxKind.ArrayLiteralExpression: return initializer.elements.map((element) => getInitializerValue(element, typeChecker));
|
|
910
|
+
case ts.SyntaxKind.StringLiteral:
|
|
911
|
+
case ts.SyntaxKind.NoSubstitutionTemplateLiteral: return initializer.text;
|
|
912
|
+
case ts.SyntaxKind.TrueKeyword: return true;
|
|
913
|
+
case ts.SyntaxKind.FalseKeyword: return false;
|
|
914
|
+
case ts.SyntaxKind.PrefixUnaryExpression: {
|
|
915
|
+
const prefixUnary = initializer;
|
|
916
|
+
switch (prefixUnary.operator) {
|
|
917
|
+
case ts.SyntaxKind.PlusToken: return Number(prefixUnary.operand.text);
|
|
918
|
+
case ts.SyntaxKind.MinusToken: return Number(`-${prefixUnary.operand.text}`);
|
|
919
|
+
default: throw new MetadataError(`Unsupported prefix operator token: ${prefixUnary.operator}`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
case ts.SyntaxKind.NumberKeyword:
|
|
923
|
+
case ts.SyntaxKind.FirstLiteralToken: return Number(initializer.text);
|
|
924
|
+
case ts.SyntaxKind.NewExpression: {
|
|
925
|
+
const newExpression = initializer;
|
|
926
|
+
if (newExpression.expression.text === "Date") {
|
|
927
|
+
let date = /* @__PURE__ */ new Date();
|
|
928
|
+
if (newExpression.arguments) {
|
|
929
|
+
const argsValue = newExpression.arguments.filter((args) => args.kind !== void 0).map((args) => getInitializerValue(args, typeChecker));
|
|
930
|
+
if (argsValue.length > 0) date = new Date(argsValue);
|
|
931
|
+
}
|
|
932
|
+
const dateString = date.toISOString();
|
|
933
|
+
if (type && type.typeName === "date") return dateString.split("T")[0];
|
|
934
|
+
return dateString;
|
|
935
|
+
}
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
case ts.SyntaxKind.NullKeyword: return null;
|
|
939
|
+
case ts.SyntaxKind.ObjectLiteralExpression: {
|
|
940
|
+
const objectLiteral = initializer;
|
|
941
|
+
const nestedObject = {};
|
|
942
|
+
objectLiteral.properties.forEach((p) => {
|
|
943
|
+
nestedObject[p.name.text] = getInitializerValue(p.initializer, typeChecker);
|
|
944
|
+
});
|
|
945
|
+
return nestedObject;
|
|
946
|
+
}
|
|
947
|
+
case ts.SyntaxKind.ImportSpecifier: {
|
|
948
|
+
if (typeof typeChecker === "undefined") return;
|
|
949
|
+
const importSpecifier = initializer;
|
|
950
|
+
const importSymbol = typeChecker.getSymbolAtLocation(importSpecifier.name);
|
|
951
|
+
if (!importSymbol) return;
|
|
952
|
+
const declarations = typeChecker.getAliasedSymbol(importSymbol).getDeclarations();
|
|
953
|
+
return getInitializerValue(extractInitializer(declarations && declarations.length > 0 ? declarations[0] : void 0), typeChecker);
|
|
954
|
+
}
|
|
955
|
+
default: {
|
|
956
|
+
if (typeof initializer === "undefined") return;
|
|
957
|
+
if (typeof initializer.parent === "undefined" || typeof typeChecker === "undefined") {
|
|
958
|
+
if (hasOwnProperty(initializer, "text")) return initializer.text;
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
const symbol = typeChecker.getSymbolAtLocation(initializer);
|
|
962
|
+
return getInitializerValue(extractInitializer(symbol.valueDeclaration) || extractInitializer(extractImportSpecifier(symbol)), typeChecker);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
const hasInitializer = (node) => Object.prototype.hasOwnProperty.call(node, "initializer");
|
|
967
|
+
const extractInitializer = (valueDeclaration) => valueDeclaration && hasInitializer(valueDeclaration) && valueDeclaration.initializer || void 0;
|
|
968
|
+
const extractImportSpecifier = (symbol) => symbol?.declarations && symbol.declarations.length > 0 && ts.isImportSpecifier(symbol.declarations[0]) && symbol.declarations[0] || void 0;
|
|
969
|
+
//#endregion
|
|
970
|
+
//#region src/adapters/decorator/typescript/utils.ts
|
|
971
|
+
/**
|
|
972
|
+
* Enumerate decorators on a TS node and classify their argument values without
|
|
973
|
+
* going through the registry. Used by read-side consumers (type resolver,
|
|
974
|
+
* extension extraction) that only need decorator names + argument values.
|
|
975
|
+
*/
|
|
976
|
+
function readNodeDecorators(node, typeChecker) {
|
|
977
|
+
if (!ts.canHaveDecorators(node)) return [];
|
|
978
|
+
const decorators = ts.getDecorators(node);
|
|
979
|
+
if (!decorators || decorators.length === 0) return [];
|
|
980
|
+
const output = [];
|
|
981
|
+
for (const decorator of decorators) {
|
|
982
|
+
const { expression } = decorator;
|
|
983
|
+
let name;
|
|
984
|
+
let argumentExpressions = [];
|
|
985
|
+
if (ts.isCallExpression(expression)) {
|
|
986
|
+
argumentExpressions = expression.arguments;
|
|
987
|
+
name = readDecoratorName$1(expression.expression);
|
|
988
|
+
} else name = readDecoratorName$1(expression);
|
|
989
|
+
if (!name) continue;
|
|
990
|
+
output.push({
|
|
991
|
+
name,
|
|
992
|
+
arguments: argumentExpressions.map((a) => buildDecoratorArgument(a, typeChecker))
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
return output;
|
|
996
|
+
}
|
|
997
|
+
function findDecoratorByName(node, name, typeChecker) {
|
|
998
|
+
return readNodeDecorators(node, typeChecker).find((d) => d.name === name);
|
|
999
|
+
}
|
|
1000
|
+
function findDecoratorsByName(node, name, typeChecker) {
|
|
1001
|
+
return readNodeDecorators(node, typeChecker).filter((d) => d.name === name);
|
|
1002
|
+
}
|
|
1003
|
+
function hasDecoratorNamed(node, name, typeChecker) {
|
|
1004
|
+
return readNodeDecorators(node, typeChecker).some((d) => d.name === name);
|
|
1005
|
+
}
|
|
1006
|
+
function readDecoratorName$1(expression) {
|
|
1007
|
+
if (ts.isIdentifier(expression)) return expression.text;
|
|
1008
|
+
if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
|
|
1009
|
+
}
|
|
1010
|
+
function buildDecoratorArgument(expr, typeChecker) {
|
|
1011
|
+
if (ts.isStringLiteral(expr) || ts.isNumericLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) return {
|
|
1012
|
+
raw: getInitializerValue(expr, typeChecker),
|
|
1013
|
+
kind: "literal"
|
|
1014
|
+
};
|
|
1015
|
+
if (expr.kind === ts.SyntaxKind.TrueKeyword) return {
|
|
1016
|
+
raw: true,
|
|
1017
|
+
kind: "literal"
|
|
1018
|
+
};
|
|
1019
|
+
if (expr.kind === ts.SyntaxKind.FalseKeyword) return {
|
|
1020
|
+
raw: false,
|
|
1021
|
+
kind: "literal"
|
|
1022
|
+
};
|
|
1023
|
+
if (expr.kind === ts.SyntaxKind.NullKeyword) return {
|
|
1024
|
+
raw: null,
|
|
1025
|
+
kind: "literal"
|
|
1026
|
+
};
|
|
1027
|
+
if (ts.isPrefixUnaryExpression(expr) && (expr.operator === ts.SyntaxKind.PlusToken || expr.operator === ts.SyntaxKind.MinusToken) && ts.isNumericLiteral(expr.operand)) return {
|
|
1028
|
+
raw: getInitializerValue(expr, typeChecker),
|
|
1029
|
+
kind: "literal"
|
|
1030
|
+
};
|
|
1031
|
+
if (ts.isObjectLiteralExpression(expr)) return {
|
|
1032
|
+
raw: getInitializerValue(expr, typeChecker),
|
|
1033
|
+
kind: "object"
|
|
1034
|
+
};
|
|
1035
|
+
if (ts.isArrayLiteralExpression(expr)) return {
|
|
1036
|
+
raw: getInitializerValue(expr, typeChecker),
|
|
1037
|
+
kind: "array"
|
|
1038
|
+
};
|
|
1039
|
+
if (ts.isIdentifier(expr) || ts.isPropertyAccessExpression(expr)) {
|
|
1040
|
+
const value = getInitializerValue(expr, typeChecker);
|
|
1041
|
+
if (typeof value !== "undefined") return {
|
|
1042
|
+
raw: value,
|
|
1043
|
+
kind: "identifier"
|
|
1044
|
+
};
|
|
1045
|
+
return {
|
|
1046
|
+
raw: void 0,
|
|
1047
|
+
kind: "unresolvable"
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
raw: void 0,
|
|
1052
|
+
kind: "unresolvable"
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
//#endregion
|
|
1056
|
+
//#region src/adapters/decorator/typescript/module.ts
|
|
1057
|
+
function buildDecoratorSources(node, options) {
|
|
1058
|
+
if (!ts.canHaveDecorators(node)) return [];
|
|
1059
|
+
const decorators = ts.getDecorators(node);
|
|
1060
|
+
if (!decorators || decorators.length === 0) return [];
|
|
1061
|
+
const output = [];
|
|
1062
|
+
for (const decorator of decorators) {
|
|
1063
|
+
const source = buildDecoratorSource(decorator, options);
|
|
1064
|
+
if (source) output.push(source);
|
|
1065
|
+
}
|
|
1066
|
+
return output;
|
|
1067
|
+
}
|
|
1068
|
+
function buildDecoratorSource(decorator, options) {
|
|
1069
|
+
const { expression } = decorator;
|
|
1070
|
+
let name;
|
|
1071
|
+
let argumentExpressions = [];
|
|
1072
|
+
let typeArgumentNodes = [];
|
|
1073
|
+
if (ts.isCallExpression(expression)) {
|
|
1074
|
+
argumentExpressions = expression.arguments;
|
|
1075
|
+
typeArgumentNodes = expression.typeArguments ?? [];
|
|
1076
|
+
name = readDecoratorName(expression.expression);
|
|
1077
|
+
} else name = readDecoratorName(expression);
|
|
1078
|
+
if (!name) return;
|
|
1079
|
+
const decoratorArguments = argumentExpressions.map((arg) => buildDecoratorArgument(arg, options.typeChecker));
|
|
1080
|
+
const decoratorTypeArguments = typeArgumentNodes.map((typeNode) => ({ resolve: () => options.resolveTypeNode(typeNode) }));
|
|
1081
|
+
const sourceFile = decorator.getSourceFile();
|
|
1082
|
+
const location = sourceFile ? {
|
|
1083
|
+
file: sourceFile.fileName,
|
|
1084
|
+
line: sourceFile.getLineAndCharacterOfPosition(decorator.getStart()).line + 1
|
|
1085
|
+
} : void 0;
|
|
1086
|
+
return {
|
|
1087
|
+
name,
|
|
1088
|
+
arguments: decoratorArguments,
|
|
1089
|
+
typeArguments: decoratorTypeArguments,
|
|
1090
|
+
target: options.target,
|
|
1091
|
+
host: options.host,
|
|
1092
|
+
location
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
function readDecoratorName(expression) {
|
|
1096
|
+
if (ts.isIdentifier(expression)) return expression.text;
|
|
1097
|
+
if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
|
|
1098
|
+
}
|
|
1099
|
+
function buildJsDocSources(node, options) {
|
|
1100
|
+
const tags = getJSDocTags(node);
|
|
1101
|
+
if (tags.length === 0) return [];
|
|
1102
|
+
const output = [];
|
|
1103
|
+
for (const tag of tags) output.push(buildJsDocSource(tag, options));
|
|
1104
|
+
return output;
|
|
1105
|
+
}
|
|
1106
|
+
function buildJsDocSource(tag, options) {
|
|
1107
|
+
const tagName = tag.tagName.text;
|
|
1108
|
+
const text = transformJSDocComment(tag.comment);
|
|
1109
|
+
let parameterName;
|
|
1110
|
+
let typeNode;
|
|
1111
|
+
if (ts.isJSDocParameterTag(tag) || ts.isJSDocPropertyTag(tag)) {
|
|
1112
|
+
if (tag.name) parameterName = readEntityName(tag.name);
|
|
1113
|
+
typeNode = tag.typeExpression?.type;
|
|
1114
|
+
} else if (ts.isJSDocReturnTag(tag) || ts.isJSDocTypeTag(tag) || ts.isJSDocThisTag(tag)) typeNode = tag.typeExpression?.type;
|
|
1115
|
+
const source = {
|
|
1116
|
+
tag: tagName,
|
|
1117
|
+
target: options.target,
|
|
1118
|
+
host: options.host
|
|
1119
|
+
};
|
|
1120
|
+
if (typeof text !== "undefined") source.text = text;
|
|
1121
|
+
if (typeof parameterName !== "undefined") source.parameterName = parameterName;
|
|
1122
|
+
if (typeNode) {
|
|
1123
|
+
const capturedTypeNode = typeNode;
|
|
1124
|
+
source.typeExpression = { resolve: () => options.resolveTypeNode(capturedTypeNode) };
|
|
1125
|
+
}
|
|
1126
|
+
return source;
|
|
1127
|
+
}
|
|
1128
|
+
function readEntityName(name) {
|
|
1129
|
+
if (ts.isIdentifier(name)) return name.text;
|
|
1130
|
+
if (ts.isQualifiedName(name)) {
|
|
1131
|
+
const left = readEntityName(name.left);
|
|
1132
|
+
return left ? `${left}.${name.right.text}` : name.right.text;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
//#endregion
|
|
1136
|
+
//#region src/adapters/decorator/orchestrator/module.ts
|
|
1137
|
+
function buildHandlerContext(source, options) {
|
|
1138
|
+
return {
|
|
1139
|
+
host: source.host,
|
|
1140
|
+
argument: (i) => source.arguments[i],
|
|
1141
|
+
arguments: () => source.arguments,
|
|
1142
|
+
typeArgument: (i) => source.typeArguments[i],
|
|
1143
|
+
typeArguments: () => source.typeArguments,
|
|
1144
|
+
parameterType: options.parameterType ?? (() => void 0)
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
function buildJsDocHandlerContext(source, options) {
|
|
1148
|
+
return {
|
|
1149
|
+
host: source.host,
|
|
1150
|
+
source,
|
|
1151
|
+
parameterType: options.parameterType ?? (() => void 0)
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function applyDecoratorHandlers(node, handlers, draft, options) {
|
|
1155
|
+
if (handlers.length === 0 && !options.onUnmatchedDecorator) return;
|
|
1156
|
+
const sources = buildDecoratorSources(node, options);
|
|
1157
|
+
if (sources.length === 0) return;
|
|
1158
|
+
for (const source of sources) {
|
|
1159
|
+
let matched = 0;
|
|
1160
|
+
for (const handler of handlers) if (matches(handler.match, source)) {
|
|
1161
|
+
handler.apply(buildHandlerContext(source, options), draft);
|
|
1162
|
+
matched += 1;
|
|
1163
|
+
}
|
|
1164
|
+
if (matched === 0 && options.onUnmatchedDecorator) {
|
|
1165
|
+
let file;
|
|
1166
|
+
let line;
|
|
1167
|
+
if (source.location) {
|
|
1168
|
+
file = source.location.file;
|
|
1169
|
+
line = source.location.line;
|
|
1170
|
+
} else {
|
|
1171
|
+
const sourceFile = node.getSourceFile();
|
|
1172
|
+
file = sourceFile.fileName;
|
|
1173
|
+
line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
1174
|
+
}
|
|
1175
|
+
options.onUnmatchedDecorator({
|
|
1176
|
+
name: source.name,
|
|
1177
|
+
target: source.target,
|
|
1178
|
+
host: source.host,
|
|
1179
|
+
file,
|
|
1180
|
+
line
|
|
1181
|
+
}, source);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function applyJsDocHandlers(node, handlers, draft, options) {
|
|
1186
|
+
if (handlers.length === 0) return;
|
|
1187
|
+
const sources = buildJsDocSources(node, options);
|
|
1188
|
+
if (sources.length === 0) return;
|
|
1189
|
+
for (const source of sources) for (const handler of handlers) if (matchesJsDoc(handler.match, source)) handler.apply(buildJsDocHandlerContext(source, options), draft);
|
|
1190
|
+
}
|
|
1191
|
+
//#endregion
|
|
1192
|
+
//#region src/core/error/validator.ts
|
|
1193
|
+
var ValidatorError$1 = class extends MetadataError {};
|
|
1194
|
+
//#endregion
|
|
1195
|
+
//#region src/core/error/validator-codes.ts
|
|
1196
|
+
const ValidatorErrorCode$1 = {
|
|
1197
|
+
EXPECTED_NUMBER: "VALIDATOR_EXPECTED_NUMBER",
|
|
1198
|
+
EXPECTED_DATE: "VALIDATOR_EXPECTED_DATE",
|
|
1199
|
+
EXPECTED_STRING: "VALIDATOR_EXPECTED_STRING"
|
|
1200
|
+
};
|
|
1201
|
+
//#endregion
|
|
1202
|
+
//#region src/adapters/typescript/validator/module.ts
|
|
1203
|
+
function getDeclarationValidators(declaration, name) {
|
|
1204
|
+
if (!declaration.parent) return {};
|
|
1205
|
+
const getCommentValue = (comment) => comment && comment.split(" ")[0];
|
|
1206
|
+
const parameterTags = getSupportedParameterTags();
|
|
1207
|
+
const tags = getJSDocTags(declaration.parent, (tag) => {
|
|
1208
|
+
const { comment } = tag;
|
|
1209
|
+
const text = transformJSDocComment(comment);
|
|
1210
|
+
if (!comment) return false;
|
|
1211
|
+
const commentValue = getCommentValue(text);
|
|
1212
|
+
return parameterTags.some((value) => {
|
|
1213
|
+
if (value !== tag.tagName.text) return false;
|
|
1214
|
+
return !(name && name !== commentValue);
|
|
1215
|
+
});
|
|
1216
|
+
});
|
|
1217
|
+
function getErrorMsg(comment, isValue = true) {
|
|
1218
|
+
if (!comment) return;
|
|
1219
|
+
if (isValue) {
|
|
1220
|
+
const indexOf = comment.indexOf(" ");
|
|
1221
|
+
if (indexOf > 0) return comment.substring(indexOf + 1);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
return comment;
|
|
1225
|
+
}
|
|
1226
|
+
const validators = {};
|
|
1227
|
+
for (const tag of tags) {
|
|
1228
|
+
if (!tag.comment) continue;
|
|
1229
|
+
const name = tag.tagName.text;
|
|
1230
|
+
let comment = transformJSDocComment(tag.comment);
|
|
1231
|
+
comment = comment.substring(comment.indexOf(" ") + 1).trim();
|
|
1232
|
+
const value = getCommentValue(comment);
|
|
1233
|
+
switch (name) {
|
|
1234
|
+
case "uniqueItems":
|
|
1235
|
+
validators[name] = {
|
|
1236
|
+
message: getErrorMsg(comment, false),
|
|
1237
|
+
value: void 0
|
|
1238
|
+
};
|
|
1239
|
+
break;
|
|
1240
|
+
case "minimum":
|
|
1241
|
+
case "maximum":
|
|
1242
|
+
case "minItems":
|
|
1243
|
+
case "maxItems":
|
|
1244
|
+
case "minLength":
|
|
1245
|
+
case "maxLength":
|
|
1246
|
+
{
|
|
1247
|
+
const parsed = Number(value);
|
|
1248
|
+
if (!Number.isFinite(parsed)) throw new ValidatorError$1({
|
|
1249
|
+
message: `@${name} validator expects a numeric value, got '${value}'.`,
|
|
1250
|
+
code: ValidatorErrorCode$1.EXPECTED_NUMBER
|
|
1251
|
+
});
|
|
1252
|
+
validators[name] = {
|
|
1253
|
+
message: getErrorMsg(comment),
|
|
1254
|
+
value: parsed
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
break;
|
|
1258
|
+
case "minDate":
|
|
1259
|
+
case "maxDate":
|
|
1260
|
+
if (typeof value !== "string") throw new ValidatorError$1({
|
|
1261
|
+
message: `@${name} validator expects a date string, got '${typeof value}'.`,
|
|
1262
|
+
code: ValidatorErrorCode$1.EXPECTED_DATE
|
|
1263
|
+
});
|
|
1264
|
+
validators[name] = {
|
|
1265
|
+
message: getErrorMsg(comment),
|
|
1266
|
+
value
|
|
1267
|
+
};
|
|
1268
|
+
break;
|
|
1269
|
+
case "pattern":
|
|
1270
|
+
if (typeof value !== "string") throw new ValidatorError$1({
|
|
1271
|
+
message: `@${name} validator expects a string pattern, got '${value}'.`,
|
|
1272
|
+
code: ValidatorErrorCode$1.EXPECTED_STRING
|
|
1273
|
+
});
|
|
1274
|
+
validators[name] = {
|
|
1275
|
+
message: getErrorMsg(comment),
|
|
1276
|
+
value: removeSurroundingQuotes(value)
|
|
1277
|
+
};
|
|
1278
|
+
break;
|
|
1279
|
+
default:
|
|
1280
|
+
if (name.toLowerCase().startsWith("is")) {
|
|
1281
|
+
const errorMsg = getErrorMsg(comment, false);
|
|
1282
|
+
if (errorMsg) validators[name] = {
|
|
1283
|
+
message: errorMsg,
|
|
1284
|
+
value: void 0
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return validators;
|
|
1291
|
+
}
|
|
1292
|
+
function getSupportedParameterTags() {
|
|
1293
|
+
return [
|
|
1294
|
+
"isString",
|
|
1295
|
+
"isBoolean",
|
|
1296
|
+
"isInt",
|
|
1297
|
+
"isLong",
|
|
1298
|
+
"isFloat",
|
|
1299
|
+
"isDouble",
|
|
1300
|
+
"isDate",
|
|
1301
|
+
"isDateTime",
|
|
1302
|
+
"minItems",
|
|
1303
|
+
"maxItems",
|
|
1304
|
+
"uniqueItems",
|
|
1305
|
+
"minLength",
|
|
1306
|
+
"maxLength",
|
|
1307
|
+
"pattern",
|
|
1308
|
+
"minimum",
|
|
1309
|
+
"maximum",
|
|
1310
|
+
"minDate",
|
|
1311
|
+
"maxDate"
|
|
1312
|
+
];
|
|
1313
|
+
}
|
|
1314
|
+
function removeSurroundingQuotes(str) {
|
|
1315
|
+
if (str.startsWith("`") && str.endsWith("`")) return str.substring(1, str.length - 1);
|
|
1316
|
+
if (str.startsWith("```") && str.endsWith("```")) return str.substring(3, str.length - 3);
|
|
1317
|
+
return str;
|
|
1318
|
+
}
|
|
1319
|
+
//#endregion
|
|
1320
|
+
//#region src/core/error/resolver.ts
|
|
1321
|
+
var ResolverError$1 = class extends MetadataError {
|
|
1322
|
+
file;
|
|
1323
|
+
line;
|
|
1324
|
+
constructor(message, node, options) {
|
|
1325
|
+
const opts = typeof options === "boolean" ? { onlyCurrent: options } : options;
|
|
1326
|
+
const onlyCurrent = opts?.onlyCurrent ?? false;
|
|
1327
|
+
const parts = [message];
|
|
1328
|
+
let file;
|
|
1329
|
+
let line;
|
|
1330
|
+
if (node) {
|
|
1331
|
+
const location = prettyLocationOfNode$1(node);
|
|
1332
|
+
if (location) {
|
|
1333
|
+
parts.push(location.text);
|
|
1334
|
+
file = location.file;
|
|
1335
|
+
line = location.line;
|
|
1336
|
+
}
|
|
1337
|
+
parts.push(prettyTroubleCause$1(node, onlyCurrent));
|
|
1338
|
+
}
|
|
1339
|
+
super({
|
|
1340
|
+
message: parts.join("\n"),
|
|
1341
|
+
cause: opts?.cause
|
|
1342
|
+
});
|
|
1343
|
+
this.file = file;
|
|
1344
|
+
this.line = line;
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
function isResolverError$1(input) {
|
|
1348
|
+
if (!isBaseError(input)) return false;
|
|
1349
|
+
return "file" in input && "line" in input;
|
|
1350
|
+
}
|
|
1351
|
+
function prettyLocationOfNode$1(node) {
|
|
1352
|
+
try {
|
|
1353
|
+
const sourceFile = node.getSourceFile();
|
|
1354
|
+
if (!sourceFile) return void 0;
|
|
1355
|
+
const token = node.getFirstToken() || node.parent?.getFirstToken();
|
|
1356
|
+
const start = token ? sourceFile.getLineAndCharacterOfPosition(token.getStart()).line + 1 : void 0;
|
|
1357
|
+
const end = token ? sourceFile.getLineAndCharacterOfPosition(token.getEnd()).line + 1 : void 0;
|
|
1358
|
+
return {
|
|
1359
|
+
text: `At: ${normalize(sourceFile.fileName)}${start ? `:${start}` : ""}${end ? `:${end}` : ""}.`,
|
|
1360
|
+
file: sourceFile.fileName,
|
|
1361
|
+
line: start
|
|
1362
|
+
};
|
|
1363
|
+
} catch {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
function prettyTroubleCause$1(node, onlyCurrent = false) {
|
|
1368
|
+
try {
|
|
1369
|
+
let name;
|
|
1370
|
+
if (onlyCurrent || !node.parent) name = node.pos !== -1 ? node.getText() : node.name.text;
|
|
1371
|
+
else name = node.parent.pos !== -1 ? node.parent.getText() : node.parent.name.text;
|
|
1372
|
+
return `This was caused by '${name}'`;
|
|
1373
|
+
} catch {
|
|
1374
|
+
return "This was caused by an unknown node";
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
//#endregion
|
|
1378
|
+
//#region src/adapters/typescript/resolver/extension/module.ts
|
|
1379
|
+
function getNodeExtensions(node, registry) {
|
|
1380
|
+
const names = namesForMarker(registry, isExtensionMarker);
|
|
1381
|
+
if (names.size === 0) return [];
|
|
1382
|
+
const output = [];
|
|
1383
|
+
for (const name of names) {
|
|
1384
|
+
const decorators = findDecoratorsByName(node, name);
|
|
1385
|
+
for (const decorator of decorators) {
|
|
1386
|
+
const keyArg = decorator.arguments[0];
|
|
1387
|
+
const valueArg = decorator.arguments[1];
|
|
1388
|
+
if (!keyArg || keyArg.kind !== "literal" || typeof keyArg.raw !== "string") continue;
|
|
1389
|
+
if (!valueArg || valueArg.kind === "unresolvable" || typeof valueArg.raw === "undefined") continue;
|
|
1390
|
+
output.push({
|
|
1391
|
+
key: keyArg.raw,
|
|
1392
|
+
value: valueArg.raw
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
return output;
|
|
1397
|
+
}
|
|
1398
|
+
//#endregion
|
|
1399
|
+
//#region src/adapters/typescript/resolver/sub/array.ts
|
|
1400
|
+
function resolveArrayType(typeNode, ctx) {
|
|
1401
|
+
if (!ts.isArrayTypeNode(typeNode)) return;
|
|
1402
|
+
return {
|
|
1403
|
+
typeName: "array",
|
|
1404
|
+
elementType: ctx.resolveType(typeNode.elementType, ctx.parentNode, ctx.context)
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
//#endregion
|
|
1408
|
+
//#region src/adapters/typescript/resolver/sub/base.ts
|
|
1409
|
+
var ResolverBase = class {
|
|
1410
|
+
hasPublicModifier(node) {
|
|
1411
|
+
if (!canHaveModifiers(node)) return true;
|
|
1412
|
+
const modifiers = getModifiers(node);
|
|
1413
|
+
if (!modifiers) return true;
|
|
1414
|
+
return modifiers.every((modifier) => modifier.kind !== SyntaxKind.ProtectedKeyword && modifier.kind !== SyntaxKind.PrivateKeyword);
|
|
1415
|
+
}
|
|
1416
|
+
hasStaticModifier(node) {
|
|
1417
|
+
if (!canHaveModifiers(node)) return false;
|
|
1418
|
+
const modifiers = getModifiers(node);
|
|
1419
|
+
return modifiers && modifiers.some((modifier) => modifier.kind === SyntaxKind.StaticKeyword);
|
|
1420
|
+
}
|
|
1421
|
+
isAccessibleParameter(node) {
|
|
1422
|
+
if (!canHaveModifiers(node)) return false;
|
|
1423
|
+
const modifiers = getModifiers(node);
|
|
1424
|
+
if (!modifiers) return false;
|
|
1425
|
+
if (modifiers.some((modifier) => modifier.kind === SyntaxKind.PublicKeyword)) return true;
|
|
1426
|
+
const isReadonly = modifiers.some((modifier) => modifier.kind === SyntaxKind.ReadonlyKeyword);
|
|
1427
|
+
const isProtectedOrPrivate = modifiers.some((modifier) => modifier.kind === SyntaxKind.ProtectedKeyword || modifier.kind === SyntaxKind.PrivateKeyword);
|
|
1428
|
+
return isReadonly && !isProtectedOrPrivate;
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
//#endregion
|
|
1432
|
+
//#region src/adapters/typescript/resolver/utils.ts
|
|
1433
|
+
function getNodeDescription(node, typeChecker) {
|
|
1434
|
+
if (!hasOwnProperty(node, "name")) return;
|
|
1435
|
+
const symbol = typeChecker.getSymbolAtLocation(node.name);
|
|
1436
|
+
if (!symbol) return;
|
|
1437
|
+
/**
|
|
1438
|
+
* TODO: Workaround for what seems like a bug in the compiler
|
|
1439
|
+
* Warrants more investigation and possibly a PR against typescript
|
|
1440
|
+
*/
|
|
1441
|
+
if (node.kind === SyntaxKind.Parameter) symbol.flags = 0;
|
|
1442
|
+
const comments = symbol.getDocumentationComment(typeChecker);
|
|
1443
|
+
if (comments.length) return displayPartsToString(comments);
|
|
1444
|
+
}
|
|
1445
|
+
function toTypeNodeOrFail(typeChecker, ...args) {
|
|
1446
|
+
const output = typeChecker.typeToTypeNode(...args);
|
|
1447
|
+
if (typeof output === "undefined") throw new ResolverError$1("Type could not be transformed to TypeNode.");
|
|
1448
|
+
return output;
|
|
1449
|
+
}
|
|
1450
|
+
//#endregion
|
|
1451
|
+
//#region src/adapters/typescript/resolver/sub/indexed-access.ts
|
|
1452
|
+
function resolveIndexedAccessType(typeNode, ctx) {
|
|
1453
|
+
if (!ts.isIndexedAccessTypeNode(typeNode)) return;
|
|
1454
|
+
if (typeNode.indexType.kind === ts.SyntaxKind.NumberKeyword || typeNode.indexType.kind === ts.SyntaxKind.StringKeyword) {
|
|
1455
|
+
const numberIndexType = typeNode.indexType.kind === ts.SyntaxKind.NumberKeyword;
|
|
1456
|
+
const objectType = ctx.typeChecker.getTypeFromTypeNode(typeNode.objectType);
|
|
1457
|
+
const type = numberIndexType ? objectType.getNumberIndexType() : objectType.getStringIndexType();
|
|
1458
|
+
if (type === void 0) throw new ResolverError$1(`Could not determine ${numberIndexType ? "number" : "string"} index on ${ctx.typeChecker.typeToString(objectType)}`, typeNode);
|
|
1459
|
+
return ctx.resolveType(toTypeNodeOrFail(ctx.typeChecker, type, void 0, void 0), typeNode, ctx.context, ctx.referencer);
|
|
1460
|
+
}
|
|
1461
|
+
if (ts.isLiteralTypeNode(typeNode.indexType) && (ts.isStringLiteral(typeNode.indexType.literal) || ts.isNumericLiteral(typeNode.indexType.literal))) {
|
|
1462
|
+
const hasType = (node) => node !== void 0 && Object.prototype.hasOwnProperty.call(node, "type");
|
|
1463
|
+
const symbol = ctx.typeChecker.getPropertyOfType(ctx.typeChecker.getTypeFromTypeNode(typeNode.objectType), typeNode.indexType.literal.text);
|
|
1464
|
+
if (symbol === void 0) throw new ResolverError$1(`Could not determine the keys on ${ctx.typeChecker.typeToString(ctx.typeChecker.getTypeFromTypeNode(typeNode.objectType))}`, typeNode);
|
|
1465
|
+
if (hasType(symbol.valueDeclaration) && symbol.valueDeclaration.type) return ctx.resolveType(symbol.valueDeclaration.type, typeNode, ctx.context, ctx.referencer);
|
|
1466
|
+
const declaration = ctx.typeChecker.getTypeOfSymbolAtLocation(symbol, typeNode.objectType);
|
|
1467
|
+
try {
|
|
1468
|
+
return ctx.resolveType(toTypeNodeOrFail(ctx.typeChecker, declaration, void 0, void 0), typeNode, ctx.context, ctx.referencer);
|
|
1469
|
+
} catch (err) {
|
|
1470
|
+
throw new ResolverError$1(`Could not determine the keys on ${ctx.typeChecker.typeToString(declaration)}`, typeNode, { cause: err });
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
//#endregion
|
|
1475
|
+
//#region src/adapters/typescript/resolver/sub/intersection.ts
|
|
1476
|
+
function resolveIntersectionType(typeNode, ctx) {
|
|
1477
|
+
if (!ts.isIntersectionTypeNode(typeNode)) return;
|
|
1478
|
+
return {
|
|
1479
|
+
typeName: "intersection",
|
|
1480
|
+
members: typeNode.types.map((type) => ctx.resolveType(type, ctx.parentNode, ctx.context))
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
//#endregion
|
|
1484
|
+
//#region src/adapters/typescript/resolver/sub/literal.ts
|
|
1485
|
+
function resolveLiteralType(typeNode, ctx) {
|
|
1486
|
+
if (typeNode.kind === ts.SyntaxKind.NullKeyword) return {
|
|
1487
|
+
typeName: "enum",
|
|
1488
|
+
members: [null]
|
|
1489
|
+
};
|
|
1490
|
+
if (typeNode.kind === ts.SyntaxKind.AnyKeyword || typeNode.kind === ts.SyntaxKind.UnknownKeyword) return { typeName: "any" };
|
|
1491
|
+
if (ts.isLiteralTypeNode(typeNode)) return {
|
|
1492
|
+
typeName: "enum",
|
|
1493
|
+
members: [getLiteralValue(typeNode)]
|
|
1494
|
+
};
|
|
1495
|
+
if (typeNode.kind === ts.SyntaxKind.TemplateLiteralType) {
|
|
1496
|
+
const type = ctx.typeChecker.getTypeFromTypeNode(ctx.referencer || typeNode);
|
|
1497
|
+
if (type.isUnion() && type.types.every((t) => t.isStringLiteral())) return {
|
|
1498
|
+
typeName: "enum",
|
|
1499
|
+
members: type.types.map((t) => t.value)
|
|
1500
|
+
};
|
|
1501
|
+
throw new ResolverError$1(`Could not resolve type: ${ctx.typeChecker.typeToString(ctx.typeChecker.getTypeFromTypeNode(typeNode), typeNode)}`, typeNode);
|
|
1502
|
+
}
|
|
1503
|
+
if (ts.isParenthesizedTypeNode(typeNode)) return ctx.resolveType(typeNode.type, typeNode, ctx.context, ctx.referencer);
|
|
1504
|
+
if (typeNode.kind === ts.SyntaxKind.ObjectKeyword || ts.isFunctionTypeNode(typeNode)) return { typeName: "object" };
|
|
1505
|
+
}
|
|
1506
|
+
function getLiteralValue(typeNode) {
|
|
1507
|
+
let value;
|
|
1508
|
+
switch (typeNode.literal.kind) {
|
|
1509
|
+
case ts.SyntaxKind.TrueKeyword:
|
|
1510
|
+
value = true;
|
|
1511
|
+
break;
|
|
1512
|
+
case ts.SyntaxKind.FalseKeyword:
|
|
1513
|
+
value = false;
|
|
1514
|
+
break;
|
|
1515
|
+
case ts.SyntaxKind.StringLiteral:
|
|
1516
|
+
value = typeNode.literal.text;
|
|
1517
|
+
break;
|
|
1518
|
+
case ts.SyntaxKind.NumericLiteral:
|
|
1519
|
+
value = Number.parseFloat(typeNode.literal.text);
|
|
1520
|
+
break;
|
|
1521
|
+
case ts.SyntaxKind.NullKeyword:
|
|
1522
|
+
value = null;
|
|
1523
|
+
break;
|
|
1524
|
+
default: if (Object.prototype.hasOwnProperty.call(typeNode.literal, "text")) value = typeNode.literal.text;
|
|
1525
|
+
else throw new ResolverError$1(`Couldn't resolve literal node: ${typeNode.literal.getText()}`, typeNode.literal);
|
|
1526
|
+
}
|
|
1527
|
+
return value;
|
|
1528
|
+
}
|
|
1529
|
+
//#endregion
|
|
1530
|
+
//#region src/adapters/typescript/resolver/sub/mapped.ts
|
|
1531
|
+
function resolveMappedType(typeNode, ctx) {
|
|
1532
|
+
if (!ts.isMappedTypeNode(typeNode) || !ctx.referencer) return;
|
|
1533
|
+
const type = ctx.typeChecker.getTypeFromTypeNode(ctx.referencer);
|
|
1534
|
+
const mappedTypeNode = typeNode;
|
|
1535
|
+
const { typeChecker } = ctx;
|
|
1536
|
+
const getDeclaration = (prop) => prop.declarations && prop.declarations[0];
|
|
1537
|
+
const isIgnored = (prop) => {
|
|
1538
|
+
const declaration = getDeclaration(prop);
|
|
1539
|
+
if (prop.getJsDocTags().findIndex((tag) => tag.name === "ignore") >= 0) return true;
|
|
1540
|
+
return !!declaration && !ts.isPropertyDeclaration(declaration) && !ts.isPropertySignature(declaration) && !ts.isParameter(declaration);
|
|
1541
|
+
};
|
|
1542
|
+
return {
|
|
1543
|
+
typeName: "nestedObjectLiteral",
|
|
1544
|
+
properties: type.getProperties().filter((property) => !isIgnored(property)).map((property) => {
|
|
1545
|
+
const declaration = getDeclaration(property);
|
|
1546
|
+
const overrideToken = mappedTypeNode.questionToken?.kind === ts.SyntaxKind.PlusToken ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : mappedTypeNode.questionToken;
|
|
1547
|
+
if (declaration && ts.isPropertySignature(declaration)) return {
|
|
1548
|
+
...ctx.propertyFromSignature(declaration, overrideToken),
|
|
1549
|
+
name: property.getName()
|
|
1550
|
+
};
|
|
1551
|
+
if (declaration && (ts.isPropertyDeclaration(declaration) || ts.isParameter(declaration))) return {
|
|
1552
|
+
...ctx.propertyFromDeclaration(declaration, overrideToken),
|
|
1553
|
+
name: property.getName()
|
|
1554
|
+
};
|
|
1555
|
+
let required = (property.flags & ts.SymbolFlags.Optional) === 0;
|
|
1556
|
+
const typeNode2 = toTypeNodeOrFail(typeChecker, typeChecker.getTypeOfSymbolAtLocation(property, typeNode), void 0, ts.NodeBuilderFlags.NoTruncation);
|
|
1557
|
+
if (mappedTypeNode.questionToken && mappedTypeNode.questionToken.kind === ts.SyntaxKind.MinusToken) required = true;
|
|
1558
|
+
else if (mappedTypeNode.questionToken && (mappedTypeNode.questionToken.kind === ts.SyntaxKind.QuestionToken || mappedTypeNode.questionToken.kind === ts.SyntaxKind.PlusToken)) required = false;
|
|
1559
|
+
return {
|
|
1560
|
+
name: property.getName(),
|
|
1561
|
+
required,
|
|
1562
|
+
deprecated: false,
|
|
1563
|
+
type: ctx.resolveType(typeNode2, typeNode, ctx.context, ctx.referencer),
|
|
1564
|
+
validators: {}
|
|
1565
|
+
};
|
|
1566
|
+
})
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
//#endregion
|
|
1570
|
+
//#region src/adapters/typescript/resolver/sub/object-literal.ts
|
|
1571
|
+
function resolveObjectLiteralType(typeNode, ctx) {
|
|
1572
|
+
if (!ts.isTypeLiteralNode(typeNode)) return;
|
|
1573
|
+
const properties = typeNode.members.filter((member) => ts.isPropertySignature(member)).reduce((res, propertySignature) => {
|
|
1574
|
+
if (!propertySignature.type) throw new ResolverError$1("No valid type found for property declaration.", propertySignature);
|
|
1575
|
+
const type = ctx.resolveType(propertySignature.type, propertySignature, ctx.context);
|
|
1576
|
+
return [{
|
|
1577
|
+
deprecated: hasJSDocTag(propertySignature, "deprecated"),
|
|
1578
|
+
example: ctx.getNodeExample(propertySignature),
|
|
1579
|
+
extensions: ctx.getNodeExtensions(propertySignature),
|
|
1580
|
+
default: getJSDocTagComment(propertySignature, "default"),
|
|
1581
|
+
description: ctx.getNodeDescription(propertySignature),
|
|
1582
|
+
format: getNodeFormat(propertySignature),
|
|
1583
|
+
name: getPropertyName(propertySignature),
|
|
1584
|
+
required: !propertySignature.questionToken,
|
|
1585
|
+
type,
|
|
1586
|
+
validators: getDeclarationValidators(propertySignature) || {}
|
|
1587
|
+
}, ...res];
|
|
1588
|
+
}, []);
|
|
1589
|
+
const indexMember = typeNode.members.find((member) => ts.isIndexSignatureDeclaration(member));
|
|
1590
|
+
let additionalType;
|
|
1591
|
+
if (indexMember) {
|
|
1592
|
+
const indexSignatureDeclaration = indexMember;
|
|
1593
|
+
if (!isStringType(ctx.resolveType(indexSignatureDeclaration.parameters[0].type, ctx.parentNode, ctx.context))) throw new ResolverError$1("Only string indexes are supported.", typeNode);
|
|
1594
|
+
additionalType = ctx.resolveType(indexSignatureDeclaration.type, ctx.parentNode, ctx.context);
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
additionalProperties: indexMember && additionalType,
|
|
1598
|
+
typeName: "nestedObjectLiteral",
|
|
1599
|
+
properties
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
function getNodeFormat(node) {
|
|
1603
|
+
return getJSDocTagComment(node, "format");
|
|
1604
|
+
}
|
|
1605
|
+
function getPropertyName(node) {
|
|
1606
|
+
if (ts.isIdentifier(node.name)) return node.name.text;
|
|
1607
|
+
if (ts.isStringLiteral(node.name) || ts.isNumericLiteral(node.name)) return node.name.text;
|
|
1608
|
+
return node.name.getText();
|
|
1609
|
+
}
|
|
1610
|
+
//#endregion
|
|
1611
|
+
//#region src/adapters/typescript/resolver/sub/primitive.ts
|
|
1612
|
+
const NUMERIC_KIND_TO_TYPE_NAME = {
|
|
1613
|
+
[NumericKind.Int]: "integer",
|
|
1614
|
+
[NumericKind.Long]: "long",
|
|
1615
|
+
[NumericKind.Float]: "float",
|
|
1616
|
+
[NumericKind.Double]: "double"
|
|
1617
|
+
};
|
|
1618
|
+
var PrimitiveResolver = class {
|
|
1619
|
+
registry;
|
|
1620
|
+
constructor(registry) {
|
|
1621
|
+
this.registry = registry;
|
|
1622
|
+
}
|
|
1623
|
+
resolve(node, parentNode) {
|
|
1624
|
+
const resolved = this.resolveSyntaxKind(node.kind);
|
|
1625
|
+
if (resolved) {
|
|
1626
|
+
if (resolved === "string") return { typeName: "string" };
|
|
1627
|
+
if (resolved === "void") return { typeName: "void" };
|
|
1628
|
+
if (resolved === "boolean") return { typeName: "boolean" };
|
|
1629
|
+
if (resolved === "undefined") return { typeName: "undefined" };
|
|
1630
|
+
if (resolved === "null") return;
|
|
1631
|
+
if (resolved === "never") return { typeName: "never" };
|
|
1632
|
+
if (resolved === "bigint") return { typeName: "bigint" };
|
|
1633
|
+
if (resolved === "number") {
|
|
1634
|
+
if (!parentNode) return { typeName: "double" };
|
|
1635
|
+
const presentJsDocTags = new Set(getJSDocTagNames(parentNode).map((tag) => tag.toLowerCase()));
|
|
1636
|
+
for (const kind of [
|
|
1637
|
+
NumericKind.Int,
|
|
1638
|
+
NumericKind.Long,
|
|
1639
|
+
NumericKind.Float,
|
|
1640
|
+
NumericKind.Double
|
|
1641
|
+
]) {
|
|
1642
|
+
const matchKind = (m) => numericMarkerKind(m) === kind;
|
|
1643
|
+
const decoratorNames = namesForMarker(this.registry, matchKind);
|
|
1644
|
+
for (const name of decoratorNames) if (hasDecoratorNamed(parentNode, name)) return { typeName: NUMERIC_KIND_TO_TYPE_NAME[kind] };
|
|
1645
|
+
const jsDocTags = tagsForMarker(this.registry, matchKind);
|
|
1646
|
+
for (const tag of jsDocTags) if (presentJsDocTags.has(tag.toLowerCase())) return { typeName: NUMERIC_KIND_TO_TYPE_NAME[kind] };
|
|
1647
|
+
}
|
|
1648
|
+
return { typeName: "double" };
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
resolveSyntaxKind(syntaxKind) {
|
|
1653
|
+
switch (syntaxKind) {
|
|
1654
|
+
case SyntaxKind.StringKeyword: return "string";
|
|
1655
|
+
case SyntaxKind.BooleanKeyword: return "boolean";
|
|
1656
|
+
case SyntaxKind.VoidKeyword: return "void";
|
|
1657
|
+
case SyntaxKind.UndefinedKeyword: return "undefined";
|
|
1658
|
+
case SyntaxKind.NullKeyword: return "null";
|
|
1659
|
+
case SyntaxKind.NumberKeyword: return "number";
|
|
1660
|
+
case SyntaxKind.BigIntKeyword: return "bigint";
|
|
1661
|
+
case SyntaxKind.NeverKeyword: return "never";
|
|
1662
|
+
default: return;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
//#endregion
|
|
1667
|
+
//#region src/adapters/typescript/resolver/sub/reference.ts
|
|
1668
|
+
var ReferenceResolver = class extends ResolverBase {
|
|
1669
|
+
typeChecker;
|
|
1670
|
+
constructor(typeChecker) {
|
|
1671
|
+
super();
|
|
1672
|
+
this.typeChecker = typeChecker;
|
|
1673
|
+
}
|
|
1674
|
+
merge(referenceTypes) {
|
|
1675
|
+
if (referenceTypes.length === 1) return referenceTypes[0];
|
|
1676
|
+
if (referenceTypes.every((refType) => refType.refName === "refEnum")) return this.mergeManyRefEnums(referenceTypes);
|
|
1677
|
+
if (referenceTypes.every((refType) => refType.refName === "refObject")) return this.mergeManyRefObjects(referenceTypes);
|
|
1678
|
+
throw new ResolverError$1(`These resolved type merge rules are not defined: ${JSON.stringify(referenceTypes)}`);
|
|
1679
|
+
}
|
|
1680
|
+
mergeManyRefEnums(many) {
|
|
1681
|
+
let merged = this.mergeRefEnums(many[0], many[1]);
|
|
1682
|
+
for (let i = 2; i < many.length; ++i) merged = this.mergeRefEnums(merged, many[i]);
|
|
1683
|
+
return merged;
|
|
1684
|
+
}
|
|
1685
|
+
mergeRefEnums(first, second) {
|
|
1686
|
+
let description;
|
|
1687
|
+
if (first.description || second.description) if (!first.description) description = second.description;
|
|
1688
|
+
else if (!second.description) description = first.description;
|
|
1689
|
+
else description = `${first.description}\n${second.description}`;
|
|
1690
|
+
return {
|
|
1691
|
+
typeName: "refEnum",
|
|
1692
|
+
example: first.example || second.example,
|
|
1693
|
+
description,
|
|
1694
|
+
members: [...first.members || [], ...second.members || []],
|
|
1695
|
+
memberNames: [...first.memberNames || [], ...second.memberNames || []],
|
|
1696
|
+
refName: first.refName,
|
|
1697
|
+
deprecated: first.deprecated || second.deprecated
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
mergeManyRefObjects(many) {
|
|
1701
|
+
let merged = this.mergeRefObject(many[0], many[1]);
|
|
1702
|
+
for (let i = 2; i < many.length; ++i) merged = this.mergeRefObject(merged, many[i]);
|
|
1703
|
+
return merged;
|
|
1704
|
+
}
|
|
1705
|
+
mergeRefObject(first, second) {
|
|
1706
|
+
let description;
|
|
1707
|
+
if (first.description || second.description) if (!first.description) description = second.description;
|
|
1708
|
+
else if (!second.description) description = first.description;
|
|
1709
|
+
else description = `${first.description}\n${second.description}`;
|
|
1710
|
+
const properties = [...first.properties, ...second.properties.filter((prop) => first.properties.every((firstProp) => firstProp.name !== prop.name))];
|
|
1711
|
+
let additionalProperties;
|
|
1712
|
+
if (first.additionalProperties || second.additionalProperties) if (!first.additionalProperties) additionalProperties = second.additionalProperties;
|
|
1713
|
+
else if (!second.additionalProperties) additionalProperties = first.additionalProperties;
|
|
1714
|
+
else additionalProperties = {
|
|
1715
|
+
typeName: "union",
|
|
1716
|
+
members: [first.additionalProperties, second.additionalProperties]
|
|
1717
|
+
};
|
|
1718
|
+
return {
|
|
1719
|
+
typeName: "refObject",
|
|
1720
|
+
description,
|
|
1721
|
+
properties,
|
|
1722
|
+
additionalProperties,
|
|
1723
|
+
refName: first.refName,
|
|
1724
|
+
deprecated: first.deprecated || second.deprecated,
|
|
1725
|
+
example: first.example || second.example
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
transformEnum(declaration, enumName) {
|
|
1729
|
+
const isNotUndefined = (item) => item !== void 0;
|
|
1730
|
+
const enums = declaration.members.map(this.typeChecker.getConstantValue.bind(this.typeChecker)).filter(isNotUndefined);
|
|
1731
|
+
const enumNames = declaration.members.map((e) => e.name.getText()).filter(isNotUndefined);
|
|
1732
|
+
return {
|
|
1733
|
+
typeName: "refEnum",
|
|
1734
|
+
description: getNodeDescription(declaration, this.typeChecker),
|
|
1735
|
+
members: enums,
|
|
1736
|
+
memberNames: enumNames,
|
|
1737
|
+
refName: enumName,
|
|
1738
|
+
deprecated: hasJSDocTag(declaration, "deprecated")
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
transformEnumMember(declaration, enumName) {
|
|
1742
|
+
return {
|
|
1743
|
+
typeName: "refEnum",
|
|
1744
|
+
refName: enumName,
|
|
1745
|
+
members: [this.typeChecker.getConstantValue(declaration)],
|
|
1746
|
+
memberNames: [declaration.name.getText()],
|
|
1747
|
+
deprecated: hasJSDocTag(declaration, "deprecated")
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
//#endregion
|
|
1752
|
+
//#region src/adapters/typescript/resolver/sub/tuple.ts
|
|
1753
|
+
function resolveTupleType(typeNode, ctx) {
|
|
1754
|
+
if (!ts.isTupleTypeNode(typeNode)) return;
|
|
1755
|
+
return {
|
|
1756
|
+
typeName: "tuple",
|
|
1757
|
+
elements: typeNode.elements.map((element) => {
|
|
1758
|
+
const isNamed = ts.isNamedTupleMember(element);
|
|
1759
|
+
const actualType = isNamed ? element.type : element;
|
|
1760
|
+
return {
|
|
1761
|
+
type: ctx.resolveType(actualType, ctx.parentNode, ctx.context),
|
|
1762
|
+
...isNamed && { name: element.name.text }
|
|
1763
|
+
};
|
|
1764
|
+
})
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
//#endregion
|
|
1768
|
+
//#region src/adapters/typescript/resolver/sub/type-operator.ts
|
|
1769
|
+
function resolveTypeOperatorType(typeNode, ctx) {
|
|
1770
|
+
if (!ts.isTypeOperatorNode(typeNode)) return;
|
|
1771
|
+
if (typeNode.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
1772
|
+
const type = ctx.typeChecker.getTypeFromTypeNode(typeNode);
|
|
1773
|
+
try {
|
|
1774
|
+
return ctx.resolveType(toTypeNodeOrFail(ctx.typeChecker, type, void 0, ts.NodeBuilderFlags.NoTruncation), typeNode, ctx.context, ctx.referencer);
|
|
1775
|
+
} catch (err) {
|
|
1776
|
+
throw new ResolverError$1(`Could not determine the keys on ${ctx.typeChecker.typeToString(ctx.typeChecker.getTypeFromTypeNode(typeNode.type))}`, typeNode, { cause: err });
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if (typeNode.operator === ts.SyntaxKind.ReadonlyKeyword) return ctx.resolveType(typeNode.type, typeNode, ctx.context, ctx.referencer);
|
|
1780
|
+
}
|
|
1781
|
+
//#endregion
|
|
1782
|
+
//#region src/adapters/typescript/resolver/sub/union.ts
|
|
1783
|
+
function resolveUnionType(typeNode, ctx) {
|
|
1784
|
+
if (!ts.isUnionTypeNode(typeNode)) return;
|
|
1785
|
+
return {
|
|
1786
|
+
typeName: "union",
|
|
1787
|
+
members: typeNode.types.map((type) => ctx.resolveType(type, ctx.parentNode, ctx.context))
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
//#endregion
|
|
1791
|
+
//#region src/adapters/typescript/resolver/module.ts
|
|
1792
|
+
var TypeNodeResolver = class TypeNodeResolver extends ResolverBase {
|
|
1793
|
+
static MAX_DEPTH = 50;
|
|
1794
|
+
typeNode;
|
|
1795
|
+
current;
|
|
1796
|
+
parentNode;
|
|
1797
|
+
context;
|
|
1798
|
+
referencer;
|
|
1799
|
+
depth;
|
|
1800
|
+
primitiveResolver;
|
|
1801
|
+
referenceResolver;
|
|
1802
|
+
constructor(typeNode, current, parentNode, context, referencer, depth) {
|
|
1803
|
+
super();
|
|
1804
|
+
this.typeNode = typeNode;
|
|
1805
|
+
this.current = current;
|
|
1806
|
+
this.parentNode = parentNode;
|
|
1807
|
+
this.context = context || {};
|
|
1808
|
+
this.referencer = referencer;
|
|
1809
|
+
this.depth = depth ?? 0;
|
|
1810
|
+
this.primitiveResolver = new PrimitiveResolver(current.registry);
|
|
1811
|
+
this.referenceResolver = new ReferenceResolver(current.typeChecker);
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* @deprecated Use resolverCache.clear() on the context instead.
|
|
1815
|
+
* Kept for backward compatibility — no-ops since cache is now instance-scoped.
|
|
1816
|
+
*/
|
|
1817
|
+
static clearCache() {}
|
|
1818
|
+
resolve() {
|
|
1819
|
+
if (this.depth > TypeNodeResolver.MAX_DEPTH) throw new ResolverError$1(`Type resolution exceeded maximum depth of ${TypeNodeResolver.MAX_DEPTH}. This usually indicates deeply nested or circular generics.`, this.typeNode);
|
|
1820
|
+
const ctx = this.createSubResolverContext();
|
|
1821
|
+
const result = this.primitiveResolver.resolve(this.typeNode, this.parentNode) ?? resolveLiteralType(this.typeNode, ctx) ?? resolveArrayType(this.typeNode, ctx) ?? resolveUnionType(this.typeNode, ctx) ?? resolveIntersectionType(this.typeNode, ctx) ?? resolveObjectLiteralType(this.typeNode, ctx) ?? resolveTupleType(this.typeNode, ctx) ?? resolveMappedType(this.typeNode, ctx) ?? this.resolveConditionalType() ?? resolveTypeOperatorType(this.typeNode, ctx) ?? resolveIndexedAccessType(this.typeNode, ctx) ?? this.resolveTypeReference();
|
|
1822
|
+
if (!result) this.throwUnknownType();
|
|
1823
|
+
return result;
|
|
1824
|
+
}
|
|
1825
|
+
createSubResolverContext() {
|
|
1826
|
+
return {
|
|
1827
|
+
typeChecker: this.current.typeChecker,
|
|
1828
|
+
current: this.current,
|
|
1829
|
+
parentNode: this.parentNode,
|
|
1830
|
+
context: this.context,
|
|
1831
|
+
referencer: this.referencer,
|
|
1832
|
+
resolveType: (typeNode, parentNode, context, referencer) => this.resolveNestedType(typeNode, parentNode, context, referencer),
|
|
1833
|
+
propertyFromSignature: (sig, overrideToken) => this.propertyFromSignature(sig, overrideToken),
|
|
1834
|
+
propertyFromDeclaration: (decl, overrideToken) => this.propertyFromDeclaration(decl, overrideToken),
|
|
1835
|
+
getNodeDescription: (node) => this.getNodeDescription(node),
|
|
1836
|
+
getNodeExample: (node) => this.getNodeExample(node),
|
|
1837
|
+
getNodeExtensions: (node) => this.getNodeExtensions(node)
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
resolveNestedType(typeNode, parentNode, context, referencer) {
|
|
1841
|
+
return new TypeNodeResolver(typeNode, this.current, parentNode, context, referencer, this.depth + 1).resolve();
|
|
1842
|
+
}
|
|
1843
|
+
throwUnknownType() {
|
|
1844
|
+
throw new ResolverError$1(`Unknown type: ${ts.SyntaxKind[this.typeNode.kind]}`, this.typeNode);
|
|
1845
|
+
}
|
|
1846
|
+
resolveConditionalType() {
|
|
1847
|
+
if (!ts.isConditionalTypeNode(this.typeNode)) return;
|
|
1848
|
+
if (Object.keys(this.context).length > 0 && this.referencer) return this.resolveTypeViaChecker(this.referencer);
|
|
1849
|
+
return this.resolveTypeViaChecker(this.typeNode);
|
|
1850
|
+
}
|
|
1851
|
+
resolveTypeReference() {
|
|
1852
|
+
if (this.typeNode.kind !== ts.SyntaxKind.TypeReference) return;
|
|
1853
|
+
const typeReference = this.typeNode;
|
|
1854
|
+
if (typeReference.typeName.kind === ts.SyntaxKind.Identifier) {
|
|
1855
|
+
if (typeReference.typeName.text === "Record" && typeReference.typeArguments) return {
|
|
1856
|
+
additionalProperties: this.resolveNestedType(typeReference.typeArguments[1], this.parentNode, this.context),
|
|
1857
|
+
typeName: "nestedObjectLiteral",
|
|
1858
|
+
properties: []
|
|
1859
|
+
};
|
|
1860
|
+
const specialReference = TypeNodeResolver.resolveSpecialReference(typeReference.typeName);
|
|
1861
|
+
if (typeof specialReference !== "undefined") return specialReference;
|
|
1862
|
+
if (typeReference.typeName.text === "Date") return this.getDateType(this.parentNode);
|
|
1863
|
+
if (typeReference.typeName.text === "Buffer" || typeReference.typeName.text === "Readable") return { typeName: "buffer" };
|
|
1864
|
+
if (typeReference.typeName.text === "Array" && typeReference.typeArguments && typeReference.typeArguments.length >= 1) return {
|
|
1865
|
+
typeName: "array",
|
|
1866
|
+
elementType: this.resolveNestedType(typeReference.typeArguments[0], this.parentNode, this.context)
|
|
1867
|
+
};
|
|
1868
|
+
if (typeReference.typeName.text === "Promise" && typeReference.typeArguments && typeReference.typeArguments.length === 1) return this.resolveNestedType(typeReference.typeArguments[0], this.parentNode, this.context);
|
|
1869
|
+
if (typeReference.typeName.text === "String") return { typeName: "string" };
|
|
1870
|
+
if (this.context[typeReference.typeName.text]) return this.resolveNestedType(this.context[typeReference.typeName.text], this.parentNode, this.context);
|
|
1871
|
+
if (TypeNodeResolver.isCheckerResolvableUtilityType(typeReference.typeName.text)) return this.resolveUtilityTypeViaChecker(typeReference);
|
|
1872
|
+
}
|
|
1873
|
+
try {
|
|
1874
|
+
const referenceType = this.getReferenceType(typeReference);
|
|
1875
|
+
this.current.addReferenceType(referenceType);
|
|
1876
|
+
return referenceType;
|
|
1877
|
+
} catch (err) {
|
|
1878
|
+
try {
|
|
1879
|
+
return this.resolveTypeViaChecker(typeReference);
|
|
1880
|
+
} catch {
|
|
1881
|
+
throw err;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
static CHECKER_RESOLVABLE_UTILITY_TYPES = new Set([
|
|
1886
|
+
"NonNullable",
|
|
1887
|
+
"Omit",
|
|
1888
|
+
"Partial",
|
|
1889
|
+
"Readonly",
|
|
1890
|
+
"Required",
|
|
1891
|
+
"Pick",
|
|
1892
|
+
"Extract",
|
|
1893
|
+
"Exclude",
|
|
1894
|
+
"ReturnType",
|
|
1895
|
+
"Parameters",
|
|
1896
|
+
"Awaited",
|
|
1897
|
+
"InstanceType",
|
|
1898
|
+
"ConstructorParameters"
|
|
1899
|
+
]);
|
|
1900
|
+
static isCheckerResolvableUtilityType(name) {
|
|
1901
|
+
return TypeNodeResolver.CHECKER_RESOLVABLE_UTILITY_TYPES.has(name);
|
|
1902
|
+
}
|
|
1903
|
+
resolveUtilityTypeViaChecker(typeReference) {
|
|
1904
|
+
if (this.hasUnboundContextArgs(typeReference) && this.referencer) return this.resolveTypeViaChecker(this.referencer);
|
|
1905
|
+
return this.resolveTypeViaChecker(typeReference);
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Shared checker delegation: resolves a type node by letting the TS
|
|
1909
|
+
* type checker evaluate it, converting back to a TypeNode, and
|
|
1910
|
+
* recursively resolving the result.
|
|
1911
|
+
*/
|
|
1912
|
+
resolveTypeViaChecker(typeNode) {
|
|
1913
|
+
const type = this.current.typeChecker.getTypeFromTypeNode(typeNode);
|
|
1914
|
+
const resolvedTypeNode = this.current.typeChecker.typeToTypeNode(type, void 0, ts.NodeBuilderFlags.NoTruncation | ts.NodeBuilderFlags.InTypeAlias);
|
|
1915
|
+
if (!resolvedTypeNode) return {
|
|
1916
|
+
typeName: "tuple",
|
|
1917
|
+
elements: []
|
|
1918
|
+
};
|
|
1919
|
+
return this.resolveNestedType(resolvedTypeNode, this.parentNode, this.context);
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Check if a type reference has type arguments that reference unbound
|
|
1923
|
+
* type parameters from this.context (e.g. `Awaited<T>` where T is a
|
|
1924
|
+
* generic parameter mapped in context).
|
|
1925
|
+
*/
|
|
1926
|
+
hasUnboundContextArgs(typeReference) {
|
|
1927
|
+
if (!typeReference.typeArguments || Object.keys(this.context).length === 0) return false;
|
|
1928
|
+
return typeReference.typeArguments.some((arg) => ts.isTypeReferenceNode(arg) && ts.isIdentifier(arg.typeName) && arg.typeName.text in this.context);
|
|
1929
|
+
}
|
|
1930
|
+
static resolveSpecialReference(node) {
|
|
1931
|
+
switch (node.text) {
|
|
1932
|
+
case "Buffer":
|
|
1933
|
+
case "DownloadBinaryData":
|
|
1934
|
+
case "DownloadResource": return { typeName: "buffer" };
|
|
1935
|
+
default: return;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
getDateType(parentNode) {
|
|
1939
|
+
if (!parentNode) return { typeName: "datetime" };
|
|
1940
|
+
const tags = getJSDocTagNames(parentNode).filter((name) => ["isDate", "isDateTime"].includes(name));
|
|
1941
|
+
if (tags.length === 0) return { typeName: "datetime" };
|
|
1942
|
+
switch (tags[0]) {
|
|
1943
|
+
case "isDate": return { typeName: "date" };
|
|
1944
|
+
default: return { typeName: "datetime" };
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
static getDesignatedModels(nodes, _typeName) {
|
|
1948
|
+
return nodes;
|
|
1949
|
+
}
|
|
1950
|
+
getEnumerateType(typeName) {
|
|
1951
|
+
const enumName = typeName.text;
|
|
1952
|
+
let enumNodes = this.current.nodes.filter((node) => node.kind === ts.SyntaxKind.EnumDeclaration && node.name.text === enumName);
|
|
1953
|
+
if (!enumNodes.length) return;
|
|
1954
|
+
enumNodes = TypeNodeResolver.getDesignatedModels(enumNodes, enumName);
|
|
1955
|
+
if (enumNodes.length > 1) throw new ResolverError$1(`Multiple matching enum found for enum ${enumName}; please make enum names unique.`);
|
|
1956
|
+
const enumDeclaration = enumNodes[0];
|
|
1957
|
+
const isNotUndefined = (item) => item !== void 0;
|
|
1958
|
+
const enums = enumDeclaration.members.map(this.current.typeChecker.getConstantValue.bind(this.current.typeChecker)).filter(isNotUndefined);
|
|
1959
|
+
const enumNames = enumDeclaration.members.map((e) => e.name.getText()).filter(isNotUndefined);
|
|
1960
|
+
return {
|
|
1961
|
+
typeName: "refEnum",
|
|
1962
|
+
description: this.getNodeDescription(enumDeclaration),
|
|
1963
|
+
members: enums,
|
|
1964
|
+
memberNames: enumNames,
|
|
1965
|
+
refName: enumName,
|
|
1966
|
+
deprecated: hasJSDocTag(enumDeclaration, "deprecated")
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
getReferenceType(node) {
|
|
1970
|
+
let type;
|
|
1971
|
+
if (ts.isTypeReferenceNode(node)) type = node.typeName;
|
|
1972
|
+
else if (ts.isExpressionWithTypeArguments(node)) type = node.expression;
|
|
1973
|
+
else throw new ResolverError$1("Can't resolve reference type.");
|
|
1974
|
+
let resolvableName = node.pos !== -1 ? node.getText() : type.text;
|
|
1975
|
+
if (node.pos === -1 && "typeArguments" in node && Array.isArray(node.typeArguments)) {
|
|
1976
|
+
const argumentsString = node.typeArguments.map((arg) => {
|
|
1977
|
+
if (ts.isLiteralTypeNode(arg)) return `'${String(getLiteralValue(arg))}'`;
|
|
1978
|
+
const resolvedType = this.primitiveResolver.resolveSyntaxKind(arg.kind);
|
|
1979
|
+
if (typeof resolvedType === "undefined") return "any";
|
|
1980
|
+
return resolvedType;
|
|
1981
|
+
});
|
|
1982
|
+
resolvableName += `<${argumentsString.join(", ")}>`;
|
|
1983
|
+
}
|
|
1984
|
+
const name = this.contextualizedName(resolvableName);
|
|
1985
|
+
this.typeArgumentsToContext(node, type, this.context);
|
|
1986
|
+
try {
|
|
1987
|
+
const existingType = this.current.resolverCache.getCachedType(name);
|
|
1988
|
+
if (existingType) return existingType;
|
|
1989
|
+
if (this.current.resolverCache.isInProgress(name)) return this.createCircularDependencyResolver(name);
|
|
1990
|
+
this.current.resolverCache.markInProgress(name);
|
|
1991
|
+
try {
|
|
1992
|
+
const refName = TypeNodeResolver.getRefTypeName(name);
|
|
1993
|
+
const declarations = this.getModelTypeDeclarations(type);
|
|
1994
|
+
const referenceTypes = [];
|
|
1995
|
+
for (const declaration of declarations) if (ts.isTypeAliasDeclaration(declaration)) referenceTypes.push(this.getTypeAliasReference(declaration, name, node));
|
|
1996
|
+
else if (isEnumDeclaration(declaration)) referenceTypes.push(this.referenceResolver.transformEnum(declaration, refName));
|
|
1997
|
+
else if (isEnumMember(declaration)) referenceTypes.push(this.referenceResolver.transformEnumMember(declaration, refName));
|
|
1998
|
+
else referenceTypes.push(this.getModelReference(declaration, name));
|
|
1999
|
+
const referenceType = this.referenceResolver.merge(referenceTypes);
|
|
2000
|
+
this.current.resolverCache.setCachedType(name, referenceType);
|
|
2001
|
+
return referenceType;
|
|
2002
|
+
} finally {
|
|
2003
|
+
this.current.resolverCache.clearInProgress(name);
|
|
2004
|
+
}
|
|
2005
|
+
} catch (err) {
|
|
2006
|
+
throw new ResolverError$1(`There was a problem resolving type of '${name}'.`, node, { cause: err });
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
getTypeAliasReference(declaration, name, referencer) {
|
|
2010
|
+
const refName = TypeNodeResolver.getRefTypeName(name);
|
|
2011
|
+
if (declaration.type.kind === ts.SyntaxKind.TypeReference) {
|
|
2012
|
+
const innerRef = declaration.type;
|
|
2013
|
+
const innerName = ts.isIdentifier(innerRef.typeName) ? innerRef.typeName.text : void 0;
|
|
2014
|
+
if (!(innerName === "Record" || innerName !== void 0 && TypeNodeResolver.isCheckerResolvableUtilityType(innerName))) {
|
|
2015
|
+
const referenceType = this.getReferenceType(innerRef);
|
|
2016
|
+
if (referenceType.refName === refName) return referenceType;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
const type = this.resolveNestedType(declaration.type, declaration, this.context, this.referencer || referencer);
|
|
2020
|
+
const example = this.getNodeExample(declaration);
|
|
2021
|
+
return {
|
|
2022
|
+
typeName: "refAlias",
|
|
2023
|
+
default: getJSDocTagComment(declaration, "default"),
|
|
2024
|
+
description: this.getNodeDescription(declaration),
|
|
2025
|
+
refName,
|
|
2026
|
+
format: TypeNodeResolver.getNodeFormat(declaration),
|
|
2027
|
+
type,
|
|
2028
|
+
validators: getDeclarationValidators(declaration) || {},
|
|
2029
|
+
deprecated: hasJSDocTag(declaration, "deprecated"),
|
|
2030
|
+
...example && { example }
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
getModelReference(modelType, name) {
|
|
2034
|
+
const example = this.getNodeExample(modelType);
|
|
2035
|
+
const description = this.getNodeDescription(modelType);
|
|
2036
|
+
const deprecatedDecoratorNames = namesForMarker(this.current.registry, isDeprecatedMarker);
|
|
2037
|
+
const deprecated = [...tagsForMarker(this.current.registry, isDeprecatedMarker)].some((tag) => hasJSDocTag(modelType, tag)) || [...deprecatedDecoratorNames].some((name) => hasDecoratorNamed(modelType, name));
|
|
2038
|
+
if (!modelType.name) throw new ResolverError$1("Can't get Symbol from anonymous class", modelType);
|
|
2039
|
+
const type = this.current.typeChecker.getTypeAtLocation(modelType.name);
|
|
2040
|
+
const toJSON = this.current.typeChecker.getPropertyOfType(type, "toJSON");
|
|
2041
|
+
if (toJSON && toJSON.valueDeclaration && (ts.isMethodDeclaration(toJSON.valueDeclaration) || ts.isMethodSignature(toJSON.valueDeclaration))) {
|
|
2042
|
+
let nodeType = toJSON.valueDeclaration.type;
|
|
2043
|
+
if (!nodeType) {
|
|
2044
|
+
const signature = this.current.typeChecker.getSignatureFromDeclaration(toJSON.valueDeclaration);
|
|
2045
|
+
if (signature) {
|
|
2046
|
+
const implicitType = this.current.typeChecker.getReturnTypeOfSignature(signature);
|
|
2047
|
+
nodeType = this.current.typeChecker.typeToTypeNode(implicitType, void 0, ts.NodeBuilderFlags.NoTruncation);
|
|
2048
|
+
} else throw new ResolverError$1("Can't get signature from toJson value declaration", modelType);
|
|
2049
|
+
}
|
|
2050
|
+
return {
|
|
2051
|
+
refName: `${TypeNodeResolver.getRefTypeName(name)}Alias`,
|
|
2052
|
+
typeName: "refAlias",
|
|
2053
|
+
description,
|
|
2054
|
+
type: this.resolveNestedType(nodeType),
|
|
2055
|
+
deprecated,
|
|
2056
|
+
validators: {},
|
|
2057
|
+
...example && { example }
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
const properties = this.getModelProperties(modelType);
|
|
2061
|
+
const referenceType = {
|
|
2062
|
+
additionalProperties: this.getModelAdditionalProperties(modelType),
|
|
2063
|
+
typeName: "refObject",
|
|
2064
|
+
description,
|
|
2065
|
+
properties: this.getModelInheritedProperties(modelType) || [],
|
|
2066
|
+
refName: TypeNodeResolver.getRefTypeName(name),
|
|
2067
|
+
deprecated,
|
|
2068
|
+
...example && { example }
|
|
2069
|
+
};
|
|
2070
|
+
referenceType.properties = referenceType.properties.concat(properties);
|
|
2071
|
+
return referenceType;
|
|
2072
|
+
}
|
|
2073
|
+
static getRefTypeName(name) {
|
|
2074
|
+
const sanitized = name.replace(/[<>]/g, "_").replace(/[{}]/g, "_").replace(/\s+/g, "").replace(/,/g, ".").replace(/'([^']*)'/g, "$1").replace(/"([^"]*)"/g, "$1").replace(/&/g, "-and-").replace(/\|/g, "-or-").replace(/\[\]/g, "-array").replace(/([a-z]+):([a-z]+)/gi, "$1-$2").replace(/;/g, "--").replace(/([a-z]+)\[([a-z]+)]/gi, "$1-at-$2").replace(/_/g, "");
|
|
2075
|
+
return encodeURIComponent(sanitized);
|
|
2076
|
+
}
|
|
2077
|
+
contextualizedName(name) {
|
|
2078
|
+
return Object.entries(this.context).reduce((acc, [key, entry]) => acc.replace(new RegExp(`<\\s*([^>]*\\s)*\\s*(${key})(\\s[^>]*)*\\s*>`, "g"), `<$1${entry.getText()}$3>`).replace(new RegExp(`<\\s*([^,]*\\s)*\\s*(${key})(\\s[^,]*)*\\s*,`, "g"), `<$1${entry.getText()}$3,`).replace(new RegExp(`,\\s*([^>]*\\s)*\\s*(${key})(\\s[^>]*)*\\s*>`, "g"), `,$1${entry.getText()}$3>`).replace(new RegExp(`<\\s*([^<]*\\s)*\\s*(${key})(\\s[^<]*)*\\s*<`, "g"), `<$1${entry.getText()}$3<`), name);
|
|
2079
|
+
}
|
|
2080
|
+
handleCachingAndCircularReferences(name, declarationResolver) {
|
|
2081
|
+
try {
|
|
2082
|
+
const existingType = this.current.resolverCache.getCachedType(name);
|
|
2083
|
+
if (existingType) return existingType;
|
|
2084
|
+
if (this.current.resolverCache.isInProgress(name)) return this.createCircularDependencyResolver(name);
|
|
2085
|
+
this.current.resolverCache.markInProgress(name);
|
|
2086
|
+
try {
|
|
2087
|
+
const reference = declarationResolver();
|
|
2088
|
+
this.current.resolverCache.setCachedType(name, reference);
|
|
2089
|
+
this.current.addReferenceType(reference);
|
|
2090
|
+
return reference;
|
|
2091
|
+
} finally {
|
|
2092
|
+
this.current.resolverCache.clearInProgress(name);
|
|
2093
|
+
}
|
|
2094
|
+
} catch (err) {
|
|
2095
|
+
throw new ResolverError$1(`There was a problem resolving type of '${name}'.`, this.typeNode, { cause: err });
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
createCircularDependencyResolver(refName) {
|
|
2099
|
+
const referenceType = {
|
|
2100
|
+
deprecated: false,
|
|
2101
|
+
properties: [],
|
|
2102
|
+
typeName: "refObject",
|
|
2103
|
+
refName
|
|
2104
|
+
};
|
|
2105
|
+
this.current.registerDependencyResolver((referenceTypes) => {
|
|
2106
|
+
const realReferenceType = referenceTypes[refName];
|
|
2107
|
+
if (!realReferenceType) return;
|
|
2108
|
+
referenceType.description = realReferenceType.description;
|
|
2109
|
+
if (realReferenceType.typeName === "refObject" && referenceType.typeName === "refObject") referenceType.properties = realReferenceType.properties;
|
|
2110
|
+
referenceType.typeName = realReferenceType.typeName;
|
|
2111
|
+
referenceType.refName = realReferenceType.refName;
|
|
2112
|
+
});
|
|
2113
|
+
return referenceType;
|
|
2114
|
+
}
|
|
2115
|
+
static nodeIsUsable(node) {
|
|
2116
|
+
switch (node.kind) {
|
|
2117
|
+
case ts.SyntaxKind.InterfaceDeclaration:
|
|
2118
|
+
case ts.SyntaxKind.ClassDeclaration:
|
|
2119
|
+
case ts.SyntaxKind.TypeAliasDeclaration:
|
|
2120
|
+
case ts.SyntaxKind.EnumDeclaration:
|
|
2121
|
+
case ts.SyntaxKind.EnumMember: return true;
|
|
2122
|
+
default: return false;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
getModelTypeDeclarations(type) {
|
|
2126
|
+
let typeName = type.kind === ts.SyntaxKind.Identifier ? type.text : type.right.text;
|
|
2127
|
+
let symbol = this.getSymbolAtLocation(type);
|
|
2128
|
+
if (!symbol && type.kind === ts.SyntaxKind.QualifiedName) symbol = this.getSymbolAtLocation(type.left).exports?.get(typeName);
|
|
2129
|
+
if (!symbol) throw new ResolverError$1(`No symbol found for referenced type ${typeName}.`);
|
|
2130
|
+
const declarations = symbol.getDeclarations();
|
|
2131
|
+
if (!declarations || declarations.length === 0) throw new ResolverError$1(`No declarations found for referenced type ${typeName}.`);
|
|
2132
|
+
if (symbol.escapedName !== typeName && symbol.escapedName !== "default") typeName = symbol.escapedName;
|
|
2133
|
+
let modelTypes = declarations.filter((node) => {
|
|
2134
|
+
if (!TypeNodeResolver.nodeIsUsable(node) || !this.current.isExportedNode(node)) return false;
|
|
2135
|
+
return node.name?.text === typeName;
|
|
2136
|
+
});
|
|
2137
|
+
if (!modelTypes.length) throw new ResolverError$1(`No matching model found for referenced type ${typeName}. If ${typeName} comes from a dependency, please create an interface in your own code that has the same structure. The compiler can not utilize interfaces from external dependencies.`);
|
|
2138
|
+
if (modelTypes.length > 1) {
|
|
2139
|
+
modelTypes = modelTypes.filter((modelType) => modelType.getSourceFile().fileName.replace(/\\/g, "/").toLowerCase().indexOf("node_modules/typescript") <= -1);
|
|
2140
|
+
modelTypes = TypeNodeResolver.getDesignatedModels(modelTypes, typeName);
|
|
2141
|
+
}
|
|
2142
|
+
return modelTypes;
|
|
2143
|
+
}
|
|
2144
|
+
getModelTypeDeclaration(type) {
|
|
2145
|
+
let typeName = type.kind === ts.SyntaxKind.Identifier ? type.text : type.right.text;
|
|
2146
|
+
const symbol = this.getSymbolAtLocation(type);
|
|
2147
|
+
const declarations = symbol.getDeclarations();
|
|
2148
|
+
if (!declarations || declarations.length === 0) throw new ResolverError$1(`No models found for referenced type ${typeName}.`);
|
|
2149
|
+
if (symbol.escapedName !== typeName && symbol.escapedName !== "default") typeName = symbol.escapedName;
|
|
2150
|
+
let modelTypes = declarations.filter((node) => {
|
|
2151
|
+
if (!TypeNodeResolver.nodeIsUsable(node) || !this.current.isExportedNode(node)) return false;
|
|
2152
|
+
return node.name?.text === typeName;
|
|
2153
|
+
});
|
|
2154
|
+
if (!modelTypes.length) throw new ResolverError$1(`No matching model found for referenced type ${typeName}. If ${typeName} comes from a dependency, please create an interface in your own code that has the same structure. The compiler can not utilize interfaces from external dependencies.`);
|
|
2155
|
+
if (modelTypes.length > 1) {
|
|
2156
|
+
modelTypes = modelTypes.filter((modelType) => modelType.getSourceFile().fileName.replace(/\\/g, "/").toLowerCase().indexOf("node_modules/typescript") <= -1);
|
|
2157
|
+
modelTypes = TypeNodeResolver.getDesignatedModels(modelTypes, typeName);
|
|
2158
|
+
}
|
|
2159
|
+
if (modelTypes.length > 1) {
|
|
2160
|
+
const conflicts = modelTypes.map((modelType) => modelType.getSourceFile().fileName).join("\"; \"");
|
|
2161
|
+
throw new ResolverError$1(`Multiple matching models found for referenced type ${typeName}; please make model names unique. Conflicts found: "${conflicts}".`);
|
|
2162
|
+
}
|
|
2163
|
+
return modelTypes[0];
|
|
2164
|
+
}
|
|
2165
|
+
hasFlag(type, flag) {
|
|
2166
|
+
return (type.flags & flag) === flag;
|
|
2167
|
+
}
|
|
2168
|
+
getSymbolAtLocation(type) {
|
|
2169
|
+
const symbol = this.current.typeChecker.getSymbolAtLocation(type) || type.symbol;
|
|
2170
|
+
return symbol && this.hasFlag(symbol, ts.SymbolFlags.Alias) && this.current.typeChecker.getAliasedSymbol(symbol) || symbol;
|
|
2171
|
+
}
|
|
2172
|
+
getModelProperties(node, overrideToken) {
|
|
2173
|
+
const isIgnored = (e) => hasJSDocTag(e, "ignore");
|
|
2174
|
+
if (ts.isInterfaceDeclaration(node)) return node.members.filter((member) => !isIgnored(member) && ts.isPropertySignature(member)).map((member) => this.propertyFromSignature(member, overrideToken));
|
|
2175
|
+
const properties = node.members.filter((member) => !isIgnored(member) && member.kind === ts.SyntaxKind.PropertyDeclaration && !this.hasStaticModifier(member) && this.hasPublicModifier(member));
|
|
2176
|
+
const classConstructor = node.members.find((member) => ts.isConstructorDeclaration(member));
|
|
2177
|
+
if (classConstructor && classConstructor.parameters) {
|
|
2178
|
+
const constructorProperties = classConstructor.parameters.filter((parameter) => this.isAccessibleParameter(parameter));
|
|
2179
|
+
properties.push(...constructorProperties);
|
|
2180
|
+
}
|
|
2181
|
+
return properties.map((property) => this.propertyFromDeclaration(property, overrideToken));
|
|
2182
|
+
}
|
|
2183
|
+
propertyFromSignature(propertySignature, overrideToken) {
|
|
2184
|
+
const identifier = propertySignature.name;
|
|
2185
|
+
if (!propertySignature.type) throw new ResolverError$1("No valid type found for property declaration.");
|
|
2186
|
+
let required = !propertySignature.questionToken;
|
|
2187
|
+
if (overrideToken && overrideToken.kind === ts.SyntaxKind.MinusToken) required = true;
|
|
2188
|
+
else if (overrideToken && overrideToken.kind === ts.SyntaxKind.QuestionToken) required = false;
|
|
2189
|
+
return {
|
|
2190
|
+
deprecated: hasJSDocTag(propertySignature, "deprecated"),
|
|
2191
|
+
default: getJSDocTagComment(propertySignature, "default"),
|
|
2192
|
+
description: this.getNodeDescription(propertySignature),
|
|
2193
|
+
example: this.getNodeExample(propertySignature),
|
|
2194
|
+
extensions: this.getNodeExtensions(propertySignature),
|
|
2195
|
+
format: TypeNodeResolver.getNodeFormat(propertySignature),
|
|
2196
|
+
name: identifier.text,
|
|
2197
|
+
required,
|
|
2198
|
+
type: this.resolveNestedType(propertySignature.type, propertySignature.type.parent, this.context, propertySignature.type),
|
|
2199
|
+
validators: getDeclarationValidators(propertySignature) || {}
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
propertyFromDeclaration(propertyDeclaration, overrideToken) {
|
|
2203
|
+
const identifier = propertyDeclaration.name;
|
|
2204
|
+
let typeNode = propertyDeclaration.type;
|
|
2205
|
+
if (!typeNode) {
|
|
2206
|
+
const tsType = this.current.typeChecker.getTypeAtLocation(propertyDeclaration);
|
|
2207
|
+
typeNode = this.current.typeChecker.typeToTypeNode(tsType, void 0, ts.NodeBuilderFlags.NoTruncation);
|
|
2208
|
+
}
|
|
2209
|
+
if (!typeNode) throw new ResolverError$1("No valid type found for property declaration.");
|
|
2210
|
+
const type = this.resolveNestedType(typeNode, propertyDeclaration, this.context, typeNode);
|
|
2211
|
+
let required = !propertyDeclaration.questionToken && !propertyDeclaration.initializer;
|
|
2212
|
+
if (overrideToken && overrideToken.kind === ts.SyntaxKind.MinusToken) required = true;
|
|
2213
|
+
else if (overrideToken && overrideToken.kind === ts.SyntaxKind.QuestionToken) required = false;
|
|
2214
|
+
return {
|
|
2215
|
+
deprecated: hasJSDocTag(propertyDeclaration, "deprecated"),
|
|
2216
|
+
default: getInitializerValue(propertyDeclaration.initializer, this.current.typeChecker),
|
|
2217
|
+
description: this.getNodeDescription(propertyDeclaration),
|
|
2218
|
+
example: this.getNodeExample(propertyDeclaration),
|
|
2219
|
+
extensions: this.getNodeExtensions(propertyDeclaration),
|
|
2220
|
+
format: TypeNodeResolver.getNodeFormat(propertyDeclaration),
|
|
2221
|
+
name: identifier.text,
|
|
2222
|
+
required,
|
|
2223
|
+
type,
|
|
2224
|
+
validators: getDeclarationValidators(propertyDeclaration) || {}
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
getModelAdditionalProperties(node) {
|
|
2228
|
+
if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
|
|
2229
|
+
const indexMember = node.members.find((member) => member.kind === ts.SyntaxKind.IndexSignature);
|
|
2230
|
+
if (!indexMember) return;
|
|
2231
|
+
const indexSignatureDeclaration = indexMember;
|
|
2232
|
+
if (this.resolveNestedType(indexSignatureDeclaration.parameters[0].type, this.parentNode, this.context).typeName !== "string") throw new ResolverError$1("Only string indexers are supported.", this.typeNode);
|
|
2233
|
+
return this.resolveNestedType(indexSignatureDeclaration.type, this.parentNode, this.context);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
typeArgumentsToContext(type, targetEntity, context) {
|
|
2237
|
+
const declaration = this.getModelTypeDeclaration(targetEntity);
|
|
2238
|
+
if (typeof declaration === "undefined" || !("typeParameters" in declaration)) return context;
|
|
2239
|
+
const { typeParameters } = declaration;
|
|
2240
|
+
if (typeParameters) for (const [index, typeParameter] of typeParameters.entries()) {
|
|
2241
|
+
const typeArg = type.typeArguments && type.typeArguments[index];
|
|
2242
|
+
let resolvedType;
|
|
2243
|
+
if (typeArg && ts.isTypeReferenceNode(typeArg) && ts.isIdentifier(typeArg.typeName) && context[typeArg.typeName.text]) resolvedType = context[typeArg.typeName.text];
|
|
2244
|
+
else if (typeArg) resolvedType = typeArg;
|
|
2245
|
+
else if (typeParameter.default) resolvedType = typeParameter.default;
|
|
2246
|
+
else throw new ResolverError$1(`Could not find a value for type parameter ${typeParameter.name.text}`, type);
|
|
2247
|
+
this.context = {
|
|
2248
|
+
...this.context,
|
|
2249
|
+
[typeParameter.name.text]: resolvedType
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
return context;
|
|
2253
|
+
}
|
|
2254
|
+
getModelInheritedProperties(modelTypeDeclaration) {
|
|
2255
|
+
let properties = [];
|
|
2256
|
+
const { heritageClauses } = modelTypeDeclaration;
|
|
2257
|
+
if (!heritageClauses) return properties;
|
|
2258
|
+
heritageClauses.forEach((clause) => {
|
|
2259
|
+
if (!clause.types) return;
|
|
2260
|
+
clause.types.forEach((t) => {
|
|
2261
|
+
const baseEntityName = t.expression;
|
|
2262
|
+
const resetCtx = this.typeArgumentsToContext(t, baseEntityName, this.context);
|
|
2263
|
+
const referenceType = this.getReferenceType(t);
|
|
2264
|
+
if (referenceType) {
|
|
2265
|
+
if (isRefAliasType(referenceType)) {
|
|
2266
|
+
let type = referenceType;
|
|
2267
|
+
while (isRefAliasType(type)) type = type.type;
|
|
2268
|
+
if (type.typeName === "refObject") properties = [...properties, ...type.properties];
|
|
2269
|
+
else if (type.typeName === "nestedObjectLiteral") properties = [...properties, ...type.properties];
|
|
2270
|
+
}
|
|
2271
|
+
if (isRefObjectType(referenceType)) referenceType.properties.forEach((property) => properties.push(property));
|
|
2272
|
+
}
|
|
2273
|
+
this.context = resetCtx;
|
|
2274
|
+
});
|
|
2275
|
+
});
|
|
2276
|
+
return properties;
|
|
2277
|
+
}
|
|
2278
|
+
getNodeDescription(node) {
|
|
2279
|
+
return getNodeDescription(node, this.current.typeChecker);
|
|
2280
|
+
}
|
|
2281
|
+
static getNodeFormat(node) {
|
|
2282
|
+
return getJSDocTagComment(node, "format");
|
|
2283
|
+
}
|
|
2284
|
+
getNodeExample(node) {
|
|
2285
|
+
const example = getJSDocTagComment(node, "example");
|
|
2286
|
+
if (example) try {
|
|
2287
|
+
return JSON.parse(example);
|
|
2288
|
+
} catch {
|
|
2289
|
+
return example;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
getNodeExtensions(node) {
|
|
2293
|
+
return getNodeExtensions(node, this.current.registry);
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
//#endregion
|
|
2297
|
+
//#region src/adapters/typescript/resolver/cache.ts
|
|
2298
|
+
var ResolverCache = class {
|
|
2299
|
+
referenceTypes = /* @__PURE__ */ new Map();
|
|
2300
|
+
inProgressTypes = /* @__PURE__ */ new Set();
|
|
2301
|
+
getCachedType(name) {
|
|
2302
|
+
return this.referenceTypes.get(name);
|
|
2303
|
+
}
|
|
2304
|
+
setCachedType(name, type) {
|
|
2305
|
+
this.referenceTypes.set(name, type);
|
|
2306
|
+
}
|
|
2307
|
+
isInProgress(name) {
|
|
2308
|
+
return this.inProgressTypes.has(name);
|
|
2309
|
+
}
|
|
2310
|
+
markInProgress(name) {
|
|
2311
|
+
this.inProgressTypes.add(name);
|
|
2312
|
+
}
|
|
2313
|
+
clearInProgress(name) {
|
|
2314
|
+
this.inProgressTypes.delete(name);
|
|
2315
|
+
}
|
|
2316
|
+
clear() {
|
|
2317
|
+
this.referenceTypes.clear();
|
|
2318
|
+
this.inProgressTypes.clear();
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
//#endregion
|
|
2322
|
+
//#region src/adapters/typescript/node-utils/module.ts
|
|
2323
|
+
/**
|
|
2324
|
+
* Get Decorators for a specific node.
|
|
2325
|
+
*
|
|
2326
|
+
* @param node
|
|
2327
|
+
* @param isMatching
|
|
2328
|
+
*/
|
|
2329
|
+
function getNodeDecorators(node, isMatching) {
|
|
2330
|
+
if (!canHaveDecorators(node)) return [];
|
|
2331
|
+
const decorators = getDecorators(node);
|
|
2332
|
+
if (typeof decorators === "undefined") return [];
|
|
2333
|
+
const items = decorators.map((d) => {
|
|
2334
|
+
const result = {
|
|
2335
|
+
arguments: [],
|
|
2336
|
+
typeArguments: []
|
|
2337
|
+
};
|
|
2338
|
+
let x = d.expression;
|
|
2339
|
+
if (isCallExpression(x)) {
|
|
2340
|
+
if (x.arguments) result.arguments = x.arguments.map((argument) => {
|
|
2341
|
+
if (isStringLiteral(argument) || isNumericLiteral(argument)) return argument.text;
|
|
2342
|
+
return argument;
|
|
2343
|
+
});
|
|
2344
|
+
if (x.typeArguments) result.typeArguments = x.typeArguments;
|
|
2345
|
+
x = x.expression;
|
|
2346
|
+
}
|
|
2347
|
+
result.text = x.text || x.name.text;
|
|
2348
|
+
return result;
|
|
2349
|
+
});
|
|
2350
|
+
return typeof isMatching === "undefined" ? items : items.filter(isMatching);
|
|
2351
|
+
}
|
|
2352
|
+
//#endregion
|
|
2353
|
+
//#region src/adapters/filesystem/source-files.ts
|
|
2354
|
+
async function scanSourceFiles(input) {
|
|
2355
|
+
const sources = Array.isArray(input) ? input : [input];
|
|
2356
|
+
const result = /* @__PURE__ */ new Set();
|
|
2357
|
+
const promises = [];
|
|
2358
|
+
for (const source of sources) if (typeof source === "string") promises.push(locateMany(source));
|
|
2359
|
+
else promises.push(locateMany(source.pattern, { path: source.cwd }));
|
|
2360
|
+
const matches = (await Promise.all(promises)).flat();
|
|
2361
|
+
for (const match of matches) result.add(buildFilePath(match));
|
|
2362
|
+
return Array.from(result);
|
|
2363
|
+
}
|
|
2364
|
+
//#endregion
|
|
2365
|
+
//#region src/adapters/filesystem/tsconfig/module.ts
|
|
2366
|
+
async function loadTSConfig(context = {}) {
|
|
2367
|
+
let fileName;
|
|
2368
|
+
let filePath;
|
|
2369
|
+
const cwd = context.cwd || process.cwd();
|
|
2370
|
+
if (typeof context.name === "string") {
|
|
2371
|
+
if (path.isAbsolute(context.name)) filePath = context.name;
|
|
2372
|
+
else filePath = path.resolve(cwd, context.name);
|
|
2373
|
+
fileName = path.basename(filePath);
|
|
2374
|
+
} else {
|
|
2375
|
+
fileName = "tsconfig.json";
|
|
2376
|
+
filePath = path.join(cwd, fileName);
|
|
2377
|
+
}
|
|
2378
|
+
const content = await load(filePath);
|
|
2379
|
+
if (!isObject(content)) throw new ConfigError({
|
|
2380
|
+
message: `The tsconfig file '${filePath}' is malformed.`,
|
|
2381
|
+
code: ConfigErrorCode.TSCONFIG_MALFORMED
|
|
2382
|
+
});
|
|
2383
|
+
if (typeof content.compilerOptions !== "undefined") {
|
|
2384
|
+
const { options: compilerOptions } = convertCompilerOptionsFromJson(content.compilerOptions, cwd, fileName);
|
|
2385
|
+
content.compilerOptions = compilerOptions;
|
|
2386
|
+
}
|
|
2387
|
+
return content;
|
|
2388
|
+
}
|
|
2389
|
+
async function softLoadTsconfig(context = {}) {
|
|
2390
|
+
try {
|
|
2391
|
+
return await loadTSConfig(context);
|
|
2392
|
+
} catch {
|
|
2393
|
+
return {};
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
//#endregion
|
|
2397
|
+
//#region src/adapters/cache/constants.ts
|
|
2398
|
+
/**
|
|
2399
|
+
* Bump whenever the on-disk cache shape changes incompatibly
|
|
2400
|
+
* (Metadata, Controller, Method, Parameter, resolver type nodes,
|
|
2401
|
+
* or the cache wrapper itself). Old cache files with a different
|
|
2402
|
+
* version are rejected on read.
|
|
2403
|
+
*/
|
|
2404
|
+
const CACHE_SCHEMA_VERSION = "3";
|
|
2405
|
+
const CACHE_FILE_PREFIX = ".trapi-metadata-";
|
|
2406
|
+
const CACHE_FILE_SUFFIX = ".json";
|
|
2407
|
+
/** 7 days. */
|
|
2408
|
+
const CACHE_DEFAULT_MAX_AGE_MS = 10080 * 60 * 1e3;
|
|
2409
|
+
//#endregion
|
|
2410
|
+
//#region src/adapters/cache/utils.ts
|
|
2411
|
+
function buildCacheOptions(input) {
|
|
2412
|
+
if (typeof input === "string") input = {
|
|
2413
|
+
enabled: true,
|
|
2414
|
+
directoryPath: input
|
|
2415
|
+
};
|
|
2416
|
+
if (typeof input === "boolean") input = { enabled: input };
|
|
2417
|
+
input = input || {};
|
|
2418
|
+
let directoryPath = tmpdir();
|
|
2419
|
+
if (typeof input.directoryPath === "string") directoryPath = path.isAbsolute(input.directoryPath) ? input.directoryPath : path.join(process.cwd(), input.directoryPath);
|
|
2420
|
+
return {
|
|
2421
|
+
fileName: input.fileName,
|
|
2422
|
+
directoryPath,
|
|
2423
|
+
enabled: input.enabled ?? true,
|
|
2424
|
+
maxAgeMs: normalizeMaxAgeMs(input.maxAgeMs)
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function normalizeMaxAgeMs(input) {
|
|
2428
|
+
if (typeof input !== "number" || !Number.isFinite(input) || input < 0) return CACHE_DEFAULT_MAX_AGE_MS;
|
|
2429
|
+
return input;
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Combines all key contributors into a single sha256 hex string. The order is
|
|
2433
|
+
* stable; each part is null-separated to avoid concatenation ambiguity.
|
|
2434
|
+
*/
|
|
2435
|
+
function composeCacheKey(parts) {
|
|
2436
|
+
const hash = crypto.createHash("sha256");
|
|
2437
|
+
hash.update(parts.schemaVersion);
|
|
2438
|
+
hash.update("\0");
|
|
2439
|
+
hash.update(parts.sourceFilesHash);
|
|
2440
|
+
hash.update("\0");
|
|
2441
|
+
hash.update(parts.compilerOptionsHash);
|
|
2442
|
+
hash.update("\0");
|
|
2443
|
+
hash.update(parts.registryHash);
|
|
2444
|
+
hash.update("\0");
|
|
2445
|
+
hash.update(parts.presetName ?? "");
|
|
2446
|
+
return hash.digest("hex");
|
|
2447
|
+
}
|
|
2448
|
+
/**
|
|
2449
|
+
* Hash a stable JSON projection of `compilerOptions`. Sorting keys keeps the
|
|
2450
|
+
* output deterministic regardless of declaration order in tsconfig.json.
|
|
2451
|
+
*/
|
|
2452
|
+
function hashCompilerOptions(options) {
|
|
2453
|
+
const hash = crypto.createHash("sha256");
|
|
2454
|
+
hash.update(stableStringify(options ?? {}));
|
|
2455
|
+
return hash.digest("hex");
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Hash the resolved registry's structural shape. Catches structural changes
|
|
2459
|
+
* (handler added/removed/renamed, marker changed, replaces policy changed)
|
|
2460
|
+
* AND most logic changes via `apply.toString()`. Misses changes that are
|
|
2461
|
+
* purely captured-variable shifts in factory-built handlers — acceptable
|
|
2462
|
+
* trade-off for catching local preset edits.
|
|
2463
|
+
*/
|
|
2464
|
+
function hashRegistry(registry) {
|
|
2465
|
+
const hash = crypto.createHash("sha256");
|
|
2466
|
+
for (const kind of [
|
|
2467
|
+
"controllers",
|
|
2468
|
+
"methods",
|
|
2469
|
+
"parameters",
|
|
2470
|
+
"controllerJsDoc",
|
|
2471
|
+
"methodJsDoc",
|
|
2472
|
+
"parameterJsDoc"
|
|
2473
|
+
]) {
|
|
2474
|
+
hash.update(kind);
|
|
2475
|
+
hash.update("\0");
|
|
2476
|
+
const handlers = registry[kind] ?? [];
|
|
2477
|
+
for (const handler of handlers) {
|
|
2478
|
+
hash.update(stableStringify(handler.match));
|
|
2479
|
+
hash.update("\0");
|
|
2480
|
+
hash.update(stableStringify(handler.marker ?? null));
|
|
2481
|
+
hash.update("\0");
|
|
2482
|
+
hash.update(String(handler.replaces ?? ""));
|
|
2483
|
+
hash.update("\0");
|
|
2484
|
+
hash.update(handler.apply.toString());
|
|
2485
|
+
hash.update("\0");
|
|
2486
|
+
}
|
|
2487
|
+
hash.update("");
|
|
2488
|
+
}
|
|
2489
|
+
return hash.digest("hex");
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* JSON.stringify with sorted object keys at every level. Functions and
|
|
2493
|
+
* undefined are omitted as usual; that's fine for the inputs we hash here.
|
|
2494
|
+
*/
|
|
2495
|
+
function stableStringify(value) {
|
|
2496
|
+
return JSON.stringify(value, (_key, val) => {
|
|
2497
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
2498
|
+
const sorted = {};
|
|
2499
|
+
for (const k of Object.keys(val).sort()) sorted[k] = val[k];
|
|
2500
|
+
return sorted;
|
|
2501
|
+
}
|
|
2502
|
+
return val;
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
//#endregion
|
|
2506
|
+
//#region src/adapters/cache/client.ts
|
|
2507
|
+
var CacheClient = class {
|
|
2508
|
+
options;
|
|
2509
|
+
constructor(input) {
|
|
2510
|
+
this.options = buildCacheOptions(input);
|
|
2511
|
+
}
|
|
2512
|
+
async save(data) {
|
|
2513
|
+
if (!this.options.enabled) return;
|
|
2514
|
+
await this.ensureDirectory();
|
|
2515
|
+
const filePath = this.resolveFilePath(data.cacheKey);
|
|
2516
|
+
const payload = stringify(data);
|
|
2517
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`;
|
|
2518
|
+
try {
|
|
2519
|
+
await fs.promises.writeFile(tmpPath, payload);
|
|
2520
|
+
await fs.promises.rename(tmpPath, filePath);
|
|
2521
|
+
} catch (err) {
|
|
2522
|
+
try {
|
|
2523
|
+
await fs.promises.unlink(tmpPath);
|
|
2524
|
+
} catch {}
|
|
2525
|
+
throw err;
|
|
2526
|
+
}
|
|
2527
|
+
this.evict().catch(() => void 0);
|
|
2528
|
+
return filePath;
|
|
2529
|
+
}
|
|
2530
|
+
async get(cacheKey) {
|
|
2531
|
+
if (!this.options.enabled) return;
|
|
2532
|
+
const filePath = this.resolveFilePath(cacheKey);
|
|
2533
|
+
let content;
|
|
2534
|
+
try {
|
|
2535
|
+
content = await fs.promises.readFile(filePath, { encoding: "utf-8" });
|
|
2536
|
+
} catch {
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
2539
|
+
let cache;
|
|
2540
|
+
try {
|
|
2541
|
+
cache = parse(content);
|
|
2542
|
+
} catch {
|
|
2543
|
+
await fs.promises.unlink(filePath).catch(() => void 0);
|
|
2544
|
+
return;
|
|
2545
|
+
}
|
|
2546
|
+
if (!cache || cache.cacheKey !== cacheKey || cache.schemaVersion !== "3") return;
|
|
2547
|
+
return cache;
|
|
2548
|
+
}
|
|
2549
|
+
/**
|
|
2550
|
+
* Prune cache files older than `maxAgeMs`. No-op when disabled or when
|
|
2551
|
+
* `maxAgeMs <= 0`. Each unlink is best-effort — a concurrent generator
|
|
2552
|
+
* may have already removed the file.
|
|
2553
|
+
*/
|
|
2554
|
+
async evict() {
|
|
2555
|
+
if (!this.options.enabled || this.options.maxAgeMs <= 0) return;
|
|
2556
|
+
const pattern = this.options.fileName ?? `.trapi-metadata-*.json`;
|
|
2557
|
+
let entries;
|
|
2558
|
+
try {
|
|
2559
|
+
entries = await locateMany(pattern, { path: this.options.directoryPath });
|
|
2560
|
+
} catch {
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2563
|
+
const cutoff = Date.now() - this.options.maxAgeMs;
|
|
2564
|
+
await Promise.all(entries.map(async (entry) => {
|
|
2565
|
+
const filePath = buildFilePath(entry);
|
|
2566
|
+
try {
|
|
2567
|
+
if ((await fs.promises.stat(filePath)).mtimeMs < cutoff) await fs.promises.unlink(filePath);
|
|
2568
|
+
} catch {}
|
|
2569
|
+
}));
|
|
2570
|
+
}
|
|
2571
|
+
resolveFilePath(cacheKey) {
|
|
2572
|
+
if (!CACHE_KEY_PATTERN.test(cacheKey)) throw new Error(`Invalid cacheKey: must match ${CACHE_KEY_PATTERN}`);
|
|
2573
|
+
const fileName = this.options.fileName ?? `.trapi-metadata-${cacheKey}.json`;
|
|
2574
|
+
return path.join(this.options.directoryPath, fileName);
|
|
2575
|
+
}
|
|
2576
|
+
async ensureDirectory() {
|
|
2577
|
+
await fs.promises.mkdir(this.options.directoryPath, { recursive: true });
|
|
2578
|
+
}
|
|
2579
|
+
};
|
|
2580
|
+
const CACHE_KEY_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
2581
|
+
//#endregion
|
|
2582
|
+
//#region src/core/error/generator.ts
|
|
2583
|
+
var GeneratorError$1 = class extends MetadataError {};
|
|
2584
|
+
function isGeneratorError$1(input) {
|
|
2585
|
+
if (!isBaseError(input)) return false;
|
|
2586
|
+
return typeof input.code === "string";
|
|
2587
|
+
}
|
|
2588
|
+
//#endregion
|
|
2589
|
+
//#region src/core/error/generator-codes.ts
|
|
2590
|
+
const GeneratorErrorCode$1 = {
|
|
2591
|
+
CONTROLLER_NO_SOURCE_FILE: "GENERATOR_CONTROLLER_NO_SOURCE_FILE",
|
|
2592
|
+
CONTROLLER_NO_NAME: "GENERATOR_CONTROLLER_NO_NAME",
|
|
2593
|
+
PARAMETER_GENERATION_FAILED: "GENERATOR_PARAMETER_GENERATION_FAILED",
|
|
2594
|
+
BODY_PARAMETER_DUPLICATE: "GENERATOR_BODY_PARAMETER_DUPLICATE",
|
|
2595
|
+
BODY_FORM_CONFLICT: "GENERATOR_BODY_FORM_CONFLICT",
|
|
2596
|
+
STRICT_UNMATCHED_DECORATORS: "GENERATOR_STRICT_UNMATCHED_DECORATORS"
|
|
2597
|
+
};
|
|
2598
|
+
//#endregion
|
|
2599
|
+
//#region src/core/error/parameter.ts
|
|
2600
|
+
var ParameterError$1 = class ParameterError$1 extends MetadataError {
|
|
2601
|
+
static typeUnsupported(context) {
|
|
2602
|
+
const location = context.node ? ParameterError$1.getCurrentLocation(context.node) : void 0;
|
|
2603
|
+
return new ParameterError$1({
|
|
2604
|
+
message: `@${context.decoratorName}('${context.propertyName}') does not support '${context.type.typeName}' type${location ? ` at ${location}` : ""}.`,
|
|
2605
|
+
code: ParameterErrorCode.TYPE_UNSUPPORTED
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
static methodUnsupported(context) {
|
|
2609
|
+
const location = context.node ? ParameterError$1.getCurrentLocation(context.node) : void 0;
|
|
2610
|
+
return new ParameterError$1({
|
|
2611
|
+
message: `@${context.decoratorName}('${context.propertyName}') does not support method '${context.method}'${location ? ` at ${location}` : ""}.`,
|
|
2612
|
+
code: ParameterErrorCode.METHOD_UNSUPPORTED
|
|
2613
|
+
});
|
|
2614
|
+
}
|
|
2615
|
+
static invalidPathMatch(context) {
|
|
2616
|
+
const location = context.node ? ParameterError$1.getCurrentLocation(context.node) : void 0;
|
|
2617
|
+
return new ParameterError$1({
|
|
2618
|
+
message: `@${context.decoratorName}('${context.propertyName}') does not exist in path '${context.path}'${location ? ` at ${location}` : ""}.`,
|
|
2619
|
+
code: ParameterErrorCode.PATH_MISMATCH
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
static scopeRequired(context) {
|
|
2623
|
+
const location = context.node ? ParameterError$1.getCurrentLocation(context.node) : void 0;
|
|
2624
|
+
return new ParameterError$1({
|
|
2625
|
+
message: `@${context.decoratorName}() requires a scope argument${location ? ` at ${location}` : ""}.`,
|
|
2626
|
+
code: ParameterErrorCode.SCOPE_REQUIRED
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
static invalidExampleSchema() {
|
|
2630
|
+
return new ParameterError$1({
|
|
2631
|
+
message: "The @example JSDoc tag contains invalid JSON.",
|
|
2632
|
+
code: ParameterErrorCode.INVALID_EXAMPLE
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
static getCurrentLocation(node) {
|
|
2636
|
+
const parts = [];
|
|
2637
|
+
if (isMethodDeclaration(node.parent)) {
|
|
2638
|
+
parts.push(node.parent.name.getText());
|
|
2639
|
+
if (isClassDeclaration(node.parent.parent)) parts.unshift(node.parent.parent.name.text);
|
|
2640
|
+
}
|
|
2641
|
+
return parts.join(".");
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
//#endregion
|
|
2645
|
+
//#region src/app/generator/parameter/module.ts
|
|
2646
|
+
const BODY_SUPPORTED_METHODS = new Set([
|
|
2647
|
+
"delete",
|
|
2648
|
+
"post",
|
|
2649
|
+
"put",
|
|
2650
|
+
"patch",
|
|
2651
|
+
"get"
|
|
2652
|
+
]);
|
|
2653
|
+
const SUPPORTED_LEAF_TYPES = new Set([
|
|
2654
|
+
"string",
|
|
2655
|
+
"integer",
|
|
2656
|
+
"long",
|
|
2657
|
+
"float",
|
|
2658
|
+
"double",
|
|
2659
|
+
"date",
|
|
2660
|
+
"datetime",
|
|
2661
|
+
"buffer",
|
|
2662
|
+
"boolean",
|
|
2663
|
+
"enum"
|
|
2664
|
+
]);
|
|
2665
|
+
var ParameterGenerator = class {
|
|
2666
|
+
parameter;
|
|
2667
|
+
method;
|
|
2668
|
+
paths;
|
|
2669
|
+
current;
|
|
2670
|
+
constructor(parameter, method, paths, current) {
|
|
2671
|
+
this.parameter = parameter;
|
|
2672
|
+
this.method = method;
|
|
2673
|
+
this.paths = paths.length === 0 ? [""] : paths;
|
|
2674
|
+
this.current = current;
|
|
2675
|
+
}
|
|
2676
|
+
generate() {
|
|
2677
|
+
const parameterName = this.getParameterName();
|
|
2678
|
+
const draft = newParameterDraft({ parameterName });
|
|
2679
|
+
const options = this.applyOptions(parameterName);
|
|
2680
|
+
applyDecoratorHandlers(this.parameter, this.current.registry.parameters, draft, options);
|
|
2681
|
+
applyJsDocHandlers(this.parameter, this.current.registry.parameterJsDoc, draft, options);
|
|
2682
|
+
const type = this.resolveType();
|
|
2683
|
+
draft.type = type;
|
|
2684
|
+
const description = this.getDescription();
|
|
2685
|
+
if (description) draft.description = description;
|
|
2686
|
+
if (draft.deprecated === void 0 && hasJSDocTag(this.parameter, "deprecated")) draft.deprecated = true;
|
|
2687
|
+
const defaultValue = getInitializerValue(this.parameter.initializer, this.current.typeChecker, type);
|
|
2688
|
+
if (defaultValue !== void 0) draft.default = defaultValue;
|
|
2689
|
+
draft.required = !this.parameter.questionToken && !this.parameter.initializer;
|
|
2690
|
+
const { examples, exampleLabels } = this.getJsDocExamples(parameterName);
|
|
2691
|
+
if (examples) {
|
|
2692
|
+
draft.examples = examples.map((value) => ({ value }));
|
|
2693
|
+
draft.exampleLabels = (exampleLabels ?? []).filter((l) => l !== void 0);
|
|
2694
|
+
}
|
|
2695
|
+
Object.assign(draft.validators, getDeclarationValidators(this.parameter, parameterName));
|
|
2696
|
+
if (!draft.in) {
|
|
2697
|
+
if (!BODY_SUPPORTED_METHODS.has(this.method)) throw ParameterError$1.methodUnsupported({
|
|
2698
|
+
decoratorName: "Body",
|
|
2699
|
+
propertyName: parameterName,
|
|
2700
|
+
method: this.method,
|
|
2701
|
+
node: this.parameter
|
|
2702
|
+
});
|
|
2703
|
+
draft.in = ParamKind.Body;
|
|
2704
|
+
}
|
|
2705
|
+
if ((draft.in === ParamKind.Body || draft.in === ParamKind.BodyProp) && !BODY_SUPPORTED_METHODS.has(this.method)) throw ParameterError$1.methodUnsupported({
|
|
2706
|
+
decoratorName: "Body",
|
|
2707
|
+
propertyName: draft.name,
|
|
2708
|
+
method: this.method,
|
|
2709
|
+
node: this.parameter
|
|
2710
|
+
});
|
|
2711
|
+
return this.finalize(draft, type, parameterName, examples, exampleLabels);
|
|
2712
|
+
}
|
|
2713
|
+
finalize(draft, type, parameterName, examples, exampleLabels) {
|
|
2714
|
+
const kind = draft.in;
|
|
2715
|
+
const wrappedExamples = examples ? examples.map((value) => ({ value })) : void 0;
|
|
2716
|
+
const filteredLabels = exampleLabels?.filter((l) => l !== void 0);
|
|
2717
|
+
if ((kind === ParameterSource.QUERY || kind === ParameterSource.PATH) && (isNestedObjectLiteralType(type) || isRefObjectType(type))) {
|
|
2718
|
+
const decomposed = this.decomposeObject(type, {
|
|
2719
|
+
in: kind === ParameterSource.QUERY ? ParameterSource.QUERY_PROP : ParameterSource.PATH,
|
|
2720
|
+
examples: wrappedExamples,
|
|
2721
|
+
exampleLabels: filteredLabels,
|
|
2722
|
+
extensions: draft.extensions
|
|
2723
|
+
});
|
|
2724
|
+
if (kind === ParameterSource.PATH) this.validatePathDecomposition(decomposed);
|
|
2725
|
+
return decomposed;
|
|
2726
|
+
}
|
|
2727
|
+
if ((kind === ParameterSource.QUERY || kind === ParameterSource.QUERY_PROP) && isArrayType(type)) {
|
|
2728
|
+
if (!this.isLeafTypeSupported(type.elementType)) throw ParameterError$1.typeUnsupported({
|
|
2729
|
+
decoratorName: kind === ParameterSource.QUERY ? "Query" : "QueryProp",
|
|
2730
|
+
propertyName: draft.name,
|
|
2731
|
+
type: type.elementType,
|
|
2732
|
+
node: this.parameter
|
|
2733
|
+
});
|
|
2734
|
+
return [{
|
|
2735
|
+
...this.draftToParameter(draft),
|
|
2736
|
+
collectionFormat: draft.collectionFormat ?? CollectionFormat.MULTI,
|
|
2737
|
+
type
|
|
2738
|
+
}];
|
|
2739
|
+
}
|
|
2740
|
+
if ((kind === ParameterSource.QUERY || kind === ParameterSource.QUERY_PROP) && !this.isLeafOrEnumOrUnion(type)) throw ParameterError$1.typeUnsupported({
|
|
2741
|
+
decoratorName: kind === ParameterSource.QUERY ? "Query" : "QueryProp",
|
|
2742
|
+
propertyName: draft.name,
|
|
2743
|
+
type,
|
|
2744
|
+
node: this.parameter
|
|
2745
|
+
});
|
|
2746
|
+
if (kind === ParameterSource.PATH) this.validatePathName(draft.name, parameterName);
|
|
2747
|
+
return [this.draftToParameter(draft)];
|
|
2748
|
+
}
|
|
2749
|
+
draftToParameter(draft) {
|
|
2750
|
+
return {
|
|
2751
|
+
allowEmptyValue: draft.allowEmptyValue,
|
|
2752
|
+
collectionFormat: draft.collectionFormat,
|
|
2753
|
+
default: draft.default,
|
|
2754
|
+
description: draft.description,
|
|
2755
|
+
examples: draft.examples.length > 0 ? draft.examples : void 0,
|
|
2756
|
+
exampleLabels: draft.exampleLabels.length > 0 ? draft.exampleLabels : void 0,
|
|
2757
|
+
extensions: draft.extensions,
|
|
2758
|
+
in: draft.in,
|
|
2759
|
+
maxItems: draft.maxItems,
|
|
2760
|
+
minItems: draft.minItems,
|
|
2761
|
+
name: draft.name,
|
|
2762
|
+
parameterName: draft.parameterName,
|
|
2763
|
+
required: draft.required,
|
|
2764
|
+
type: draft.type,
|
|
2765
|
+
deprecated: draft.deprecated,
|
|
2766
|
+
validators: draft.validators
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
decomposeObject(type, details) {
|
|
2770
|
+
if (type.properties.length === 0) return [];
|
|
2771
|
+
const parameterName = this.getParameterName();
|
|
2772
|
+
const initializerValue = getInitializerValue(this.parameter.initializer, this.current.typeChecker, type);
|
|
2773
|
+
const description = this.getDescription();
|
|
2774
|
+
const isParamOptional = !!this.parameter.questionToken;
|
|
2775
|
+
const isParamDeprecated = hasJSDocTag(this.parameter, "deprecated");
|
|
2776
|
+
const output = [];
|
|
2777
|
+
for (const property of type.properties) {
|
|
2778
|
+
let propertyDefaultValue = property.default;
|
|
2779
|
+
if (typeof propertyDefaultValue === "undefined" && isObject(initializerValue)) propertyDefaultValue = initializerValue[property.name];
|
|
2780
|
+
const required = isParamOptional ? false : property.required;
|
|
2781
|
+
output.push({
|
|
2782
|
+
extensions: [...details.extensions],
|
|
2783
|
+
in: details.in,
|
|
2784
|
+
examples: details.examples,
|
|
2785
|
+
exampleLabels: details.exampleLabels,
|
|
2786
|
+
default: propertyDefaultValue,
|
|
2787
|
+
description: property.description || description,
|
|
2788
|
+
name: property.name,
|
|
2789
|
+
parameterName,
|
|
2790
|
+
required,
|
|
2791
|
+
type: property.type,
|
|
2792
|
+
deprecated: property.deprecated || isParamDeprecated,
|
|
2793
|
+
validators: {}
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
2796
|
+
return output;
|
|
2797
|
+
}
|
|
2798
|
+
pathContainsParam(name) {
|
|
2799
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2800
|
+
const colonPattern = new RegExp(`:${escaped}\\b`);
|
|
2801
|
+
const bracePattern = `{${name}}`;
|
|
2802
|
+
for (const p of this.paths) if (p.includes(bracePattern) || colonPattern.test(p)) return true;
|
|
2803
|
+
return false;
|
|
2804
|
+
}
|
|
2805
|
+
validatePathDecomposition(decomposed) {
|
|
2806
|
+
for (const element of decomposed) if (!this.pathContainsParam(element.name)) throw ParameterError$1.invalidPathMatch({
|
|
2807
|
+
decoratorName: "Path",
|
|
2808
|
+
propertyName: element.name,
|
|
2809
|
+
path: this.paths.join(" | "),
|
|
2810
|
+
node: this.parameter
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
validatePathName(name, parameterName) {
|
|
2814
|
+
const candidate = name || parameterName;
|
|
2815
|
+
if (!this.pathContainsParam(candidate)) throw ParameterError$1.invalidPathMatch({
|
|
2816
|
+
decoratorName: "Path",
|
|
2817
|
+
propertyName: candidate,
|
|
2818
|
+
path: this.paths.join(" | "),
|
|
2819
|
+
node: this.parameter
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
isLeafTypeSupported(type) {
|
|
2823
|
+
return SUPPORTED_LEAF_TYPES.has(type.typeName);
|
|
2824
|
+
}
|
|
2825
|
+
isLeafOrEnumOrUnion(type) {
|
|
2826
|
+
return SUPPORTED_LEAF_TYPES.has(type.typeName) || isRefEnumType(type) || isUnionType(type);
|
|
2827
|
+
}
|
|
2828
|
+
resolveType() {
|
|
2829
|
+
let typeNode = this.parameter.type;
|
|
2830
|
+
if (!typeNode) {
|
|
2831
|
+
const t = this.current.typeChecker.getTypeAtLocation(this.parameter);
|
|
2832
|
+
typeNode = this.current.typeChecker.typeToTypeNode(t, void 0, ts.NodeBuilderFlags.NoTruncation);
|
|
2833
|
+
}
|
|
2834
|
+
if (!typeNode) throw new ParameterError$1({ message: `Could not resolve type for parameter '${this.getParameterName()}'.` });
|
|
2835
|
+
return new TypeNodeResolver(typeNode, this.current, this.parameter).resolve();
|
|
2836
|
+
}
|
|
2837
|
+
getParameterName() {
|
|
2838
|
+
if (!ts.isIdentifier(this.parameter.name)) throw new ParameterError$1({ message: "Destructured parameters are not supported. Use a simple identifier name." });
|
|
2839
|
+
return this.parameter.name.text;
|
|
2840
|
+
}
|
|
2841
|
+
getDescription() {
|
|
2842
|
+
const symbol = this.current.typeChecker.getSymbolAtLocation(this.parameter.name);
|
|
2843
|
+
if (symbol) {
|
|
2844
|
+
const comments = symbol.getDocumentationComment(this.current.typeChecker);
|
|
2845
|
+
if (comments.length > 0) return ts.displayPartsToString(comments);
|
|
2846
|
+
}
|
|
2847
|
+
return "";
|
|
2848
|
+
}
|
|
2849
|
+
getJsDocExamples(parameterName) {
|
|
2850
|
+
const exampleLabels = [];
|
|
2851
|
+
const examples = getJSDocTags(this.parameter.parent, (tag) => {
|
|
2852
|
+
const comment = transformJSDocComment(tag.comment);
|
|
2853
|
+
const isExample = (tag.tagName.text === "example" || tag.tagName.escapedText === "example") && !!comment && comment.startsWith(parameterName);
|
|
2854
|
+
if (isExample && comment) {
|
|
2855
|
+
const hasExampleLabel = (comment.split(" ")[0].indexOf(".") || -1) > 0;
|
|
2856
|
+
exampleLabels.push(hasExampleLabel ? comment.split(" ")[0].split(".").slice(1).join(".") : void 0);
|
|
2857
|
+
}
|
|
2858
|
+
return isExample ?? false;
|
|
2859
|
+
}).map((tag) => (transformJSDocComment(tag.comment) || "").replace(`${transformJSDocComment(tag.comment)?.split(" ")[0] || ""}`, "").replace(/\r/g, ""));
|
|
2860
|
+
if (examples.length === 0) return {
|
|
2861
|
+
examples: void 0,
|
|
2862
|
+
exampleLabels: void 0
|
|
2863
|
+
};
|
|
2864
|
+
try {
|
|
2865
|
+
return {
|
|
2866
|
+
examples: examples.map((example) => JSON.parse(example)),
|
|
2867
|
+
exampleLabels
|
|
2868
|
+
};
|
|
2869
|
+
} catch {
|
|
2870
|
+
throw ParameterError$1.invalidExampleSchema();
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
applyOptions(parameterName) {
|
|
2874
|
+
return {
|
|
2875
|
+
target: "parameter",
|
|
2876
|
+
host: {
|
|
2877
|
+
name: parameterName,
|
|
2878
|
+
parentName: this.method
|
|
2879
|
+
},
|
|
2880
|
+
resolveTypeNode: (n) => new TypeNodeResolver(n, this.current).resolve(),
|
|
2881
|
+
typeChecker: this.current.typeChecker,
|
|
2882
|
+
parameterType: () => this.parameter.type ? new TypeNodeResolver(this.parameter.type, this.current).resolve() : void 0,
|
|
2883
|
+
onUnmatchedDecorator: this.current.config.strict || this.current.config.onUnmatchedDecorator ? (report) => this.current.reportUnmatchedDecorator?.(report) : void 0
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
//#endregion
|
|
2888
|
+
//#region src/app/generator/method/module.ts
|
|
2889
|
+
var MethodGenerator = class {
|
|
2890
|
+
node;
|
|
2891
|
+
current;
|
|
2892
|
+
constructor(node, current) {
|
|
2893
|
+
this.node = node;
|
|
2894
|
+
this.current = current;
|
|
2895
|
+
}
|
|
2896
|
+
getMethodName() {
|
|
2897
|
+
return this.node.name.text;
|
|
2898
|
+
}
|
|
2899
|
+
generate(controllerPaths) {
|
|
2900
|
+
const name = this.getMethodName();
|
|
2901
|
+
const draft = newMethodDraft({ name });
|
|
2902
|
+
const options = this.applyOptions();
|
|
2903
|
+
applyDecoratorHandlers(this.node, this.current.registry.methods, draft, options);
|
|
2904
|
+
applyJsDocHandlers(this.node, this.current.registry.methodJsDoc, draft, options);
|
|
2905
|
+
if (!draft.verb) return null;
|
|
2906
|
+
if (draft.hidden) return null;
|
|
2907
|
+
const returnType = this.resolveReturnType();
|
|
2908
|
+
const defaultResponse = buildDefaultResponse(returnType, draft.defaultResponseExamples);
|
|
2909
|
+
const responses = mergeDefaultResponse(draft.responses, defaultResponse);
|
|
2910
|
+
const parameters = this.buildParameters(controllerPaths, draft.path, draft.verb);
|
|
2911
|
+
const description = getJSDocDescription(this.node) ?? draft.description;
|
|
2912
|
+
const summary = draft.summary ?? getJSDocTagComment(this.node, "summary");
|
|
2913
|
+
return {
|
|
2914
|
+
consumes: draft.consumes,
|
|
2915
|
+
deprecated: draft.deprecated ?? false,
|
|
2916
|
+
description: description ?? "",
|
|
2917
|
+
extensions: draft.extensions,
|
|
2918
|
+
hidden: draft.hidden,
|
|
2919
|
+
method: draft.verb,
|
|
2920
|
+
name,
|
|
2921
|
+
path: normalizePath(draft.path),
|
|
2922
|
+
produces: draft.produces,
|
|
2923
|
+
responses,
|
|
2924
|
+
security: draft.security,
|
|
2925
|
+
summary,
|
|
2926
|
+
tags: draft.tags,
|
|
2927
|
+
type: returnType,
|
|
2928
|
+
parameters
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
applyOptions() {
|
|
2932
|
+
const parentName = this.node.parent.name?.text;
|
|
2933
|
+
return {
|
|
2934
|
+
target: "method",
|
|
2935
|
+
host: {
|
|
2936
|
+
name: this.getMethodName(),
|
|
2937
|
+
parentName
|
|
2938
|
+
},
|
|
2939
|
+
resolveTypeNode: (n) => new TypeNodeResolver(n, this.current).resolve(),
|
|
2940
|
+
typeChecker: this.current.typeChecker,
|
|
2941
|
+
onUnmatchedDecorator: this.current.config.strict || this.current.config.onUnmatchedDecorator ? (report) => this.current.reportUnmatchedDecorator?.(report) : void 0
|
|
2942
|
+
};
|
|
2943
|
+
}
|
|
2944
|
+
resolveReturnType() {
|
|
2945
|
+
let nodeType = this.node.type;
|
|
2946
|
+
if (!nodeType) {
|
|
2947
|
+
const { typeChecker } = this.current;
|
|
2948
|
+
const signature = typeChecker.getSignatureFromDeclaration(this.node);
|
|
2949
|
+
const implicitType = typeChecker.getReturnTypeOfSignature(signature);
|
|
2950
|
+
nodeType = typeChecker.typeToTypeNode(implicitType, void 0, NodeBuilderFlags.NoTruncation);
|
|
2951
|
+
}
|
|
2952
|
+
return new TypeNodeResolver(nodeType, this.current).resolve();
|
|
2953
|
+
}
|
|
2954
|
+
buildParameters(controllerPaths, methodPath, verb) {
|
|
2955
|
+
const controllerId = this.node.parent.name;
|
|
2956
|
+
const methodId = this.node.name;
|
|
2957
|
+
const fullPaths = (controllerPaths.length === 0 ? [""] : controllerPaths).map((cp) => path.posix.join("/", cp, methodPath));
|
|
2958
|
+
const output = [];
|
|
2959
|
+
let bodyParameterCount = 0;
|
|
2960
|
+
let formParameterCount = 0;
|
|
2961
|
+
for (let i = 0; i < this.node.parameters.length; i++) try {
|
|
2962
|
+
const parameters = new ParameterGenerator(this.node.parameters[i], verb, fullPaths, this.current).generate();
|
|
2963
|
+
for (const parameter of parameters) {
|
|
2964
|
+
if (parameter.in === ParameterSource.BODY) bodyParameterCount += 1;
|
|
2965
|
+
if (parameter.in === ParameterSource.FORM_DATA) formParameterCount += 1;
|
|
2966
|
+
if (parameter.in !== ParameterSource.CONTEXT) output.push(parameter);
|
|
2967
|
+
}
|
|
2968
|
+
} catch (e) {
|
|
2969
|
+
const causeMsg = e instanceof Error ? `: ${e.message}` : "";
|
|
2970
|
+
throw new GeneratorError$1({
|
|
2971
|
+
message: `Parameter generation failed for '${controllerId.text}.${methodId.text}' argument index ${i}${causeMsg}`,
|
|
2972
|
+
code: GeneratorErrorCode$1.PARAMETER_GENERATION_FAILED,
|
|
2973
|
+
cause: e
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2976
|
+
if (bodyParameterCount > 1) throw new GeneratorError$1({
|
|
2977
|
+
message: `Only one body parameter allowed in '${controllerId.text}.${methodId.text}' method.`,
|
|
2978
|
+
code: GeneratorErrorCode$1.BODY_PARAMETER_DUPLICATE
|
|
2979
|
+
});
|
|
2980
|
+
if (bodyParameterCount > 0 && formParameterCount > 0) throw new GeneratorError$1({
|
|
2981
|
+
message: `Cannot mix body and form parameters in '${controllerId.text}.${methodId.text}' method.`,
|
|
2982
|
+
code: GeneratorErrorCode$1.BODY_FORM_CONFLICT
|
|
2983
|
+
});
|
|
2984
|
+
return output;
|
|
2985
|
+
}
|
|
2986
|
+
};
|
|
2987
|
+
function buildDefaultResponse(returnType, examples) {
|
|
2988
|
+
const isVoid = isVoidType(returnType);
|
|
2989
|
+
return {
|
|
2990
|
+
description: isVoid ? "No content" : "Ok",
|
|
2991
|
+
examples,
|
|
2992
|
+
schema: returnType,
|
|
2993
|
+
status: isVoid ? "204" : "200",
|
|
2994
|
+
name: isVoid ? "204" : "200"
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
function mergeDefaultResponse(handlerResponses, defaultResponse) {
|
|
2998
|
+
if (handlerResponses.length === 0) return [defaultResponse];
|
|
2999
|
+
const existing = handlerResponses.findIndex((r) => r.status === defaultResponse.status);
|
|
3000
|
+
if (existing >= 0) {
|
|
3001
|
+
const target = handlerResponses[existing];
|
|
3002
|
+
if (defaultResponse.examples && defaultResponse.examples.length > 0 && (!target.examples || target.examples.length === 0)) target.examples = defaultResponse.examples;
|
|
3003
|
+
return handlerResponses;
|
|
3004
|
+
}
|
|
3005
|
+
return [...handlerResponses, defaultResponse];
|
|
3006
|
+
}
|
|
3007
|
+
//#endregion
|
|
3008
|
+
//#region src/app/generator/controller/module.ts
|
|
3009
|
+
var ControllerGenerator = class {
|
|
3010
|
+
node;
|
|
3011
|
+
current;
|
|
3012
|
+
constructor(node, current) {
|
|
3013
|
+
this.node = node;
|
|
3014
|
+
this.current = current;
|
|
3015
|
+
}
|
|
3016
|
+
isValid() {
|
|
3017
|
+
return !!this.node.name;
|
|
3018
|
+
}
|
|
3019
|
+
generate() {
|
|
3020
|
+
if (!this.node.parent) throw new GeneratorError$1({
|
|
3021
|
+
message: "Controller node doesn't have a valid parent source file.",
|
|
3022
|
+
code: GeneratorErrorCode$1.CONTROLLER_NO_SOURCE_FILE
|
|
3023
|
+
});
|
|
3024
|
+
if (!this.node.name) throw new GeneratorError$1({
|
|
3025
|
+
message: "Controller node doesn't have a valid name.",
|
|
3026
|
+
code: GeneratorErrorCode$1.CONTROLLER_NO_NAME
|
|
3027
|
+
});
|
|
3028
|
+
const sourceFile = this.node.parent.getSourceFile();
|
|
3029
|
+
const draft = newControllerDraft({
|
|
3030
|
+
name: this.node.name.text,
|
|
3031
|
+
location: sourceFile.fileName
|
|
3032
|
+
});
|
|
3033
|
+
const options = this.applyOptions();
|
|
3034
|
+
applyDecoratorHandlers(this.node, this.current.registry.controllers, draft, options);
|
|
3035
|
+
applyJsDocHandlers(this.node, this.current.registry.controllerJsDoc, draft, options);
|
|
3036
|
+
if (draft.paths === void 0) return null;
|
|
3037
|
+
const normalized = (draft.paths.length === 0 ? [""] : draft.paths).map(normalizePath);
|
|
3038
|
+
const paths = [...new Set(normalized)];
|
|
3039
|
+
const methods = this.buildMethods(paths);
|
|
3040
|
+
return {
|
|
3041
|
+
consumes: draft.consumes,
|
|
3042
|
+
extensions: draft.extensions,
|
|
3043
|
+
hidden: draft.hidden,
|
|
3044
|
+
location: draft.location,
|
|
3045
|
+
name: draft.name,
|
|
3046
|
+
paths,
|
|
3047
|
+
produces: draft.produces,
|
|
3048
|
+
responses: draft.responses,
|
|
3049
|
+
security: draft.security,
|
|
3050
|
+
tags: draft.tags,
|
|
3051
|
+
methods
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
applyOptions() {
|
|
3055
|
+
return {
|
|
3056
|
+
target: "class",
|
|
3057
|
+
host: { name: this.node.name.text },
|
|
3058
|
+
resolveTypeNode: (n) => new TypeNodeResolver(n, this.current).resolve(),
|
|
3059
|
+
typeChecker: this.current.typeChecker,
|
|
3060
|
+
onUnmatchedDecorator: this.current.config.strict || this.current.config.onUnmatchedDecorator ? (report) => this.current.reportUnmatchedDecorator?.(report) : void 0
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
buildMethods(controllerPaths) {
|
|
3064
|
+
const set = /* @__PURE__ */ new Set();
|
|
3065
|
+
const output = [];
|
|
3066
|
+
for (const member of this.node.members) {
|
|
3067
|
+
if (!isMethodDeclaration(member)) continue;
|
|
3068
|
+
const generator = new MethodGenerator(member, this.current);
|
|
3069
|
+
const methodName = generator.getMethodName();
|
|
3070
|
+
if (set.has(methodName)) continue;
|
|
3071
|
+
const method = generator.generate(controllerPaths);
|
|
3072
|
+
if (!method) continue;
|
|
3073
|
+
set.add(methodName);
|
|
3074
|
+
output.push(method);
|
|
3075
|
+
}
|
|
3076
|
+
const inheritedMethods = this.collectInheritedMethodDeclarations(this.node);
|
|
3077
|
+
for (const node of inheritedMethods) {
|
|
3078
|
+
const generator = new MethodGenerator(node, this.current);
|
|
3079
|
+
const methodName = generator.getMethodName();
|
|
3080
|
+
if (set.has(methodName)) continue;
|
|
3081
|
+
try {
|
|
3082
|
+
const method = generator.generate(controllerPaths);
|
|
3083
|
+
if (!method) continue;
|
|
3084
|
+
set.add(methodName);
|
|
3085
|
+
output.push(method);
|
|
3086
|
+
} catch (error) {
|
|
3087
|
+
if (isResolverError$1(error) || isGeneratorError$1(error) && error.code === GeneratorErrorCode$1.PARAMETER_GENERATION_FAILED) continue;
|
|
3088
|
+
throw error;
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
return output;
|
|
3092
|
+
}
|
|
3093
|
+
collectInheritedMethodDeclarations(node) {
|
|
3094
|
+
const methods = [];
|
|
3095
|
+
if (!node.heritageClauses) return methods;
|
|
3096
|
+
for (const clause of node.heritageClauses) {
|
|
3097
|
+
if (clause.token !== SyntaxKind.ExtendsKeyword) continue;
|
|
3098
|
+
for (const type of clause.types) {
|
|
3099
|
+
const baseDeclaration = this.resolveBaseClassDeclaration(type.expression);
|
|
3100
|
+
if (!baseDeclaration) continue;
|
|
3101
|
+
for (const member of baseDeclaration.members) if (isMethodDeclaration(member)) methods.push(member);
|
|
3102
|
+
methods.push(...this.collectInheritedMethodDeclarations(baseDeclaration));
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
return methods;
|
|
3106
|
+
}
|
|
3107
|
+
resolveBaseClassDeclaration(expression) {
|
|
3108
|
+
let symbol = this.current.typeChecker.getSymbolAtLocation(expression);
|
|
3109
|
+
if (!symbol) return;
|
|
3110
|
+
if (symbol.flags & SymbolFlags.Alias) symbol = this.current.typeChecker.getAliasedSymbol(symbol);
|
|
3111
|
+
const declarations = symbol.getDeclarations();
|
|
3112
|
+
if (!declarations || declarations.length === 0) return;
|
|
3113
|
+
const declaration = declarations[0];
|
|
3114
|
+
if (isClassDeclaration(declaration)) return declaration;
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
//#endregion
|
|
3118
|
+
//#region src/app/generator/metadata/module.ts
|
|
3119
|
+
var MetadataGenerator = class {
|
|
3120
|
+
nodes;
|
|
3121
|
+
typeChecker;
|
|
3122
|
+
registry;
|
|
3123
|
+
resolverCache;
|
|
3124
|
+
config;
|
|
3125
|
+
program;
|
|
3126
|
+
cache;
|
|
3127
|
+
controllers;
|
|
3128
|
+
referenceTypes = {};
|
|
3129
|
+
circularDependencyResolvers = new Array();
|
|
3130
|
+
unmatchedDecorators = /* @__PURE__ */ new Map();
|
|
3131
|
+
constructor(context) {
|
|
3132
|
+
this.nodes = [];
|
|
3133
|
+
this.config = context.options;
|
|
3134
|
+
this.cache = new CacheClient(context.options.cache);
|
|
3135
|
+
this.registry = createRegistry();
|
|
3136
|
+
this.resolverCache = new ResolverCache();
|
|
3137
|
+
this.program = createProgram(context.sourceFiles, context.compilerOptions || {});
|
|
3138
|
+
this.typeChecker = this.program.getTypeChecker();
|
|
3139
|
+
}
|
|
3140
|
+
async generate() {
|
|
3141
|
+
const sourceFilesHash = this.buildNodesFromSourceFiles();
|
|
3142
|
+
if (this.config.preset) this.registry = await loadRegistryByName(this.config.preset);
|
|
3143
|
+
const cacheKey = composeCacheKey({
|
|
3144
|
+
schemaVersion: "3",
|
|
3145
|
+
sourceFilesHash,
|
|
3146
|
+
compilerOptionsHash: hashCompilerOptions(this.program.getCompilerOptions()),
|
|
3147
|
+
registryHash: hashRegistry(this.registry),
|
|
3148
|
+
presetName: this.config.preset
|
|
3149
|
+
});
|
|
3150
|
+
const bypassCache = !!(this.config.strict || this.config.onUnmatchedDecorator);
|
|
3151
|
+
let cache = bypassCache ? void 0 : await this.cache.get(cacheKey);
|
|
3152
|
+
if (!cache) {
|
|
3153
|
+
this.buildControllers();
|
|
3154
|
+
this.assertPresetProducedControllers();
|
|
3155
|
+
this.circularDependencyResolvers.forEach((resolve) => resolve(this.referenceTypes));
|
|
3156
|
+
cache = {
|
|
3157
|
+
controllers: this.controllers,
|
|
3158
|
+
referenceTypes: this.referenceTypes,
|
|
3159
|
+
cacheKey,
|
|
3160
|
+
schemaVersion: "3"
|
|
3161
|
+
};
|
|
3162
|
+
if (!bypassCache) await this.cache.save(cache);
|
|
3163
|
+
}
|
|
3164
|
+
if (this.config.strict || this.config.onUnmatchedDecorator) this.dispatchUnmatchedDecoratorReports();
|
|
3165
|
+
return {
|
|
3166
|
+
controllers: cache.controllers,
|
|
3167
|
+
referenceTypes: cache.referenceTypes
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3170
|
+
assertPresetProducedControllers() {
|
|
3171
|
+
if (this.config.preset || this.controllers.length > 0) return;
|
|
3172
|
+
if (this.nodes.length === 0) return;
|
|
3173
|
+
throw new ConfigError({
|
|
3174
|
+
message: "No preset configured and no controllers detected. Provide `preset: '@trapi/preset-decorators-express'` (or another preset) so handlers can match your decorators.",
|
|
3175
|
+
code: ConfigErrorCode.PRESET_MISSING
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
reportUnmatchedDecorator(report) {
|
|
3179
|
+
const key = `${report.target}:${report.name}`;
|
|
3180
|
+
const existing = this.unmatchedDecorators.get(key);
|
|
3181
|
+
if (existing) {
|
|
3182
|
+
existing.push(report);
|
|
3183
|
+
return;
|
|
3184
|
+
}
|
|
3185
|
+
this.unmatchedDecorators.set(key, [report]);
|
|
3186
|
+
}
|
|
3187
|
+
dispatchUnmatchedDecoratorReports() {
|
|
3188
|
+
if (this.unmatchedDecorators.size === 0) return;
|
|
3189
|
+
const flat = [];
|
|
3190
|
+
for (const reports of this.unmatchedDecorators.values()) flat.push(...reports);
|
|
3191
|
+
if (this.config.onUnmatchedDecorator) {
|
|
3192
|
+
this.config.onUnmatchedDecorator(flat);
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
3195
|
+
const summary = this.formatUnmatchedSummary();
|
|
3196
|
+
if (this.config.strict === "throw") throw new GeneratorError$1({
|
|
3197
|
+
message: summary,
|
|
3198
|
+
code: GeneratorErrorCode$1.STRICT_UNMATCHED_DECORATORS
|
|
3199
|
+
});
|
|
3200
|
+
console.warn(summary);
|
|
3201
|
+
}
|
|
3202
|
+
formatUnmatchedSummary() {
|
|
3203
|
+
const lines = ["[trapi] strict mode: decorators with no matching handler:"];
|
|
3204
|
+
for (const reports of this.unmatchedDecorators.values()) {
|
|
3205
|
+
const first = reports[0];
|
|
3206
|
+
const occurrences = reports.length;
|
|
3207
|
+
const location = `${first.file}:${first.line}`;
|
|
3208
|
+
const suffix = occurrences > 1 ? ` (${occurrences} occurrences; first at ${location})` : ` (${location})`;
|
|
3209
|
+
lines.push(` - @${first.name} on ${first.target} '${first.host.name}'${suffix}`);
|
|
3210
|
+
}
|
|
3211
|
+
return lines.join("\n");
|
|
3212
|
+
}
|
|
3213
|
+
buildNodesFromSourceFiles() {
|
|
3214
|
+
const hash = crypto.createHash("sha256");
|
|
3215
|
+
this.program.getSourceFiles().forEach((sf) => {
|
|
3216
|
+
if (this.isIgnoredPath(sf.fileName) && !this.isAllowedPath(sf.fileName)) return;
|
|
3217
|
+
hash.update(sf.fileName);
|
|
3218
|
+
hash.update("\0");
|
|
3219
|
+
hash.update(sf.text);
|
|
3220
|
+
hash.update("\0");
|
|
3221
|
+
forEachChild(sf, (node) => {
|
|
3222
|
+
if (isModuleDeclaration(node)) {
|
|
3223
|
+
/**
|
|
3224
|
+
* For some reason unknown to me, TS resolves both `declare module` and `namespace` to
|
|
3225
|
+
* the same kind (`ModuleDeclaration`). In order to figure out whether it's one or the other,
|
|
3226
|
+
* we check the node flags. They tell us whether it is a namespace or not.
|
|
3227
|
+
*/
|
|
3228
|
+
if ((node.flags & NodeFlags.Namespace) === 0 && node.body && isModuleBlock(node.body)) {
|
|
3229
|
+
node.body.statements.forEach((statement) => {
|
|
3230
|
+
this.nodes.push(statement);
|
|
3231
|
+
});
|
|
3232
|
+
return;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
this.nodes.push(node);
|
|
3236
|
+
});
|
|
3237
|
+
});
|
|
3238
|
+
return hash.digest("hex");
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* Check if the source file path is in the ignored path list.
|
|
3242
|
+
*
|
|
3243
|
+
* @param filePath
|
|
3244
|
+
* @protected
|
|
3245
|
+
*/
|
|
3246
|
+
isIgnoredPath(filePath) {
|
|
3247
|
+
if (typeof this.config.ignore === "undefined") return false;
|
|
3248
|
+
return this.config.ignore.some((item) => minimatch(filePath, item));
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Check if the source file path is in the ignored path list.
|
|
3252
|
+
*
|
|
3253
|
+
* @param filePath
|
|
3254
|
+
* @protected
|
|
3255
|
+
*/
|
|
3256
|
+
isAllowedPath(filePath) {
|
|
3257
|
+
if (typeof this.config.allow === "undefined") return false;
|
|
3258
|
+
return this.config.allow.some((item) => minimatch(filePath, item));
|
|
3259
|
+
}
|
|
3260
|
+
isExportedNode(_node) {
|
|
3261
|
+
return true;
|
|
3262
|
+
}
|
|
3263
|
+
addReferenceType(referenceType) {
|
|
3264
|
+
if (!referenceType.refName) return;
|
|
3265
|
+
this.referenceTypes[referenceType.refName] = referenceType;
|
|
3266
|
+
}
|
|
3267
|
+
getReferenceType(refName) {
|
|
3268
|
+
return this.referenceTypes[refName];
|
|
3269
|
+
}
|
|
3270
|
+
registerDependencyResolver(callback) {
|
|
3271
|
+
this.circularDependencyResolvers.push(callback);
|
|
3272
|
+
}
|
|
3273
|
+
buildControllers() {
|
|
3274
|
+
this.controllers = [];
|
|
3275
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
3276
|
+
const node = this.nodes[i];
|
|
3277
|
+
if (!isClassDeclaration(node)) continue;
|
|
3278
|
+
const generator = new ControllerGenerator(node, this);
|
|
3279
|
+
if (!generator.isValid()) continue;
|
|
3280
|
+
const controller = generator.generate();
|
|
3281
|
+
if (controller) this.controllers.push(controller);
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
};
|
|
3285
|
+
//#endregion
|
|
3286
|
+
//#region src/app/generator/metadata/utils.ts
|
|
3287
|
+
function isMetadata(input) {
|
|
3288
|
+
return isObject(input) && hasOwnProperty$1(input, "controllers") && Array.isArray(input.controllers) && hasOwnProperty$1(input, "referenceTypes") && isObject(input.referenceTypes);
|
|
3289
|
+
}
|
|
3290
|
+
//#endregion
|
|
3291
|
+
//#region src/app/generate.ts
|
|
3292
|
+
async function generateMetadata(options) {
|
|
3293
|
+
let compilerOptions;
|
|
3294
|
+
if (options.tsconfig) {
|
|
3295
|
+
let { tsconfig } = options;
|
|
3296
|
+
if (typeof tsconfig === "string") {
|
|
3297
|
+
tsconfig = await softLoadTsconfig({ name: tsconfig });
|
|
3298
|
+
compilerOptions = tsconfig.compilerOptions;
|
|
3299
|
+
} else compilerOptions = tsconfig.compilerOptions || {};
|
|
3300
|
+
}
|
|
3301
|
+
return new MetadataGenerator({
|
|
3302
|
+
sourceFiles: await scanSourceFiles(options.entryPoint),
|
|
3303
|
+
compilerOptions,
|
|
3304
|
+
options
|
|
3305
|
+
}).generate();
|
|
3306
|
+
}
|
|
3307
|
+
//#endregion
|
|
3308
|
+
export { CACHE_DEFAULT_MAX_AGE_MS, CACHE_FILE_PREFIX, CACHE_FILE_SUFFIX, CACHE_SCHEMA_VERSION, CacheClient, CollectionFormat, CollectionKind, ConfigError, ConfigErrorCode, ControllerGenerator, DecoratorArgumentKindName, DecoratorTargetKind, GeneratorError, GeneratorErrorCode, JSDocTagName, MarkerName, MetadataError, MetadataGenerator, MethodGenerator, MethodName, NumericKind, ParamKind, ParameterError, ParameterErrorCode, ParameterGenerator, ParameterSource, PrimitiveResolver, ReferenceResolver, ResolverBase, ResolverCache, ResolverError, TypeName, TypeNodeResolver, UtilityTypeName, ValidatorError, ValidatorErrorCode, ValidatorName, append, applyDecoratorHandlers, applyJsDocHandlers, buildCacheOptions, buildDecoratorArgument, buildDecoratorSources, buildHandlerContext, buildJsDocHandlerContext, buildJsDocSources, composeCacheKey, controller, controllerHandlerSchema, controllerJsDoc, controllerJsDocHandlerSchema, createPresetContainer, createRegistry, findDecoratorByName, findDecoratorsByName, flag, generateMetadata, generatePresetLookupPaths, getDeclarationValidators, getInitializerValue, getJSDoc, getJSDocDescription, getJSDocTagComment, getJSDocTagNames, getJSDocTags, getLiteralValue, getNodeDecorators, getNodeDescription, getNodeExtensions, hasDecoratorNamed, hasInitializer, hasJSDocTag, hasOwnProperty, hashCompilerOptions, hashRegistry, into, isAnyType, isArrayType, isBigintType, isBinaryType, isBooleanType, isBufferType, isByteType, isDateTimeType, isDateType, isDeprecatedMarker, isDoubleType, isEnumType, isExtensionMarker, isFileType, isFloatType, isGeneratorError, isHiddenMarker, isIntegerType, isIntersectionType, isLongType, isMetadata, isNestedObjectLiteralType, isNeverType, isObjectType, isPrimitiveType, isRefAliasType, isRefEnumType, isRefObjectType, isReferenceType, isResolverError, isStringArray, isStringType, isTupleType, isUndefinedType, isUnionType, isVoidType, jsDocMatchSchema, loadRegistry, loadRegistryByName, loadTSConfig, matchSchema, matches, matchesJsDoc, method, methodHandlerSchema, methodJsDoc, methodJsDocHandlerSchema, namesForMarker, newControllerDraft, newMethodDraft, newParameterDraft, normalizePath, numericMarkerKind, parameter, parameterHandlerSchema, parameterJsDoc, parameterJsDocHandlerSchema, presetSchema, prettyLocationOfNode, prettyTroubleCause, readNodeDecorators, resolveArrayType, resolveIndexedAccessType, resolveIntersectionType, resolveLiteralType, resolveMappedType, resolveObjectLiteralType, resolvePresetByName, resolveTupleType, resolveTypeOperatorType, resolveUnionType, scanSourceFiles, softLoadTsconfig, tagsForMarker, toTypeNodeOrFail, transformJSDocComment, validatePreset };
|
|
3309
|
+
|
|
3310
|
+
//# sourceMappingURL=index.mjs.map
|