fix(schema-input): unify $ref to another top-level schema source - #3020
fix(schema-input): unify $ref to another top-level schema source#3020schani wants to merge 8 commits into
Conversation
Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts: # test/fixtures.ts
The directory-source fixture for #1543 cannot run through the round-trip harness: a directory always yields multiple top-level types (one per file), and renderers emit per-type entry points (java `TopLevelFromJsonString`, elm per-type decoders, etc.) instead of the single `fromJsonString`/top-level the shared fixture drivers assume, so the sample failed to compile for java, elm, and others. The scenario also can't be reduced to a single top-level, since the bug requires two top-level sources where one $refs the other. The round-trip harness also can't detect the actual bug anyway: the fix only removes a duplicate structurally-identical interface, and both the buggy and fixed output round-trip the sample identically. That regression is already covered by test/unit/schema-multiple-sources.test.ts, which asserts the duplicate `Info` interface is not generated and `info` reuses `OrderCustomer`. Remove the fixture registration and its round-trip sample JSON; keep the schema files under test/inputs/schema/multi-source-1543 as the unit test's input, and clarify the test comment. Co-Authored-By: Claude <noreply@anthropic.com>
a0d5235 to
0ca2f44
Compare
|
Heads up on a change to the test setup here. The The round-trip harness also can't observe the actual bug — the fix only removes a duplicate structurally-identical interface, so buggy and fixed output round-trip the sample identically. That regression is fully covered by I dropped the fixture registration and its round-trip sample JSON, kept the schema files under |
No generated-output differences✅ Generated outputs are unchanged between the PR base and head revisions. |
Generated-output differences1 files differ — 0 modified, 1 new, 0 deleted |
Generated-output differences1 files differ — 0 modified, 1 new, 0 deleted |
Bug
When generating from multiple JSON Schema sources where one schema's property
$refs another top-level source, quicktype emitted two separate, structurally-identical interfaces instead of reusing the top-level type.Repro (
--src=<dir>with two files, one$ref-ing the other):res/WebsiteOrder.schemahas a propertyinfothat is"$ref": "OrderCustomer.schema#", andres/OrderCustomer.schemais itself a top-level source. Before the fix, quicktype produced bothOrderCustomer(from the top-level source) and a duplicateInfointerface (synthesized for the$ref-ed property), withWebsiteOrder.info: Info. Expected: a singleOrderCustomerinterface reused forWebsiteOrder.info: OrderCustomer.Root cause
In
addTypesInSchema(packages/quicktype-core/src/input/JSONSchemaInput.ts), a property schema that was a pure$refalias (only$ref, a matchingtype, or the draft-3 booleanrequiredkeyword present) was still routed through the same "needs a union/intersection" path as schemas with real narrowing keywords (properties,items,enum, etc.). That path always built a fresh type for the schema instead of reusing the identity of the resolved$reftarget, so a$refpointing at another top-level source got re-synthesized as a brand-new type rather than unified with the type already generated for that source.Fix
$refresolution is hoisted earlier so it's available when deciding whether a union/intersection is needed. A newisRefAliascheck recognizes schemas that are pure$refaliases (no narrowing keywords beyond a same-typedtypeor booleanrequired) and skips the union path for them, letting the existing$ref-target-reuse logic (already present further down) unify the type instead of synthesizing a duplicate.Test coverage
test/inputs/schema/multi-source-1543/plus positive and negative JSON samples: the exact directory-as-source repro is registered forschema-typescriptand runs end to end through the standard TypeScript compile/round-trip fixture.test/unit/schema-multiple-sources.test.ts: the round trip cannot detect structurally identical duplicate interfaces, so this targeted assertion verifies thatinforeusesOrderCustomerand no redundantInfointerface is emitted.Verification
npm run buildpasses.OrderCustomerandInfointerfaces to oneOrderCustomer, withTopLevel.info: OrderCustomer.npx vitest run test/unit/schema-multiple-sources.test.tspasses.QUICKTEST=true CPUs=2 FIXTURE=schema-typescript npm run test:fixturespasses all 81 cases, including the positive and expected-failuremulti-source-1543samples.schema-typescript/test/inputs/schema/multi-source-1543/default/TopLevel.ts, containinginfo: OrderCustomerand noInfointerface.Fixes #1543
🤖 Generated with Claude Code