Skip to content

feat(openapi-resource-gen): accept Swagger 2.0 specs by converting them to OpenAPI 3.0 in memory - #82

Merged
constantant merged 1 commit into
masterfrom
feat/swagger2-input
Oct 2, 2026
Merged

constantant merged 1 commit into
masterfrom
feat/swagger2-input

Conversation

@constantant

Copy link
Copy Markdown
Owner

Quick win #4 from the roadmap (docs/roadmap/quick-wins.md).

The problem

Swagger 2.0 specs were rejected with "convert first with swagger2openapi". Hey API and OpenAPI Generator accept them, and many real APIs still publish 2.0.

The change

Point specPath at a Swagger 2.0 spec and the generator converts it to OpenAPI 3.0 in memory (with @scalar/openapi-upgrader, actively maintained; swagger2openapi was last published in 2022), prints a one-line notice, and generates as usual. The spec on disk is untouched. New option convertSwagger2 (default true); false rejects Swagger 2.0 instead. Swagger 1.x is still rejected, with a message naming both supported formats.

Swagger 2.0 spec detected — converting to OpenAPI 3.0 in memory.

An upgrader bug worked around

When a spec has a global produces (nearly all do) and an operation overrides it, the upgrader applies the global one to responses. A download declaring produces: [image/png] came out as application/json, so the generator would type a file download as JSON. swagger2.ts pushes the global produces/consumes down into operations that lack their own before converting, so each operation's declared types win. (The upgrader also mutates its input, so it gets a copy.) Reported here so it can be raised upstream.

ESM-only dependency in a CommonJS generator

The upgrader is ESM-only. It is loaded with require() (works on Node ≥ 20.19 / 22.12) and falls back to a dynamic import() when require reports ERR_REQUIRE_ESM. Verified on real Node 20.18 (fallback path) and Node 24 (direct path) against the built package, and on every Nx version in the compat matrix.

Verification

  • 187 generator tests: pure-function tests for the module (including that the input isn't mutated), conversion-wiring tests (notice, opt-out, Swagger 1.x, no version field), and a real-upgrader test of each mapping. This replaces a test that only built an Error and asserted its own text.
  • Integration test: the real generator and parser on a realistic Swagger 2.0 spec (global produces with overrides, a file download, multipart formData, collectionFormat, shared parameters/responses, securityDefinitions), with the output compiled by TypeScript; checks a blob download, a text response, a multipart body, join('|') serialization, a typed 404 error, and one security token per scheme. Also with --clientType=httpClient.
  • Negative control: bypassing the push-down makes 3 tests fail (the download becomes JSON)
  • Real-world spec: the classic Petstore v2 generates 21 tokens + 2 security tokens, compiles under strict tsc, and uploadFile correctly becomes a multipart/form-data body
  • The Nx compat matrix (20–23) installs the upgrader and runs a Swagger 2.0 generation under each (185 tests per version)
  • lint, builds, mocks / api-explorer tests

Notes

  • New runtime dependency @scalar/openapi-upgrader (declared in the package and the workspace root). The integration test helper is shared with the readWriteMarkers spec and is excluded from the library build so it doesn't ship (caught while preparing this).
  • Additive: a minor bump for gen. Docs: README option row and a Swagger 2.0 input section (what maps to what, and the caveats), CLAUDE.md pipeline step, schema input and decision row.

🤖 Generated with Claude Code

…em to OpenAPI 3.0 in memory

Swagger 2.0 specs used to be rejected with "convert first with swagger2openapi".
They are now converted in memory with @scalar/openapi-upgrader (a notice is
printed, the spec on disk is untouched) and generated as usual; the new
`convertSwagger2: false` rejects them instead. Swagger 1.x is still rejected, and
the error now names both supported formats.

The upgrader applies a global `produces` even to operations that override it,
which turned an `image/png` download into JSON and made the generator pick the
wrong response variant. swagger2.ts therefore pushes the global `produces` /
`consumes` down into operations that lack their own before converting. The
upgrader also mutates its input, so it is given a copy.

The upgrader is ESM-only and the generator is CommonJS: it is loaded with
require() (Node >= 20.19 / 22.12) and falls back to a dynamic import() when
require reports ERR_REQUIRE_ESM (verified on Node 20.18 and 24).

Tests: unit tests for the module and the conversion wiring (replacing a test that
only asserted its own error text), plus an integration test that runs the real
generator and parser on a realistic Swagger 2.0 spec (global produces with
overrides, a file download, multipart formData, collectionFormat, shared
parameters/responses, securityDefinitions) and compiles the output. The
integration helper is shared with the readWriteMarkers spec and excluded from the
library build. The compat Nx harness installs the upgrader and runs a Swagger 2.0
generation on every Nx version.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@constantant
constantant merged commit 7b76294 into master Oct 2, 2026
9 checks passed
@constantant
constantant deleted the feat/swagger2-input branch October 2, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant