From 667c1f36b72d60be78819bd7b4f3a488f89bee6d Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Sat, 11 Jul 2026 15:23:19 +0900 Subject: [PATCH] fix(plugin): generate object examples from @example doc tags Object values in @example JSDoc tags were parsed successfully but createLiteralFromAnyValue only handled arrays and primitives, returning undefined for objects. The resulting exception was swallowed by the property visitor, dropping the entire property from _OPENAPI_METADATA_FACTORY. Handle plain objects (and null) by emitting object literal expressions with string literal keys. --- lib/plugin/utils/ast-utils.ts | 24 ++++++++++++++++----- test/plugin/fixtures/create-cat-alt2.dto.ts | 9 +++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/plugin/utils/ast-utils.ts b/lib/plugin/utils/ast-utils.ts index 910f72113..1193b6496 100644 --- a/lib/plugin/utils/ast-utils.ts +++ b/lib/plugin/utils/ast-utils.ts @@ -422,10 +422,24 @@ export function createPrimitiveLiteral( export function createLiteralFromAnyValue( factory: ts.NodeFactory, item: unknown -) { - return Array.isArray(item) - ? factory.createArrayLiteralExpression( - item.map((item) => createLiteralFromAnyValue(factory, item)) +): ts.Expression { + if (Array.isArray(item)) { + return factory.createArrayLiteralExpression( + item.map((item) => createLiteralFromAnyValue(factory, item)) + ); + } + if (item === null) { + return factory.createNull(); + } + if (typeof item === 'object') { + return factory.createObjectLiteralExpression( + Object.entries(item).map(([key, value]) => + factory.createPropertyAssignment( + factory.createStringLiteral(key), + createLiteralFromAnyValue(factory, value) + ) ) - : createPrimitiveLiteral(factory, item); + ); + } + return createPrimitiveLiteral(factory, item); } diff --git a/test/plugin/fixtures/create-cat-alt2.dto.ts b/test/plugin/fixtures/create-cat-alt2.dto.ts index 63594d78a..599f648f8 100644 --- a/test/plugin/fixtures/create-cat-alt2.dto.ts +++ b/test/plugin/fixtures/create-cat-alt2.dto.ts @@ -82,6 +82,13 @@ export abstract class Audit { */ testNumber: number; + /** + * testObject + * + * @example { "Content-Type": "video/mp4", "key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } + */ + testObject: Record; + /** * privateProperty * @example 'secret' @@ -102,7 +109,7 @@ export class Audit { _Audit_privateProperty.set(this, void 0); } static _OPENAPI_METADATA_FACTORY() { - return { createdAt: { required: true, type: () => Object, description: "test on createdAt" }, updatedAt: { required: true, type: () => Object }, version: { required: true, type: () => String, description: "test\\nversion", example: "version 123" }, testVersion: { required: true, type: () => Object, description: "testVersion", examples: ["0.0.1", "0.0.2"], deprecated: true }, testVersion2: { required: true, type: () => Object, description: "testVersion2", examples: ["0.0.1", "0.0.2"], deprecated: true }, testVersionArray: { required: true, type: () => [String], description: "testVersionArray", example: ["0.0.1", "0.0.2"] }, testVersionArray2: { required: true, type: () => [String], description: "testVersionArray", example: ["version 123", "version 321"] }, testVersionArray3: { required: true, type: () => [Number], description: "testVersionArray", example: [123, 321] }, testBoolean: { required: true, type: () => Boolean, description: "testBoolean", example: true }, testNumber: { required: true, type: () => Number, description: "testNumber", examples: [1, 5] } }; + return { createdAt: { required: true, type: () => Object, description: "test on createdAt" }, updatedAt: { required: true, type: () => Object }, version: { required: true, type: () => String, description: "test\\nversion", example: "version 123" }, testVersion: { required: true, type: () => Object, description: "testVersion", examples: ["0.0.1", "0.0.2"], deprecated: true }, testVersion2: { required: true, type: () => Object, description: "testVersion2", examples: ["0.0.1", "0.0.2"], deprecated: true }, testVersionArray: { required: true, type: () => [String], description: "testVersionArray", example: ["0.0.1", "0.0.2"] }, testVersionArray2: { required: true, type: () => [String], description: "testVersionArray", example: ["version 123", "version 321"] }, testVersionArray3: { required: true, type: () => [Number], description: "testVersionArray", example: [123, 321] }, testBoolean: { required: true, type: () => Boolean, description: "testBoolean", example: true }, testNumber: { required: true, type: () => Number, description: "testNumber", examples: [1, 5] }, testObject: { required: true, type: "object", additionalProperties: { type: "string" }, description: "testObject", example: { "Content-Type": "video/mp4", "key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } }; } } _Audit_privateProperty = new WeakMap();