@twin.org/tools-core 0.0.3-next.28 → 0.0.3-next.29

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.
@@ -87,6 +87,24 @@ export class JsDoc {
87
87
  }
88
88
  return undefined;
89
89
  }
90
+ /**
91
+ * Read the plain comment text for all matching JSDoc tags on a node.
92
+ * @param node The node to inspect.
93
+ * @param tagName The tag name to filter by (e.g., 'example').
94
+ * @returns An array of trimmed comment texts for all matching tags.
95
+ */
96
+ static getNodeTagComments(node, tagName) {
97
+ const results = [];
98
+ for (const jsDocTag of ts.getJSDocTags(node)) {
99
+ if (jsDocTag.tagName.text === tagName) {
100
+ const commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();
101
+ if (commentText) {
102
+ results.push(commentText);
103
+ }
104
+ }
105
+ }
106
+ return results;
107
+ }
90
108
  /**
91
109
  * Parse a custom JSDoc tag value into JSON-compatible data.
92
110
  * Values that begin with a JSON token character (open brace, open bracket, double quote, true, false, null) or look like a
@@ -1 +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 (Is.array(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 (open brace, open bracket, double quote, 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
+ {"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 (Is.array(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 * Read the plain comment text for all matching JSDoc tags on a node.\n\t * @param node The node to inspect.\n\t * @param tagName The tag name to filter by (e.g., 'example').\n\t * @returns An array of trimmed comment texts for all matching tags.\n\t */\n\tpublic static getNodeTagComments(node: ts.Node, tagName: string): string[] {\n\t\tconst results: 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)?.trim();\n\t\t\t\tif (commentText) {\n\t\t\t\t\tresults.push(commentText);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn results;\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 (open brace, open bracket, double quote, 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"]}
@@ -276,6 +276,13 @@ export class JsonSchemaBuilder {
276
276
  ObjectHelper.propertySet(schema, schemaKey, parsedValue);
277
277
  }
278
278
  }
279
+ const exampleTagComments = JsDoc.getNodeTagComments(node, "example");
280
+ if (exampleTagComments.length > 0) {
281
+ const parsedExamples = exampleTagComments.map(v => JsDoc.parseTagValue(v));
282
+ schema.examples = Array.isArray(schema.examples)
283
+ ? [...parsedExamples, ...schema.examples]
284
+ : parsedExamples;
285
+ }
279
286
  }
280
287
  /**
281
288
  * Validate that a @json-schema constraint key is compatible with the given schema type,