feat(plugin-ts,client): add a comments option - #747
Closed
stijnvanhulle wants to merge 11 commits into
Closed
Conversation
Generated JSDoc is a third of what Kubb writes on a large spec, and most of it is spec prose that repeats what the type signature already says. There was no way to turn it down: plugin-ts had no option for comments at all. Add `comments: 'full' | 'brief' | 'none'`, defaulting to 'brief', which keeps the opening sentence of each description and caps it at 120 characters. Every other tag stays, so each type remains documented. `comments: 'full'` restores the previous output exactly. The truncation does not treat an abbreviation such as `e.g.` as the end of a sentence, and will not cut where that would strand an open bracket. Without those guards the OpenAI spec produced 43 descriptions cut to fragments like "The role of the message (e.g.". The client plugins share one options type in internals/client, so the option lands there once and reaches plugin-axios and plugin-fetch together, covering the operation JSDoc as well as the types. Measured on the OpenAI spec (281 operations, 2.76 MB of output): 'brief' trims 199,225 bytes (6.9%) and 'none' trims 1,026,758 (35.5%). At 'full' every generated file is byte-identical to before this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
🦋 Changeset detectedLatest commit: a9e621b The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
3 tasks
The Build job on ea3c7f7 was killed by a GitHub runner shutdown, not by anything in the diff — every package compiled before the signal landed. That cancellation skipped Tests, Typecheck, Compressed size and the rest of the pipeline, so this empty commit re-runs it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
Drops JSDoc from the private sentence helpers, whose names already say what they do, and keeps a comment only where the reason is not obvious: why the abbreviation list exists at all. Also folds the bracket-balance loop into a single expression. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
`@internals/shared` builds with `neverBundle: [/@internals/]`, so a type
imported from `@internals/utils` survives into its `.d.ts` as
`import { CommentLevel } from "@internals/utils"`. Most plugins depend on
`@internals/shared` without depending on `@internals/utils`, so that
import does not resolve for them.
Spell the union out on `BuildOperationCommentsOptions` instead. The
runtime import of `applyCommentLevel` stays, matching how this package
already uses `camelCase` and `toFilePath` from the same place.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
`@internals/shared` was the only internals package keeping `@internals/*` external. Every plugin that consumes it bundles `@internals/*`, so those plugins inherited shared's unresolved `@internals/utils` import, which they cannot resolve from their own node_modules. Nothing reachable from those plugin entries pulled a `@internals/utils` symbol through shared before, so the mismatch stayed hidden. Calling `applyCommentLevel` inside `buildOperationComments` created the first such edge, and the six plugins that reach it hung during their dts build instead of failing with a resolution error. `@internals/client` and `@internals/utils` already use this setting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
Contributor
e18e dependency analysisNo dependency warnings found. |
`@internals/client` bundles `@internals/*`, so importing the level type from `@internals/utils` puts a cross-package type edge in the graph that every client and query plugin then pulls in. Declare it here instead and point the components and the SDK builder at the local type. This mirrors the same change already made in `@internals/shared`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
More templates
@kubb/plugin-axios
@kubb/plugin-cypress
@kubb/plugin-faker
@kubb/plugin-fetch
@kubb/plugin-mcp
@kubb/plugin-msw
@kubb/plugin-react-query
@kubb/plugin-redoc
@kubb/plugin-swr
@kubb/plugin-ts
@kubb/plugin-vue-query
@kubb/plugin-zod
commit: |
`CommentLevel` now exists in `@internals/utils`, `@internals/shared` and `@internals/client`, which reads like duplication worth collapsing. It is not: both internals packages are bundled into the client and query plugins, and importing the type across packages leaves an import their `.d.ts` cannot resolve and sends their dts build time through the roof. Records that on both copies, and aligns the wording in utils with the rest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
Contributor
|
Size Change: +12.2 kB (+3.98%) Total Size: 318 kB 📦 View Changed
ℹ️ View Unchanged
|
Swaps the em-dash option bullets for the verb form the neighbouring `syntaxType` entry already uses, drops a clause semicolon from the axios and fetch pages, and rewrites the sentence that managed to say "description" twice about the OpenAPI description of the OpenAI API. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
The brief level cut every description at 120 characters, which on the OpenAI spec meant 206 hard cuts and 28 of them landing inside a markdown link or a code span. `[here](https://...` reads as broken markup on hover. A sentence is now kept whole up to 150 characters, and a cut that would leave a link or code span half written backs off to before it opened. That drops the hard cuts to 115 and the broken ones to zero, for 2 KB more across 6,878 descriptions. Also regenerates the examples that were still carrying full descriptions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
Making brief the default meant every existing user's generated files changed on upgrade, for a saving most of them never asked for. The option is worth having; changing what people get without asking is not. comments now defaults to 'full', so generated output is byte-identical to main for every example in the repo. Anyone who wants the bytes back opts in with 'brief' or 'none'. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 Changes
Generated JSDoc is 35.1% of everything Kubb writes on a large spec — 1,015,442 of 2,894,451 bytes on the OpenAI spec, where
types.tsalone is 64% comments. Most of it is spec prose repeating what the type signature already says, and there was no way to turn it down:plugin-tshad no option for comments at all.This adds
comments: 'full' | 'brief' | 'none':'full'(default) emits every description in full. Nothing about the current output changes.'brief'keeps the opening sentence of each@description. Every other tag stays, so each type remains documented.'none'emits no JSDoc. The generated-by banner is unaffected.Not a breaking change
An earlier revision of this PR defaulted to
'brief'. It no longer does. Every generated.tsfile underexamples/is byte-identical tomain— the only diff left in that tree is an unrelatedvalidateStatusfix that arrived with the upstream merge. Shrinking output is opt-in.Naming
commentsis already this codebase's own word for the construct —buildJSDoc(comments)ininternals/utils,buildOperationComments()ininternals/shared. It also matches TypeScript's ownremoveComments(both the tsconfig flag andts.createPrinter, which we print through) and hey-api'scomments: false, so people arriving from either look for the word they already know.Sentence detection
Two rules, each added because the simpler version mangled real output on the OpenAI spec.
Cutting at the first period produced 43 mangled descriptions, including
The role of the message (e.g.— content lost, bracket left open. The truncation skips abbreviations (e.g.,i.e.,etc., …) and refuses to cut where that would strand an open bracket. Verified against the real spec: 0 abbreviation cuts across 6,878 descriptions.A flat 120-character cap then hard-cut 206 descriptions, 28 of them mid-link or mid-code-span (
[here](https://...reads as broken markup on hover). A whole sentence is now kept up to 150 characters, and only a description that never finishes a sentence is cut — at the last word before 120, backing off further when that would leave a markdown link or code span half written. On the real spec that is 115 hard cuts, 0 broken, for 1,821 more bytes.Scope
The client plugins share a single options type in
internals/client, so the option landed there once and reaches plugin-axios and plugin-fetch together — covering operation JSDoc inclients.tsas well as the types.Measured
Generated from the OpenAI spec (281 operations) with the locally built packages:
comments'full'(default)'brief''none'On the +3.98% bundle size
The size bot reports +12.2 kB across the published plugins, ~880 B into each package that reaches
buildOperationCommentsand ~940 B into plugin-ts. That isapplyCommentLevel, its abbreviation list, and the markup back-off being bundled in.The cost is paid once per published package; the saving is paid per generated project that opts in — 197 KB at
'brief', 1.03 MB at'none'on a large spec.Four of those packages — plugin-mcp, plugin-react-query, plugin-swr, plugin-vue-query — bundle the code but have no
commentsoption of their own yet, so they always call it at'full'. Extending the option to them is the obvious follow-up.🛠 Build fix included in this PR
The first pass at this broke
pnpm build: six packages (plugin-axios, plugin-fetch, plugin-mcp, and the three query plugins — exactly those reachingbuildOperationComments) never finished their dts build, and CI's runner was reaped rather than timing out.Cause: a cross-package type import into an internals package that gets bundled. Both
@internals/sharedand@internals/clientdidimport type { CommentLevel } from '@internals/utils', and all six affected plugins bundle those packages. That edge leaves an import their.d.tscannot resolve and inflates dts generation enormously.Fix: each internals package that exposes the level declares it locally, and
@internals/sharedbundles@internals/*like its two sibling internals packages already do.CommentLeveltherefore appears in three packages on purpose — both non-utils copies carry a comment saying why, since collapsing them reintroduces the failure. Both halves were confirmed load-bearing by reverting each independently.Measured on a 4-core box, full
pnpm build --force:mainThe +56s over main is the cost of threading
commentsthrough six more packages.Unrelated observation while debugging:
pnpm buildruns at turbo's default concurrency of 10 while the repo'sgeneratescript caps at 5. Main builds fine, so it is not what broke this PR, but on a 4-core runner it is worth a look separately.✅ Checklist
pnpm run test.pnpm test1259 passed ·pnpm typecheck15/15 ·pnpm lintclean ·pnpm build148s. New coverage incodegen.test.ts,utils.test.ts,operation.test.ts, plus generator snapshots for all three levels.🚀 Release Impact
Minor on
@kubb/plugin-ts,@kubb/plugin-axios,@kubb/plugin-fetch. Docs in kubb-labs/docs#174.🤖 Generated with Claude Code
https://claude.ai/code/session_019QYdRKpk7zCy1XVbydevXf