@twin.org/tools-core 0.0.3-next.15 → 0.0.3-next.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +20 -11
- package/dist/es/index.js.map +1 -1
- package/dist/es/models/ITypeScriptToSchemaContext.js +2 -0
- package/dist/es/models/ITypeScriptToSchemaContext.js.map +1 -0
- package/dist/es/models/ITypeScriptToSchemaDiagnostics.js +4 -0
- package/dist/es/models/ITypeScriptToSchemaDiagnostics.js.map +1 -0
- package/dist/es/models/ITypeScriptToSchemaOptions.js +4 -0
- package/dist/es/models/ITypeScriptToSchemaOptions.js.map +1 -0
- package/dist/es/utils/constants.js +43 -0
- package/dist/es/utils/constants.js.map +1 -0
- package/dist/es/utils/diagnosticReporter.js +32 -0
- package/dist/es/utils/diagnosticReporter.js.map +1 -0
- package/dist/es/utils/disallowedTypeGuard.js +151 -0
- package/dist/es/utils/disallowedTypeGuard.js.map +1 -0
- package/dist/es/utils/enum.js +152 -0
- package/dist/es/utils/enum.js.map +1 -0
- package/dist/es/utils/fileUtils.js +130 -0
- package/dist/es/utils/fileUtils.js.map +1 -0
- package/dist/es/utils/importTypeQuerySchemaResolver.js +328 -0
- package/dist/es/utils/importTypeQuerySchemaResolver.js.map +1 -0
- package/dist/es/utils/indexSignaturePatternResolver.js +94 -0
- package/dist/es/utils/indexSignaturePatternResolver.js.map +1 -0
- package/dist/es/utils/intersectionSchemaMerger.js +85 -0
- package/dist/es/utils/intersectionSchemaMerger.js.map +1 -0
- package/dist/es/utils/jsonSchemaBuilder.js +3189 -0
- package/dist/es/utils/jsonSchemaBuilder.js.map +1 -0
- package/dist/es/utils/mappedTypeSchemaResolver.js +231 -0
- package/dist/es/utils/mappedTypeSchemaResolver.js.map +1 -0
- package/dist/es/utils/objectTransformer.js +162 -0
- package/dist/es/utils/objectTransformer.js.map +1 -0
- package/dist/es/utils/regEx.js +128 -0
- package/dist/es/utils/regEx.js.map +1 -0
- package/dist/es/utils/resolver.js +164 -0
- package/dist/es/utils/resolver.js.map +1 -0
- package/dist/es/utils/templateLiteralPatternBuilder.js +94 -0
- package/dist/es/utils/templateLiteralPatternBuilder.js.map +1 -0
- package/dist/es/utils/typeScriptToSchema.js +102 -0
- package/dist/es/utils/typeScriptToSchema.js.map +1 -0
- package/dist/es/utils/utility.js +134 -0
- package/dist/es/utils/utility.js.map +1 -0
- package/dist/es/utils/utilityTypeSchemaMapper.js +412 -0
- package/dist/es/utils/utilityTypeSchemaMapper.js.map +1 -0
- package/dist/types/index.d.ts +20 -11
- package/dist/types/models/ITypeScriptToSchemaContext.d.ts +64 -0
- package/dist/types/models/ITypeScriptToSchemaDiagnostics.d.ts +31 -0
- package/dist/types/models/ITypeScriptToSchemaOptions.d.ts +22 -0
- package/dist/types/utils/constants.d.ts +13 -0
- package/dist/types/utils/diagnosticReporter.d.ts +17 -0
- package/dist/types/utils/disallowedTypeGuard.d.ts +16 -0
- package/dist/types/utils/enum.d.ts +42 -0
- package/dist/types/utils/fileUtils.d.ts +66 -0
- package/dist/types/utils/importTypeQuerySchemaResolver.d.ts +52 -0
- package/dist/types/utils/indexSignaturePatternResolver.d.ts +21 -0
- package/dist/types/utils/intersectionSchemaMerger.d.ts +16 -0
- package/dist/types/utils/jsonSchemaBuilder.d.ts +721 -0
- package/dist/types/utils/mappedTypeSchemaResolver.d.ts +46 -0
- package/dist/types/utils/objectTransformer.d.ts +33 -0
- package/dist/types/utils/regEx.d.ts +24 -0
- package/dist/types/utils/resolver.d.ts +16 -0
- package/dist/types/utils/templateLiteralPatternBuilder.d.ts +12 -0
- package/dist/types/utils/typeScriptToSchema.d.ts +31 -0
- package/dist/types/utils/utility.d.ts +58 -0
- package/dist/types/utils/utilityTypeSchemaMapper.d.ts +92 -0
- package/docs/changelog.md +15 -1
- package/docs/examples.md +55 -148
- package/docs/reference/classes/Constants.md +29 -0
- package/docs/reference/classes/DiagnosticReporter.md +49 -0
- package/docs/reference/classes/DisallowedTypeGuard.md +35 -0
- package/docs/reference/classes/Enum.md +93 -0
- package/docs/reference/classes/FileUtils.md +237 -0
- package/docs/reference/classes/ImportTypeQuerySchemaResolver.md +87 -0
- package/docs/reference/classes/IndexSignaturePatternResolver.md +69 -0
- package/docs/reference/classes/IntersectionSchemaMerger.md +48 -0
- package/docs/reference/classes/JsonSchemaBuilder.md +2772 -0
- package/docs/reference/classes/MappedTypeSchemaResolver.md +211 -0
- package/docs/reference/classes/ObjectTransformer.md +119 -0
- package/docs/reference/classes/RegEx.md +99 -0
- package/docs/reference/classes/Resolver.md +41 -0
- package/docs/reference/classes/TemplateLiteralPatternBuilder.md +35 -0
- package/docs/reference/classes/TypeScriptToSchema.md +91 -0
- package/docs/reference/classes/Utility.md +164 -0
- package/docs/reference/classes/UtilityTypeSchemaMapper.md +341 -0
- package/docs/reference/index.md +20 -14
- package/docs/reference/interfaces/ITypeScriptToSchemaContext.md +113 -0
- package/docs/reference/interfaces/ITypeScriptToSchemaDiagnostics.md +55 -0
- package/docs/reference/interfaces/ITypeScriptToSchemaOptions.md +44 -0
- package/locales/en.json +30 -1
- package/package.json +3 -2
- package/dist/es/models/IJsonSchema.js +0 -2
- package/dist/es/models/IJsonSchema.js.map +0 -1
- package/dist/es/models/IOpenApi.js +0 -2
- package/dist/es/models/IOpenApi.js.map +0 -1
- package/dist/es/models/IOpenApiExample.js +0 -4
- package/dist/es/models/IOpenApiExample.js.map +0 -1
- package/dist/es/models/IOpenApiHeader.js +0 -4
- package/dist/es/models/IOpenApiHeader.js.map +0 -1
- package/dist/es/models/IOpenApiPathMethod.js +0 -2
- package/dist/es/models/IOpenApiPathMethod.js.map +0 -1
- package/dist/es/models/IOpenApiResponse.js +0 -2
- package/dist/es/models/IOpenApiResponse.js.map +0 -1
- package/dist/es/models/IOpenApiSecurityScheme.js +0 -4
- package/dist/es/models/IOpenApiSecurityScheme.js.map +0 -1
- package/dist/es/models/IPackageJson.js +0 -4
- package/dist/es/models/IPackageJson.js.map +0 -1
- package/dist/es/models/jsonTypeName.js +0 -2
- package/dist/es/models/jsonTypeName.js.map +0 -1
- package/dist/es/utils/jsonSchemaHelper.js +0 -282
- package/dist/es/utils/jsonSchemaHelper.js.map +0 -1
- package/dist/es/utils/openApiHelper.js +0 -12
- package/dist/es/utils/openApiHelper.js.map +0 -1
- package/dist/types/models/IJsonSchema.d.ts +0 -5
- package/dist/types/models/IOpenApi.d.ts +0 -54
- package/dist/types/models/IOpenApiExample.d.ts +0 -13
- package/dist/types/models/IOpenApiHeader.d.ts +0 -19
- package/dist/types/models/IOpenApiPathMethod.d.ts +0 -65
- package/dist/types/models/IOpenApiResponse.d.ts +0 -32
- package/dist/types/models/IOpenApiSecurityScheme.d.ts +0 -25
- package/dist/types/models/IPackageJson.d.ts +0 -15
- package/dist/types/models/jsonTypeName.d.ts +0 -5
- package/dist/types/utils/jsonSchemaHelper.d.ts +0 -78
- package/dist/types/utils/openApiHelper.d.ts +0 -9
- package/docs/reference/classes/JsonSchemaHelper.md +0 -233
- package/docs/reference/classes/OpenApiHelper.md +0 -21
- package/docs/reference/interfaces/IOpenApi.md +0 -103
- package/docs/reference/interfaces/IOpenApiExample.md +0 -19
- package/docs/reference/interfaces/IOpenApiHeader.md +0 -31
- package/docs/reference/interfaces/IOpenApiPathMethod.md +0 -119
- package/docs/reference/interfaces/IOpenApiResponse.md +0 -35
- package/docs/reference/interfaces/IOpenApiSecurityScheme.md +0 -43
- package/docs/reference/interfaces/IPackageJson.md +0 -23
- package/docs/reference/type-aliases/IJsonSchema.md +0 -5
- package/docs/reference/type-aliases/JsonTypeName.md +0 -5
package/dist/es/index.js
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
// Copyright 2024 IOTA Stiftung.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
-
export * from "./models/
|
|
4
|
-
export * from "./models/
|
|
5
|
-
export * from "./models/
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./
|
|
11
|
-
export * from "./
|
|
12
|
-
export * from "./utils/
|
|
13
|
-
export * from "./utils/
|
|
3
|
+
export * from "./models/ITypeScriptToSchemaContext.js";
|
|
4
|
+
export * from "./models/ITypeScriptToSchemaDiagnostics.js";
|
|
5
|
+
export * from "./models/ITypeScriptToSchemaOptions.js";
|
|
6
|
+
export * from "./utils/diagnosticReporter.js";
|
|
7
|
+
export * from "./utils/disallowedTypeGuard.js";
|
|
8
|
+
export * from "./utils/enum.js";
|
|
9
|
+
export * from "./utils/fileUtils.js";
|
|
10
|
+
export * from "./utils/importTypeQuerySchemaResolver.js";
|
|
11
|
+
export * from "./utils/indexSignaturePatternResolver.js";
|
|
12
|
+
export * from "./utils/intersectionSchemaMerger.js";
|
|
13
|
+
export * from "./utils/jsonSchemaBuilder.js";
|
|
14
|
+
export * from "./utils/constants.js";
|
|
15
|
+
export * from "./utils/mappedTypeSchemaResolver.js";
|
|
16
|
+
export * from "./utils/objectTransformer.js";
|
|
17
|
+
export * from "./utils/regEx.js";
|
|
18
|
+
export * from "./utils/resolver.js";
|
|
19
|
+
export * from "./utils/templateLiteralPatternBuilder.js";
|
|
20
|
+
export * from "./utils/typeScriptToSchema.js";
|
|
21
|
+
export * from "./utils/utility.js";
|
|
22
|
+
export * from "./utils/utilityTypeSchemaMapper.js";
|
|
14
23
|
//# sourceMappingURL=index.js.map
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,wCAAwC,CAAC;AACvD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0CAA0C,CAAC;AACzD,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0CAA0C,CAAC;AACzD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,oCAAoC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/ITypeScriptToSchemaContext.js\";\nexport * from \"./models/ITypeScriptToSchemaDiagnostics.js\";\nexport * from \"./models/ITypeScriptToSchemaOptions.js\";\nexport * from \"./utils/diagnosticReporter.js\";\nexport * from \"./utils/disallowedTypeGuard.js\";\nexport * from \"./utils/enum.js\";\nexport * from \"./utils/fileUtils.js\";\nexport * from \"./utils/importTypeQuerySchemaResolver.js\";\nexport * from \"./utils/indexSignaturePatternResolver.js\";\nexport * from \"./utils/intersectionSchemaMerger.js\";\nexport * from \"./utils/jsonSchemaBuilder.js\";\nexport * from \"./utils/constants.js\";\nexport * from \"./utils/mappedTypeSchemaResolver.js\";\nexport * from \"./utils/objectTransformer.js\";\nexport * from \"./utils/regEx.js\";\nexport * from \"./utils/resolver.js\";\nexport * from \"./utils/templateLiteralPatternBuilder.js\";\nexport * from \"./utils/typeScriptToSchema.js\";\nexport * from \"./utils/utility.js\";\nexport * from \"./utils/utilityTypeSchemaMapper.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITypeScriptToSchemaContext.js","sourceRoot":"","sources":["../../../src/models/ITypeScriptToSchemaContext.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IJsonSchema } from \"@twin.org/tools-models\";\nimport type * as ts from \"typescript\";\nimport type { ITypeScriptToSchemaOptions } from \"./ITypeScriptToSchemaOptions.js\";\n\n/**\n * Context for TypeScript to JSON schema generation.\n */\nexport interface ITypeScriptToSchemaContext {\n\t/**\n\t * The namespace for generated schema ids.\n\t */\n\tnamespace: string;\n\n\t/**\n\t * The package name for generated schema ids.\n\t */\n\tpackageName: string;\n\n\t/**\n\t * The schema cache indexed by package and title.\n\t */\n\tschemas: { [id: string]: { [id: string]: IJsonSchema } };\n\n\t/**\n\t * The currently active source file being mapped.\n\t */\n\tactiveSourceFile?: ts.SourceFile;\n\n\t/**\n\t * The currently active enclosing object (interface/type) being mapped.\n\t */\n\tactiveEnclosingObjectName?: string;\n\n\t/**\n\t * The first disallowed type encountered while mapping the active enclosing object.\n\t */\n\tactiveDisallowedType?: {\n\t\tdisallowedTypeName: string;\n\t\tpropertyName: string;\n\t\tenclosingObjectName: string;\n\t};\n\n\t/**\n\t * Type names currently being resolved to avoid recursive local-type expansion loops.\n\t */\n\tresolvingTypeNames?: Set<string>;\n\n\t/**\n\t * Utility type names currently being processed to avoid infinite recursion cycles\n\t * when encountering complex combinations of indexed access types, keyof, and utility types.\n\t */\n\tresolvingUtilityTypes?: Set<string>;\n\n\t/**\n\t * Imported utility base schemas currently being resolved to avoid recursive re-entry\n\t * while parsing module graphs for utility type application.\n\t */\n\tresolvingImportedObjectSchemas?: Set<string>;\n\n\t/**\n\t * Generic type parameter bindings active for the current mapping scope.\n\t */\n\ttypeParameterBindings?: { [id: string]: ts.TypeNode | null };\n\n\t/**\n\t * Optional schema generation options.\n\t */\n\toptions?: ITypeScriptToSchemaOptions;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITypeScriptToSchemaDiagnostics.js","sourceRoot":"","sources":["../../../src/models/ITypeScriptToSchemaDiagnostics.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Diagnostic payload for non-fatal schema generation issues.\n */\nexport interface ITypeScriptToSchemaDiagnostics {\n\t/**\n\t * Stable diagnostic code identifying the issue type.\n\t */\n\tcode: string;\n\n\t/**\n\t * Additional structured metadata related to the diagnostic.\n\t */\n\tproperties?: { [key: string]: unknown };\n\n\t/**\n\t * Schema path where the issue was detected.\n\t */\n\tpath: string;\n\n\t/**\n\t * Source file where the diagnostic originated, if available.\n\t */\n\tfileName?: string;\n\n\t/**\n\t * One-based source line number, when available.\n\t */\n\tline?: number;\n\n\t/**\n\t * One-based source column number, when available.\n\t */\n\tcolumn?: number;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITypeScriptToSchemaOptions.js","sourceRoot":"","sources":["../../../src/models/ITypeScriptToSchemaOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\nimport type { ITypeScriptToSchemaDiagnostics } from \"./ITypeScriptToSchemaDiagnostics.js\";\n\n/**\n * Options for TypeScript to JSON schema generation.\n */\nexport interface ITypeScriptToSchemaOptions {\n\t/**\n\t * Mapping of package ids, type ids, wildcard patterns, or regex patterns to schema id prefixes\n\t * or replacement templates for referenced schemas.\n\t */\n\texternalReferences?: { [id: string]: string };\n\n\t/**\n\t * Optional diagnostic callback for non-fatal generation issues.\n\t * @param diagnostic The diagnostic details for the generation issue.\n\t */\n\tonDiagnostic?: (diagnostic: ITypeScriptToSchemaDiagnostics) => void;\n\n\t/**\n\t * Package names where diagnostics should be suppressed, e.g. jose.\n\t */\n\tsuppressPackageWarnings?: string[];\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
/**
|
|
4
|
+
* Shared constants for TypeScript to JSON schema generation.
|
|
5
|
+
*/
|
|
6
|
+
export class Constants {
|
|
7
|
+
/**
|
|
8
|
+
* Utility type names currently unsupported and mapped to open schemas with diagnostics.
|
|
9
|
+
*/
|
|
10
|
+
static UNSUPPORTED_UTILITY_TYPE_NAMES = [
|
|
11
|
+
"Awaited",
|
|
12
|
+
"ReturnType",
|
|
13
|
+
"Parameters",
|
|
14
|
+
"ConstructorParameters",
|
|
15
|
+
"InstanceType",
|
|
16
|
+
"ThisParameterType",
|
|
17
|
+
"OmitThisParameter",
|
|
18
|
+
"ThisType",
|
|
19
|
+
"CallableFunction",
|
|
20
|
+
"NewableFunction",
|
|
21
|
+
"Uppercase",
|
|
22
|
+
"Lowercase",
|
|
23
|
+
"Capitalize",
|
|
24
|
+
"Uncapitalize"
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Native typed-array and binary type names mapped to JSON number arrays.
|
|
28
|
+
*/
|
|
29
|
+
static ARRAY_NUMBER_TYPE_NAMES = [
|
|
30
|
+
"ArrayBuffer",
|
|
31
|
+
"SharedArrayBuffer",
|
|
32
|
+
"Int8Array",
|
|
33
|
+
"Uint8Array",
|
|
34
|
+
"Uint8ClampedArray",
|
|
35
|
+
"Int16Array",
|
|
36
|
+
"Uint16Array",
|
|
37
|
+
"Int32Array",
|
|
38
|
+
"Uint32Array",
|
|
39
|
+
"Float32Array",
|
|
40
|
+
"Float64Array"
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/utils/constants.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AAEvC;;GAEG;AACH,MAAM,OAAO,SAAS;IACrB;;OAEG;IACI,MAAM,CAAU,8BAA8B,GAAa;QACjE,SAAS;QACT,YAAY;QACZ,YAAY;QACZ,uBAAuB;QACvB,cAAc;QACd,mBAAmB;QACnB,mBAAmB;QACnB,UAAU;QACV,kBAAkB;QAClB,iBAAiB;QACjB,WAAW;QACX,WAAW;QACX,YAAY;QACZ,cAAc;KACd,CAAC;IAEF;;OAEG;IACI,MAAM,CAAU,uBAAuB,GAAa;QAC1D,aAAa;QACb,mBAAmB;QACnB,WAAW;QACX,YAAY;QACZ,mBAAmB;QACnB,YAAY;QACZ,aAAa;QACb,YAAY;QACZ,aAAa;QACb,cAAc;QACd,cAAc;KACd,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Shared constants for TypeScript to JSON schema generation.\n */\nexport class Constants {\n\t/**\n\t * Utility type names currently unsupported and mapped to open schemas with diagnostics.\n\t */\n\tpublic static readonly UNSUPPORTED_UTILITY_TYPE_NAMES: string[] = [\n\t\t\"Awaited\",\n\t\t\"ReturnType\",\n\t\t\"Parameters\",\n\t\t\"ConstructorParameters\",\n\t\t\"InstanceType\",\n\t\t\"ThisParameterType\",\n\t\t\"OmitThisParameter\",\n\t\t\"ThisType\",\n\t\t\"CallableFunction\",\n\t\t\"NewableFunction\",\n\t\t\"Uppercase\",\n\t\t\"Lowercase\",\n\t\t\"Capitalize\",\n\t\t\"Uncapitalize\"\n\t];\n\n\t/**\n\t * Native typed-array and binary type names mapped to JSON number arrays.\n\t */\n\tpublic static readonly ARRAY_NUMBER_TYPE_NAMES: string[] = [\n\t\t\"ArrayBuffer\",\n\t\t\"SharedArrayBuffer\",\n\t\t\"Int8Array\",\n\t\t\"Uint8Array\",\n\t\t\"Uint8ClampedArray\",\n\t\t\"Int16Array\",\n\t\t\"Uint16Array\",\n\t\t\"Int32Array\",\n\t\t\"Uint32Array\",\n\t\t\"Float32Array\",\n\t\t\"Float64Array\"\n\t];\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emits non-fatal diagnostics during schema mapping.
|
|
3
|
+
*/
|
|
4
|
+
export class DiagnosticReporter {
|
|
5
|
+
/**
|
|
6
|
+
* Emit an optional non-fatal schema generation diagnostic.
|
|
7
|
+
* @param context The generation context.
|
|
8
|
+
* @param node The related AST node.
|
|
9
|
+
* @param code The diagnostic code.
|
|
10
|
+
* @param properties The values to substitute into the localised message.
|
|
11
|
+
*/
|
|
12
|
+
static report(context, node, code, properties) {
|
|
13
|
+
if (!context.options?.onDiagnostic) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const sourceFile = context.activeSourceFile;
|
|
17
|
+
const sourcePosition = sourceFile
|
|
18
|
+
? sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
|
|
19
|
+
: undefined;
|
|
20
|
+
context.options.onDiagnostic({
|
|
21
|
+
code,
|
|
22
|
+
properties,
|
|
23
|
+
path: sourceFile
|
|
24
|
+
? `${sourceFile.fileName}:${(sourcePosition?.line ?? 0) + 1}:${(sourcePosition?.character ?? 0) + 1}`
|
|
25
|
+
: "unknown",
|
|
26
|
+
fileName: sourceFile?.fileName,
|
|
27
|
+
line: sourcePosition ? sourcePosition.line + 1 : undefined,
|
|
28
|
+
column: sourcePosition ? sourcePosition.character + 1 : undefined
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=diagnosticReporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticReporter.js","sourceRoot":"","sources":["../../../src/utils/diagnosticReporter.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAC9B;;;;;;OAMG;IACI,MAAM,CAAC,MAAM,CACnB,OAAmC,EACnC,IAAa,EACb,IAAY,EACZ,UAAuC;QAEvC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YACpC,OAAO;QACR,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC;QAC5C,MAAM,cAAc,GAAG,UAAU;YAChC,CAAC,CAAC,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YACrE,CAAC,CAAC,SAAS,CAAC;QAEb,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC;YAC5B,IAAI;YACJ,UAAU;YACV,IAAI,EAAE,UAAU;gBACf,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE;gBACrG,CAAC,CAAC,SAAS;YACZ,QAAQ,EAAE,UAAU,EAAE,QAAQ;YAC9B,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;YAC1D,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;SACjE,CAAC,CAAC;IACJ,CAAC;CACD","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type * as ts from \"typescript\";\nimport type { ITypeScriptToSchemaContext } from \"../models/ITypeScriptToSchemaContext.js\";\n\n/**\n * Emits non-fatal diagnostics during schema mapping.\n */\nexport class DiagnosticReporter {\n\t/**\n\t * Emit an optional non-fatal schema generation diagnostic.\n\t * @param context The generation context.\n\t * @param node The related AST node.\n\t * @param code The diagnostic code.\n\t * @param properties The values to substitute into the localised message.\n\t */\n\tpublic static report(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\tnode: ts.Node,\n\t\tcode: string,\n\t\tproperties?: { [key: string]: unknown }\n\t): void {\n\t\tif (!context.options?.onDiagnostic) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst sourceFile = context.activeSourceFile;\n\t\tconst sourcePosition = sourceFile\n\t\t\t? sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))\n\t\t\t: undefined;\n\n\t\tcontext.options.onDiagnostic({\n\t\t\tcode,\n\t\t\tproperties,\n\t\t\tpath: sourceFile\n\t\t\t\t? `${sourceFile.fileName}:${(sourcePosition?.line ?? 0) + 1}:${(sourcePosition?.character ?? 0) + 1}`\n\t\t\t\t: \"unknown\",\n\t\t\tfileName: sourceFile?.fileName,\n\t\t\tline: sourcePosition ? sourcePosition.line + 1 : undefined,\n\t\t\tcolumn: sourcePosition ? sourcePosition.character + 1 : undefined\n\t\t});\n\t}\n}\n"]}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import * as ts from "typescript";
|
|
4
|
+
/**
|
|
5
|
+
* Validates whether a type node is allowed for schema generation.
|
|
6
|
+
*/
|
|
7
|
+
export class DisallowedTypeGuard {
|
|
8
|
+
/**
|
|
9
|
+
* Type names that are not supported for JSON schema generation.
|
|
10
|
+
*/
|
|
11
|
+
static _DISALLOWED_TYPE_NAMES = [
|
|
12
|
+
"Date",
|
|
13
|
+
"RegExp",
|
|
14
|
+
"Function",
|
|
15
|
+
"Error",
|
|
16
|
+
"TypeError",
|
|
17
|
+
"RangeError",
|
|
18
|
+
"EvalError",
|
|
19
|
+
"ReferenceError",
|
|
20
|
+
"SyntaxError",
|
|
21
|
+
"URIError",
|
|
22
|
+
"AggregateError",
|
|
23
|
+
"bigint",
|
|
24
|
+
"BigInt",
|
|
25
|
+
"BigInt64Array",
|
|
26
|
+
"BigUint64Array",
|
|
27
|
+
"DataView",
|
|
28
|
+
"Promise",
|
|
29
|
+
"WeakRef",
|
|
30
|
+
"URL",
|
|
31
|
+
"URLSearchParams",
|
|
32
|
+
"Blob",
|
|
33
|
+
"File",
|
|
34
|
+
"FormData",
|
|
35
|
+
"ReadableStream",
|
|
36
|
+
"WeakMap",
|
|
37
|
+
"WeakSet",
|
|
38
|
+
"Symbol"
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a disallowed type name from a type node.
|
|
42
|
+
* @param typeNode The type node to inspect.
|
|
43
|
+
* @returns The disallowed type name, if found.
|
|
44
|
+
*/
|
|
45
|
+
static getDisallowedTypeName(typeNode) {
|
|
46
|
+
// (string | number) (parenthesised type, unwrap and recurse)
|
|
47
|
+
if (ts.isParenthesizedTypeNode(typeNode)) {
|
|
48
|
+
return DisallowedTypeGuard.getDisallowedTypeName(typeNode.type);
|
|
49
|
+
}
|
|
50
|
+
// readonly T[] or keyof T or unique symbol (type operator node)
|
|
51
|
+
if (ts.isTypeOperatorNode(typeNode)) {
|
|
52
|
+
if (typeNode.operator === ts.SyntaxKind.UniqueKeyword &&
|
|
53
|
+
typeNode.type.kind === ts.SyntaxKind.SymbolKeyword) {
|
|
54
|
+
return "symbol";
|
|
55
|
+
}
|
|
56
|
+
return DisallowedTypeGuard.getDisallowedTypeName(typeNode.type);
|
|
57
|
+
}
|
|
58
|
+
// bigint (bigint keyword type, always disallowed)
|
|
59
|
+
if (typeNode.kind === ts.SyntaxKind.BigIntKeyword) {
|
|
60
|
+
return "bigint";
|
|
61
|
+
}
|
|
62
|
+
// symbol (symbol keyword type, always disallowed)
|
|
63
|
+
if (typeNode.kind === ts.SyntaxKind.SymbolKeyword) {
|
|
64
|
+
return "symbol";
|
|
65
|
+
}
|
|
66
|
+
// 9007199254740991n (bigint literal wrapped in a literal type node)
|
|
67
|
+
if (ts.isLiteralTypeNode(typeNode) && ts.isBigIntLiteral(typeNode.literal)) {
|
|
68
|
+
return "bigint";
|
|
69
|
+
}
|
|
70
|
+
// Date or RegExp or Promise<T> (named type reference)
|
|
71
|
+
if (ts.isTypeReferenceNode(typeNode)) {
|
|
72
|
+
// Namespace.TypeName (qualified name) or TypeName (plain identifier)
|
|
73
|
+
const rawTypeName = ts.isIdentifier(typeNode.typeName)
|
|
74
|
+
? typeNode.typeName.text
|
|
75
|
+
: typeNode.typeName.right.text;
|
|
76
|
+
const normalizedTypeName = rawTypeName.toLowerCase();
|
|
77
|
+
const matchedTypeName = DisallowedTypeGuard._DISALLOWED_TYPE_NAMES.find(typeName => typeName.toLowerCase() === normalizedTypeName);
|
|
78
|
+
if (matchedTypeName) {
|
|
79
|
+
return matchedTypeName;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// string[] (array type, check the element type)
|
|
83
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
84
|
+
return DisallowedTypeGuard.getDisallowedTypeName(typeNode.elementType);
|
|
85
|
+
}
|
|
86
|
+
// [string, number] (tuple type, check each element)
|
|
87
|
+
if (ts.isTupleTypeNode(typeNode)) {
|
|
88
|
+
return DisallowedTypeGuard.getDisallowedTypeNameFromTypeNodes(typeNode.elements);
|
|
89
|
+
}
|
|
90
|
+
// string | number (union) or TypeA & TypeB (intersection)
|
|
91
|
+
if (ts.isUnionTypeNode(typeNode) || ts.isIntersectionTypeNode(typeNode)) {
|
|
92
|
+
return DisallowedTypeGuard.getDisallowedTypeNameFromTypeNodes(typeNode.types);
|
|
93
|
+
}
|
|
94
|
+
// T["key"] (indexed access type, check both object and index sides)
|
|
95
|
+
if (ts.isIndexedAccessTypeNode(typeNode)) {
|
|
96
|
+
return (DisallowedTypeGuard.getDisallowedTypeName(typeNode.objectType) ??
|
|
97
|
+
DisallowedTypeGuard.getDisallowedTypeName(typeNode.indexType));
|
|
98
|
+
}
|
|
99
|
+
// T extends U ? X : Y (conditional type, check all four branches)
|
|
100
|
+
if (ts.isConditionalTypeNode(typeNode)) {
|
|
101
|
+
return (DisallowedTypeGuard.getDisallowedTypeName(typeNode.checkType) ??
|
|
102
|
+
DisallowedTypeGuard.getDisallowedTypeName(typeNode.extendsType) ??
|
|
103
|
+
DisallowedTypeGuard.getDisallowedTypeName(typeNode.trueType) ??
|
|
104
|
+
DisallowedTypeGuard.getDisallowedTypeName(typeNode.falseType));
|
|
105
|
+
}
|
|
106
|
+
// { [K in keyof T]: T[K] } (mapped type, check constraint, name type, and value type)
|
|
107
|
+
if (ts.isMappedTypeNode(typeNode)) {
|
|
108
|
+
return ((typeNode.typeParameter.constraint
|
|
109
|
+
? DisallowedTypeGuard.getDisallowedTypeName(typeNode.typeParameter.constraint)
|
|
110
|
+
: undefined) ??
|
|
111
|
+
(typeNode.nameType
|
|
112
|
+
? DisallowedTypeGuard.getDisallowedTypeName(typeNode.nameType)
|
|
113
|
+
: undefined) ??
|
|
114
|
+
(typeNode.type ? DisallowedTypeGuard.getDisallowedTypeName(typeNode.type) : undefined));
|
|
115
|
+
}
|
|
116
|
+
// { prop: string } (inline object type literal, inspect each member)
|
|
117
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
118
|
+
for (const member of typeNode.members) {
|
|
119
|
+
if (
|
|
120
|
+
// prop: string or [key: string]: Value (property or index signature)
|
|
121
|
+
(ts.isPropertySignature(member) || ts.isIndexSignatureDeclaration(member)) &&
|
|
122
|
+
member.type) {
|
|
123
|
+
const memberDisallowedTypeName = DisallowedTypeGuard.getDisallowedTypeName(member.type);
|
|
124
|
+
if (memberDisallowedTypeName) {
|
|
125
|
+
return memberDisallowedTypeName;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolve the first disallowed type name from a collection of type nodes.
|
|
134
|
+
* @param typeNodes The nodes to inspect.
|
|
135
|
+
* @returns The disallowed type name, if found.
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
static getDisallowedTypeNameFromTypeNodes(typeNodes) {
|
|
139
|
+
if (!typeNodes) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
for (const typeNode of typeNodes) {
|
|
143
|
+
const disallowedTypeName = DisallowedTypeGuard.getDisallowedTypeName(typeNode);
|
|
144
|
+
if (disallowedTypeName) {
|
|
145
|
+
return disallowedTypeName;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=disallowedTypeGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disallowedTypeGuard.js","sourceRoot":"","sources":["../../../src/utils/disallowedTypeGuard.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport * as ts from \"typescript\";\n\n/**\n * Validates whether a type node is allowed for schema generation.\n */\nexport class DisallowedTypeGuard {\n\t/**\n\t * Type names that are not supported for JSON schema generation.\n\t */\n\tprivate static readonly _DISALLOWED_TYPE_NAMES: string[] = [\n\t\t\"Date\",\n\t\t\"RegExp\",\n\t\t\"Function\",\n\t\t\"Error\",\n\t\t\"TypeError\",\n\t\t\"RangeError\",\n\t\t\"EvalError\",\n\t\t\"ReferenceError\",\n\t\t\"SyntaxError\",\n\t\t\"URIError\",\n\t\t\"AggregateError\",\n\t\t\"bigint\",\n\t\t\"BigInt\",\n\t\t\"BigInt64Array\",\n\t\t\"BigUint64Array\",\n\t\t\"DataView\",\n\t\t\"Promise\",\n\t\t\"WeakRef\",\n\t\t\"URL\",\n\t\t\"URLSearchParams\",\n\t\t\"Blob\",\n\t\t\"File\",\n\t\t\"FormData\",\n\t\t\"ReadableStream\",\n\t\t\"WeakMap\",\n\t\t\"WeakSet\",\n\t\t\"Symbol\"\n\t];\n\n\t/**\n\t * Resolve a disallowed type name from a type node.\n\t * @param typeNode The type node to inspect.\n\t * @returns The disallowed type name, if found.\n\t */\n\tpublic static getDisallowedTypeName(typeNode: ts.TypeNode): string | undefined {\n\t\t// (string | number) (parenthesised type, unwrap and recurse)\n\t\tif (ts.isParenthesizedTypeNode(typeNode)) {\n\t\t\treturn DisallowedTypeGuard.getDisallowedTypeName(typeNode.type);\n\t\t}\n\n\t\t// readonly T[] or keyof T or unique symbol (type operator node)\n\t\tif (ts.isTypeOperatorNode(typeNode)) {\n\t\t\tif (\n\t\t\t\ttypeNode.operator === ts.SyntaxKind.UniqueKeyword &&\n\t\t\t\ttypeNode.type.kind === ts.SyntaxKind.SymbolKeyword\n\t\t\t) {\n\t\t\t\treturn \"symbol\";\n\t\t\t}\n\n\t\t\treturn DisallowedTypeGuard.getDisallowedTypeName(typeNode.type);\n\t\t}\n\n\t\t// bigint (bigint keyword type, always disallowed)\n\t\tif (typeNode.kind === ts.SyntaxKind.BigIntKeyword) {\n\t\t\treturn \"bigint\";\n\t\t}\n\n\t\t// symbol (symbol keyword type, always disallowed)\n\t\tif (typeNode.kind === ts.SyntaxKind.SymbolKeyword) {\n\t\t\treturn \"symbol\";\n\t\t}\n\n\t\t// 9007199254740991n (bigint literal wrapped in a literal type node)\n\t\tif (ts.isLiteralTypeNode(typeNode) && ts.isBigIntLiteral(typeNode.literal)) {\n\t\t\treturn \"bigint\";\n\t\t}\n\n\t\t// Date or RegExp or Promise<T> (named type reference)\n\t\tif (ts.isTypeReferenceNode(typeNode)) {\n\t\t\t// Namespace.TypeName (qualified name) or TypeName (plain identifier)\n\t\t\tconst rawTypeName = ts.isIdentifier(typeNode.typeName)\n\t\t\t\t? typeNode.typeName.text\n\t\t\t\t: typeNode.typeName.right.text;\n\t\t\tconst normalizedTypeName = rawTypeName.toLowerCase();\n\t\t\tconst matchedTypeName = DisallowedTypeGuard._DISALLOWED_TYPE_NAMES.find(\n\t\t\t\ttypeName => typeName.toLowerCase() === normalizedTypeName\n\t\t\t);\n\t\t\tif (matchedTypeName) {\n\t\t\t\treturn matchedTypeName;\n\t\t\t}\n\t\t}\n\n\t\t// string[] (array type, check the element type)\n\t\tif (ts.isArrayTypeNode(typeNode)) {\n\t\t\treturn DisallowedTypeGuard.getDisallowedTypeName(typeNode.elementType);\n\t\t}\n\n\t\t// [string, number] (tuple type, check each element)\n\t\tif (ts.isTupleTypeNode(typeNode)) {\n\t\t\treturn DisallowedTypeGuard.getDisallowedTypeNameFromTypeNodes(typeNode.elements);\n\t\t}\n\n\t\t// string | number (union) or TypeA & TypeB (intersection)\n\t\tif (ts.isUnionTypeNode(typeNode) || ts.isIntersectionTypeNode(typeNode)) {\n\t\t\treturn DisallowedTypeGuard.getDisallowedTypeNameFromTypeNodes(typeNode.types);\n\t\t}\n\n\t\t// T[\"key\"] (indexed access type, check both object and index sides)\n\t\tif (ts.isIndexedAccessTypeNode(typeNode)) {\n\t\t\treturn (\n\t\t\t\tDisallowedTypeGuard.getDisallowedTypeName(typeNode.objectType) ??\n\t\t\t\tDisallowedTypeGuard.getDisallowedTypeName(typeNode.indexType)\n\t\t\t);\n\t\t}\n\n\t\t// T extends U ? X : Y (conditional type, check all four branches)\n\t\tif (ts.isConditionalTypeNode(typeNode)) {\n\t\t\treturn (\n\t\t\t\tDisallowedTypeGuard.getDisallowedTypeName(typeNode.checkType) ??\n\t\t\t\tDisallowedTypeGuard.getDisallowedTypeName(typeNode.extendsType) ??\n\t\t\t\tDisallowedTypeGuard.getDisallowedTypeName(typeNode.trueType) ??\n\t\t\t\tDisallowedTypeGuard.getDisallowedTypeName(typeNode.falseType)\n\t\t\t);\n\t\t}\n\n\t\t// { [K in keyof T]: T[K] } (mapped type, check constraint, name type, and value type)\n\t\tif (ts.isMappedTypeNode(typeNode)) {\n\t\t\treturn (\n\t\t\t\t(typeNode.typeParameter.constraint\n\t\t\t\t\t? DisallowedTypeGuard.getDisallowedTypeName(typeNode.typeParameter.constraint)\n\t\t\t\t\t: undefined) ??\n\t\t\t\t(typeNode.nameType\n\t\t\t\t\t? DisallowedTypeGuard.getDisallowedTypeName(typeNode.nameType)\n\t\t\t\t\t: undefined) ??\n\t\t\t\t(typeNode.type ? DisallowedTypeGuard.getDisallowedTypeName(typeNode.type) : undefined)\n\t\t\t);\n\t\t}\n\n\t\t// { prop: string } (inline object type literal, inspect each member)\n\t\tif (ts.isTypeLiteralNode(typeNode)) {\n\t\t\tfor (const member of typeNode.members) {\n\t\t\t\tif (\n\t\t\t\t\t// prop: string or [key: string]: Value (property or index signature)\n\t\t\t\t\t(ts.isPropertySignature(member) || ts.isIndexSignatureDeclaration(member)) &&\n\t\t\t\t\tmember.type\n\t\t\t\t) {\n\t\t\t\t\tconst memberDisallowedTypeName = DisallowedTypeGuard.getDisallowedTypeName(member.type);\n\t\t\t\t\tif (memberDisallowedTypeName) {\n\t\t\t\t\t\treturn memberDisallowedTypeName;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Resolve the first disallowed type name from a collection of type nodes.\n\t * @param typeNodes The nodes to inspect.\n\t * @returns The disallowed type name, if found.\n\t * @internal\n\t */\n\tprivate static getDisallowedTypeNameFromTypeNodes(\n\t\ttypeNodes: readonly ts.TypeNode[] | ts.NodeArray<ts.TypeNode> | undefined\n\t): string | undefined {\n\t\tif (!typeNodes) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tfor (const typeNode of typeNodes) {\n\t\t\tconst disallowedTypeName = DisallowedTypeGuard.getDisallowedTypeName(typeNode);\n\t\t\tif (disallowedTypeName) {\n\t\t\t\treturn disallowedTypeName;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { Is } from "@twin.org/core";
|
|
4
|
+
import * as ts from "typescript";
|
|
5
|
+
import { Utility } from "./utility.js";
|
|
6
|
+
/**
|
|
7
|
+
* Utility methods for extracting enum values from TypeScript AST nodes.
|
|
8
|
+
*
|
|
9
|
+
* Handles three distinct patterns used to define enumerable sets of values:
|
|
10
|
+
* - Native enum declarations: `enum Color { Red = "red" }`
|
|
11
|
+
* - Const-object-with-matching-type patterns: `const Foo = { A: "a" } as const` paired with a type alias named identically to the const
|
|
12
|
+
*/
|
|
13
|
+
export class Enum {
|
|
14
|
+
/**
|
|
15
|
+
* Extract enum values from a const-and-type pair where a const object and a type alias share the
|
|
16
|
+
* same name. The const object is located first because its members may carry JSDoc descriptions
|
|
17
|
+
* that would be lost if the type alias were processed in isolation.
|
|
18
|
+
* @param name The name to search for.
|
|
19
|
+
* @param sourceFile The source file to search.
|
|
20
|
+
* @returns The extracted enum entries or undefined when the pattern is not present.
|
|
21
|
+
*/
|
|
22
|
+
static extractEnumValuesFromConstAndType(name, sourceFile) {
|
|
23
|
+
// const Foo = { A: "a", B: "b" } as const
|
|
24
|
+
const varStmt = sourceFile.statements.find(stmt => {
|
|
25
|
+
// const Foo = ...
|
|
26
|
+
if (!ts.isVariableStatement(stmt)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const decl = stmt.declarationList.declarations[0];
|
|
30
|
+
// Foo (the identifier that names the const)
|
|
31
|
+
return decl?.name && ts.isIdentifier(decl.name) && decl.name.text === name;
|
|
32
|
+
});
|
|
33
|
+
if (!varStmt) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const decl = varStmt.declarationList.declarations[0];
|
|
37
|
+
if (!decl?.initializer) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
let objLiteral;
|
|
41
|
+
// { A: "a", B: "b" }
|
|
42
|
+
if (ts.isObjectLiteralExpression(decl.initializer)) {
|
|
43
|
+
objLiteral = decl.initializer;
|
|
44
|
+
}
|
|
45
|
+
else if (
|
|
46
|
+
// { A: "a", B: "b" } as const
|
|
47
|
+
ts.isAsExpression(decl.initializer) &&
|
|
48
|
+
ts.isObjectLiteralExpression(decl.initializer.expression)) {
|
|
49
|
+
objLiteral = decl.initializer.expression;
|
|
50
|
+
}
|
|
51
|
+
if (!objLiteral) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const entries = Enum.extractEnumEntriesFromConstObject(objLiteral);
|
|
55
|
+
return entries.length > 0 ? entries : undefined;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Extract enum entries from a const object literal.
|
|
59
|
+
* Only properties whose initializer is a string or numeric literal are included.
|
|
60
|
+
* @param objLiteral The object literal expression.
|
|
61
|
+
* @returns The extracted enum entries.
|
|
62
|
+
*/
|
|
63
|
+
static extractEnumEntriesFromConstObject(objLiteral) {
|
|
64
|
+
return (objLiteral.properties
|
|
65
|
+
// A: "a" (property assignment with an initializer)
|
|
66
|
+
.filter(prop => ts.isPropertyAssignment(prop) && prop.initializer)
|
|
67
|
+
.map(prop => {
|
|
68
|
+
const assignment = prop;
|
|
69
|
+
const initializer = assignment.initializer;
|
|
70
|
+
const description = Utility.getNodeJsDocDescription(assignment);
|
|
71
|
+
// "a" (string literal initializer)
|
|
72
|
+
if (ts.isStringLiteral(initializer)) {
|
|
73
|
+
return {
|
|
74
|
+
value: initializer.text,
|
|
75
|
+
description: description ?? undefined
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// 42 (numeric literal initializer)
|
|
79
|
+
if (ts.isNumericLiteral(initializer)) {
|
|
80
|
+
return {
|
|
81
|
+
value: Number(initializer.text),
|
|
82
|
+
description: description ?? undefined
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
})
|
|
87
|
+
.filter((entry) => entry !== null));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Extract enum values from a native TypeScript enum declaration.
|
|
91
|
+
* Numeric members without an explicit initializer are auto-incremented from the previous value.
|
|
92
|
+
* @param declaration The enum declaration.
|
|
93
|
+
* @returns The extracted enum entries.
|
|
94
|
+
*/
|
|
95
|
+
static extractEnumValuesFromEnumDeclaration(declaration) {
|
|
96
|
+
const entries = [];
|
|
97
|
+
let nextNumericValue = 0;
|
|
98
|
+
// enum Color { Red = "red", Count = 1 }
|
|
99
|
+
for (const member of declaration.members) {
|
|
100
|
+
const resolvedValue = Enum.resolveEnumMemberValue(member, nextNumericValue);
|
|
101
|
+
if (resolvedValue !== undefined) {
|
|
102
|
+
entries.push({
|
|
103
|
+
value: resolvedValue,
|
|
104
|
+
description: Utility.getNodeJsDocDescription(member)
|
|
105
|
+
});
|
|
106
|
+
if (Is.number(resolvedValue)) {
|
|
107
|
+
nextNumericValue = resolvedValue + 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return entries;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the literal value of an enum member when possible.
|
|
115
|
+
* Members without an initializer inherit the current auto-increment counter.
|
|
116
|
+
* Negative numeric members expressed as unary minus (e.g. -1) are also resolved.
|
|
117
|
+
* @param member The enum member node.
|
|
118
|
+
* @param defaultNumericValue The default numeric value for auto-incremented members.
|
|
119
|
+
* @returns The resolved enum value, or undefined when the initializer cannot be evaluated statically.
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
122
|
+
static resolveEnumMemberValue(member, defaultNumericValue) {
|
|
123
|
+
// Red (member with no explicit initializer inherits the auto-increment counter)
|
|
124
|
+
if (!member.initializer) {
|
|
125
|
+
return defaultNumericValue;
|
|
126
|
+
}
|
|
127
|
+
// Red = "red" (string literal enum member)
|
|
128
|
+
if (ts.isStringLiteral(member.initializer)) {
|
|
129
|
+
return member.initializer.text;
|
|
130
|
+
}
|
|
131
|
+
// Count = 42 (positive numeric literal enum member)
|
|
132
|
+
if (ts.isNumericLiteral(member.initializer)) {
|
|
133
|
+
return Number(member.initializer.text);
|
|
134
|
+
}
|
|
135
|
+
if (
|
|
136
|
+
// Negative = -1 (prefix unary minus applied to a numeric literal)
|
|
137
|
+
ts.isPrefixUnaryExpression(member.initializer) &&
|
|
138
|
+
member.initializer.operator === ts.SyntaxKind.MinusToken &&
|
|
139
|
+
ts.isNumericLiteral(member.initializer.operand)) {
|
|
140
|
+
return -Number(member.initializer.operand.text);
|
|
141
|
+
}
|
|
142
|
+
if (
|
|
143
|
+
// Explicit = +1 (prefix unary plus applied to a numeric literal)
|
|
144
|
+
ts.isPrefixUnaryExpression(member.initializer) &&
|
|
145
|
+
member.initializer.operator === ts.SyntaxKind.PlusToken &&
|
|
146
|
+
ts.isNumericLiteral(member.initializer.operand)) {
|
|
147
|
+
return Number(member.initializer.operand.text);
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../src/utils/enum.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,OAAO,IAAI;IAChB;;;;;;;OAOG;IACI,MAAM,CAAC,iCAAiC,CAC9C,IAAY,EACZ,UAAyB;QAEzB,0CAA0C;QAC1C,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACjD,kBAAkB;YAClB,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,OAAO,KAAK,CAAC;YACd,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAClD,4CAA4C;YAC5C,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAC5E,CAAC,CAAqC,CAAC;QAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;YACxB,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,IAAI,UAAkD,CAAC;QAEvD,qBAAqB;QACrB,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACpD,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC;QAC/B,CAAC;aAAM;QACN,8BAA8B;QAC9B,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;YACnC,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EACxD,CAAC;YACF,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;QAC1C,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,iCAAiC,CAAC,UAAU,CAAC,CAAC;QACnE,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,iCAAiC,CAC9C,UAAsC;QAEtC,OAAO,CACN,UAAU,CAAC,UAAU;YACpB,oDAAoD;aACnD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC;aACjE,GAAG,CAA0D,IAAI,CAAC,EAAE;YACpE,MAAM,UAAU,GAAG,IAA6B,CAAC;YACjD,MAAM,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC;YAC3C,MAAM,WAAW,GAAG,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;YAEhE,oCAAoC;YACpC,IAAI,EAAE,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,OAAO;oBACN,KAAK,EAAE,WAAW,CAAC,IAAuB;oBAC1C,WAAW,EAAE,WAAW,IAAI,SAAS;iBACrC,CAAC;YACH,CAAC;YAED,oCAAoC;YACpC,IAAI,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAAE,CAAC;gBACtC,OAAO;oBACN,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC;oBAC/B,WAAW,EAAE,WAAW,IAAI,SAAS;iBACrC,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACb,CAAC,CAAC;aACD,MAAM,CACN,CAAC,KAAK,EAA6D,EAAE,CAAC,KAAK,KAAK,IAAI,CACpF,CACF,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,oCAAoC,CACjD,WAA+B;QAE/B,MAAM,OAAO,GAAuD,EAAE,CAAC;QACvE,IAAI,gBAAgB,GAAG,CAAC,CAAC;QAEzB,wCAAwC;QACxC,KAAK,MAAM,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;YAC5E,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,aAAa;oBACpB,WAAW,EAAE,OAAO,CAAC,uBAAuB,CAAC,MAAM,CAAC;iBACpD,CAAC,CAAC;gBAEH,IAAI,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;oBAC9B,gBAAgB,GAAG,aAAa,GAAG,CAAC,CAAC;gBACtC,CAAC;YACF,CAAC;QACF,CAAC;QAED,OAAO,OAAO,CAAC;IAChB,CAAC;IAED;;;;;;;;OAQG;IACK,MAAM,CAAC,sBAAsB,CACpC,MAAqB,EACrB,mBAA2B;QAE3B,iFAAiF;QACjF,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACzB,OAAO,mBAAmB,CAAC;QAC5B,CAAC;QAED,4CAA4C;QAC5C,IAAI,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5C,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC;QAChC,CAAC;QAED,qDAAqD;QACrD,IAAI,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7C,OAAO,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAED;QACC,mEAAmE;QACnE,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,WAAW,CAAC;YAC9C,MAAM,CAAC,WAAW,CAAC,QAAQ,KAAK,EAAE,CAAC,UAAU,CAAC,UAAU;YACxD,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAC9C,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;QAED;QACC,kEAAkE;QAClE,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,WAAW,CAAC;YAC9C,MAAM,CAAC,WAAW,CAAC,QAAQ,KAAK,EAAE,CAAC,UAAU,CAAC,SAAS;YACvD,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAC9C,CAAC;YACF,OAAO,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,SAAS,CAAC;IAClB,CAAC;CACD","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"@twin.org/core\";\nimport * as ts from \"typescript\";\nimport { Utility } from \"./utility.js\";\n\n/**\n * Utility methods for extracting enum values from TypeScript AST nodes.\n *\n * Handles three distinct patterns used to define enumerable sets of values:\n * - Native enum declarations: `enum Color { Red = \"red\" }`\n * - Const-object-with-matching-type patterns: `const Foo = { A: \"a\" } as const` paired with a type alias named identically to the const\n */\nexport class Enum {\n\t/**\n\t * Extract enum values from a const-and-type pair where a const object and a type alias share the\n\t * same name. The const object is located first because its members may carry JSDoc descriptions\n\t * that would be lost if the type alias were processed in isolation.\n\t * @param name The name to search for.\n\t * @param sourceFile The source file to search.\n\t * @returns The extracted enum entries or undefined when the pattern is not present.\n\t */\n\tpublic static extractEnumValuesFromConstAndType(\n\t\tname: string,\n\t\tsourceFile: ts.SourceFile\n\t): { value: string | number; description?: string }[] | undefined {\n\t\t// const Foo = { A: \"a\", B: \"b\" } as const\n\t\tconst varStmt = sourceFile.statements.find(stmt => {\n\t\t\t// const Foo = ...\n\t\t\tif (!ts.isVariableStatement(stmt)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst decl = stmt.declarationList.declarations[0];\n\t\t\t// Foo (the identifier that names the const)\n\t\t\treturn decl?.name && ts.isIdentifier(decl.name) && decl.name.text === name;\n\t\t}) as ts.VariableStatement | undefined;\n\n\t\tif (!varStmt) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst decl = varStmt.declarationList.declarations[0];\n\t\tif (!decl?.initializer) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet objLiteral: ts.ObjectLiteralExpression | undefined;\n\n\t\t// { A: \"a\", B: \"b\" }\n\t\tif (ts.isObjectLiteralExpression(decl.initializer)) {\n\t\t\tobjLiteral = decl.initializer;\n\t\t} else if (\n\t\t\t// { A: \"a\", B: \"b\" } as const\n\t\t\tts.isAsExpression(decl.initializer) &&\n\t\t\tts.isObjectLiteralExpression(decl.initializer.expression)\n\t\t) {\n\t\t\tobjLiteral = decl.initializer.expression;\n\t\t}\n\n\t\tif (!objLiteral) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst entries = Enum.extractEnumEntriesFromConstObject(objLiteral);\n\t\treturn entries.length > 0 ? entries : undefined;\n\t}\n\n\t/**\n\t * Extract enum entries from a const object literal.\n\t * Only properties whose initializer is a string or numeric literal are included.\n\t * @param objLiteral The object literal expression.\n\t * @returns The extracted enum entries.\n\t */\n\tpublic static extractEnumEntriesFromConstObject(\n\t\tobjLiteral: ts.ObjectLiteralExpression\n\t): { value: string | number; description?: string }[] {\n\t\treturn (\n\t\t\tobjLiteral.properties\n\t\t\t\t// A: \"a\" (property assignment with an initializer)\n\t\t\t\t.filter(prop => ts.isPropertyAssignment(prop) && prop.initializer)\n\t\t\t\t.map<{ value: string | number; description?: string } | null>(prop => {\n\t\t\t\t\tconst assignment = prop as ts.PropertyAssignment;\n\t\t\t\t\tconst initializer = assignment.initializer;\n\t\t\t\t\tconst description = Utility.getNodeJsDocDescription(assignment);\n\n\t\t\t\t\t// \"a\" (string literal initializer)\n\t\t\t\t\tif (ts.isStringLiteral(initializer)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tvalue: initializer.text as string | number,\n\t\t\t\t\t\t\tdescription: description ?? undefined\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// 42 (numeric literal initializer)\n\t\t\t\t\tif (ts.isNumericLiteral(initializer)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tvalue: Number(initializer.text),\n\t\t\t\t\t\t\tdescription: description ?? undefined\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn null;\n\t\t\t\t})\n\t\t\t\t.filter(\n\t\t\t\t\t(entry): entry is { value: string | number; description?: string } => entry !== null\n\t\t\t\t)\n\t\t);\n\t}\n\n\t/**\n\t * Extract enum values from a native TypeScript enum declaration.\n\t * Numeric members without an explicit initializer are auto-incremented from the previous value.\n\t * @param declaration The enum declaration.\n\t * @returns The extracted enum entries.\n\t */\n\tpublic static extractEnumValuesFromEnumDeclaration(\n\t\tdeclaration: ts.EnumDeclaration\n\t): { value: string | number; description?: string }[] {\n\t\tconst entries: { value: string | number; description?: string }[] = [];\n\t\tlet nextNumericValue = 0;\n\n\t\t// enum Color { Red = \"red\", Count = 1 }\n\t\tfor (const member of declaration.members) {\n\t\t\tconst resolvedValue = Enum.resolveEnumMemberValue(member, nextNumericValue);\n\t\t\tif (resolvedValue !== undefined) {\n\t\t\t\tentries.push({\n\t\t\t\t\tvalue: resolvedValue,\n\t\t\t\t\tdescription: Utility.getNodeJsDocDescription(member)\n\t\t\t\t});\n\n\t\t\t\tif (Is.number(resolvedValue)) {\n\t\t\t\t\tnextNumericValue = resolvedValue + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/**\n\t * Resolve the literal value of an enum member when possible.\n\t * Members without an initializer inherit the current auto-increment counter.\n\t * Negative numeric members expressed as unary minus (e.g. -1) are also resolved.\n\t * @param member The enum member node.\n\t * @param defaultNumericValue The default numeric value for auto-incremented members.\n\t * @returns The resolved enum value, or undefined when the initializer cannot be evaluated statically.\n\t * @internal\n\t */\n\tprivate static resolveEnumMemberValue(\n\t\tmember: ts.EnumMember,\n\t\tdefaultNumericValue: number\n\t): string | number | undefined {\n\t\t// Red (member with no explicit initializer inherits the auto-increment counter)\n\t\tif (!member.initializer) {\n\t\t\treturn defaultNumericValue;\n\t\t}\n\n\t\t// Red = \"red\" (string literal enum member)\n\t\tif (ts.isStringLiteral(member.initializer)) {\n\t\t\treturn member.initializer.text;\n\t\t}\n\n\t\t// Count = 42 (positive numeric literal enum member)\n\t\tif (ts.isNumericLiteral(member.initializer)) {\n\t\t\treturn Number(member.initializer.text);\n\t\t}\n\n\t\tif (\n\t\t\t// Negative = -1 (prefix unary minus applied to a numeric literal)\n\t\t\tts.isPrefixUnaryExpression(member.initializer) &&\n\t\t\tmember.initializer.operator === ts.SyntaxKind.MinusToken &&\n\t\t\tts.isNumericLiteral(member.initializer.operand)\n\t\t) {\n\t\t\treturn -Number(member.initializer.operand.text);\n\t\t}\n\n\t\tif (\n\t\t\t// Explicit = +1 (prefix unary plus applied to a numeric literal)\n\t\t\tts.isPrefixUnaryExpression(member.initializer) &&\n\t\t\tmember.initializer.operator === ts.SyntaxKind.PlusToken &&\n\t\t\tts.isNumericLiteral(member.initializer.operand)\n\t\t) {\n\t\t\treturn Number(member.initializer.operand.text);\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n"]}
|