@sanity/cli 3.59.2-canary.33 → 3.59.2-corel-presentation-lcapi.562
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/_chunks-cjs/cli.js +34360 -26058
- package/lib/_chunks-cjs/cli.js.map +1 -1
- package/lib/_chunks-cjs/cliWorker.js.map +1 -1
- package/lib/_chunks-cjs/generateAction.js.map +1 -1
- package/lib/_chunks-cjs/getCliConfig.js +1 -1
- package/lib/_chunks-cjs/getCliConfig.js.map +1 -1
- package/lib/_chunks-cjs/journeyConfig.js.map +1 -1
- package/lib/_chunks-cjs/loadEnv.js +200 -202
- package/lib/_chunks-cjs/loadEnv.js.map +1 -1
- package/lib/index.d.mts +37 -1
- package/lib/index.d.ts +37 -1
- package/lib/index.esm.js +223 -224
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +223 -224
- package/lib/index.mjs.map +1 -1
- package/lib/workers/getCliConfig.js.map +1 -1
- package/lib/workers/typegenGenerate.js.map +1 -1
- package/package.json +17 -19
- package/src/CommandRunner.ts +1 -2
- package/src/actions/init-project/{bootstrapTemplate.ts → bootstrapLocalTemplate.ts} +8 -21
- package/src/actions/init-project/bootstrapRemoteTemplate.ts +118 -0
- package/src/actions/init-project/git.ts +2 -2
- package/src/actions/init-project/initProject.ts +158 -146
- package/src/actions/init-project/readPackageJson.ts +18 -0
- package/src/actions/init-project/templates/nextjs/index.ts +16 -0
- package/src/actions/init-project/templates/nextjs/schemaTypes/blog.ts +2 -2
- package/src/actions/init-project/updateInitialTemplateMetadata.ts +24 -0
- package/src/actions/login/login.ts +2 -3
- package/src/actions/versions/findSanityModuleVersions.ts +0 -1
- package/src/commands/index.ts +2 -2
- package/src/commands/init/initCommand.ts +7 -67
- package/src/commands/learn/learnCommand.ts +20 -0
- package/src/commands/logout/logoutCommand.ts +1 -1
- package/src/outputters/cliOutputter.ts +21 -8
- package/src/studioDependencies.ts +1 -1
- package/src/types.ts +41 -1
- package/src/util/frameworkPort.ts +63 -0
- package/src/util/generateCommandsDocumentation.ts +7 -4
- package/src/util/getCliConfig.ts +1 -1
- package/src/util/getProviderName.ts +9 -0
- package/src/util/remoteTemplate.ts +320 -0
- package/templates/get-started/plugins/sanity-plugin-tutorial/GetStartedTutorial.tsx +4 -4
- package/src/actions/init-plugin/initPlugin.ts +0 -119
- package/src/actions/init-plugin/pluginTemplates.ts +0 -38
- package/src/actions/init-project/reconfigureV2Project.ts +0 -446
- package/src/commands/upgrade/upgradeCommand.ts +0 -38
- package/src/commands/upgrade/upgradeDependencies.ts +0 -289
@@ -1,16 +1,16 @@
|
|
1
|
-
import {existsSync
|
1
|
+
import {existsSync} from 'node:fs'
|
2
2
|
import fs from 'node:fs/promises'
|
3
3
|
import path from 'node:path'
|
4
4
|
|
5
5
|
import {type DatasetAclMode, type SanityProject} from '@sanity/client'
|
6
6
|
import {type Framework} from '@vercel/frameworks'
|
7
|
+
import {type detectFrameworkRecord} from '@vercel/fs-detectors'
|
7
8
|
import dotenv from 'dotenv'
|
8
9
|
import execa, {type CommonOptions} from 'execa'
|
9
10
|
import {deburr, noop} from 'lodash'
|
10
11
|
import pFilter from 'p-filter'
|
11
12
|
import resolveFrom from 'resolve-from'
|
12
|
-
import
|
13
|
-
import which from 'which'
|
13
|
+
import semver from 'semver'
|
14
14
|
|
15
15
|
import {CLIInitStepCompleted} from '../../__telemetry__/init.telemetry'
|
16
16
|
import {type InitFlags} from '../../commands/init/initCommand'
|
@@ -33,17 +33,21 @@ import {
|
|
33
33
|
type CliCommandDefinition,
|
34
34
|
type SanityCore,
|
35
35
|
type SanityModuleInternal,
|
36
|
+
type SanityUser,
|
36
37
|
} from '../../types'
|
37
38
|
import {getClientWrapper} from '../../util/clientWrapper'
|
38
39
|
import {dynamicRequire} from '../../util/dynamicRequire'
|
39
40
|
import {getProjectDefaults, type ProjectDefaults} from '../../util/getProjectDefaults'
|
41
|
+
import {getProviderName} from '../../util/getProviderName'
|
40
42
|
import {getUserConfig} from '../../util/getUserConfig'
|
41
43
|
import {isCommandGroup} from '../../util/isCommandGroup'
|
42
44
|
import {isInteractive} from '../../util/isInteractive'
|
43
45
|
import {fetchJourneyConfig} from '../../util/journeyConfig'
|
46
|
+
import {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from '../../util/remoteTemplate'
|
44
47
|
import {login, type LoginFlags} from '../login/login'
|
45
48
|
import {createProject} from '../project/createProject'
|
46
|
-
import {
|
49
|
+
import {bootstrapLocalTemplate} from './bootstrapLocalTemplate'
|
50
|
+
import {bootstrapRemoteTemplate} from './bootstrapRemoteTemplate'
|
47
51
|
import {type GenerateConfigOptions} from './createStudioConfig'
|
48
52
|
import {absolutify, validateEmptyPath} from './fsUtils'
|
49
53
|
import {tryGitInit} from './git'
|
@@ -55,7 +59,7 @@ import {
|
|
55
59
|
promptForNextTemplate,
|
56
60
|
promptForStudioPath,
|
57
61
|
} from './prompts/nextjs'
|
58
|
-
import {
|
62
|
+
import {readPackageJson} from './readPackageJson'
|
59
63
|
import templates from './templates'
|
60
64
|
import {
|
61
65
|
sanityCliTemplate,
|
@@ -65,19 +69,13 @@ import {
|
|
65
69
|
} from './templates/nextjs'
|
66
70
|
|
67
71
|
// eslint-disable-next-line no-process-env
|
68
|
-
const isCI = process.env.CI
|
72
|
+
const isCI = Boolean(process.env.CI)
|
69
73
|
|
70
74
|
/**
|
71
75
|
* @deprecated - No longer used
|
72
76
|
*/
|
73
77
|
export interface InitOptions {
|
74
78
|
template: string
|
75
|
-
// /**
|
76
|
-
// * Used for initializing a project from a server schema that is saved in the Journey API
|
77
|
-
// * This will override the `template` option.
|
78
|
-
// * @beta
|
79
|
-
// */
|
80
|
-
// journeyProjectId?: string
|
81
79
|
outputDir: string
|
82
80
|
name: string
|
83
81
|
displayName: string
|
@@ -110,24 +108,20 @@ export interface ProjectOrganization {
|
|
110
108
|
// eslint-disable-next-line max-statements, complexity
|
111
109
|
export default async function initSanity(
|
112
110
|
args: CliCommandArguments<InitFlags>,
|
113
|
-
context: CliCommandContext & {
|
111
|
+
context: CliCommandContext & {
|
112
|
+
detectedFramework: Awaited<ReturnType<typeof detectFrameworkRecord>>
|
113
|
+
},
|
114
114
|
): Promise<void> {
|
115
|
-
const {
|
116
|
-
output,
|
117
|
-
prompt,
|
118
|
-
workDir,
|
119
|
-
apiClient,
|
120
|
-
chalk,
|
121
|
-
sanityMajorVersion,
|
122
|
-
telemetry,
|
123
|
-
detectedFramework,
|
124
|
-
} = context
|
115
|
+
const {output, prompt, workDir, apiClient, chalk, telemetry, detectedFramework} = context
|
125
116
|
|
126
117
|
const trace = telemetry.trace(CLIInitStepCompleted)
|
127
118
|
|
128
119
|
const cliFlags = args.extOptions
|
129
120
|
const unattended = cliFlags.y || cliFlags.yes
|
130
121
|
const print = unattended ? noop : output.print
|
122
|
+
const success = output.success
|
123
|
+
const warn = output.warn
|
124
|
+
|
131
125
|
const intendedPlan = cliFlags['project-plan']
|
132
126
|
const intendedCoupon = cliFlags.coupon
|
133
127
|
const reconfigure = cliFlags.reconfigure
|
@@ -137,6 +131,11 @@ export default async function initSanity(
|
|
137
131
|
const env = cliFlags.env
|
138
132
|
const packageManager = cliFlags['package-manager']
|
139
133
|
|
134
|
+
let remoteTemplateInfo: RepoInfo | undefined
|
135
|
+
if (cliFlags.template && checkIsRemoteTemplate(cliFlags.template)) {
|
136
|
+
remoteTemplateInfo = await getGitHubRepoInfo(cliFlags.template, cliFlags['template-token'])
|
137
|
+
}
|
138
|
+
|
140
139
|
let defaultConfig = cliFlags['dataset-default']
|
141
140
|
let showDefaultConfigPrompt = !defaultConfig
|
142
141
|
|
@@ -155,9 +154,10 @@ export default async function initSanity(
|
|
155
154
|
},
|
156
155
|
})
|
157
156
|
|
158
|
-
if (
|
159
|
-
|
160
|
-
|
157
|
+
if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {
|
158
|
+
throw new Error(
|
159
|
+
`A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,
|
160
|
+
)
|
161
161
|
}
|
162
162
|
|
163
163
|
// Only allow either --project-plan or --coupon
|
@@ -246,24 +246,8 @@ export default async function initSanity(
|
|
246
246
|
}
|
247
247
|
const envFilename = typeof env === 'string' ? env : envFilenameDefault
|
248
248
|
if (!envFilename.startsWith('.env')) {
|
249
|
-
throw new Error(
|
250
|
-
}
|
251
|
-
|
252
|
-
const usingBareOrEnv = cliFlags.bare || cliFlags.env
|
253
|
-
print(
|
254
|
-
cliFlags.quickstart
|
255
|
-
? "You're ejecting a remote Sanity project!"
|
256
|
-
: `You're setting up a new project!`,
|
257
|
-
)
|
258
|
-
print(`We'll make sure you have an account with Sanity.io. ${usingBareOrEnv ? '' : `Then we'll`}`)
|
259
|
-
if (!usingBareOrEnv) {
|
260
|
-
print('install an open-source JS content editor that connects to')
|
261
|
-
print('the real-time hosted API on Sanity.io. Hang on.\n')
|
249
|
+
throw new Error('Env filename must start with .env')
|
262
250
|
}
|
263
|
-
print('Press ctrl + C at any time to quit.\n')
|
264
|
-
print('Prefer web interfaces to terminals?')
|
265
|
-
print('You can also set up best practice Sanity projects with')
|
266
|
-
print('your favorite frontends on https://www.sanity.io/templates\n')
|
267
251
|
|
268
252
|
// If the user isn't already authenticated, make it so
|
269
253
|
const userConfig = getUserConfig()
|
@@ -272,12 +256,20 @@ export default async function initSanity(
|
|
272
256
|
debug(hasToken ? 'User already has a token' : 'User has no token')
|
273
257
|
if (hasToken) {
|
274
258
|
trace.log({step: 'login', alreadyLoggedIn: true})
|
275
|
-
|
259
|
+
const user = await getUserData(apiClient)
|
260
|
+
success('You are logged in as %s using %s', user.email, getProviderName(user.provider))
|
276
261
|
} else if (!unattended) {
|
277
262
|
trace.log({step: 'login'})
|
278
263
|
await getOrCreateUser()
|
279
264
|
}
|
280
265
|
|
266
|
+
let introMessage = 'Fetching existing projects'
|
267
|
+
if (cliFlags.quickstart) {
|
268
|
+
introMessage = "Eject your existing project's Sanity configuration"
|
269
|
+
}
|
270
|
+
success(introMessage)
|
271
|
+
print('')
|
272
|
+
|
281
273
|
const flags = await prepareFlags()
|
282
274
|
// We're authenticated, now lets select or create a project
|
283
275
|
const {projectId, displayName, isFirstProject, datasetName, schemaUrl} = await getProjectDetails()
|
@@ -288,7 +280,8 @@ export default async function initSanity(
|
|
288
280
|
|
289
281
|
// If user doesn't want to output any template code
|
290
282
|
if (bareOutput) {
|
291
|
-
|
283
|
+
success('Below are your project details')
|
284
|
+
print('')
|
292
285
|
print(`Project ID: ${chalk.cyan(projectId)}`)
|
293
286
|
print(`Dataset: ${chalk.cyan(datasetName)}`)
|
294
287
|
print(
|
@@ -298,7 +291,8 @@ export default async function initSanity(
|
|
298
291
|
}
|
299
292
|
|
300
293
|
let initNext = false
|
301
|
-
|
294
|
+
const isNextJs = detectedFramework?.slug === 'nextjs'
|
295
|
+
if (isNextJs) {
|
302
296
|
initNext = await prompt.single({
|
303
297
|
type: 'confirm',
|
304
298
|
message:
|
@@ -327,6 +321,26 @@ export default async function initSanity(
|
|
327
321
|
// Ensure we are using the output path provided by user
|
328
322
|
outputPath = answers.outputPath
|
329
323
|
|
324
|
+
if (isNextJs) {
|
325
|
+
const packageJson = readPackageJson(`${outputPath}/package.json`)
|
326
|
+
const reactVersion = packageJson?.dependencies?.react
|
327
|
+
|
328
|
+
if (reactVersion) {
|
329
|
+
const isUsingReact19 = semver.coerce(reactVersion)?.major === 19
|
330
|
+
const isUsingNextJs15 = semver.coerce(detectedFramework?.detectedVersion)?.major === 15
|
331
|
+
|
332
|
+
if (isUsingNextJs15 && isUsingReact19) {
|
333
|
+
warn('╭────────────────────────────────────────────────────────────╮')
|
334
|
+
warn('│ │')
|
335
|
+
warn('│ It looks like you are using Next.js 15 and React 19 │')
|
336
|
+
warn('│ Please read our compatibility guide. │')
|
337
|
+
warn('│ https://www.sanity.io/help/react-19 │')
|
338
|
+
warn('│ │')
|
339
|
+
warn('╰────────────────────────────────────────────────────────────╯')
|
340
|
+
}
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
330
344
|
if (initNext) {
|
331
345
|
const useTypeScript = unattended ? true : await promptForTypeScript(prompt)
|
332
346
|
trace.log({step: 'useTypeScript', selectedOption: useTypeScript ? 'yes' : 'no'})
|
@@ -419,21 +433,6 @@ export default async function initSanity(
|
|
419
433
|
|
420
434
|
await writeSourceFiles(sanityFolder(useTypeScript, templateToUse), undefined, hasSrcFolder)
|
421
435
|
|
422
|
-
// set tsconfig.json target to ES2017
|
423
|
-
const tsConfigPath = path.join(workDir, 'tsconfig.json')
|
424
|
-
|
425
|
-
if (useTypeScript && existsSync(tsConfigPath)) {
|
426
|
-
const tsConfigFile = readFileSync(tsConfigPath, 'utf8')
|
427
|
-
const config = evaluate(tsConfigFile)
|
428
|
-
|
429
|
-
if (config.compilerOptions.target?.toLowerCase() !== 'es2017') {
|
430
|
-
config.compilerOptions.target = 'ES2017'
|
431
|
-
|
432
|
-
const newConfig = patch(tsConfigFile, config)
|
433
|
-
await fs.writeFile(tsConfigPath, Buffer.from(newConfig))
|
434
|
-
}
|
435
|
-
}
|
436
|
-
|
437
436
|
const appendEnv = unattended ? true : await promptForAppendEnv(prompt, envFilename)
|
438
437
|
|
439
438
|
if (appendEnv) {
|
@@ -496,7 +495,7 @@ export default async function initSanity(
|
|
496
495
|
}
|
497
496
|
|
498
497
|
if (chosen === 'npm') {
|
499
|
-
await execa('npm', ['install', 'next-sanity@9'], execOptions)
|
498
|
+
await execa('npm', ['install', '--legacy-peer-deps', 'next-sanity@9'], execOptions)
|
500
499
|
} else if (chosen === 'yarn') {
|
501
500
|
await execa('npx', ['install-peerdeps', '--yarn', 'next-sanity@9'], execOptions)
|
502
501
|
} else if (chosen === 'pnpm') {
|
@@ -553,18 +552,20 @@ export default async function initSanity(
|
|
553
552
|
const templateName = await selectProjectTemplate()
|
554
553
|
trace.log({step: 'selectProjectTemplate', selectedOption: templateName})
|
555
554
|
const template = templates[templateName]
|
556
|
-
if (!template) {
|
555
|
+
if (!remoteTemplateInfo && !template) {
|
557
556
|
throw new Error(`Template "${templateName}" not found`)
|
558
557
|
}
|
559
558
|
|
560
559
|
// Use typescript?
|
561
|
-
const typescriptOnly = template.typescriptOnly === true
|
562
560
|
let useTypeScript = true
|
563
|
-
if (!
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
561
|
+
if (!remoteTemplateInfo && template) {
|
562
|
+
const typescriptOnly = template.typescriptOnly === true
|
563
|
+
if (!typescriptOnly && typeof cliFlags.typescript === 'boolean') {
|
564
|
+
useTypeScript = cliFlags.typescript
|
565
|
+
} else if (!typescriptOnly && !unattended) {
|
566
|
+
useTypeScript = await promptForTypeScript(prompt)
|
567
|
+
trace.log({step: 'useTypeScript', selectedOption: useTypeScript ? 'yes' : 'no'})
|
568
|
+
}
|
568
569
|
}
|
569
570
|
|
570
571
|
// we enable auto-updates by default, but allow users to specify otherwise
|
@@ -573,47 +574,15 @@ export default async function initSanity(
|
|
573
574
|
autoUpdates = cliFlags['auto-updates']
|
574
575
|
}
|
575
576
|
|
576
|
-
// Build a full set of resolved options
|
577
|
-
const templateOptions: BootstrapOptions = {
|
578
|
-
outputPath,
|
579
|
-
packageName: sluggedName,
|
580
|
-
templateName,
|
581
|
-
schemaUrl,
|
582
|
-
useTypeScript,
|
583
|
-
variables: {
|
584
|
-
autoUpdates,
|
585
|
-
dataset: datasetName,
|
586
|
-
projectId,
|
587
|
-
projectName: displayName || answers.projectName,
|
588
|
-
},
|
589
|
-
}
|
590
|
-
|
591
577
|
// If the template has a sample dataset, prompt the user whether or not we should import it
|
592
578
|
const shouldImport =
|
593
|
-
!unattended && template
|
579
|
+
!unattended && template?.datasetUrl && (await promptForDatasetImport(template.importPrompt))
|
594
580
|
|
595
581
|
trace.log({step: 'importTemplateDataset', selectedOption: shouldImport ? 'yes' : 'no'})
|
596
582
|
|
597
583
|
const [_, bootstrapPromise] = await Promise.allSettled([
|
598
|
-
|
599
|
-
|
600
|
-
.request<SanityProject>({uri: `/projects/${projectId}`})
|
601
|
-
.then((project: SanityProject) => {
|
602
|
-
if (!project?.metadata?.cliInitializedAt) {
|
603
|
-
return apiClient({api: {projectId}}).request({
|
604
|
-
method: 'PATCH',
|
605
|
-
uri: `/projects/${projectId}`,
|
606
|
-
body: {metadata: {cliInitializedAt: new Date().toISOString()}},
|
607
|
-
})
|
608
|
-
}
|
609
|
-
return Promise.resolve()
|
610
|
-
})
|
611
|
-
.catch(() => {
|
612
|
-
// Non-critical update
|
613
|
-
debug('Failed to update cliInitializedAt metadata')
|
614
|
-
}),
|
615
|
-
// Bootstrap Sanity, creating required project files, manifests etc
|
616
|
-
bootstrapTemplate(templateOptions, context),
|
584
|
+
updateProjectCliInitializedMetadata(),
|
585
|
+
bootstrapTemplate(),
|
617
586
|
])
|
618
587
|
|
619
588
|
if (bootstrapPromise.status === 'rejected' && bootstrapPromise.reason instanceof Error) {
|
@@ -667,13 +636,11 @@ export default async function initSanity(
|
|
667
636
|
context,
|
668
637
|
})
|
669
638
|
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
print(` ${chalk.cyan(`sanity dataset create <name>`)}\n`)
|
676
|
-
}
|
639
|
+
print('')
|
640
|
+
print('If you want to delete the imported data, use')
|
641
|
+
print(` ${chalk.cyan(`npx sanity dataset delete ${datasetName}`)}`)
|
642
|
+
print('and create a new clean dataset with')
|
643
|
+
print(` ${chalk.cyan(`npx sanity dataset create <name>`)}\n`)
|
677
644
|
}
|
678
645
|
|
679
646
|
const devCommandMap: Record<PackageManager, string> = {
|
@@ -695,12 +662,10 @@ export default async function initSanity(
|
|
695
662
|
print(`Then: ${chalk.cyan(devCommand)} - to run Sanity Studio\n`)
|
696
663
|
}
|
697
664
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
print(`sanity help - to explore the CLI manual`)
|
703
|
-
}
|
665
|
+
print(`Other helpful commands`)
|
666
|
+
print(`npx sanity docs - to open the documentation in a browser`)
|
667
|
+
print(`npx sanity manage - to open the project settings in a browser`)
|
668
|
+
print(`npx sanity help - to explore the CLI manual`)
|
704
669
|
|
705
670
|
const sendInvite =
|
706
671
|
isFirstProject &&
|
@@ -725,16 +690,13 @@ export default async function initSanity(
|
|
725
690
|
trace.complete()
|
726
691
|
|
727
692
|
async function getOrCreateUser() {
|
728
|
-
|
729
|
-
print('
|
693
|
+
warn('No authentication credentials found in your Sanity config')
|
694
|
+
print('')
|
730
695
|
|
731
696
|
// Provide login options (`sanity login`)
|
732
697
|
const {extOptions, ...otherArgs} = args
|
733
698
|
const loginArgs: CliCommandArguments<LoginFlags> = {...otherArgs, extOptions: {}}
|
734
699
|
await login(loginArgs, {...context, telemetry: trace.newContext('login')})
|
735
|
-
|
736
|
-
print("Good stuff, you're now authenticated. You'll need a project to keep your")
|
737
|
-
print('datasets and collaborators safe and snug.')
|
738
700
|
}
|
739
701
|
|
740
702
|
async function getProjectDetails(): Promise<{
|
@@ -795,21 +757,19 @@ export default async function initSanity(
|
|
795
757
|
isFirstProject: boolean
|
796
758
|
userAction: 'create' | 'select'
|
797
759
|
}> {
|
798
|
-
const
|
760
|
+
const client = apiClient({requireUser: true, requireProject: false})
|
799
761
|
let projects
|
800
762
|
let organizations: ProjectOrganization[]
|
763
|
+
|
801
764
|
try {
|
802
|
-
const client = apiClient({requireUser: true, requireProject: false})
|
803
765
|
const [allProjects, allOrgs] = await Promise.all([
|
804
766
|
client.projects.list({includeMembers: false}),
|
805
767
|
client.request({uri: '/organizations'}),
|
806
768
|
])
|
807
769
|
projects = allProjects.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
808
770
|
organizations = allOrgs
|
809
|
-
spinner.succeed()
|
810
771
|
} catch (err) {
|
811
772
|
if (unattended && flags.project) {
|
812
|
-
spinner.succeed()
|
813
773
|
return {
|
814
774
|
projectId: flags.project,
|
815
775
|
displayName: 'Unknown project',
|
@@ -817,7 +777,6 @@ export default async function initSanity(
|
|
817
777
|
userAction: 'select',
|
818
778
|
}
|
819
779
|
}
|
820
|
-
spinner.fail()
|
821
780
|
throw new Error(`Failed to communicate with the Sanity API:\n${err.message}`)
|
822
781
|
}
|
823
782
|
|
@@ -887,11 +846,11 @@ export default async function initSanity(
|
|
887
846
|
|
888
847
|
const projectChoices = projects.map((project) => ({
|
889
848
|
value: project.id,
|
890
|
-
name: `${project.displayName}
|
849
|
+
name: `${project.displayName} (${project.id})`,
|
891
850
|
}))
|
892
851
|
|
893
852
|
const selected = await prompt.single({
|
894
|
-
message: '
|
853
|
+
message: 'Create a new project or select an existing one',
|
895
854
|
type: 'list',
|
896
855
|
choices: [
|
897
856
|
{value: 'new', name: 'Create new project'},
|
@@ -1070,25 +1029,77 @@ export default async function initSanity(
|
|
1070
1029
|
type: 'list',
|
1071
1030
|
choices: [
|
1072
1031
|
{
|
1073
|
-
value: '
|
1074
|
-
name: '
|
1075
|
-
},
|
1076
|
-
{
|
1077
|
-
value: 'shopify',
|
1078
|
-
name: 'E-commerce (Shopify)',
|
1032
|
+
value: 'clean',
|
1033
|
+
name: 'Clean project with no predefined schema types',
|
1079
1034
|
},
|
1080
1035
|
{
|
1081
1036
|
value: 'blog',
|
1082
1037
|
name: 'Blog (schema)',
|
1083
1038
|
},
|
1084
1039
|
{
|
1085
|
-
value: '
|
1086
|
-
name: '
|
1040
|
+
value: 'shopify',
|
1041
|
+
name: 'E-commerce (Shopify)',
|
1042
|
+
},
|
1043
|
+
{
|
1044
|
+
value: 'moviedb',
|
1045
|
+
name: 'Movie project (schema + sample data)',
|
1087
1046
|
},
|
1088
1047
|
],
|
1089
1048
|
})
|
1090
1049
|
}
|
1091
1050
|
|
1051
|
+
async function updateProjectCliInitializedMetadata() {
|
1052
|
+
try {
|
1053
|
+
const client = apiClient({api: {projectId}})
|
1054
|
+
const project = await client.request<SanityProject>({uri: `/projects/${projectId}`})
|
1055
|
+
|
1056
|
+
if (!project?.metadata?.cliInitializedAt) {
|
1057
|
+
await client.request({
|
1058
|
+
method: 'PATCH',
|
1059
|
+
uri: `/projects/${projectId}`,
|
1060
|
+
body: {metadata: {cliInitializedAt: new Date().toISOString()}},
|
1061
|
+
})
|
1062
|
+
}
|
1063
|
+
} catch (err) {
|
1064
|
+
// Non-critical update
|
1065
|
+
debug('Failed to update cliInitializedAt metadata')
|
1066
|
+
}
|
1067
|
+
}
|
1068
|
+
|
1069
|
+
async function bootstrapTemplate() {
|
1070
|
+
const bootstrapVariables: GenerateConfigOptions['variables'] = {
|
1071
|
+
autoUpdates,
|
1072
|
+
dataset: datasetName,
|
1073
|
+
projectId,
|
1074
|
+
projectName: displayName || answers.projectName,
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
if (remoteTemplateInfo) {
|
1078
|
+
return bootstrapRemoteTemplate(
|
1079
|
+
{
|
1080
|
+
outputPath,
|
1081
|
+
packageName: sluggedName,
|
1082
|
+
repoInfo: remoteTemplateInfo,
|
1083
|
+
bearerToken: cliFlags['template-token'],
|
1084
|
+
variables: bootstrapVariables,
|
1085
|
+
},
|
1086
|
+
context,
|
1087
|
+
)
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
return bootstrapLocalTemplate(
|
1091
|
+
{
|
1092
|
+
outputPath,
|
1093
|
+
packageName: sluggedName,
|
1094
|
+
templateName,
|
1095
|
+
schemaUrl,
|
1096
|
+
useTypeScript,
|
1097
|
+
variables: bootstrapVariables,
|
1098
|
+
},
|
1099
|
+
context,
|
1100
|
+
)
|
1101
|
+
}
|
1102
|
+
|
1092
1103
|
async function getProjectInfo(): Promise<ProjectDefaults & {outputPath: string}> {
|
1093
1104
|
const specifiedPath = flags['output-path'] && path.resolve(flags['output-path'])
|
1094
1105
|
|
@@ -1451,6 +1462,16 @@ async function getPlanFromCoupon(apiClient: CliApiClient, couponCode: string): P
|
|
1451
1462
|
return planId
|
1452
1463
|
}
|
1453
1464
|
|
1465
|
+
async function getUserData(apiClient: CliApiClient): Promise<SanityUser> {
|
1466
|
+
return await apiClient({
|
1467
|
+
requireUser: true,
|
1468
|
+
requireProject: false,
|
1469
|
+
}).request({
|
1470
|
+
method: 'GET',
|
1471
|
+
uri: 'users/me',
|
1472
|
+
})
|
1473
|
+
}
|
1474
|
+
|
1454
1475
|
async function getPlanFromId(apiClient: CliApiClient, planId: string): Promise<string> {
|
1455
1476
|
const response = await apiClient({
|
1456
1477
|
requireUser: false,
|
@@ -1509,12 +1530,3 @@ function getImportCommand(
|
|
1509
1530
|
!isCommandGroup(cmd) && cmd.name === 'import' && cmd.group === 'dataset',
|
1510
1531
|
)
|
1511
1532
|
}
|
1512
|
-
|
1513
|
-
async function hasGlobalCli(): Promise<boolean> {
|
1514
|
-
try {
|
1515
|
-
const globalCliPath = await which('sanity')
|
1516
|
-
return Boolean(globalCliPath)
|
1517
|
-
} catch (err) {
|
1518
|
-
return false
|
1519
|
-
}
|
1520
|
-
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import fs from 'node:fs'
|
2
|
+
|
3
|
+
import {type PackageJson} from '../../types'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Read the `package.json` file at the given path
|
7
|
+
*
|
8
|
+
* @param filePath - Path to package.json to read
|
9
|
+
* @returns The parsed package.json
|
10
|
+
*/
|
11
|
+
export function readPackageJson(filePath: string): PackageJson | undefined {
|
12
|
+
try {
|
13
|
+
// eslint-disable-next-line no-sync
|
14
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
15
|
+
} catch (err) {
|
16
|
+
return undefined
|
17
|
+
}
|
18
|
+
}
|
@@ -165,6 +165,21 @@ export const client = createClient({
|
|
165
165
|
})
|
166
166
|
`
|
167
167
|
|
168
|
+
const live = `// Querying with "sanityFetch" will keep content automatically updated
|
169
|
+
// Before using it, import and render "<SanityLive />" in your layout, see
|
170
|
+
// https://github.com/sanity-io/next-sanity#live-content-api for more information.
|
171
|
+
import { defineLive } from "next-sanity";
|
172
|
+
import { client } from './client'
|
173
|
+
|
174
|
+
export const { sanityFetch, SanityLive } = defineLive({
|
175
|
+
client: client.withConfig({
|
176
|
+
// Live content is currently only available on the experimental API
|
177
|
+
// https://www.sanity.io/docs/api-versioning
|
178
|
+
apiVersion: 'vX'
|
179
|
+
})
|
180
|
+
});
|
181
|
+
`
|
182
|
+
|
168
183
|
const imageTS = `import createImageUrlBuilder from '@sanity/image-url'
|
169
184
|
import { SanityImageSource } from "@sanity/image-url/lib/types/types";
|
170
185
|
|
@@ -201,6 +216,7 @@ export const sanityFolder = (
|
|
201
216
|
'env.': useTypeScript ? envTS : envJS,
|
202
217
|
'lib': {
|
203
218
|
'client.': client,
|
219
|
+
'live.': live,
|
204
220
|
'image.': useTypeScript ? imageTS : imageJS,
|
205
221
|
},
|
206
222
|
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import {debug} from '../../debug'
|
2
|
+
import {type CliApiClient} from '../../types'
|
3
|
+
|
4
|
+
export async function updateInitialTemplateMetadata(
|
5
|
+
apiClient: CliApiClient,
|
6
|
+
projectId: string,
|
7
|
+
templateName: string,
|
8
|
+
): Promise<void> {
|
9
|
+
try {
|
10
|
+
await apiClient({api: {projectId}}).request({
|
11
|
+
method: 'PATCH',
|
12
|
+
uri: `/projects/${projectId}`,
|
13
|
+
body: {metadata: {initialTemplate: templateName}},
|
14
|
+
})
|
15
|
+
} catch (err: unknown) {
|
16
|
+
// Non-critical that we update this metadata, and user does not need to be aware
|
17
|
+
let message = typeof err === 'string' ? err : '<unknown error>'
|
18
|
+
if (err instanceof Error) {
|
19
|
+
message = err.message
|
20
|
+
}
|
21
|
+
|
22
|
+
debug('Failed to update initial template metadata for project: %s', message)
|
23
|
+
}
|
24
|
+
}
|
@@ -2,7 +2,6 @@ import http, {type Server} from 'node:http'
|
|
2
2
|
import os from 'node:os'
|
3
3
|
|
4
4
|
import {type SanityClient} from '@sanity/client'
|
5
|
-
import chalk from 'chalk'
|
6
5
|
import open from 'open'
|
7
6
|
|
8
7
|
import {debug as debugIt} from '../../debug'
|
@@ -146,7 +145,7 @@ export async function login(
|
|
146
145
|
})
|
147
146
|
}
|
148
147
|
|
149
|
-
output.
|
148
|
+
output.success('Login successful')
|
150
149
|
trace.complete()
|
151
150
|
}
|
152
151
|
|
@@ -335,7 +334,7 @@ async function promptProviders(
|
|
335
334
|
|
336
335
|
const provider = await prompt.single({
|
337
336
|
type: 'list',
|
338
|
-
message: '
|
337
|
+
message: 'Please log in or create a new account',
|
339
338
|
choices: providers.map((choice) => choice.title),
|
340
339
|
})
|
341
340
|
|