Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/content/1.getting-started/3.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,16 @@ Used to enable and configure Visual Editing. See the [Visual Editing](/getting-s

Used to enable and configure automatic TypeScript type generation for GROQ queries. See the [Type Generation](/getting-started/typegen) section for more details.

When a Sanity config file exists at `configFile`, the schema is resolved from it, including types contributed by plugins. `schemaTypesPath` is used when there is no Sanity config to read.

Available options:

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | `boolean` | `false` | Enable type generation |
| `schemaTypesPath` | `string` | - | Path to schema types module (required when enabled) |
| `schemaTypesPath` | `string` | - | Path to schema types module (required when there is no Sanity config file) |
| `schemaTypesExport` | `string` | `'schemaTypes'` | Export name to read schema types from |
| `workspace` | `string` | - | Workspace name to use when the Sanity config defines multiple workspaces |
| `queryPaths` | `string \| string[]` | `['**/*.{ts,tsx,js,jsx,mjs,cjs,vue,astro}']` | Glob patterns for files to scan |
| `overloadClientMethods` | `boolean` | `true` | Generate `@sanity/client` method overloads |

Expand Down
15 changes: 12 additions & 3 deletions docs/content/1.getting-started/7.typegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

### Prerequisites

Your project must have a Sanity schema types file that exports an array of schema type definitions. This is typically located at `cms/schemaTypes/index.ts`:
Your project must have a Sanity config file (see [`configFile`](/getting-started/configuration#configfile)), or a schema types file that exports an array of schema type definitions.

When a Sanity config file is present, the schema is resolved from it, so types contributed by plugins are included. Otherwise the schema is read from `schemaTypesPath`, which is typically located at `cms/schemaTypes/index.ts`:

```ts{}[cms/schemaTypes/index.ts]
import { movie } from './movie'
Expand Down Expand Up @@ -99,9 +101,15 @@ Enable or disable type generation.
### `typegen.schemaTypesPath`

- Type: **string**
- Required when `enabled` is `true`
- Required when `enabled` is `true` and there is no Sanity config file

Path to your schema types module. This should be a file that exports an array of Sanity schema type definitions. It is ignored when a Sanity config file is found, as the config is a superset of it.

### `typegen.workspace`

- Type: **string**

Path to your schema types module. This should be a file that exports an array of Sanity schema type definitions.
The name of the workspace to generate types for, when your Sanity config defines more than one. If it is omitted, the workspace matching your `projectId` and `dataset` is used.

### `typegen.schemaTypesExport`

Expand Down Expand Up @@ -134,6 +142,7 @@ Currently, you must manually specify the result type in composables (e.g., `useS

In development mode, the module watches for changes to:

- Your Sanity config file (`configFile`)
- Your schema types file (`schemaTypesPath`)
- Any files matching `queryPaths` patterns

Expand Down
23 changes: 23 additions & 0 deletions playground/cms/typegen-fixtures/plugin.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineConfig } from 'sanity'
import { typegenTestPlugin } from './test-plugin'

export default defineConfig({
name: 'default',
projectId: 'typegen-test',
dataset: 'production',
plugins: [typegenTestPlugin()],
schema: {
types: [
{
name: 'pluginDocument',
type: 'document',
fields: [
{
name: 'pluginField',
type: 'pluginString',
},
],
},
],
},
})
13 changes: 13 additions & 0 deletions playground/cms/typegen-fixtures/test-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { definePlugin } from 'sanity'

export const typegenTestPlugin = definePlugin({
name: 'typegen-test-plugin',
schema: {
types: [
{
name: 'pluginString',
type: 'string',
},
],
},
})
38 changes: 38 additions & 0 deletions playground/cms/typegen-fixtures/workspaces.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { defineConfig, definePlugin } from 'sanity'

const firstPlugin = definePlugin({
name: 'first-plugin',
schema: {
types: [{ name: 'firstPluginString', type: 'string' }],
},
})

const secondPlugin = definePlugin({
name: 'second-plugin',
schema: {
types: [{ name: 'secondPluginString', type: 'string' }],
},
})

export default defineConfig([
{
name: 'first',
basePath: '/first',
projectId: 'typegen-test',
dataset: 'production',
plugins: [firstPlugin()],
schema: {
types: [{ name: 'firstDocument', type: 'document', fields: [] }],
},
},
{
name: 'second',
basePath: '/second',
projectId: 'typegen-test',
dataset: 'production',
plugins: [secondPlugin()],
schema: {
types: [{ name: 'secondDocument', type: 'document', fields: [] }],
},
},
])
48 changes: 35 additions & 13 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { fileURLToPath } from 'node:url'
import crypto from 'node:crypto'
import { existsSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
import { createJiti } from 'jiti'
import { createRegExp, exactly } from 'magic-regexp'
import {
addComponent,
Expand Down Expand Up @@ -35,6 +34,7 @@ import { name, version } from '../package.json'
import type { ClientConfig as MinimalClientConfig } from './runtime/minimal-client'
import type { SanityGroqQueryArray, SanityGroqQueryMap, SanityPublicRuntimeConfig, SanityRuntimeConfig, SanityVisualEditingZIndex } from './runtime/types'
import { extractSchemaFromTypesFile } from './runtime/typegen/schema-extractor'
import { loadStudioConfig, selectStudioWorkspace } from './runtime/typegen/studio-config'
import { generateSanityTypes } from './runtime/typegen/type-generator'

export type SanityVisualEditingMode = 'live-visual-editing' | 'visual-editing' | 'custom'
Expand Down Expand Up @@ -120,6 +120,9 @@ export interface SanityTypegenOptions {
enabled?: boolean
/**
* Path to a module exporting your schema types array (e.g. `cms/schemaTypes/index.ts`).
*
* Only used when no Sanity config file is found at `configFile`, as the schema declared there
* includes types contributed by plugins.
*/
schemaTypesPath?: string
/**
Expand All @@ -128,6 +131,10 @@ export interface SanityTypegenOptions {
* @default 'schemaTypes'
*/
schemaTypesExport?: string
/**
* Sanity workspace name to use when the Studio config defines multiple workspaces.
*/
workspace?: string
/**
* Glob(s) to scan for GROQ queries.
*/
Expand Down Expand Up @@ -212,20 +219,23 @@ export default defineNuxtModule<SanityModuleOptions>({
configFile: '~~/cms/sanity.config',
},
async setup(options, nuxt) {
const sanityConfigPath = await resolvePath(options.configFile!)
|| /* backwards compatibility */ resolve(nuxt.options.rootDir, './sanity.json')
const hasSanityConfig = existsSync(sanityConfigPath)

// If explicit configuration is not provided, attempt to load it from `sanity.config.ts`
if (!options.projectId || !options.dataset) {
// Register watcher on sanity.config.ts
const sanityConfigPath = await resolvePath(options.configFile!) || /* backwards compatibility */ resolve(nuxt.options.rootDir, './sanity.json')
const relativeSanityConfigPath = relative(nuxt.options.rootDir, sanityConfigPath)
if (!relativeSanityConfigPath.startsWith('..')) {
nuxt.options.watch.push(createRegExp(exactly(relativeSanityConfigPath)))
}
const jiti = createJiti(import.meta.url, { jsx: true })
if (existsSync(sanityConfigPath)) {
const sanityConfig = await jiti.import(sanityConfigPath, { default: true, try: true }) as { projectId?: string, dataset?: string }
if (sanityConfig) {
options.projectId ||= sanityConfig.projectId
options.dataset ||= sanityConfig.dataset
if (hasSanityConfig) {
const config = await loadStudioConfig(sanityConfigPath)
const workspace = config && selectStudioWorkspace(config.workspaces, { name: options.typegen?.workspace })
if (workspace) {
options.projectId ||= workspace.projectId
options.dataset ||= workspace.dataset
}
}
}
Expand Down Expand Up @@ -383,11 +393,14 @@ export default defineNuxtModule<SanityModuleOptions>({
let typegenTemplate: { filename: string, dst: string } | null = null

if (options.typegen?.enabled) {
if (!options.typegen.schemaTypesPath) {
logger.warn('Sanity typegen is enabled but `schemaTypesPath` is missing.')
if (!options.typegen.schemaTypesPath && !hasSanityConfig) {
logger.warn('Sanity typegen is enabled but no Sanity config was found and `schemaTypesPath` is missing.')
}
else {
const schemaTypesPath = await resolvePath(options.typegen.schemaTypesPath)
const schemaTypesPath = options.typegen.schemaTypesPath
? await resolvePath(options.typegen.schemaTypesPath)
: undefined
const typegenConfigPath = hasSanityConfig ? sanityConfigPath : undefined

const queryPaths = options.typegen.queryPaths
? (Array.isArray(options.typegen.queryPaths) ? options.typegen.queryPaths : [options.typegen.queryPaths])
Expand All @@ -400,6 +413,10 @@ export default defineNuxtModule<SanityModuleOptions>({
const schema = await extractSchemaFromTypesFile({
typesPath: schemaTypesPath,
exportName: options.typegen?.schemaTypesExport,
configPath: typegenConfigPath,
dataset,
projectId,
workspace: options.typegen?.workspace,
})

const result = await generateSanityTypes({
Expand Down Expand Up @@ -464,14 +481,19 @@ export default defineNuxtModule<SanityModuleOptions>({
})

if (nuxt.options.dev) {
nuxt.options.watch.push(schemaTypesPath)
if (schemaTypesPath) {
nuxt.options.watch.push(schemaTypesPath)
}
if (typegenConfigPath) {
nuxt.options.watch.push(typegenConfigPath)
}

nuxt.hook('builder:watch', async (_event, path) => {
if (!typegenTemplate) return

const changedPath = isAbsolute(path) ? path : resolve(nuxt.options.rootDir, path)

const isSchemaChange = changedPath === schemaTypesPath
const isSchemaChange = changedPath === schemaTypesPath || changedPath === typegenConfigPath
const relativeToSrc = relative(nuxt.options.srcDir, changedPath)
const isInSrcDir = !relativeToSrc.startsWith('..')
const isSupportedExt = /\.(?:ts|tsx|js|jsx|mjs|cjs|vue|astro)$/.test(changedPath)
Expand Down
Loading
Loading