Skip to content

fix(naming): respect casing of top-level names that collide with acronym dictionary#3023

Open
schani wants to merge 7 commits into
masterfrom
agent/fix-issue-2328
Open

fix(naming): respect casing of top-level names that collide with acronym dictionary#3023
schani wants to merge 7 commits into
masterfrom
agent/fix-issue-2328

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

Bug

Top-level type name capitalization was not respected when the name (case-insensitively) matched an entry in quicktype's built-in acronym dictionary. For example:

node dist/index.js --lang ts --just-types -t Acme --src-lang schema --src schema.json

produced export interface ACME { ... } instead of the expected export interface Acme { ... }, because "acme" happens to be in the acronym dictionary and the default TypeScript pascal acronym style all-uppercases any word flagged as an acronym — even when the input word was not actually written in all-caps.

Root cause

In splitIntoWords (packages/quicktype-core/src/support/Strings.ts), a word was marked isAcronym if it was either an all-uppercase run or matched the known-acronym dictionary, independently of casing:

const isAcronym =
    (lastLowerCaseIndex !== undefined && allUpper) ||
    knownAcronyms.has(word.toLowerCase() as (typeof acronyms)[number]);

The second clause matched the dictionary regardless of whether the source word was actually all-uppercase, so mixed-case input like Acme or lowercase acme was still flagged as an acronym and re-cased to ACME by the pascal acronym style.

Fix

Only treat a dictionary match as an acronym when the word was actually written in all-uppercase in the source name:

const isAcronym =
    allUpper &&
    (lastLowerCaseIndex !== undefined ||
        knownAcronyms.has(word.toLowerCase() as (typeof acronyms)[number]));

This preserves the existing behavior for genuinely all-caps input (ACME stays ACME, HTMLParser still treats HTML as an acronym) while no longer forcing mixed-case or lowercase names that happen to collide with the dictionary into all-caps.

Test coverage

  • Extended test/inputs/json/priority/name-style.json (which runs for every language fixture) with a top-level-style-relevant property, "Acme": { "HTMLParser": true }, exercising the same word-splitting path across all languages.
  • Added test/unit/top-level-name-capitalization.test.ts, which asserts the exact emitted TypeScript identifier for top-level names Acme, acme, ACME, and HTMLParser — this is needed because round-trip fixtures don't detect identifier casing (generated identifiers are self-consistent), so a unit test is the right complement per repo conventions.

Verification

  • npm run build passes.
  • Repro command now produces export interface Acme { ... } (previously ACME); also manually checked acmeAcme, ACMEACME, HTMLParserHTMLParser, FoobarFoobar, foobarFoobar.
  • npx vitest run test/unit/ — all 167 unit tests pass (26 files).
  • QUICKTEST=true FIXTURE=typescript script/test — all 78/78 TypeScript fixtures pass, including the updated name-style.json under every renderer-option combination (e.g. acronym-style: pascal).
  • Other language fixtures are left to CI.

Fixes #2328

🤖 Generated with Claude Code

schani and others added 3 commits July 20, 2026 17:50
…nym dictionary (#2328)

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
The #2328 fix made splitIntoWords treat a word as an acronym only when it
was written in all-uppercase, which also stopped uniformly lowercase words
that match the known-acronym dictionary (e.g. "json", "id", "asm") from
being styled as acronyms. That regressed generated class names from "JSON"
to "Json", "ID" to "Id", etc., breaking compilation for several languages —
most visibly Kotlin/Klaxon, where a generated `data class Json` shadows
Klaxon's imported `@Json` annotation in the keywords.json fixture.

Narrow the exemption to mixed-case words only: a word carrying an explicit
capitalization ("Foobar", "Acme") is never forced to an acronym, while a
uniformly cased word ("foobar", "FOOBAR", "json") keeps the previous
dictionary behavior. This keeps issue #2328 fixed (top-level "Acme" no
longer becomes "ACME") and restores keywords.json output byte-for-byte to
its pre-change form for java, kotlin, kotlin-jackson, csharp, haskell,
elixir and scala3.

Update the top-level-name unit test accordingly: lowercase "acme" has no
capitalization to respect and stays "ACME", consistent with "json" → "JSON".

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Generated-output differences

1 files differ — 0 modified, 1 new, 0 deleted
195 changed lines — +195 / −0

Open the generated-output report →

@github-actions

Copy link
Copy Markdown

Generated-output differences

1 files differ — 0 modified, 1 new, 0 deleted
195 changed lines — +195 / −0

Open the generated-output report →

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.

Top-level name capitalization is not respected

1 participant