From a8fc403e0b17f4540d8930164f54fd13c3f450a9 Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Thu, 5 Nov 2026 11:48:03 +0100 Subject: [PATCH 1/3] feat: added a config option to disable jsdoc generation --- README.md | 5 ++ native/src/config.rs | 2 + native/src/render.rs | 75 +++++++++++-------- scripts/renderer-parity-configs.ts | 1 + src/commands/generate.command.ts | 3 + src/generators/const/options.const.ts | 1 + src/generators/generate/generateAcl.ts | 30 ++++---- .../generate/generateDomainErrors.test.ts | 8 +- .../generate/generateDomainErrors.ts | 2 +- .../generate/generateModels.test.ts | 35 +++++++++ src/generators/generate/generateModels.ts | 1 + src/generators/generate/generateQueries.ts | 1 + src/generators/types/options.ts | 2 + 13 files changed, 120 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 3092782..dbb7715 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ export default { }; ``` +## JSDoc generation + +Set `generateJsDoc: false` in your generator configuration or pass `--no-generateJsDoc` to omit generated JSDoc comments while keeping schemas, inferred types, and runtime behavior. Defaults to `true` and applies to both TypeScript and native generation. Combine with `modelsOnly: true` if you only want model files. + ## Lazy schemas With `tsNamespaces` enabled, every schema of a models namespace is built the moment the module is imported, even when the importer only needs one of them. Set `lazySchemas: true` (or pass `--lazySchemas`) to build each schema on first access instead: @@ -127,6 +131,7 @@ bunx openapi-codegen generate --config my-config.ts --tsPath (Requires `--importPath` to be 'ts') Typescript import path (default: '@/data') --removeOperationPrefixEndingWith Remove operation name prefixes that end with the specified string (default: 'Controller_') --compileSchemas Eagerly compile non-recursive Zod schemas (default: false) + --generateJsDoc Emit JSDoc comments (default: true; disable with --no-generateJsDoc) --lazySchemas Build namespace model schemas on first access instead of at import; requires `--tsNamespaces` (default: false) --extractEnums Extract enums into separate Zod schemas (default: true) --modelsInCommon Keep all schema declarations in defaultTag models and emit per-module proxy exports (default: false) diff --git a/native/src/config.rs b/native/src/config.rs index 460f66b..2f9b4c9 100644 --- a/native/src/config.rs +++ b/native/src/config.rs @@ -37,6 +37,8 @@ pub struct GenerateOptions { #[serde(default)] pub lazy_schemas: bool, #[serde(default = "default_true")] + pub generate_js_doc: bool, + #[serde(default = "default_true")] pub split_by_tags: bool, #[serde(default = "default_tag")] pub default_tag: String, diff --git a/native/src/render.rs b/native/src/render.rs index bb406f3..9f5fb98 100644 --- a/native/src/render.rs +++ b/native/src/render.rs @@ -562,6 +562,7 @@ fn render_common_schema_lines( generated_objects, enum_objects, property_ref_names, + options.generate_js_doc, ); let type_name = remove_suffix(name, suffix); let is_circular = circular_schemas.iter().any(|schema| schema == name); @@ -600,6 +601,7 @@ fn render_schema_docs<'a>( generated_objects: &'a Map, enum_objects: &'a HashMap, property_ref_names: &HashMap<&str, String>, + generate_js_doc: bool, ) -> (Vec, Option<&'a Value>) { let schema_object = generated_objects .get(name) @@ -610,6 +612,9 @@ fn render_schema_docs<'a>( .and_then(|reference| resolve_document_ref(document, reference)) }) .or_else(|| enum_objects.get(code)); + if !generate_js_doc { + return (Vec::new(), schema_object); + } let mut lines = vec!["/** ".into(), format!(" * {name} ")]; let schema_type = if code.starts_with("z.enum(") { "enum" @@ -746,6 +751,7 @@ fn render_lazy_schema_parts( generated_objects, enum_objects, property_ref_names, + options.generate_js_doc, ); let type_name = remove_suffix(name, suffix); let is_circular = circular_schemas.iter().any(|schema| schema == name); @@ -2313,7 +2319,7 @@ pub fn render_shared(endpoints: &[Value], options: &GenerateOptions) -> Map Option { Some(lines.join("\n")) } -fn render_domain_errors(endpoints: &[Value]) -> Option { +fn render_domain_errors(endpoints: &[Value], options: &GenerateOptions) -> Option { let mut domains: HashMap)>> = HashMap::default(); for endpoint in endpoints { for error in endpoint @@ -2503,6 +2509,7 @@ fn render_domain_errors(endpoints: &[Value]) -> Option { .into_iter() .map(|(code, key, description)| { let prefix = description + .filter(|_| options.generate_js_doc) .map(|description| format!(" /** {description} */\n ")) .unwrap_or_else(|| " ".into()); format!("{prefix}{key}: {}", value_text(&code)) @@ -2703,37 +2710,39 @@ fn render_ability( } else { format!("`{query_name}` mutation") }; - lines.push("/**".into()); - lines.push(format!( - " * Use for {ability_query} ability. {}", - if has_conditions { - "For global ability, omit the object parameter." - } else { - "" - } - )); - if let Some(description) = endpoint - .pointer("/acl/0/description") - .and_then(Value::as_str) - { - lines.push(format!(" * @description {description}")); - } - for condition in &conditions { - let name = condition - .get("name") - .and_then(Value::as_str) - .unwrap_or_default(); - let info = condition - .get("info") - .and_then(Value::as_str) - .unwrap_or_default(); + if options.generate_js_doc { + lines.push("/**".into()); lines.push(format!( - " * @param {{ {} }} object.{name} {name} from {info}", - condition_type(condition, models_namespace, options) + " * Use for {ability_query} ability. {}", + if has_conditions { + "For global ability, omit the object parameter." + } else { + "" + } )); + if let Some(description) = endpoint + .pointer("/acl/0/description") + .and_then(Value::as_str) + { + lines.push(format!(" * @description {description}")); + } + for condition in &conditions { + let name = condition + .get("name") + .and_then(Value::as_str) + .unwrap_or_default(); + let info = condition + .get("info") + .and_then(Value::as_str) + .unwrap_or_default(); + lines.push(format!( + " * @param {{ {} }} object.{name} {name} from {info}", + condition_type(condition, models_namespace, options) + )); + } + lines.push(format!(" * @returns {{ AbilityTuple }} An ability tuple indicating the user's ability to use {ability_query}")); + lines.push(" */".into()); } - lines.push(format!(" * @returns {{ AbilityTuple }} An ability tuple indicating the user's ability to use {ability_query}")); - lines.push(" */".into()); lines.push(format!("export const {ability_name} = (")); if has_conditions { let object = conditions @@ -3737,6 +3746,9 @@ fn render_query_docs( models_namespace: &str, infinite: bool, ) { + if !options.generate_js_doc { + return; + } let (_, cap) = query_names(endpoint); lines.push("/** ".into()); lines.push(if infinite { @@ -4305,6 +4317,9 @@ fn render_mutation_docs( options: &GenerateOptions, models_namespace: &str, ) { + if !options.generate_js_doc { + return; + } let (_, cap) = query_names(endpoint); let mutation_cap = if endpoint.get("method").and_then(Value::as_str) == Some("get") { format!("{cap}Mutation") diff --git a/scripts/renderer-parity-configs.ts b/scripts/renderer-parity-configs.ts index 1d3b711..e488325 100644 --- a/scripts/renderer-parity-configs.ts +++ b/scripts/renderer-parity-configs.ts @@ -90,6 +90,7 @@ export const optionCases = { })), abilityContextImportPath: [{ acl: true, abilityContextImportPath: "@test/ability-context" }], aclCheckImportPath: [{ acl: true, aclCheckImportPath: "@test/acl" }], + generateJsDoc: bool("generateJsDoc"), builderConfigs: bool("builderConfigs"), filterParamName: [{ builderConfigs: true, filterParamName: "filters" }], dataResponseParamNames: [{ builderConfigs: true, dataResponseParamNames: ["results"] }], diff --git a/src/commands/generate.command.ts b/src/commands/generate.command.ts index 814ac8a..a200a82 100644 --- a/src/commands/generate.command.ts +++ b/src/commands/generate.command.ts @@ -58,6 +58,9 @@ class GenerateOptions implements GenerateParams { @YargOption({ envAlias: "lazySchemas", type: "boolean" }) lazySchemas?: boolean; + @YargOption({ envAlias: "generateJsDoc", type: "boolean" }) + generateJsDoc?: boolean; + @YargOption({ envAlias: "extractEnums", type: "boolean" }) extractEnums?: boolean; diff --git a/src/generators/const/options.const.ts b/src/generators/const/options.const.ts index f74f6a5..bfef8c8 100644 --- a/src/generators/const/options.const.ts +++ b/src/generators/const/options.const.ts @@ -48,6 +48,7 @@ export const DEFAULT_GENERATE_OPTIONS: GenerateOptions = { // Zod options compileSchemas: false, lazySchemas: false, + generateJsDoc: true, schemaSuffix: SCHEMA_SUFFIX, enumSuffix: ENUM_SUFFIX, modelsInCommon: false, diff --git a/src/generators/generate/generateAcl.ts b/src/generators/generate/generateAcl.ts index 7981eda..cb0b40b 100644 --- a/src/generators/generate/generateAcl.ts +++ b/src/generators/generate/generateAcl.ts @@ -202,22 +202,24 @@ function renderAbilityFunction({ ? `\`${getQueryName(endpoint)}\` query or \`${getQueryName(endpoint, true)}\` mutation` : `\`${getQueryName(endpoint)}\` query` : `\`${getQueryName(endpoint)}\` mutation`; - lines.push("/**"); - lines.push( - ` * Use for ${abilityQuery} ability. ${hasConditions ? "For global ability, omit the object parameter." : ""}${getAbilityDescription(endpoint) ? "" : ""}`, - ); - if (getAbilityDescription(endpoint)) { - lines.push(` * @description ${getAbilityDescription(endpoint)}`); - } - if (hasConditions) { - for (const propertyType of abilityConditionsTypes) { - lines.push( - ` * @param { ${renderConditionType(resolver, endpoint, propertyType)} } object.${propertyType.name} ${propertyType.name} from ${propertyType.info}`, - ); + if (resolver.options.generateJsDoc !== false) { + lines.push("/**"); + lines.push( + ` * Use for ${abilityQuery} ability. ${hasConditions ? "For global ability, omit the object parameter." : ""}${getAbilityDescription(endpoint) ? "" : ""}`, + ); + if (getAbilityDescription(endpoint)) { + lines.push(` * @description ${getAbilityDescription(endpoint)}`); + } + if (hasConditions) { + for (const propertyType of abilityConditionsTypes) { + lines.push( + ` * @param { ${renderConditionType(resolver, endpoint, propertyType)} } object.${propertyType.name} ${propertyType.name} from ${propertyType.info}`, + ); + } } + lines.push(` * @returns { AbilityTuple } An ability tuple indicating the user's ability to use ${abilityQuery}`); + lines.push(" */"); } - lines.push(` * @returns { AbilityTuple } An ability tuple indicating the user's ability to use ${abilityQuery}`); - lines.push(" */"); lines.push(`export const ${getAbilityFunctionName(endpoint)} = (`); if (hasConditions) { lines.push( diff --git a/src/generators/generate/generateDomainErrors.test.ts b/src/generators/generate/generateDomainErrors.test.ts index 0cd14ac..9c4f3d7 100644 --- a/src/generators/generate/generateDomainErrors.test.ts +++ b/src/generators/generate/generateDomainErrors.test.ts @@ -34,9 +34,15 @@ function makeEndpointWithDomainError( }; } -const stubResolver = {} as never; +const stubResolver = { options: {} } as never; describe("generateDomainErrors", () => { + it("omits descriptions when JSDoc is disabled", () => { + const data = makeData([[makeEndpointWithDomainError("rocket", 1, "Launch failed")]]); + const result = generateDomainErrors({ resolver: { options: { generateJsDoc: false } } as never, data })!; + expect(result).not.toContain("/**"); + expect(result).toContain("ERROR_1: 1"); + }); it("returns undefined when no domain errors exist", () => { const data = makeData([[{ errors: [{ status: 400, zodSchema: "z.void()" }] }]]); expect(generateDomainErrors({ resolver: stubResolver, data })).toBeUndefined(); diff --git a/src/generators/generate/generateDomainErrors.ts b/src/generators/generate/generateDomainErrors.ts index 9a9ba2c..1f857df 100644 --- a/src/generators/generate/generateDomainErrors.ts +++ b/src/generators/generate/generateDomainErrors.ts @@ -61,7 +61,7 @@ export function generateDomainErrors({ const entries = sortedCodes .map(({ code, name, description }) => { - const comment = description ? ` /** ${description} */\n ` : " "; + const comment = resolver.options.generateJsDoc !== false && description ? ` /** ${description} */\n ` : " "; const key = name ?? (typeof code === "string" ? code : `ERROR_${code}`); if (!VALID_IDENTIFIER.test(key)) { throw new Error( diff --git a/src/generators/generate/generateModels.test.ts b/src/generators/generate/generateModels.test.ts index d7b0bca..bf3f50f 100644 --- a/src/generators/generate/generateModels.test.ts +++ b/src/generators/generate/generateModels.test.ts @@ -12,6 +12,41 @@ import { getNativeBindings } from "@/native/native-bindings"; import { generateModels } from "./generateModels"; describe("generateModels", () => { + test.each([false, true])("can omit JSDoc while preserving schemas (lazy: %s)", (lazySchemas) => { + const doc = { + openapi: "3.0.3", + info: { title: "Docs", version: "1.0.0" }, + paths: {}, + components: { + schemas: { + Pet: { + type: "object", + description: "A pet", + properties: { name: { type: "string", description: "Pet name" } }, + }, + }, + }, + } as OpenAPIV3.Document; + const render = (generateJsDoc: boolean) => { + const options = { + ...DEFAULT_GENERATE_OPTIONS, + generateJsDoc, + lazySchemas, + splitByTags: false, + excludeRedundantZodSchemas: false, + }; + const { resolver, data } = getDataFromOpenAPIDoc(doc, options); + return generateModels({ resolver, data, tag: options.defaultTag })!; + }; + const documented = render(true); + const plain = render(false); + expect(documented).toContain("@description A pet"); + expect(plain).not.toContain("/**"); + expect(plain).toContain("PetSchema"); + expect(plain).toContain("export type Pet ="); + expect(plain.replace(/\s/g, "")).toBe(documented.replace(/\/\*\*[\s\S]*?\*\//g, "").replace(/\s/g, "")); + }); + test("adds explicit Zod object annotations to mutually recursive schemas", () => { const doc = { openapi: "3.0.3", diff --git a/src/generators/generate/generateModels.ts b/src/generators/generate/generateModels.ts index dbacab1..6569e8c 100644 --- a/src/generators/generate/generateModels.ts +++ b/src/generators/generate/generateModels.ts @@ -371,6 +371,7 @@ function renderModelJsDocs({ tag: string; resolver: GenerateTypeParams["resolver"]; }) { + if (resolver.options.generateJsDoc === false) return ""; const lines = [`/** `, ` * ${name} `, ` * @type { ${getZodSchemaType(zodSchema)} }`]; const description = getZodSchemaDescription(zodSchema); diff --git a/src/generators/generate/generateQueries.ts b/src/generators/generate/generateQueries.ts index b2bbba8..9263e3b 100644 --- a/src/generators/generate/generateQueries.ts +++ b/src/generators/generate/generateQueries.ts @@ -559,6 +559,7 @@ function renderQueryJsDocs({ mode: "query" | "mutation" | "infiniteQuery"; tag: string; }) { + if (resolver.options.generateJsDoc === false) return ""; const lines: string[] = ["/** "]; if (mode === "infiniteQuery") { diff --git a/src/generators/types/options.ts b/src/generators/types/options.ts index 51508b6..3dd21bb 100644 --- a/src/generators/types/options.ts +++ b/src/generators/types/options.ts @@ -5,6 +5,8 @@ interface ZodGenerateOptions { compileSchemas?: boolean; /** Build namespace model schemas on first access instead of at import. Requires tsNamespaces. */ lazySchemas?: boolean; + /** Emit JSDoc comments in generated code. Defaults to true. */ + generateJsDoc?: boolean; schemaSuffix: string; enumSuffix: string; modelsInCommon?: boolean; From 09a8d95b00c242698cb3c917964ea3b2cb2801de Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Thu, 5 Nov 2026 11:53:20 +0100 Subject: [PATCH 2/3] fix: formatting issue --- src/generators/generate/generateModels.test.ts | 1 + src/generators/generate/generateModels.ts | 8 ++++++-- src/generators/generate/generateQueries.test.ts | 17 +++++++++++++++++ src/generators/generate/generateQueries.ts | 12 +++++++++--- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/generators/generate/generateModels.test.ts b/src/generators/generate/generateModels.test.ts index bf3f50f..218bcbd 100644 --- a/src/generators/generate/generateModels.test.ts +++ b/src/generators/generate/generateModels.test.ts @@ -42,6 +42,7 @@ describe("generateModels", () => { const plain = render(false); expect(documented).toContain("@description A pet"); expect(plain).not.toContain("/**"); + expect(plain).toBe(documented.replace(/^[ \t]*\/\*\*[\s\S]*?\*\/\n/gm, "")); expect(plain).toContain("PetSchema"); expect(plain).toContain("export type Pet ="); expect(plain.replace(/\s/g, "")).toBe(documented.replace(/\/\*\*[\s\S]*?\*\//g, "").replace(/\s/g, "")); diff --git a/src/generators/generate/generateModels.ts b/src/generators/generate/generateModels.ts index 6569e8c..35c5a5e 100644 --- a/src/generators/generate/generateModels.ts +++ b/src/generators/generate/generateModels.ts @@ -77,7 +77,9 @@ export function generateModels({ resolver, data, tag }: GenerateTypeParams) { } for (const [name, zodSchema] of Object.entries(zodSchemasData)) { - lines.push(renderModelJsDocs({ name, zodSchema, tag, resolver })); + if (resolver.options.generateJsDoc !== false) { + lines.push(renderModelJsDocs({ name, zodSchema, tag, resolver })); + } const inferredTypeName = getZodSchemaInferedTypeName(name, resolver.options); if (zodSchema.isCircular && zodSchema.schemaObj) { lines.push(`export type ${inferredTypeName} = ${renderSchemaType(zodSchema.schemaObj, resolver)};`); @@ -132,7 +134,9 @@ function renderLazyModels({ const schemaCode = resolver.options.compileSchemas && !zodSchema.isCircular ? `z.compile(${zodSchema.code})` : zodSchema.code; lines.push(`const ${name}${typeAnnotation} = once(() => ${callLazySchemaReferences(schemaCode, schemaNames)});`); - members.push(...indent(renderModelJsDocs({ name, zodSchema, tag, resolver }).split("\n"))); + if (resolver.options.generateJsDoc !== false) { + members.push(...indent(renderModelJsDocs({ name, zodSchema, tag, resolver }).split("\n"))); + } members.push(...renderLazyMember(name, `${name}()`)); if (zodSchema.isCircular && zodSchema.schemaObj) { types.push(` export type ${inferredTypeName} = ${renderSchemaType(zodSchema.schemaObj, resolver)};`); diff --git a/src/generators/generate/generateQueries.test.ts b/src/generators/generate/generateQueries.test.ts index 7104b2d..cb64aee 100644 --- a/src/generators/generate/generateQueries.test.ts +++ b/src/generators/generate/generateQueries.test.ts @@ -167,6 +167,23 @@ const openApiDoc = { } as OpenAPIV3.Document; describe("generateQueries workspaceContext", () => { + it("omits JSDoc without leaving extra blank lines", () => { + const render = (generateJsDoc: boolean) => + generateCodeFromOpenAPIDoc(openApiDoc, { + ...DEFAULT_GENERATE_OPTIONS, + generateJsDoc, + output: "test-output", + }).filter((file) => file.fileName.endsWith(".queries.ts")); + const documented = render(true); + const plain = render(false); + expect(plain.length).toBeGreaterThan(0); + expect(plain).toEqual( + documented.map((file) => ({ + ...file, + content: file.content.replace(/^[ \t]*\/\*\*[\s\S]*?\*\/\n/gm, ""), + })), + ); + }); it("passes runtime allowInvalidResponseData config through generated GET query hooks", () => { const files = generateCodeFromOpenAPIDoc(openApiDoc, { ...DEFAULT_GENERATE_OPTIONS, diff --git a/src/generators/generate/generateQueries.ts b/src/generators/generate/generateQueries.ts index 9263e3b..fde9bef 100644 --- a/src/generators/generate/generateQueries.ts +++ b/src/generators/generate/generateQueries.ts @@ -946,7 +946,9 @@ function renderQuery({ const queryOptionsArgs = `${resolvedEndpointArgs ? `{ ${resolvedEndpointArgs} }, ` : ""}${requestConfig}`; const lines: string[] = []; - lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "query", tag })); + if (resolver.options.generateJsDoc !== false) { + lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "query", tag })); + } lines.push( `export const ${getQueryName(endpoint)} = (${endpointParams ? `{ ${endpointArgs} }: { ${endpointParams} }, ` : ""}options?: AppQueryOptions${hasAxiosRequestConfig ? `, ${AXIOS_REQUEST_CONFIG_NAME}?: ${getRequestConfigTypeName(resolver.options.restClient)}` : ""}) => {`, ); @@ -1061,7 +1063,9 @@ function renderMutation({ : `, { ${mutationVariablesType} }`; const lines: string[] = []; - lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "mutation", tag })); + if (resolver.options.generateJsDoc !== false) { + lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "mutation", tag })); + } lines.push( `export const ${getQueryName(endpoint, true)} = (${pathParamFirstArg}options?: AppMutationOptions${hasMutationEffects ? ` & ${MUTATION_EFFECTS.optionsType}` : ""}${hasAxiosRequestConfig ? `, ${AXIOS_REQUEST_CONFIG_NAME}?: ${getRequestConfigTypeName(resolver.options.restClient)}` : ""}) => {`, ); @@ -1257,7 +1261,9 @@ function renderInfiniteQuery({ const hasQueryFnOverride = hasAclCheck; const lines: string[] = []; - lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "infiniteQuery", tag })); + if (resolver.options.generateJsDoc !== false) { + lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "infiniteQuery", tag })); + } lines.push( `export const ${getInfiniteQueryName(endpoint)} = (${endpointParams ? `{ ${endpointArgsWithoutPage} }: { ${endpointParams} }, ` : ""}options?: AppInfiniteQueryOptions${hasAxiosRequestConfig ? `, ${AXIOS_REQUEST_CONFIG_NAME}?: ${getRequestConfigTypeName(resolver.options.restClient)}` : ""}) => {`, ); From ddb840533da1ebde9df5fe296447d91192d81ce9 Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Thu, 5 Nov 2026 12:10:57 +0100 Subject: [PATCH 3/3] chore: set generateJsDoc to false by default --- README.md | 4 ++-- native/src/config.rs | 2 +- src/generators/const/options.const.ts | 2 +- src/generators/core/resolveConfig.test.ts | 10 ++++++++++ src/generators/generate/generateAcl.ts | 2 +- src/generators/generate/generateDomainErrors.test.ts | 2 +- src/generators/generate/generateDomainErrors.ts | 2 +- src/generators/generate/generateModels.ts | 6 +++--- src/generators/generate/generateQueries.ts | 8 ++++---- src/generators/types/options.ts | 2 +- 10 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index dbb7715..869f393 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ export default { ## JSDoc generation -Set `generateJsDoc: false` in your generator configuration or pass `--no-generateJsDoc` to omit generated JSDoc comments while keeping schemas, inferred types, and runtime behavior. Defaults to `true` and applies to both TypeScript and native generation. Combine with `modelsOnly: true` if you only want model files. +JSDoc comments are omitted by default while schemas, inferred types, and runtime behavior are preserved. Set `generateJsDoc: true` in your generator configuration or pass `--generateJsDoc` to include comments. This applies to both TypeScript and native generation. Combine with `modelsOnly: true` if you only want model files. ## Lazy schemas @@ -131,7 +131,7 @@ bunx openapi-codegen generate --config my-config.ts --tsPath (Requires `--importPath` to be 'ts') Typescript import path (default: '@/data') --removeOperationPrefixEndingWith Remove operation name prefixes that end with the specified string (default: 'Controller_') --compileSchemas Eagerly compile non-recursive Zod schemas (default: false) - --generateJsDoc Emit JSDoc comments (default: true; disable with --no-generateJsDoc) + --generateJsDoc Emit JSDoc comments (default: false; enable with --generateJsDoc) --lazySchemas Build namespace model schemas on first access instead of at import; requires `--tsNamespaces` (default: false) --extractEnums Extract enums into separate Zod schemas (default: true) --modelsInCommon Keep all schema declarations in defaultTag models and emit per-module proxy exports (default: false) diff --git a/native/src/config.rs b/native/src/config.rs index 2f9b4c9..fa5cb1d 100644 --- a/native/src/config.rs +++ b/native/src/config.rs @@ -36,7 +36,7 @@ pub struct GenerateOptions { pub compile_schemas: bool, #[serde(default)] pub lazy_schemas: bool, - #[serde(default = "default_true")] + #[serde(default)] pub generate_js_doc: bool, #[serde(default = "default_true")] pub split_by_tags: bool, diff --git a/src/generators/const/options.const.ts b/src/generators/const/options.const.ts index bfef8c8..fe7527e 100644 --- a/src/generators/const/options.const.ts +++ b/src/generators/const/options.const.ts @@ -48,7 +48,7 @@ export const DEFAULT_GENERATE_OPTIONS: GenerateOptions = { // Zod options compileSchemas: false, lazySchemas: false, - generateJsDoc: true, + generateJsDoc: false, schemaSuffix: SCHEMA_SUFFIX, enumSuffix: ENUM_SUFFIX, modelsInCommon: false, diff --git a/src/generators/core/resolveConfig.test.ts b/src/generators/core/resolveConfig.test.ts index 25435d2..558b212 100644 --- a/src/generators/core/resolveConfig.test.ts +++ b/src/generators/core/resolveConfig.test.ts @@ -3,6 +3,16 @@ import { describe, expect, it } from "vitest"; import { resolveConfig } from "@/generators/core/resolveConfig"; describe("resolveConfig", () => { + it("omits JSDoc by default and honors file and CLI overrides", () => { + expect(resolveConfig({ params: {} }).generateJsDoc).toBe(false); + expect(resolveConfig({ fileConfig: { generateJsDoc: true }, params: {} }).generateJsDoc).toBe(true); + expect(resolveConfig({ fileConfig: { generateJsDoc: true }, params: { generateJsDoc: false } }).generateJsDoc).toBe( + false, + ); + expect(resolveConfig({ fileConfig: { generateJsDoc: false }, params: { generateJsDoc: true } }).generateJsDoc).toBe( + true, + ); + }); it("disables compilation by default and honors file and CLI overrides", () => { expect(resolveConfig({ params: {} }).compileSchemas).toBe(false); expect(resolveConfig({ fileConfig: { compileSchemas: true }, params: {} }).compileSchemas).toBe(true); diff --git a/src/generators/generate/generateAcl.ts b/src/generators/generate/generateAcl.ts index cb0b40b..6dddb81 100644 --- a/src/generators/generate/generateAcl.ts +++ b/src/generators/generate/generateAcl.ts @@ -202,7 +202,7 @@ function renderAbilityFunction({ ? `\`${getQueryName(endpoint)}\` query or \`${getQueryName(endpoint, true)}\` mutation` : `\`${getQueryName(endpoint)}\` query` : `\`${getQueryName(endpoint)}\` mutation`; - if (resolver.options.generateJsDoc !== false) { + if (resolver.options.generateJsDoc === true) { lines.push("/**"); lines.push( ` * Use for ${abilityQuery} ability. ${hasConditions ? "For global ability, omit the object parameter." : ""}${getAbilityDescription(endpoint) ? "" : ""}`, diff --git a/src/generators/generate/generateDomainErrors.test.ts b/src/generators/generate/generateDomainErrors.test.ts index 9c4f3d7..74cad18 100644 --- a/src/generators/generate/generateDomainErrors.test.ts +++ b/src/generators/generate/generateDomainErrors.test.ts @@ -34,7 +34,7 @@ function makeEndpointWithDomainError( }; } -const stubResolver = { options: {} } as never; +const stubResolver = { options: { generateJsDoc: true } } as never; describe("generateDomainErrors", () => { it("omits descriptions when JSDoc is disabled", () => { diff --git a/src/generators/generate/generateDomainErrors.ts b/src/generators/generate/generateDomainErrors.ts index 1f857df..44927b8 100644 --- a/src/generators/generate/generateDomainErrors.ts +++ b/src/generators/generate/generateDomainErrors.ts @@ -61,7 +61,7 @@ export function generateDomainErrors({ const entries = sortedCodes .map(({ code, name, description }) => { - const comment = resolver.options.generateJsDoc !== false && description ? ` /** ${description} */\n ` : " "; + const comment = resolver.options.generateJsDoc === true && description ? ` /** ${description} */\n ` : " "; const key = name ?? (typeof code === "string" ? code : `ERROR_${code}`); if (!VALID_IDENTIFIER.test(key)) { throw new Error( diff --git a/src/generators/generate/generateModels.ts b/src/generators/generate/generateModels.ts index 35c5a5e..3c63b47 100644 --- a/src/generators/generate/generateModels.ts +++ b/src/generators/generate/generateModels.ts @@ -77,7 +77,7 @@ export function generateModels({ resolver, data, tag }: GenerateTypeParams) { } for (const [name, zodSchema] of Object.entries(zodSchemasData)) { - if (resolver.options.generateJsDoc !== false) { + if (resolver.options.generateJsDoc === true) { lines.push(renderModelJsDocs({ name, zodSchema, tag, resolver })); } const inferredTypeName = getZodSchemaInferedTypeName(name, resolver.options); @@ -134,7 +134,7 @@ function renderLazyModels({ const schemaCode = resolver.options.compileSchemas && !zodSchema.isCircular ? `z.compile(${zodSchema.code})` : zodSchema.code; lines.push(`const ${name}${typeAnnotation} = once(() => ${callLazySchemaReferences(schemaCode, schemaNames)});`); - if (resolver.options.generateJsDoc !== false) { + if (resolver.options.generateJsDoc === true) { members.push(...indent(renderModelJsDocs({ name, zodSchema, tag, resolver }).split("\n"))); } members.push(...renderLazyMember(name, `${name}()`)); @@ -375,7 +375,7 @@ function renderModelJsDocs({ tag: string; resolver: GenerateTypeParams["resolver"]; }) { - if (resolver.options.generateJsDoc === false) return ""; + if (resolver.options.generateJsDoc !== true) return ""; const lines = [`/** `, ` * ${name} `, ` * @type { ${getZodSchemaType(zodSchema)} }`]; const description = getZodSchemaDescription(zodSchema); diff --git a/src/generators/generate/generateQueries.ts b/src/generators/generate/generateQueries.ts index fde9bef..6e449c4 100644 --- a/src/generators/generate/generateQueries.ts +++ b/src/generators/generate/generateQueries.ts @@ -559,7 +559,7 @@ function renderQueryJsDocs({ mode: "query" | "mutation" | "infiniteQuery"; tag: string; }) { - if (resolver.options.generateJsDoc === false) return ""; + if (resolver.options.generateJsDoc !== true) return ""; const lines: string[] = ["/** "]; if (mode === "infiniteQuery") { @@ -946,7 +946,7 @@ function renderQuery({ const queryOptionsArgs = `${resolvedEndpointArgs ? `{ ${resolvedEndpointArgs} }, ` : ""}${requestConfig}`; const lines: string[] = []; - if (resolver.options.generateJsDoc !== false) { + if (resolver.options.generateJsDoc === true) { lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "query", tag })); } lines.push( @@ -1063,7 +1063,7 @@ function renderMutation({ : `, { ${mutationVariablesType} }`; const lines: string[] = []; - if (resolver.options.generateJsDoc !== false) { + if (resolver.options.generateJsDoc === true) { lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "mutation", tag })); } lines.push( @@ -1261,7 +1261,7 @@ function renderInfiniteQuery({ const hasQueryFnOverride = hasAclCheck; const lines: string[] = []; - if (resolver.options.generateJsDoc !== false) { + if (resolver.options.generateJsDoc === true) { lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "infiniteQuery", tag })); } lines.push( diff --git a/src/generators/types/options.ts b/src/generators/types/options.ts index 3dd21bb..5bd8682 100644 --- a/src/generators/types/options.ts +++ b/src/generators/types/options.ts @@ -5,7 +5,7 @@ interface ZodGenerateOptions { compileSchemas?: boolean; /** Build namespace model schemas on first access instead of at import. Requires tsNamespaces. */ lazySchemas?: boolean; - /** Emit JSDoc comments in generated code. Defaults to true. */ + /** Emit JSDoc comments in generated code. Defaults to false. */ generateJsDoc?: boolean; schemaSuffix: string; enumSuffix: string;