From 8b0ccd25e595d3f7bad8127813862e3e40937e8e Mon Sep 17 00:00:00 2001 From: Jonah Snider Date: Tue, 19 May 2026 00:15:32 -0700 Subject: [PATCH 1/3] feat: add polyglot typegen config Signed-off-by: Jonah Snider --- .../cli-core/src/config/cli/schemas.ts | 66 ++++++++++++- .../src/config/cli/types/cliConfig.ts | 94 +++++++++++++++++-- 2 files changed, 148 insertions(+), 12 deletions(-) diff --git a/packages/@sanity/cli-core/src/config/cli/schemas.ts b/packages/@sanity/cli-core/src/config/cli/schemas.ts index f9bfb031a..f3f6bdd34 100644 --- a/packages/@sanity/cli-core/src/config/cli/schemas.ts +++ b/packages/@sanity/cli-core/src/config/cli/schemas.ts @@ -1,9 +1,71 @@ import {type PluginOptions as ReactCompilerConfig} from 'babel-plugin-react-compiler' import {z} from 'zod/mini' -import {type CliConfig, type TypeGenConfig} from './types/cliConfig' +import { + type CliConfig, + type GoLanguageConfig, + type PhpLanguageConfig, + type PolyglotTypeGenConfig, + type SwiftLanguageConfig, + type TypeGenConfig, + type TypeScriptLanguageConfig, +} from './types/cliConfig' import {type UserViteConfig} from './types/userViteConfig' +const K_NEW = ['typescript', 'go', 'php', 'swift'] as const +const K_LEGACY = [ + 'schema', + 'generates', + 'path', + 'overloadClientMethods', + 'formatGeneratedCode', +] as const + +const GO_PACKAGE_RE = /^[a-z][a-z0-9_]*$/ +const PHP_NAMESPACE_RE = /^[A-Z][A-Za-z0-9_]*(\\[A-Z][A-Za-z0-9_]*)*$/ + +const isRecord = (v: unknown): v is Record => + typeof v === 'object' && v !== null && !Array.isArray(v) + +const present = (block: Record, keys: readonly string[]): string[] => + keys.filter((k) => block[k] !== undefined) + +const typegenSchema = z.custom< + (Partial & {enabled?: boolean}) | PolyglotTypeGenConfig +>((raw) => { + if (raw === undefined || raw === null) return true + if (!isRecord(raw)) return false + const block = raw as Record + + const newKeys = present(block, K_NEW) + const legacyKeys = present(block, K_LEGACY) + if (newKeys.length > 0 && legacyKeys.length > 0) return false + + if (block.go !== undefined) { + const go = block.go as GoLanguageConfig + if (!isRecord(go)) return false + if (typeof go.schema !== 'string' || typeof go.generates !== 'string') return false + if (go.packageName !== undefined && !GO_PACKAGE_RE.test(String(go.packageName))) return false + } + if (block.php !== undefined) { + const php = block.php as PhpLanguageConfig + if (!isRecord(php)) return false + if (typeof php.schema !== 'string' || typeof php.generates !== 'string') return false + if (php.namespace !== undefined && !PHP_NAMESPACE_RE.test(String(php.namespace))) return false + } + if (block.swift !== undefined) { + const swift = block.swift as SwiftLanguageConfig + if (!isRecord(swift)) return false + if (typeof swift.schema !== 'string' || typeof swift.generates !== 'string') return false + } + if (block.typescript !== undefined) { + const ts = block.typescript as TypeScriptLanguageConfig + if (!isRecord(ts)) return false + if (typeof ts.schema !== 'string' || typeof ts.generates !== 'string') return false + } + return true +}, 'typegen: invalid shape — mixed legacy+per-language form or invalid per-language fields') + /** * @public */ @@ -86,5 +148,5 @@ export const cliConfigSchema = z.object({ vite: z.optional(z.custom()), - typegen: z.optional(z.custom & {enabled?: boolean}>()), + typegen: z.optional(typegenSchema), }) satisfies z.core.$ZodType diff --git a/packages/@sanity/cli-core/src/config/cli/types/cliConfig.ts b/packages/@sanity/cli-core/src/config/cli/types/cliConfig.ts index b823c078e..6ca6d4ad1 100644 --- a/packages/@sanity/cli-core/src/config/cli/types/cliConfig.ts +++ b/packages/@sanity/cli-core/src/config/cli/types/cliConfig.ts @@ -2,14 +2,82 @@ import {type PluginOptions as ReactCompilerConfig} from 'babel-plugin-react-comp import {type UserViteConfig} from './userViteConfig' +/** + * Legacy flat-form typegen config. Equivalent to nesting these fields under `typegen.typescript`. + * @public + */ export interface TypeGenConfig { - formatGeneratedCode: boolean + formatGeneratedCode: 'oxfmt' | 'prettier' | boolean generates: string overloadClientMethods: boolean path: string | string[] schema: string } +/** + * Fields shared by every per-language typegen sub-block. + * @public + */ +export interface BaseLanguageConfig { + /** Output file path. Must end in the language's file extension. */ + generates: string + /** Path to the pre-extracted Sanity schema JSON file. */ + schema: string + + /** Format the emitted source. Defaults to `true`. */ + formatGeneratedCode?: 'oxfmt' | 'prettier' | boolean +} + +/** + * TypeScript per-language typegen config. + * @public + */ +export interface TypeScriptLanguageConfig extends BaseLanguageConfig { + /** Overload `@sanity/client` methods to return typed results. Default true. */ + overloadClientMethods?: boolean + /** Source file globs for GROQ query extraction. */ + path?: string | string[] +} + +/** + * Go per-language typegen config. + * @public + */ +export interface GoLanguageConfig extends BaseLanguageConfig { + /** Go package name. Defaults to the basename of `dirname(generates)`. */ + packageName?: string +} + +/** + * PHP per-language typegen config. + * @public + */ +export interface PhpLanguageConfig extends BaseLanguageConfig { + /** PHP namespace. Defaults to `Sanity\\Generated`. */ + namespace?: string +} + +/** + * Swift per-language typegen config. Reserved for future per-language knobs. + * @public + */ +export type SwiftLanguageConfig = BaseLanguageConfig + +/** + * Polyglot typegen configuration. Configure one or more language sub-blocks under + * `typegen`. The legacy flat shape (`typegen: {schema, generates, ...}`) is still + * accepted and folds into `typegen.typescript` with a deprecation warning. + * @public + */ +export interface PolyglotTypeGenConfig { + /** Enable typegen as part of sanity dev and sanity build. */ + enabled?: boolean + go?: GoLanguageConfig + php?: PhpLanguageConfig + swift?: SwiftLanguageConfig + typescript?: TypeScriptLanguageConfig +} + /** * @public */ @@ -134,16 +202,22 @@ export interface CliConfig { studioHost?: string /** - * Configuration for Sanity typegen + * Configuration for Sanity typegen. + * + * Accepts either: + * - The new per-language form: `{ typescript?, go?, php?, swift?, enabled? }` + * - The legacy flat form: `{ schema, generates, path?, overloadClientMethods?, formatGeneratedCode?, enabled? }` (deprecated; folds into `typegen.typescript`) */ - typegen?: Partial & { - /** - * Enable typegen as part of sanity dev and sanity build. - * When enabled, types are generated on startup and when files change. - * Defaults to `false` - */ - enabled?: boolean - } + typegen?: + | (Partial & { + /** + * Enable typegen as part of sanity dev and sanity build. + * When enabled, types are generated on startup and when files change. + * Defaults to `false` + */ + enabled?: boolean + }) + | PolyglotTypeGenConfig /** Exposes the default Vite configuration for custom apps and the Studio so it can be changed and extended. */ vite?: UserViteConfig From 0a02bd5e4c5097754683f724ad728fa506c9524e Mon Sep 17 00:00:00 2001 From: "squiggler-app[bot]" <265501495+squiggler-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 09:16:49 +0000 Subject: [PATCH 2/3] chore: update auto-generated changeset for PR #1091 --- .changeset/pr-1091.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/pr-1091.md diff --git a/.changeset/pr-1091.md b/.changeset/pr-1091.md new file mode 100644 index 000000000..dd614c072 --- /dev/null +++ b/.changeset/pr-1091.md @@ -0,0 +1,6 @@ + +--- +'@sanity/cli-core': minor +--- + +feat: add polyglot typegen config \ No newline at end of file From 326a9530b5d49a61cf1e07605d84cb07d03fb0e9 Mon Sep 17 00:00:00 2001 From: "squiggler-app[bot]" <265501495+squiggler-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 09:17:15 +0000 Subject: [PATCH 3/3] chore: update auto-generated changeset for PR #1091 --- .changeset/pr-1091.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/pr-1091.md b/.changeset/pr-1091.md index dd614c072..6689985f7 100644 --- a/.changeset/pr-1091.md +++ b/.changeset/pr-1091.md @@ -3,4 +3,4 @@ '@sanity/cli-core': minor --- -feat: add polyglot typegen config \ No newline at end of file +feat: polyglot typegen \ No newline at end of file