@twin.org/tools-core 0.0.3-next.17 → 0.0.3-next.18

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 CHANGED
@@ -18,6 +18,6 @@ export * from "./utils/regEx.js";
18
18
  export * from "./utils/resolver.js";
19
19
  export * from "./utils/templateLiteralPatternBuilder.js";
20
20
  export * from "./utils/typeScriptToSchema.js";
21
- export * from "./utils/utility.js";
21
+ export * from "./utils/jsDoc.js";
22
22
  export * from "./utils/utilityTypeSchemaMapper.js";
23
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
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"]}
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,kBAAkB,CAAC;AACjC,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/jsDoc.js\";\nexport * from \"./utils/utilityTypeSchemaMapper.js\";\n"]}
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { Is } from "@twin.org/core";
4
4
  import * as ts from "typescript";
5
- import { Utility } from "./utility.js";
5
+ import { JsDoc } from "./jsDoc.js";
6
6
  /**
7
7
  * Utility methods for extracting enum values from TypeScript AST nodes.
8
8
  *
@@ -67,7 +67,7 @@ export class Enum {
67
67
  .map(prop => {
68
68
  const assignment = prop;
69
69
  const initializer = assignment.initializer;
70
- const description = Utility.getNodeJsDocDescription(assignment);
70
+ const description = JsDoc.getNodeJsDocDescription(assignment);
71
71
  // "a" (string literal initializer)
72
72
  if (ts.isStringLiteral(initializer)) {
73
73
  return {
@@ -101,7 +101,7 @@ export class Enum {
101
101
  if (resolvedValue !== undefined) {
102
102
  entries.push({
103
103
  value: resolvedValue,
104
- description: Utility.getNodeJsDocDescription(member)
104
+ description: JsDoc.getNodeJsDocDescription(member)
105
105
  });
106
106
  if (Is.number(resolvedValue)) {
107
107
  nextNumericValue = resolvedValue + 1;
@@ -1 +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"]}
1
+ {"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../src/utils/enum.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"@twin.org/core\";\nimport * as ts from \"typescript\";\nimport { JsDoc } from \"./jsDoc.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 = JsDoc.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: JsDoc.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"]}
@@ -3,40 +3,9 @@
3
3
  import { Is } from "@twin.org/core";
4
4
  import * as ts from "typescript";
5
5
  /**
6
- * General-purpose utility methods for working with TypeScript AST nodes.
7
- *
8
- * Enum-related helpers live in TypeScriptEnum.
9
- * Reference-mapping regex helpers live in TypeScriptRegEx.
6
+ * General-purpose utility methods for working with JsDoc.
10
7
  */
