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
643 changes: 39 additions & 604 deletions packages/@sanity/cli/src/actions/init/initAction.ts

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions packages/@sanity/cli/src/actions/init/initApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {styleText} from 'node:util'

import {subdebug, type TelemetryUserProperties} from '@sanity/cli-core'
import {logSymbols} from '@sanity/cli-core/ux'
import {type TelemetryTrace} from '@sanity/telemetry'

import {type InitStepResult} from '../../telemetry/init.telemetry.js'
import {type EditorName} from '../mcp/editorConfigs.js'
import {InitError} from './initError.js'
import {getPostInitMCPPrompt} from './initHelpers.js'
import {type RepoInfo} from './remoteTemplate.js'
import {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'
import {type InitContext, type InitOptions} from './types.js'

const debug = subdebug('init:app')

interface InitAppParams {
defaults: {projectName: string}
mcpConfigured: EditorName[]
options: InitOptions
organizationId: string | undefined
output: InitContext['output']
outputPath: string
remoteTemplateInfo: RepoInfo | undefined
sluggedName: string
trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>
workDir: string
}

export async function initApp({
defaults,
mcpConfigured,
options,
organizationId,
output,
outputPath,
remoteTemplateInfo,
sluggedName,
trace,
workDir,
}: InitAppParams): Promise<void> {
debug('Scaffolding app template')

// Prompt for template and TypeScript
const {template, templateName, useTypeScript} = await selectTemplate(
options,
remoteTemplateInfo,
trace,
)
if (!remoteTemplateInfo && !template) {
throw new InitError(`Template "${templateName}" not found`, 1)
}

// Bootstrap, install deps, git init
const {pkgManager} = await scaffoldAndInstall({
datasetName: '',
defaults,
displayName: '',
options,
organizationId,
output,
outputPath,
projectId: '',
remoteTemplateInfo,
sluggedName,
templateName,
trace,
useTypeScript,
workDir,
})

// App-specific success messages
const isCurrentDir = outputPath === workDir
const goToProjectDir = `\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`

output.log(
`${logSymbols.success} ${styleText(['green', 'bold'], 'Success!')} Your custom app has been scaffolded.`,
)
if (!isCurrentDir) output.log(goToProjectDir)
output.log(
`\n${styleText('bold', 'Next')}, configure the project(s) and dataset(s) your app should work with.`,
)
output.log('\nGet started in `src/App.tsx`, or refer to our documentation for a walkthrough:')
output.log(
styleText(['blue', 'underline'], 'https://www.sanity.io/docs/app-sdk/sdk-configuration'),
)
if (mcpConfigured && mcpConfigured.length > 0) {
const message = await getPostInitMCPPrompt(mcpConfigured)
output.log(`\n${message}`)
output.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)
output.log(
`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,
)
}
output.log('\n')
output.log(`Other helpful commands:`)
output.log(`npx sanity docs browse to open the documentation in a browser`)
output.log(`npx sanity dev to start the development server for your app`)
output.log(`npx sanity deploy to deploy your app`)

debug('App scaffolding complete (pkgManager=%s)', pkgManager)
}
43 changes: 43 additions & 0 deletions packages/@sanity/cli/src/actions/init/initHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {getSanityEnv} from '../../util/getSanityEnv.js'
import {type EditorName} from '../mcp/editorConfigs.js'
import {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'
import {fetchPostInitPrompt} from './fetchPostInitPrompt.js'
import {type InitContext} from './types.js'

// ---------------------------------------------------------------------------
// Helpers shared across init flows (orchestrator, Next.js, studio, etc.)
// ---------------------------------------------------------------------------

export function shouldPrompt(unattended: boolean, flagValue: unknown): boolean {
return !unattended && flagValue === undefined
}

export function flagOrDefault(flagValue: boolean | undefined, defaultValue: boolean): boolean {
return typeof flagValue === 'boolean' ? flagValue : defaultValue
}

export async function getPostInitMCPPrompt(editorsNames: EditorName[]): Promise<string> {
return fetchPostInitPrompt(new Intl.ListFormat('en').format(editorsNames))
}

/**
* When running in a non-production Sanity environment (e.g. staging), write the
* `SANITY_INTERNAL_ENV` variable to a `.env` file in the output directory so that
* the bootstrapped project continues to target the same environment.
*/
export async function writeStagingEnvIfNeeded(
output: InitContext['output'],
outputPath: string,
): Promise<void> {
const sanityEnv = getSanityEnv()
if (sanityEnv === 'production') return

await createOrAppendEnvVars({
envVars: {INTERNAL_ENV: sanityEnv},
filename: '.env',
framework: null,
log: false,
output,
outputPath,
})
}
Loading
Loading