Skip to content

feat: polyglot typegen#1091

Draft
jonahsnider wants to merge 3 commits into
mainfrom
001-polyglot-typegen
Draft

feat: polyglot typegen#1091
jonahsnider wants to merge 3 commits into
mainfrom
001-polyglot-typegen

Conversation

@jonahsnider
Copy link
Copy Markdown
Member

agent week project

Signed-off-by: Jonah Snider <jonah@jonahsnider.com>
@jonahsnider jonahsnider changed the title feat: add polyglot typegen config feat: polyglot typegen May 19, 2026
@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

Claude finished @jonahsnider's task in 1m 45s —— View job


Claude PR Review

  • Read PR diff
  • Read modified files (schemas.ts, cliConfig.ts)
  • Check for changeset
  • Check test coverage
  • Post review

Issues to fix

1. Consumer code not updated — new polyglot shape is silently broken at runtime.
The Vite plugin in packages/@sanity/cli/src/actions/build/getViteConfig.ts:180 still passes typegen directly to sanityTypegenPlugin, which destructures legacy flat fields:

sanityTypegenPlugin({ config: typegen, ... })

And plugin-typegen.ts:89-95 reads inputConfig.schema, inputConfig.generates, etc. If a user adopts the new form (typegen: { typescript: { schema, generates } }), those properties are undefined and silently fall back to defaults ('schema.json', 'sanity.types.ts') — no error, no warning, and definitely no folding of the legacy form into typegen.typescript as the JSDoc on cliConfig.ts:69 claims:

"is still accepted and folds into typegen.typescript with a deprecation warning"

No such folding code exists in this PR. Either implement it (a normalizer at the consumer boundary in getViteConfig.ts / devServer.ts, plus the promised deprecation warning) or remove the misleading documentation.

2. Missing changeset.
This is a public-API change to CliConfig (@public types added, TypeGenConfig.formatGeneratedCode widened from boolean to 'oxfmt' | 'prettier' | boolean). Per repo policy a .changeset/*.md file is required. Add a minor-bump changeset for @sanity/cli-core (and @sanity/cli if you wire the consumer side too).

3. Widening TypeGenConfig.formatGeneratedCode is a breaking-ish change.
cliConfig.ts:10 changes formatGeneratedCode: boolean to 'oxfmt' | 'prettier' | boolean. The consumer at plugin-typegen.ts:90 still treats it as a boolean (inputConfig.formatGeneratedCode ?? false), so if a user sets 'oxfmt' it will be coerced to truthy and the plugin will not actually format with oxfmt. Either revert the widening here (keep the new formatter values opt-in to the new per-language types only), or thread the value through sanityTypegenPlugin and act on it.

4. Doc/impl mismatch on formatGeneratedCode default.
BaseLanguageConfig.formatGeneratedCode JSDoc on cliConfig.ts:27 says "Defaults to true", but plugin-typegen.ts:90 defaults to false. Pick one and align both sides.

5. No tests.
The new typegenSchema has six branches (mixed-form rejection + four per-language validators + the Go/PHP regexes), none covered. Add tests under packages/@sanity/cli-core/src/config/__tests__/ validating: legacy passes, polyglot passes, mixed rejected, invalid Go package name rejected, invalid PHP namespace rejected, missing required schema/generates rejected.

6. Generic validation error message.
schemas.ts:67 emits one catch-all string for any failure ("typegen: invalid shape — mixed legacy+per-language form or invalid per-language fields"). For a custom validator with 6+ failure modes, this is hard to act on. Either compose distinct messages per branch (z.refine lets you return a specific message) or — better — replace the z.custom with a discriminated union of typed z.object schemas; you get free per-field error paths.

7. Misleading type assertions in the validator.
Lines 45, 51, 57, 62 cast block.go etc. to the typed interface before validating. The value is unknown at that point — as GoLanguageConfig lies to the reader and the typeof go.schema !== 'string' check is what's actually doing the work. Drop the cast (const go = block.go; if (!isRecord(go)) return false) so the type only narrows once the runtime check passes.

8. PHP namespace regex won't match the documented default.
cliConfig.ts:56 documents the default as Sanity\\Generated (one literal backslash). The regex on schemas.ts:25 (/^[A-Z][A-Za-z0-9_]*(\\[A-Z][A-Za-z0-9_]*)*$/) does correctly match that — but it rejects \\Sanity\\Generated (PHP fully-qualified leading-backslash form), which is a common style. Worth confirming whether you want to allow the leading separator.

9. Helper used once.
present() on schemas.ts:30 is only called twice on adjacent lines (40-41). Inlining as K_NEW.filter(k => block[k] !== undefined) removes a layer of indirection — minor, take or leave.


Summary: the type/schema work looks reasonable as a contract, but this PR ships a config surface whose new branch has no consumer, no folding/deprecation behavior, no tests, and no changeset. I'd hold this until at least items 1, 2, and 5 are addressed.
· branch 001-polyglot-typegen

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

📦 Bundle Stats — @sanity/cli

Compared against main (347ea449)

@sanity/cli

Metric Value vs main (347ea44)
Internal (raw) 2.1 KB -8 B, -0.4%
Internal (gzip) 799 B -2 B, -0.2%
Bundled (raw) 10.97 MB +2.4 KB, +0.0%
Bundled (gzip) 2.06 MB +740 B, +0.0%
Import time 832ms +4ms, +0.5%

bin:sanity

Metric Value vs main (347ea44)
Internal (raw) 1023 B -
Internal (gzip) 486 B -
Bundled (raw) 9.84 MB -
Bundled (gzip) 1.77 MB -
Import time 2.03s +15ms, +0.7%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (347ea449)

Metric Value vs main (347ea44)
Internal (raw) 97.4 KB +1.2 KB, +1.2%
Internal (gzip) 23.0 KB +481 B, +2.1%
Bundled (raw) 21.61 MB +1.8 KB, +0.0%
Bundled (gzip) 3.42 MB +468 B, +0.0%
Import time 795ms +8ms, +1.0%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (347ea449)

Metric Value vs main (347ea44)
Internal (raw) 976 B -
Internal (gzip) 507 B -
Bundled (raw) 50.7 KB -
Bundled (gzip) 12.6 KB -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@github-actions
Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli-core/src/config/cli/schemas.ts 44.9% (- 55.1%)

Comparing 1 changed file against main @ 347ea449243da5b59bde5e126daa341adcf7fdf1

Overall Coverage

Metric Coverage
Statements 84.1% (- 0.2%)
Branches 73.9% (- 0.4%)
Functions 84.2% (+ 0.0%)
Lines 84.7% (- 0.1%)

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