11
- export class Utility {
12
- /**
13
- * Determine whether an input value is a valid TypeScript type identifier.
14
- * An identifier must start with a letter, underscore, or dollar sign and contain only
15
- * alphanumerics, underscores, or dollar signs thereafter.
16
- * @param value The value to inspect.
17
- * @returns True if the value looks like a type name.
18
- */
19
- static isTypeNameInput(value) {
20
- return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(value);
21
- }
22
- /**
23
- * Extract the inner type node from a named or rest tuple element.
24
- * Named tuple members and rest elements both wrap an inner type node; this unwraps them.
25
- * Plain type nodes are returned as-is.
26
- * @param element The tuple element.
27
- * @returns The inner type node.
28
- */
29
- static extractTupleElementType(element) {
30
- // label: string (named tuple member, e.g. [label: string, count: number])
31
- if (ts.isNamedTupleMember(element)) {
32
- return element.type;
33
- }
34
- // ...string[] (rest element in a tuple, e.g. [first: string, ...rest: string[]])
35
- if (ts.isRestTypeNode(element)) {
36
- return element.type;
37
- }
38
- return element;
39
- }
8
+ export class JsDoc {
40
9
  /**
41
10
  * Extract the JSDoc description comment for an AST node.
42
11
  * Only top-level JSDoc block comments are considered; inline tags are ignored.
@@ -86,7 +55,7 @@ export class Utility {
86
55
  const output = {};
87
56
  for (const jsDocTag of ts.getJSDocTags(node)) {
88
57
  if (jsDocTag.tagName.text === tagName) {
89
- const commentText = Utility.getJSDocTagCommentText(jsDocTag);
58
+ const commentText = JsDoc.getJSDocTagCommentText(jsDocTag);
90
59
  if (commentText) {
91
60
  const separatorIndex = commentText.indexOf(":");
92
61
  if (separatorIndex > 0) {
@@ -101,6 +70,23 @@ export class Utility {
101
70
  }
102
71
  return output;
103
72
  }
73
+ /**
74
+ * Read the plain comment text for the first matching JSDoc tag on a node.
75
+ * @param node The node to inspect.
76
+ * @param tagName The tag name to filter by (e.g., 'default').
77
+ * @returns The trimmed comment text, or undefined when absent.
78
+ */
79
+ static getNodeTagComment(node, tagName) {
80
+ for (const jsDocTag of ts.getJSDocTags(node)) {
81
+ if (jsDocTag.tagName.text === tagName) {
82
+ const commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();
83
+ if (commentText) {
84
+ return commentText;
85
+ }
86
+ }
87
+ }
88
+ return undefined;
89
+ }
104
90
  /**
105
91
  * Parse a custom JSDoc tag value into JSON-compatible data.
106
92
  * Values that begin with a JSON token character ({, [, ", true, false, null) or look like a
@@ -131,4 +117,4 @@ export class Utility {
131
117
  }
132
118
  }
133
119
  }
134
- //# sourceMappingURL=utility.js.map
120
+ //# sourceMappingURL=jsDoc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsDoc.js","sourceRoot":"","sources":["../../../src/utils/jsDoc.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"@twin.org/core\";\nimport * as ts from \"typescript\";\n\n/**\n * General-purpose utility methods for working with JsDoc.\n */\nexport class JsDoc {\n\t/**\n\t * Extract the JSDoc description comment for an AST node.\n\t * Only top-level JSDoc block comments are considered; inline tags are ignored.\n\t * @param node The node to inspect.\n\t * @returns The trimmed description text, or undefined when absent.\n\t */\n\tpublic static getNodeJsDocDescription(node: ts.Node): string | undefined {\n\t\t// /** Description text */ (JSDoc block attached to a declaration)\n\t\tconst jsDocNodes = ts\n\t\t\t.getJSDocCommentsAndTags(node)\n\t\t\t.filter(commentOrTag => ts.isJSDoc(commentOrTag));\n\t\tconst description = jsDocNodes\n\t\t\t.map(jsDocNode => ts.getTextOfJSDocComment(jsDocNode.comment)?.trim())\n\t\t\t.find((value): value is string => Boolean(value));\n\n\t\tif (description) {\n\t\t\treturn description;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Convert a JSDoc tag's comment portion to a plain text string.\n\t * JSDoc tag comments may be plain strings or arrays of link/text parts.\n\t * @param jsDocTag The JSDoc tag.\n\t * @returns The comment text, or undefined when absent.\n\t */\n\tpublic static getJSDocTagCommentText(jsDocTag: ts.JSDocTag): string | undefined {\n\t\tif (Is.string(jsDocTag.comment)) {\n\t\t\treturn jsDocTag.comment;\n\t\t}\n\n\t\tif (Array.isArray(jsDocTag.comment)) {\n\t\t\treturn jsDocTag.comment\n\t\t\t\t.map(part => part.text)\n\t\t\t\t.join(\"\")\n\t\t\t\t.trim();\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Read all custom JSDoc tags matching a tag name from a node and convert them to key/value pairs.\n\t * Each matching tag's comment must be in the form \"key: value\"; entries that do not follow this\n\t * convention are silently skipped.\n\t * @param node The node to inspect.\n\t * @param tagName The tag name to filter by (e.g., 'json-schema').\n\t * @returns The extracted key/value pairs.\n\t */\n\tpublic static getNodeTags(node: ts.Node, tagName: string): { [id: string]: string } {\n\t\tconst output: { [id: string]: string } = {};\n\t\tfor (const jsDocTag of ts.getJSDocTags(node)) {\n\t\t\tif (jsDocTag.tagName.text === tagName) {\n\t\t\t\tconst commentText = JsDoc.getJSDocTagCommentText(jsDocTag);\n\t\t\t\tif (commentText) {\n\t\t\t\t\tconst separatorIndex = commentText.indexOf(\":\");\n\t\t\t\t\tif (separatorIndex > 0) {\n\t\t\t\t\t\tconst key = commentText.slice(0, separatorIndex).trim();\n\t\t\t\t\t\tconst value = commentText.slice(separatorIndex + 1).trim();\n\t\t\t\t\t\tif (key.length > 0) {\n\t\t\t\t\t\t\toutput[key] = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn output;\n\t}\n\n\t/**\n\t * Read the plain comment text for the first matching JSDoc tag on a node.\n\t * @param node The node to inspect.\n\t * @param tagName The tag name to filter by (e.g., 'default').\n\t * @returns The trimmed comment text, or undefined when absent.\n\t */\n\tpublic static getNodeTagComment(node: ts.Node, tagName: string): string | undefined {\n\t\tfor (const jsDocTag of ts.getJSDocTags(node)) {\n\t\t\tif (jsDocTag.tagName.text === tagName) {\n\t\t\t\tconst commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();\n\t\t\t\tif (commentText) {\n\t\t\t\t\treturn commentText;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Parse a custom JSDoc tag value into JSON-compatible data.\n\t * Values that begin with a JSON token character ({, [, \", true, false, null) or look like a\n\t * number are parsed with JSON.parse. All other values are returned as plain strings.\n\t * @param value The raw value text.\n\t * @returns The parsed value.\n\t */\n\tpublic static parseTagValue(value: string): unknown {\n\t\tconst trimmedValue = value.trim();\n\t\tif (trimmedValue.length === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst startsWithJsonToken =\n\t\t\ttrimmedValue.startsWith(\"{\") ||\n\t\t\ttrimmedValue.startsWith(\"[\") ||\n\t\t\ttrimmedValue.startsWith('\"') ||\n\t\t\ttrimmedValue === \"true\" ||\n\t\t\ttrimmedValue === \"false\" ||\n\t\t\ttrimmedValue === \"null\" ||\n\t\t\t/^-?\\d+(\\.\\d+)?$/u.test(trimmedValue);\n\n\t\tif (!startsWithJsonToken) {\n\t\t\treturn trimmedValue;\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(trimmedValue);\n\t\t} catch {\n\t\t\treturn trimmedValue;\n\t\t}\n\t}\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  // Copyright 2026 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- import { GeneralError, Is, ObjectHelper, JsonHelper, StringHelper } from "@twin.org/core";
3
+ import { GeneralError, Is, JsonHelper, ObjectHelper, StringHelper } from "@twin.org/core";
4
4
  import { JsonSchemaTagNames } from "@twin.org/tools-models";
5
5
  import * as ts from "typescript";
6
6
  import { Constants } from "./constants.js";
@@ -11,12 +11,12 @@ import { FileUtils } from "./fileUtils.js";
11
11
  import { ImportTypeQuerySchemaResolver } from "./importTypeQuerySchemaResolver.js";
12
12
  import { IndexSignaturePatternResolver } from "./indexSignaturePatternResolver.js";
13
13
  import { IntersectionSchemaMerger } from "./intersectionSchemaMerger.js";
14
+ import { JsDoc } from "./jsDoc.js";
14
15
  import { MappedTypeSchemaResolver } from "./mappedTypeSchemaResolver.js";
15
16
  import { ObjectTransformer } from "./objectTransformer.js";
16
17
  import { RegEx } from "./regEx.js";
17
18
  import { Resolver } from "./resolver.js";
18
19
  import { TemplateLiteralPatternBuilder } from "./templateLiteralPatternBuilder.js";
19
- import { Utility } from "./utility.js";
20
20
  import { UtilityTypeSchemaMapper } from "./utilityTypeSchemaMapper.js";
21
21
  /**
22
22
  * Builder for composing JSON schema fragments from TypeScript AST nodes.
@@ -248,7 +248,11 @@ export class JsonSchemaBuilder {
248
248
  * @throws GeneralError Thrown when a tag key is not supported by IJsonSchema.
249
249
  */
250
250
  static applyJsonSchemaTags(schema, node) {
251
- const tags = Utility.getNodeTags(node, "json-schema");
251
+ const defaultTagComment = JsDoc.getNodeTagComment(node, "default");
252
+ if (defaultTagComment !== undefined && schema.default === undefined) {
253
+ schema.default = JsDoc.parseTagValue(defaultTagComment);
254
+ }
255
+ const tags = JsDoc.getNodeTags(node, "json-schema");
252
256
  for (const [rawKey, rawValue] of Object.entries(tags)) {
253
257
  const schemaKey = JsonSchemaBuilder.mapJsonSchemaTagKey(rawKey);
254
258
  if (!JsonSchemaBuilder.isAllowedJsonSchemaTagKey(schemaKey)) {
@@ -257,7 +261,7 @@ export class JsonSchemaBuilder {
257
261
  schemaKey
258
262
  });
259
263
  }
260
- const parsedValue = Utility.parseTagValue(rawValue);
264
+ const parsedValue = JsDoc.parseTagValue(rawValue);
261
265
  ObjectHelper.propertySet(schema, schemaKey, parsedValue);
262
266
  }
263
267
  }
@@ -282,7 +286,7 @@ export class JsonSchemaBuilder {
282
286
  $id: `${namespace}${title}`,
283
287
  title
284
288
  };
285
- const description = Utility.getNodeJsDocDescription(statement);
289
+ const description = JsDoc.getNodeJsDocDescription(statement);
286
290
  if (description) {
287
291
  schema.description = description;
288
292
  }
@@ -362,7 +366,7 @@ export class JsonSchemaBuilder {
362
366
  ? JsonSchemaBuilder.mapMemberTypeToSchema(context, member.type)
363
367
  : undefined;
364
368
  if (memberName && memberTypeSchema) {
365
- const memberDescription = Utility.getNodeJsDocDescription(member);
369
+ const memberDescription = JsDoc.getNodeJsDocDescription(member);
366
370
  if (memberDescription) {
367
371
  memberTypeSchema.description = memberDescription;
368
372
  }
@@ -1922,7 +1926,7 @@ export class JsonSchemaBuilder {
1922
1926
  let restSchema;
1923
1927
  let restIndex = -1;
1924
1928
  for (const [index, element] of tupleTypeNode.elements.entries()) {
1925
- const tupleElementType = Utility.extractTupleElementType(element);
1929
+ const tupleElementType = JsonSchemaBuilder.extractTupleElementType(element);
1926
1930
  if (!tupleElementType) {
1927
1931
  return undefined;
1928
1932
  }
@@ -3265,5 +3269,23 @@ export class JsonSchemaBuilder {
3265
3269
  }
3266
3270
  return key;
3267
3271
  }
3272
+ /**
3273
+ * Extract the inner type node from a named or rest tuple element.
3274
+ * Named tuple members and rest elements both wrap an inner type node; this unwraps them.
3275
+ * Plain type nodes are returned as-is.
3276
+ * @param element The tuple element.
3277
+ * @returns The inner type node.
3278
+ */
3279
+ static extractTupleElementType(element) {
3280
+ // label: string (named tuple member, e.g. [label: string, count: number])
3281
+ if (ts.isNamedTupleMember(element)) {
3282
+ return element.type;
3283
+ }
3284
+ // ...string[] (rest element in a tuple, e.g. [first: string, ...rest: string[]])
3285
+ if (ts.isRestTypeNode(element)) {
3286
+ return element.type;
3287
+ }
3288
+ return element;
3289
+ }
3268
3290
  }
3269
3291
  //# sourceMappingURL=jsonSchemaBuilder.js.map