UNPKG

@sanity/cli

Version:

Sanity CLI tool for managing Sanity installations, managing plugins, schemas and datasets

388 lines (325 loc) • 10.3 kB
import {type SanityClient} from '@sanity/client' import {type TelemetryLogger} from '@sanity/telemetry' import type chalk from 'chalk' import {type Answers, type ChoiceCollection, type DistinctQuestion, type Separator} from 'inquirer' import {type Options, type Ora} from 'ora' import {type ConfigEnv, type InlineConfig} from 'vite' import {type CliPackageManager} from './packageManager' import {type ClientRequirements} from './util/clientWrapper' import {type CliConfigResult} from './util/getCliConfig' export interface SanityCore { requiredCliVersionRange: string commands: (CliCommandDefinition | CliCommandGroupDefinition)[] } export interface SanityModuleInternal { cliProjectCommands: { requiredCliVersionRange: string commands: (CliCommandDefinition | CliCommandGroupDefinition)[] } } export interface PackageJson { name: string version: string scripts?: Record<string, string> description?: string author?: string license?: string private?: boolean dependencies?: Record<string, string> devDependencies?: Record<string, string> peerDependencies?: Record<string, string> repository?: {type: string; url: string} } export interface CliCommandGroupDefinition { name: string signature: string isGroupRoot: boolean description: string hideFromHelp?: boolean } export interface ResolvedCliCommand { command: CliCommandDefinition | CliCommandGroupDefinition commandName: string parentName?: string isGroup: boolean isCommand: boolean } export type CliCommandAction<F = Record<string, unknown>> = ( args: CliCommandArguments<F>, context: CliCommandContext, ) => Promise<unknown> export interface CliCommandDefinition<F = Record<string, unknown>> { name: string group?: string signature: string description: string helpText: string action: CliCommandAction<F> hideFromHelp?: boolean } export interface CliCommandArguments<F = Record<string, unknown>> { groupOrCommand: string argv: string[] extOptions: F argsWithoutOptions: string[] extraArguments: string[] } export type CliCommandContext = CliV2CommandContext | CliV3CommandContext export interface CliBaseCommandContext { output: CliOutputter prompt: CliPrompter apiClient: CliApiClient sanityMajorVersion: 2 | 3 cliConfigPath?: string cliRoot: string workDir: string corePath?: string chalk: typeof chalk commandRunner: CliCommandRunner fromInitCommand?: boolean } export interface TelemetryUserProperties { runtime: string runtimeVersion: string cliVersion: string machinePlatform: string cpuArchitecture: string projectId?: string dataset?: string } export interface CliV2CommandContext extends CliBaseCommandContext { sanityMajorVersion: 2 cliConfig?: SanityJson cliPackageManager?: CliPackageManager telemetry: TelemetryLogger<TelemetryUserProperties> } export interface CliV3CommandContext extends CliBaseCommandContext { sanityMajorVersion: 3 cliConfig?: CliConfig cliPackageManager: CliPackageManager telemetry: TelemetryLogger<TelemetryUserProperties> } export interface CliCommandRunner { commands: Readonly<(CliCommandDefinition | CliCommandGroupDefinition)[]> commandGroups: Readonly<Record<string, (CliCommandDefinition | CliCommandGroupDefinition)[]>> runCommand( commandOrGroup: string, args: CliCommandArguments, options: CommandRunnerOptions, ): Promise<unknown> resolveSubcommand( group: (CliCommandDefinition | CliCommandGroupDefinition)[], subCommandName: string, parentGroupName: string, ): ResolvedCliCommand | null } export interface CliUserConfig { cliLastUpdateCheck?: number cliLastUpdateNag?: number authToken?: string authType?: string } export interface CommandRunnerOptions { cliConfig: CliConfigResult | null cliRoot: string workDir: string corePath: string | undefined telemetry: TelemetryLogger<TelemetryUserProperties> } export interface CliOutputter { print: (...args: unknown[]) => void success: (...args: unknown[]) => void warn: (...args: unknown[]) => void error: (...args: unknown[]) => void clear: () => void spinner(options: Options | string): Ora } export type SinglePrompt = | (Omit<DistinctQuestion, 'name'> & {type: 'list'; choices: ChoiceCollection}) | (Omit<DistinctQuestion, 'name'> & {type: 'confirm'}) | (Omit<DistinctQuestion, 'name'> & {type: 'input'}) export type CliPrompter = (<T extends Answers = Answers>( questions: DistinctQuestion<T>[], ) => Promise<T>) & { Separator: typeof Separator single: <T = string>(question: SinglePrompt) => Promise<T> } export type CliApiClient = (options?: ClientRequirements) => SanityClient export interface CliYarnOptions { print?: CliOutputter['print'] error?: CliOutputter['error'] rootDir?: string } export type CliStubbedYarn = (args: string[], options?: CliYarnOptions) => Promise<void> export interface CliApiConfig { projectId?: string dataset?: string } export interface SanityJson { root?: boolean project?: { name?: string basePath?: string } api?: CliApiConfig // eslint-disable-next-line camelcase __experimental_spaces?: { name: string title: string default?: true api: { projectId?: string dataset?: string } }[] plugins?: string[] parts?: { name?: string path?: string implements?: string description?: string }[] env?: { production?: SanityJson staging?: SanityJson development?: SanityJson } } export interface GraphQLAPIConfig { /** * ID of GraphQL API. Only (currently) required when using the `--api` flag * for `sanity graphql deploy`, in order to only deploy a specific API. */ id?: string /** * Name of workspace containing the schema to deploy * * Optional, defaults to `default` (eg the one used if no `name` is defined) */ workspace?: string /** * Name of source containing the schema to deploy, within the configured workspace * * Optional, defaults to `default` (eg the one used if no `name` is defined) */ source?: string /** * API tag for this API - allows deploying multiple different APIs to a single dataset * * Optional, defaults to `default` */ tag?: string /** * Whether or not to deploy a "GraphQL Playground" to the API url - an HTML interface that allows * running queries and introspecting the schema from the browser. Note that this interface is not * secured in any way, but as the schema definition and API route is generally open, this does not * expose any more information than is otherwise available - it only makes it more discoverable. * * Optional, defaults to `true` */ playground?: boolean /** * Generation of API to auto-generate from schema. New APIs should use the latest (`gen3`). * * Optional, defaults to `gen3` */ generation?: 'gen3' | 'gen2' | 'gen1' /** * Define document interface fields (`_id`, `_type` etc) as non-nullable. * If you never use a document type as an object (within other documents) in your schema types, * you can (and probably should) set this to `true`. Because a document type _could_ be used * inside other documents, it is by default set to `false`, as in these cases these fields * _can_ be null. * * Optional, defaults to `false` */ nonNullDocumentFields?: boolean /** * Suffix to use for generated filter types. * * Optional, Defaults to `Filter`. * */ filterSuffix?: string } /** * Until these types are on npm: https://github.com/facebook/react/blob/0bc30748730063e561d87a24a4617526fdd38349/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts#L39-L122 * @beta */ export interface ReactCompilerConfig { /** * @see https://react.dev/learn/react-compiler#existing-projects */ sources?: Array<string> | ((filename: string) => boolean) | null /** * The minimum major version of React that the compiler should emit code for. If the target is 19 * or higher, the compiler emits direct imports of React runtime APIs needed by the compiler. On * versions prior to 19, an extra runtime package react-compiler-runtime is necessary to provide * a userspace approximation of runtime APIs. * @see https://react.dev/learn/react-compiler#using-react-compiler-with-react-17-or-18 */ target: '18' | '19' panicThreshold?: 'ALL_ERRORS' | 'CRITICAL_ERRORS' | 'NONE' compilationMode?: 'infer' | 'syntax' | 'annotation' | 'all' } interface AppConfig { organizationId: string /** * Defaults to './src/App' */ entry?: string id?: string } export interface CliConfig { api?: CliApiConfig project?: { basePath?: string } /** * Wraps the Studio in `<React.StrictMode>` root to aid flagging potential problems related to concurrent features (`startTransition`, `useTransition`, `useDeferredValue`, `Suspense`) * Can also be enabled by setting `SANITY_STUDIO_REACT_STRICT_MODE="true"|"false"`. * It only applies to `sanity dev` in dev mode, it's ignored in `sanity build` and in production. * Defaults to `false` */ reactStrictMode?: boolean /** * The React Compiler is currently in beta, and is disabled by default. * @see https://react.dev/learn/react-compiler * @beta */ reactCompiler?: ReactCompilerConfig server?: { hostname?: string port?: number } graphql?: GraphQLAPIConfig[] vite?: UserViteConfig autoUpdates?: boolean studioHost?: string /** * Parameter used to configure other kinds of applications. * Signals to `sanity` commands that this is not a studio. */ app?: AppConfig /** * Configuration for Sanity media libraries. */ mediaLibrary?: { /** * The path to the Media Library aspects directory. When using the CLI to manage aspects, this * is the directory they will be read from and written to. */ aspectsPath: string } } export type UserViteConfig = | InlineConfig | ((config: InlineConfig, env: ConfigEnv) => InlineConfig | Promise<InlineConfig>) export type SanityUser = { id: string name: string email: string profileImage?: string tosAcceptedAt?: string provider: 'google' | 'github' | 'sanity' | `saml-${string}` }