Skip to content
Merged
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
121 changes: 121 additions & 0 deletions packages/@sanity/cli/src/actions/init/initApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {styleText} from 'node:util'

import {type Output, 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 {getPostInitMCPPrompt} from './initHelpers.js'
import {type RepoInfo} from './remoteTemplate.js'
import {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'

export async function initApp({
autoUpdates,
defaults,
error,
git,
noGit,
mcpConfigured,
organizationId,
output,
outputPath,
overwriteFiles,
packageManager,
remoteTemplateInfo,
sluggedName,
template,
templateToken,
trace,
typescript,
unattended,
workDir,
}: {
autoUpdates: boolean
defaults: {projectName: string}
error: Output['error']
git?: boolean | string
noGit?: boolean
mcpConfigured: EditorName[]
organizationId: string | undefined
output: Output
outputPath: string
overwriteFiles?: boolean
packageManager?: string
remoteTemplateInfo: RepoInfo | undefined
sluggedName: string
template?: string
templateToken?: string
trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>
typescript?: boolean
unattended: boolean
workDir: string
}): Promise<void> {
const {
template: resolvedTemplate,
templateName,
useTypeScript,
} = await selectTemplate({
remoteTemplateInfo,
template,
trace,
typescript,
unattended,
})

if (!remoteTemplateInfo && !resolvedTemplate) {
error(`Template "${templateName}" not found`, {exit: 1})
}

await scaffoldAndInstall({
autoUpdates,
datasetName: '',
defaults,
displayName: '',
git,
noGit,
organizationId,
output,
outputPath,
overwriteFiles,
packageManager,
projectId: '',
remoteTemplateInfo,
sluggedName,
templateName,
templateToken,
trace,
unattended,
useTypeScript,
workDir,
})

const isCurrentDir = outputPath === process.cwd()
const goToProjectDir = `\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`

//output for custom apps here
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`)
}
44 changes: 44 additions & 0 deletions packages/@sanity/cli/src/actions/init/initHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {type Output} from '@sanity/cli-core'

import {getSanityEnv} from '../../util/getSanityEnv.js'
import {type EditorName} from '../mcp/editorConfigs.js'
import {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'
import {fetchPostInitPrompt} from './fetchPostInitPrompt.js'

/**
* Returns `true` when the user should be prompted for a flag value:
* i.e. we are NOT in unattended mode AND the flag was not explicitly provided.
*/
export function shouldPrompt(unattended: boolean, flagValue: unknown): boolean {
return !unattended && flagValue === undefined
}

/**
* Returns the flag value if it is a boolean, otherwise returns the default.
*/
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: 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