Skip to content

Commit

Permalink
feat: initial branch commit
Browse files Browse the repository at this point in the history
  • Loading branch information
RostiMelk committed Nov 22, 2024
1 parent da20886 commit 48035a1
Show file tree
Hide file tree
Showing 6 changed files with 399 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface BootstrapOptions {
variables: GenerateConfigOptions['variables']
}

export async function bootstrapTemplate(
export async function bootstrapLocalTemplate(
opts: BootstrapOptions,
context: CliCommandContext,
): Promise<ProjectTemplate> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import fs from 'node:fs/promises'
import path from 'node:path'

import {debug} from '../../debug'
import {type CliCommandContext} from '../../types'
import {
applyEnvVariables,
downloadAndExtractRepo,
getGihubRepoInfo,
isNextJsTemplate,
validateRemoteTemplate,
} from '../../util/remoteTemplate'
import {type GenerateConfigOptions} from './createStudioConfig'

export interface BootstrapOptions {
packageName: string
templateName: string
outputPath: string
variables: GenerateConfigOptions['variables']
}

export async function bootstrapRemoteTemplate(
opts: BootstrapOptions,
context: CliCommandContext,
): Promise<void> {
const {apiClient, cliRoot, output} = context

const templatesDir = path.join(cliRoot, 'templates')
const {outputPath, templateName, packageName, variables} = opts
const {projectId, dataset} = variables
const sourceDir = path.join(templatesDir, templateName)
const sharedDir = path.join(templatesDir, 'shared')

debug('Getting Github repo info for "%s"', templateName)
const repoInfo = await getGihubRepoInfo(templateName)

// Validate repo here
await validateRemoteTemplate(repoInfo)

debug('Create new directory "%s"', outputPath)
await fs.mkdir(outputPath, {recursive: true})

await downloadAndExtractRepo(outputPath, repoInfo)

const isNext = await isNextJsTemplate(outputPath)
const envName = isNext ? '.env.local' : '.env'
await applyEnvVariables(outputPath, variables, envName)
}
82 changes: 52 additions & 30 deletions packages/@sanity/cli/src/actions/init-project/initProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,21 @@ import {
type CliCommandDefinition,
type SanityCore,
type SanityModuleInternal,
type SanityUser,
} from '../../types'
import {getClientWrapper} from '../../util/clientWrapper'
import {dynamicRequire} from '../../util/dynamicRequire'
import {getProjectDefaults, type ProjectDefaults} from '../../util/getProjectDefaults'
import {getProviderName} from '../../util/getProviderName'
import {getUserConfig} from '../../util/getUserConfig'
import {isCommandGroup} from '../../util/isCommandGroup'
import {isInteractive} from '../../util/isInteractive'
import {fetchJourneyConfig} from '../../util/journeyConfig'
import {checkIsRemoteTemplate} from '../../util/remoteTemplate'
import {login, type LoginFlags} from '../login/login'
import {createProject} from '../project/createProject'
import {type BootstrapOptions, bootstrapTemplate} from './bootstrapTemplate'
import {bootstrapLocalTemplate, type BootstrapOptions} from './bootstrapLocalTemplate'
import {bootstrapRemoteTemplate} from './bootstrapRemoteTemplate'
import {type GenerateConfigOptions} from './createStudioConfig'
import {absolutify, validateEmptyPath} from './fsUtils'
import {tryGitInit} from './git'
Expand Down Expand Up @@ -143,6 +147,7 @@ export default async function initSanity(
const bareOutput = cliFlags.bare
const env = cliFlags.env
const packageManager = cliFlags['package-manager']
const isRemoteTemplate = checkIsRemoteTemplate(cliFlags.template)

let defaultConfig = cliFlags['dataset-default']
let showDefaultConfigPrompt = !defaultConfig
Expand All @@ -167,6 +172,10 @@ export default async function initSanity(
return
}

if (detectedFramework && isRemoteTemplate) {
throw new Error('A remote template cannot be used with a detected framework.')
}

// Only allow either --project-plan or --coupon
if (intendedCoupon && intendedPlan) {
throw new Error(
Expand Down Expand Up @@ -253,24 +262,20 @@ export default async function initSanity(
}
const envFilename = typeof env === 'string' ? env : envFilenameDefault
if (!envFilename.startsWith('.env')) {
throw new Error(`Env filename must start with .env`)
throw new Error('Env filename must start with .env')
}

const usingBareOrEnv = cliFlags.bare || cliFlags.env
print(
cliFlags.quickstart
? "You're ejecting a remote Sanity project!"
: `You're setting up a new project!`,
)
print(`We'll make sure you have an account with Sanity.io. ${usingBareOrEnv ? '' : `Then we'll`}`)
if (!usingBareOrEnv) {
print('install an open-source JS content editor that connects to')
print('the real-time hosted API on Sanity.io. Hang on.\n')

let introMessage = "Let's get you started with a new project"
if (cliFlags.quickstart) {
introMessage = "Let's get you started with remote Sanity project"
} else if (isRemoteTemplate) {
introMessage = "Let's get you started with a remote Sanity template"
}
print('Press ctrl + C at any time to quit.\n')
print('Prefer web interfaces to terminals?')
print('You can also set up best practice Sanity projects with')
print('your favorite frontends on https://www.sanity.io/templates\n')
print('')
print(` ➡️ ${chalk.gray(introMessage)}`)
print('')

// If the user isn't already authenticated, make it so
const userConfig = getUserConfig()
Expand All @@ -279,7 +284,13 @@ export default async function initSanity(
debug(hasToken ? 'User already has a token' : 'User has no token')
if (hasToken) {
trace.log({step: 'login', alreadyLoggedIn: true})
print('Looks like you already have a Sanity-account. Sweet!\n')
const user = await getUserData(apiClient)
print(
`${chalk.gray(" 👤 You're logged in as %s using %s")}`,
user.name,
getProviderName(user.provider),
)
print('')
} else if (!unattended) {
trace.log({step: 'login'})
await getOrCreateUser()
Expand Down Expand Up @@ -581,18 +592,20 @@ export default async function initSanity(
const templateName = await selectProjectTemplate()
trace.log({step: 'selectProjectTemplate', selectedOption: templateName})
const template = templates[templateName]
if (!template) {
if (isRemoteTemplate === false && !template) {
throw new Error(`Template "${templateName}" not found`)
}

// Use typescript?
const typescriptOnly = template.typescriptOnly === true
let useTypeScript = true
if (!typescriptOnly && typeof cliFlags.typescript === 'boolean') {
useTypeScript = cliFlags.typescript
} else if (!typescriptOnly && !unattended) {
useTypeScript = await promptForTypeScript(prompt)
trace.log({step: 'useTypeScript', selectedOption: useTypeScript ? 'yes' : 'no'})
if (isRemoteTemplate === false && template) {
const typescriptOnly = template.typescriptOnly === true
if (!typescriptOnly && typeof cliFlags.typescript === 'boolean') {
useTypeScript = cliFlags.typescript
} else if (!typescriptOnly && !unattended) {
useTypeScript = await promptForTypeScript(prompt)
trace.log({step: 'useTypeScript', selectedOption: useTypeScript ? 'yes' : 'no'})
}
}

// we enable auto-updates by default, but allow users to specify otherwise
Expand All @@ -618,7 +631,7 @@ export default async function initSanity(

// If the template has a sample dataset, prompt the user whether or not we should import it
const shouldImport =
!unattended && template.datasetUrl && (await promptForDatasetImport(template.importPrompt))
!unattended && template?.datasetUrl && (await promptForDatasetImport(template.importPrompt))

trace.log({step: 'importTemplateDataset', selectedOption: shouldImport ? 'yes' : 'no'})

Expand All @@ -641,7 +654,9 @@ export default async function initSanity(
debug('Failed to update cliInitializedAt metadata')
}),
// Bootstrap Sanity, creating required project files, manifests etc
bootstrapTemplate(templateOptions, context),
isRemoteTemplate
? bootstrapRemoteTemplate(templateOptions, context)
: bootstrapLocalTemplate(templateOptions, context),
])

if (bootstrapPromise.status === 'rejected' && bootstrapPromise.reason instanceof Error) {
Expand Down Expand Up @@ -823,29 +838,26 @@ export default async function initSanity(
isFirstProject: boolean
userAction: 'create' | 'select'
}> {
const spinner = context.output.spinner('Fetching existing projects').start()
const client = apiClient({requireUser: true, requireProject: false})
let projects
let organizations: ProjectOrganization[]

try {
const client = apiClient({requireUser: true, requireProject: false})
const [allProjects, allOrgs] = await Promise.all([
client.projects.list({includeMembers: false}),
client.request({uri: '/organizations'}),
])
projects = allProjects.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
organizations = allOrgs
spinner.succeed()
} catch (err) {
if (unattended && flags.project) {
spinner.succeed()
return {
projectId: flags.project,
displayName: 'Unknown project',
isFirstProject: false,
userAction: 'select',
}
}
spinner.fail()
throw new Error(`Failed to communicate with the Sanity API:\n${err.message}`)
}

Expand Down Expand Up @@ -1479,6 +1491,16 @@ async function getPlanFromCoupon(apiClient: CliApiClient, couponCode: string): P
return planId
}

async function getUserData(apiClient: CliApiClient): Promise<SanityUser> {
return await apiClient({
requireUser: true,
requireProject: false,
}).request({
method: 'GET',
uri: 'users/me',
})
}

async function getPlanFromId(apiClient: CliApiClient, planId: string): Promise<string> {
const response = await apiClient({
requireUser: false,
Expand Down
9 changes: 9 additions & 0 deletions packages/@sanity/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,12 @@ export interface CliConfig {
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}`
}
9 changes: 9 additions & 0 deletions packages/@sanity/cli/src/util/getProviderName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {type SanityUser} from '../types'

export function getProviderName(provider: SanityUser['provider']) {
if (provider === 'google') return 'Google'
if (provider === 'github') return 'GitHub'
if (provider === 'sanity') return 'Email'
if (provider.startsWith('saml-')) return 'SAML'
return provider
}
Loading

0 comments on commit 48035a1

Please sign in to comment.