@sanity/cli 6.3.2 → 6.4.0
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/README.md +16 -10
- package/dist/actions/init/bootstrapLocalTemplate.js +16 -1
- package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
- package/dist/actions/init/initApp.js +72 -0
- package/dist/actions/init/initApp.js.map +1 -0
- package/dist/actions/init/initHelpers.js +37 -0
- package/dist/actions/init/initHelpers.js.map +1 -0
- package/dist/actions/init/initNextJs.js +246 -0
- package/dist/actions/init/initNextJs.js.map +1 -0
- package/dist/actions/init/initStudio.js +127 -0
- package/dist/actions/init/initStudio.js.map +1 -0
- package/dist/actions/init/scaffoldTemplate.js +114 -0
- package/dist/actions/init/scaffoldTemplate.js.map +1 -0
- package/dist/actions/init/templates/appQuickstart.js +2 -1
- package/dist/actions/init/templates/appQuickstart.js.map +1 -1
- package/dist/actions/init/templates/appSanityUi.js +2 -1
- package/dist/actions/init/templates/appSanityUi.js.map +1 -1
- package/dist/actions/init/templates/shopify.js +6 -6
- package/dist/actions/init/templates/shopify.js.map +1 -1
- package/dist/actions/init/templates/shopifyOnline.js +2 -2
- package/dist/actions/init/templates/shopifyOnline.js.map +1 -1
- package/dist/actions/mcp/detectAvailableEditors.js +16 -3
- package/dist/actions/mcp/detectAvailableEditors.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +192 -132
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +4 -1
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/mcp/writeMCPConfig.js +2 -2
- package/dist/actions/mcp/writeMCPConfig.js.map +1 -1
- package/dist/actions/schema/extractSchema.js +5 -7
- package/dist/actions/schema/extractSchema.js.map +1 -1
- package/dist/commands/datasets/copy.js +14 -0
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/init.js +149 -482
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp/configure.js +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/hooks/prerun/injectEnvVariables.js +3 -5
- package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
- package/dist/services/datasets.js +2 -1
- package/dist/services/datasets.js.map +1 -1
- package/dist/telemetry/init.telemetry.js.map +1 -1
- package/dist/util/packageManager/installationInfo/detectPackages.js +13 -7
- package/dist/util/packageManager/installationInfo/detectPackages.js.map +1 -1
- package/dist/util/update/fetchUpdateInfo.js +40 -0
- package/dist/util/update/fetchUpdateInfo.js.map +1 -0
- package/dist/util/update/fetchUpdateInfo.worker.js +19 -0
- package/dist/util/update/fetchUpdateInfo.worker.js.map +1 -0
- package/dist/util/update/getRunnerUpdateCommand.js +33 -0
- package/dist/util/update/getRunnerUpdateCommand.js.map +1 -0
- package/dist/util/update/getUpdateCommand.js +6 -7
- package/dist/util/update/getUpdateCommand.js.map +1 -1
- package/dist/util/update/packageRunner.js +10 -0
- package/dist/util/update/packageRunner.js.map +1 -0
- package/dist/util/update/resolveRunnerPackage.js +45 -0
- package/dist/util/update/resolveRunnerPackage.js.map +1 -0
- package/dist/util/update/resolveUpdateTarget.js +31 -0
- package/dist/util/update/resolveUpdateTarget.js.map +1 -0
- package/dist/util/update/showNotificationUpdate.js +8 -6
- package/dist/util/update/showNotificationUpdate.js.map +1 -1
- package/dist/util/update/updateChecker.js +73 -38
- package/dist/util/update/updateChecker.js.map +1 -1
- package/oclif.manifest.json +540 -525
- package/package.json +6 -6
- package/templates/app-quickstart/src/App.tsx +2 -2
- package/templates/app-sanity-ui/src/App.tsx +2 -2
- package/templates/shopify/schemaTypes/objects/hotspot/imageWithProductHotspotsType.ts +1 -1
- package/dist/util/update/fetchLatestVersion.js +0 -21
- package/dist/util/update/fetchLatestVersion.js.map +0 -1
package/dist/commands/init.js
CHANGED
|
@@ -1,56 +1,44 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
1
|
import path from 'node:path';
|
|
4
2
|
import { styleText } from 'node:util';
|
|
5
3
|
import { Args, Flags } from '@oclif/core';
|
|
6
4
|
import { CLIError } from '@oclif/core/errors';
|
|
7
|
-
import {
|
|
5
|
+
import { SanityCommand, subdebug } from '@sanity/cli-core';
|
|
8
6
|
import { confirm, input, logSymbols, select, Separator, spinner } from '@sanity/cli-core/ux';
|
|
9
7
|
import { isHttpError } from '@sanity/client';
|
|
10
8
|
import { frameworks } from '@vercel/frameworks';
|
|
11
|
-
import { execa } from 'execa';
|
|
12
9
|
import deburr from 'lodash-es/deburr.js';
|
|
13
10
|
import { validateSession } from '../actions/auth/ensureAuthenticated.js';
|
|
14
11
|
import { getProviderName } from '../actions/auth/getProviderName.js';
|
|
15
12
|
import { login } from '../actions/auth/login/login.js';
|
|
16
13
|
import { createDataset } from '../actions/dataset/create.js';
|
|
17
|
-
import { bootstrapTemplate } from '../actions/init/bootstrapTemplate.js';
|
|
18
14
|
import { checkNextJsReactCompatibility } from '../actions/init/checkNextJsReactCompatibility.js';
|
|
19
|
-
import { countNestedFolders } from '../actions/init/countNestedFolders.js';
|
|
20
15
|
import { determineAppTemplate } from '../actions/init/determineAppTemplate.js';
|
|
21
16
|
import { createOrAppendEnvVars } from '../actions/init/env/createOrAppendEnvVars.js';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
17
|
+
import { initApp } from '../actions/init/initApp.js';
|
|
18
|
+
import { flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded } from '../actions/init/initHelpers.js';
|
|
19
|
+
import { initNextJs } from '../actions/init/initNextJs.js';
|
|
20
|
+
import { initStudio } from '../actions/init/initStudio.js';
|
|
24
21
|
import { checkIsRemoteTemplate, getGitHubRepoInfo } from '../actions/init/remoteTemplate.js';
|
|
25
|
-
import { resolvePackageManager } from '../actions/init/resolvePackageManager.js';
|
|
26
|
-
import templates from '../actions/init/templates/index.js';
|
|
27
|
-
import { sanityCliTemplate, sanityConfigTemplate, sanityFolder, sanityStudioTemplate } from '../actions/init/templates/nextjs/index.js';
|
|
28
22
|
import { setupMCP } from '../actions/mcp/setupMCP.js';
|
|
29
23
|
import { findOrganizationByUserName } from '../actions/organizations/findOrganizationByUserName.js';
|
|
30
24
|
import { getOrganizationChoices } from '../actions/organizations/getOrganizationChoices.js';
|
|
31
25
|
import { getOrganizationsWithAttachGrantInfo } from '../actions/organizations/getOrganizationsWithAttachGrantInfo.js';
|
|
32
26
|
import { hasProjectAttachGrant } from '../actions/organizations/hasProjectAttachGrant.js';
|
|
33
|
-
import {
|
|
34
|
-
import { promptForTypeScript } from '../prompts/init/promptForTypescript.js';
|
|
27
|
+
import { promptForConfigFiles } from '../prompts/init/nextjs.js';
|
|
35
28
|
import { promptForDatasetName } from '../prompts/promptForDatasetName.js';
|
|
36
29
|
import { promptForDefaultConfig } from '../prompts/promptForDefaultConfig.js';
|
|
37
30
|
import { promptForOrganizationName } from '../prompts/promptForOrganizationName.js';
|
|
38
|
-
import { createCorsOrigin, listCorsOrigins } from '../services/cors.js';
|
|
39
31
|
import { createDataset as createDatasetService, listDatasets } from '../services/datasets.js';
|
|
40
32
|
import { getProjectFeatures } from '../services/getProjectFeatures.js';
|
|
41
33
|
import { createOrganization, listOrganizations } from '../services/organizations.js';
|
|
42
34
|
import { getPlanId, getPlanIdFromCoupon } from '../services/plans.js';
|
|
43
|
-
import { createProject, listProjects
|
|
35
|
+
import { createProject, listProjects } from '../services/projects.js';
|
|
44
36
|
import { getCliUser } from '../services/user.js';
|
|
45
37
|
import { CLIInitStepCompleted } from '../telemetry/init.telemetry.js';
|
|
46
38
|
import { detectFrameworkRecord } from '../util/detectFramework.js';
|
|
47
39
|
import { absolutify, validateEmptyPath } from '../util/fsUtils.js';
|
|
48
40
|
import { getProjectDefaults } from '../util/getProjectDefaults.js';
|
|
49
41
|
import { getSanityEnv } from '../util/getSanityEnv.js';
|
|
50
|
-
import { getPeerDependencies } from '../util/packageManager/getPeerDependencies.js';
|
|
51
|
-
import { installDeclaredPackages, installNewPackages } from '../util/packageManager/installPackages.js';
|
|
52
|
-
import { getPartialEnvWithNpmPath } from '../util/packageManager/packageManagerChoice.js';
|
|
53
|
-
import { ImportDatasetCommand } from './datasets/import.js';
|
|
54
42
|
const debug = subdebug('init');
|
|
55
43
|
export class InitCommand extends SanityCommand {
|
|
56
44
|
static args = {
|
|
@@ -395,8 +383,8 @@ export class InitCommand extends SanityCommand {
|
|
|
395
383
|
this.log(`\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\n`);
|
|
396
384
|
return;
|
|
397
385
|
}
|
|
398
|
-
let initNext = this.
|
|
399
|
-
if (isNextJs && this.
|
|
386
|
+
let initNext = flagOrDefault(this.flags['nextjs-add-config-files'], false);
|
|
387
|
+
if (isNextJs && shouldPrompt(this.isUnattended(), this.flags['nextjs-add-config-files'])) {
|
|
400
388
|
initNext = await promptForConfigFiles();
|
|
401
389
|
}
|
|
402
390
|
this._trace.log({
|
|
@@ -453,14 +441,25 @@ export class InitCommand extends SanityCommand {
|
|
|
453
441
|
});
|
|
454
442
|
}
|
|
455
443
|
if (initNext) {
|
|
456
|
-
await
|
|
444
|
+
await initNextJs({
|
|
457
445
|
datasetName,
|
|
458
446
|
detectedFramework,
|
|
459
447
|
envFilename,
|
|
460
448
|
mcpConfigured,
|
|
449
|
+
nextjsAppendEnv: this.flags['nextjs-append-env'],
|
|
450
|
+
nextjsEmbedStudio: this.flags['nextjs-embed-studio'],
|
|
451
|
+
output: this.output,
|
|
452
|
+
overwriteFiles: this.flags['overwrite-files'],
|
|
453
|
+
packageManager: this.flags['package-manager'],
|
|
461
454
|
projectId,
|
|
455
|
+
template: this.flags.template,
|
|
456
|
+
trace: this._trace,
|
|
457
|
+
typescript: this.flags.typescript,
|
|
458
|
+
unattended: this.isUnattended(),
|
|
462
459
|
workDir
|
|
463
460
|
});
|
|
461
|
+
this._trace.complete();
|
|
462
|
+
return;
|
|
464
463
|
}
|
|
465
464
|
// user wants to write environment variables to file
|
|
466
465
|
if (this.flags.env) {
|
|
@@ -475,179 +474,42 @@ export class InitCommand extends SanityCommand {
|
|
|
475
474
|
output: this.output,
|
|
476
475
|
outputPath
|
|
477
476
|
});
|
|
478
|
-
await this.
|
|
477
|
+
await writeStagingEnvIfNeeded(this.output, outputPath);
|
|
479
478
|
this.exit(0);
|
|
480
479
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
this.error(`Template "${templateName}" not found`, {
|
|
490
|
-
exit: 1
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
let useTypeScript = this.flags.typescript;
|
|
494
|
-
if (!remoteTemplateInfo && template && template.typescriptOnly === true) {
|
|
495
|
-
useTypeScript = true;
|
|
496
|
-
} else if (this.promptForUndefinedFlag(this.flags.typescript)) {
|
|
497
|
-
useTypeScript = await promptForTypeScript();
|
|
498
|
-
this._trace.log({
|
|
499
|
-
selectedOption: useTypeScript ? 'yes' : 'no',
|
|
500
|
-
step: 'useTypeScript'
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
// If the template has a sample dataset, prompt the user whether or not we should import it
|
|
504
|
-
const importDatasetFlag = this.flags['import-dataset'];
|
|
505
|
-
const shouldImport = template?.datasetUrl && (importDatasetFlag ?? (!this.isUnattended() && await this.promptForDatasetImport(template.importPrompt)));
|
|
506
|
-
this._trace.log({
|
|
507
|
-
selectedOption: shouldImport ? 'yes' : 'no',
|
|
508
|
-
step: 'importTemplateDataset'
|
|
509
|
-
});
|
|
510
|
-
try {
|
|
511
|
-
await updateProjectInitializedAt(projectId);
|
|
512
|
-
} catch (err) {
|
|
513
|
-
// Non-critical update
|
|
514
|
-
debug('Failed to update cliInitializedAt metadata', err);
|
|
515
|
-
}
|
|
516
|
-
try {
|
|
517
|
-
await bootstrapTemplate({
|
|
518
|
-
autoUpdates: this.flags['auto-updates'],
|
|
519
|
-
bearerToken: this.flags['template-token'],
|
|
520
|
-
dataset: datasetName,
|
|
521
|
-
organizationId,
|
|
522
|
-
output: this.output,
|
|
523
|
-
outputPath,
|
|
524
|
-
overwriteFiles: this.flags['overwrite-files'],
|
|
525
|
-
packageName: sluggedName,
|
|
526
|
-
projectId,
|
|
527
|
-
projectName: displayName || defaults.projectName,
|
|
528
|
-
remoteTemplateInfo,
|
|
529
|
-
templateName,
|
|
530
|
-
useTypeScript
|
|
531
|
-
});
|
|
532
|
-
} catch (error) {
|
|
533
|
-
if (error instanceof Error) {
|
|
534
|
-
throw error;
|
|
535
|
-
}
|
|
536
|
-
throw new Error(String(error), {
|
|
537
|
-
cause: error
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
const pkgManager = await resolvePackageManager({
|
|
541
|
-
interactive: !this.isUnattended(),
|
|
480
|
+
const sharedParams = {
|
|
481
|
+
autoUpdates: this.flags['auto-updates'],
|
|
482
|
+
defaults,
|
|
483
|
+
error: this.error.bind(this),
|
|
484
|
+
git: this.flags.git,
|
|
485
|
+
mcpConfigured,
|
|
486
|
+
noGit: this.flags['no-git'],
|
|
487
|
+
organizationId,
|
|
542
488
|
output: this.output,
|
|
489
|
+
outputPath,
|
|
490
|
+
overwriteFiles: this.flags['overwrite-files'],
|
|
543
491
|
packageManager: this.flags['package-manager'],
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
await installDeclaredPackages(outputPath, pkgManager, {
|
|
552
|
-
output: this.output,
|
|
492
|
+
remoteTemplateInfo,
|
|
493
|
+
sluggedName,
|
|
494
|
+
template: this.flags.template,
|
|
495
|
+
templateToken: this.flags['template-token'],
|
|
496
|
+
trace: this._trace,
|
|
497
|
+
typescript: this.flags.typescript,
|
|
498
|
+
unattended: this.isUnattended(),
|
|
553
499
|
workDir
|
|
554
|
-
});
|
|
555
|
-
const useGit = !this.flags['no-git'] && (this.flags.git === undefined || Boolean(this.flags.git));
|
|
556
|
-
const commitMessage = this.flags.git;
|
|
557
|
-
await this.writeStagingEnvIfNeeded(outputPath);
|
|
558
|
-
// Try initializing a git repository
|
|
559
|
-
if (useGit) {
|
|
560
|
-
tryGitInit(outputPath, typeof commitMessage === 'string' ? commitMessage : undefined);
|
|
561
|
-
}
|
|
562
|
-
// Prompt for dataset import (if a dataset is defined)
|
|
563
|
-
if (shouldImport && template?.datasetUrl) {
|
|
564
|
-
const token = await getCliToken();
|
|
565
|
-
if (!token) {
|
|
566
|
-
this.error('Authentication required to import dataset', {
|
|
567
|
-
exit: 1
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
await ImportDatasetCommand.run([
|
|
571
|
-
template.datasetUrl,
|
|
572
|
-
'--project-id',
|
|
573
|
-
projectId,
|
|
574
|
-
'--dataset',
|
|
575
|
-
datasetName,
|
|
576
|
-
'--token',
|
|
577
|
-
token,
|
|
578
|
-
'--missing'
|
|
579
|
-
], {
|
|
580
|
-
root: outputPath
|
|
581
|
-
});
|
|
582
|
-
this.log('');
|
|
583
|
-
this.log('If you want to delete the imported data, use');
|
|
584
|
-
this.log(` ${styleText('cyan', `npx sanity dataset delete ${datasetName}`)}`);
|
|
585
|
-
this.log('and create a new clean dataset with');
|
|
586
|
-
this.log(` ${styleText('cyan', `npx sanity dataset create <name>`)}\n`);
|
|
587
|
-
}
|
|
588
|
-
const devCommandMap = {
|
|
589
|
-
bun: 'bun dev',
|
|
590
|
-
manual: 'npm run dev',
|
|
591
|
-
npm: 'npm run dev',
|
|
592
|
-
pnpm: 'pnpm dev',
|
|
593
|
-
yarn: 'yarn dev'
|
|
594
500
|
};
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
this.log(styleText([
|
|
608
|
-
'blue',
|
|
609
|
-
'underline'
|
|
610
|
-
], 'https://www.sanity.io/docs/app-sdk/sdk-configuration'));
|
|
611
|
-
if (mcpConfigured && mcpConfigured.length > 0) {
|
|
612
|
-
const message = await this.getPostInitMCPPrompt(mcpConfigured);
|
|
613
|
-
this.log(`\n${message}`);
|
|
614
|
-
this.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`);
|
|
615
|
-
this.log(`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`);
|
|
616
|
-
}
|
|
617
|
-
this.log('\n');
|
|
618
|
-
this.log(`Other helpful commands:`);
|
|
619
|
-
this.log(`npx sanity docs browse to open the documentation in a browser`);
|
|
620
|
-
this.log(`npx sanity dev to start the development server for your app`);
|
|
621
|
-
this.log(`npx sanity deploy to deploy your app`);
|
|
622
|
-
} else {
|
|
623
|
-
//output for Studios here
|
|
624
|
-
this.log(`✅ ${styleText([
|
|
625
|
-
'green',
|
|
626
|
-
'bold'
|
|
627
|
-
], 'Success!')} Your Studio has been created.`);
|
|
628
|
-
if (!isCurrentDir) this.log(goToProjectDir);
|
|
629
|
-
this.log(`\nGet started by running ${styleText('cyan', devCommand)} to launch your Studio's development server`);
|
|
630
|
-
if (mcpConfigured && mcpConfigured.length > 0) {
|
|
631
|
-
const message = await this.getPostInitMCPPrompt(mcpConfigured);
|
|
632
|
-
this.log(`\n${message}`);
|
|
633
|
-
this.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`);
|
|
634
|
-
this.log(`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`);
|
|
635
|
-
}
|
|
636
|
-
this.log('\n');
|
|
637
|
-
this.log(`Other helpful commands:`);
|
|
638
|
-
this.log(`npx sanity docs browse to open the documentation in a browser`);
|
|
639
|
-
this.log(`npx sanity manage to open the project settings in a browser`);
|
|
640
|
-
this.log(`npx sanity help to explore the CLI manual`);
|
|
641
|
-
}
|
|
642
|
-
if (isFirstProject) {
|
|
643
|
-
this._trace.log({
|
|
644
|
-
selectedOption: 'yes',
|
|
645
|
-
step: 'sendCommunityInvite'
|
|
646
|
-
});
|
|
647
|
-
const DISCORD_INVITE_LINK = 'https://www.sanity.io/community/join';
|
|
648
|
-
this.log(`\nJoin the Sanity community: ${styleText('cyan', DISCORD_INVITE_LINK)}`);
|
|
649
|
-
this.log('We look forward to seeing you there!\n');
|
|
650
|
-
}
|
|
501
|
+
await (isAppTemplate ? initApp({
|
|
502
|
+
...sharedParams,
|
|
503
|
+
datasetName,
|
|
504
|
+
projectId
|
|
505
|
+
}) : initStudio({
|
|
506
|
+
...sharedParams,
|
|
507
|
+
datasetName,
|
|
508
|
+
displayName,
|
|
509
|
+
importDataset: this.flags['import-dataset'],
|
|
510
|
+
isFirstProject,
|
|
511
|
+
projectId
|
|
512
|
+
}));
|
|
651
513
|
this._trace.complete();
|
|
652
514
|
}
|
|
653
515
|
checkFlagsInUnattendedMode({ createProjectName, isAppTemplate, isNextJs }) {
|
|
@@ -756,9 +618,6 @@ export class InitCommand extends SanityCommand {
|
|
|
756
618
|
user: loggedInUser
|
|
757
619
|
};
|
|
758
620
|
}
|
|
759
|
-
flagOrDefault(flag, defaultValue) {
|
|
760
|
-
return typeof this.flags[flag] === 'boolean' ? this.flags[flag] : defaultValue;
|
|
761
|
-
}
|
|
762
621
|
async getOrCreateDataset(opts) {
|
|
763
622
|
const visibility = this.flags.visibility;
|
|
764
623
|
const dataset = this.flags.dataset;
|
|
@@ -1003,38 +862,36 @@ export class InitCommand extends SanityCommand {
|
|
|
1003
862
|
return undefined;
|
|
1004
863
|
}
|
|
1005
864
|
}
|
|
1006
|
-
async getPostInitMCPPrompt(editorsNames) {
|
|
1007
|
-
return fetchPostInitPrompt(new Intl.ListFormat('en').format(editorsNames));
|
|
1008
|
-
}
|
|
1009
865
|
async getProjectDetails({ isAppTemplate, newProject, planId, showDefaultConfigPrompt, user }) {
|
|
1010
866
|
if (isAppTemplate) {
|
|
1011
|
-
|
|
1012
|
-
if (
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
867
|
+
let organizationId = this.flags.organization;
|
|
868
|
+
if (!organizationId) {
|
|
869
|
+
let organizations;
|
|
870
|
+
try {
|
|
871
|
+
organizations = await listOrganizations();
|
|
872
|
+
} catch (err) {
|
|
873
|
+
this.error(`Failed to communicate with the Sanity API:\n${err.message}`, {
|
|
874
|
+
exit: 1
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
organizationId = await this.promptUserForOrganization({
|
|
878
|
+
isAppTemplate: true,
|
|
879
|
+
organizations,
|
|
880
|
+
user
|
|
881
|
+
});
|
|
1020
882
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
includeMembers: 'true'
|
|
1026
|
-
});
|
|
1027
|
-
const appOrganizationId = await this.promptUserForOrganization({
|
|
1028
|
-
isAppTemplate: true,
|
|
1029
|
-
organizations,
|
|
883
|
+
const { datasetName, displayName, projectId } = await this.promptForAppTemplateSetup({
|
|
884
|
+
newProject,
|
|
885
|
+
organizationId,
|
|
886
|
+
planId,
|
|
1030
887
|
user
|
|
1031
888
|
});
|
|
1032
889
|
return {
|
|
1033
|
-
datasetName
|
|
1034
|
-
displayName
|
|
890
|
+
datasetName,
|
|
891
|
+
displayName,
|
|
1035
892
|
isFirstProject: false,
|
|
1036
|
-
organizationId
|
|
1037
|
-
projectId
|
|
893
|
+
organizationId,
|
|
894
|
+
projectId
|
|
1038
895
|
};
|
|
1039
896
|
}
|
|
1040
897
|
debug('Prompting user to select or create a project');
|
|
@@ -1078,183 +935,90 @@ export class InitCommand extends SanityCommand {
|
|
|
1078
935
|
});
|
|
1079
936
|
return absolutify(inputPath);
|
|
1080
937
|
}
|
|
1081
|
-
async
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
});
|
|
1090
|
-
const fileExtension = useTypeScript ? 'ts' : 'js';
|
|
1091
|
-
let embeddedStudio = this.flagOrDefault('nextjs-embed-studio', true);
|
|
1092
|
-
if (this.promptForUndefinedFlag(this.flags['nextjs-embed-studio'])) {
|
|
1093
|
-
embeddedStudio = await promptForEmbeddedStudio();
|
|
1094
|
-
}
|
|
1095
|
-
let hasSrcFolder = false;
|
|
1096
|
-
if (embeddedStudio) {
|
|
1097
|
-
// find source path (app or src/app)
|
|
1098
|
-
const appDir = 'app';
|
|
1099
|
-
let srcPath = path.join(workDir, appDir);
|
|
1100
|
-
if (!existsSync(srcPath)) {
|
|
1101
|
-
srcPath = path.join(workDir, 'src', appDir);
|
|
1102
|
-
hasSrcFolder = true;
|
|
1103
|
-
if (!existsSync(srcPath)) {
|
|
1104
|
-
try {
|
|
1105
|
-
await mkdir(srcPath, {
|
|
1106
|
-
recursive: true
|
|
1107
|
-
});
|
|
1108
|
-
} catch {
|
|
1109
|
-
debug('Error creating folder %s', srcPath);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
938
|
+
async promptForAppTemplateSetup({ newProject, organizationId, planId, user }) {
|
|
939
|
+
if (this.isUnattended()) {
|
|
940
|
+
if (!this.flags.project && !newProject) {
|
|
941
|
+
return {
|
|
942
|
+
datasetName: '',
|
|
943
|
+
displayName: '',
|
|
944
|
+
projectId: ''
|
|
945
|
+
};
|
|
1112
946
|
}
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
await this.writeSourceFiles({
|
|
1130
|
-
fileExtension,
|
|
1131
|
-
files: sanityFolder(useTypeScript, templateToUse),
|
|
1132
|
-
folderPath: undefined,
|
|
1133
|
-
srcFolderPrefix: hasSrcFolder,
|
|
1134
|
-
workDir
|
|
1135
|
-
});
|
|
1136
|
-
let appendEnv = this.flagOrDefault('nextjs-append-env', true);
|
|
1137
|
-
if (this.promptForUndefinedFlag(this.flags['nextjs-append-env'])) {
|
|
1138
|
-
appendEnv = await promptForAppendEnv(envFilename);
|
|
947
|
+
const project = await this.getOrCreateProject({
|
|
948
|
+
newProject,
|
|
949
|
+
planId,
|
|
950
|
+
user
|
|
951
|
+
});
|
|
952
|
+
const dataset = await this.getOrCreateDataset({
|
|
953
|
+
displayName: project.displayName,
|
|
954
|
+
projectId: project.projectId,
|
|
955
|
+
showDefaultConfigPrompt: false
|
|
956
|
+
});
|
|
957
|
+
return {
|
|
958
|
+
datasetName: dataset.datasetName,
|
|
959
|
+
displayName: project.displayName,
|
|
960
|
+
projectId: project.projectId
|
|
961
|
+
};
|
|
1139
962
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
963
|
+
const projects = (await listProjects()).toSorted((a, b)=>b.createdAt.localeCompare(a.createdAt));
|
|
964
|
+
const projectChoices = projects.map((project)=>({
|
|
965
|
+
name: `${project.displayName} (${project.id})`,
|
|
966
|
+
value: project.id
|
|
967
|
+
}));
|
|
968
|
+
const SKIP_PROJECT = '__skip__';
|
|
969
|
+
const NEW_PROJECT = '__new__';
|
|
970
|
+
const selected = await select({
|
|
971
|
+
choices: [
|
|
972
|
+
{
|
|
973
|
+
name: "Skip — I'll configure later",
|
|
974
|
+
value: SKIP_PROJECT
|
|
1145
975
|
},
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
976
|
+
{
|
|
977
|
+
name: 'Create new project',
|
|
978
|
+
value: NEW_PROJECT
|
|
979
|
+
},
|
|
980
|
+
...projectChoices.length > 0 ? [
|
|
981
|
+
new Separator(),
|
|
982
|
+
...projectChoices
|
|
983
|
+
] : []
|
|
984
|
+
],
|
|
985
|
+
message: 'Configure a project for this app?'
|
|
986
|
+
});
|
|
987
|
+
if (selected === SKIP_PROJECT) {
|
|
988
|
+
this._trace.log({
|
|
989
|
+
selectedOption: 'skip',
|
|
990
|
+
step: 'configureAppProject'
|
|
1151
991
|
});
|
|
992
|
+
return {
|
|
993
|
+
datasetName: '',
|
|
994
|
+
displayName: '',
|
|
995
|
+
projectId: ''
|
|
996
|
+
};
|
|
1152
997
|
}
|
|
1153
|
-
if (embeddedStudio) {
|
|
1154
|
-
const nextjsLocalDevOrigin = 'http://localhost:3000';
|
|
1155
|
-
const existingCorsOrigins = await listCorsOrigins(projectId);
|
|
1156
|
-
const hasExistingCorsOrigin = existingCorsOrigins.some((item)=>item.origin === nextjsLocalDevOrigin);
|
|
1157
|
-
if (!hasExistingCorsOrigin) {
|
|
1158
|
-
try {
|
|
1159
|
-
const createCorsRes = await createCorsOrigin({
|
|
1160
|
-
allowCredentials: true,
|
|
1161
|
-
origin: nextjsLocalDevOrigin,
|
|
1162
|
-
projectId
|
|
1163
|
-
});
|
|
1164
|
-
this.log(createCorsRes.id ? `Added ${nextjsLocalDevOrigin} to CORS origins` : `Failed to add ${nextjsLocalDevOrigin} to CORS origins`);
|
|
1165
|
-
} catch (error) {
|
|
1166
|
-
debug(`Error creating new CORS Origin ${nextjsLocalDevOrigin}: ${error}`);
|
|
1167
|
-
this.error(`Failed to add ${nextjsLocalDevOrigin} to CORS origins: ${error}`, {
|
|
1168
|
-
exit: 1
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
const chosen = await resolvePackageManager({
|
|
1174
|
-
interactive: !this.isUnattended(),
|
|
1175
|
-
output: this.output,
|
|
1176
|
-
packageManager: this.flags['package-manager'],
|
|
1177
|
-
targetDir: workDir
|
|
1178
|
-
});
|
|
1179
998
|
this._trace.log({
|
|
1180
|
-
selectedOption:
|
|
1181
|
-
step: '
|
|
999
|
+
selectedOption: selected === NEW_PROJECT ? 'create' : 'existing',
|
|
1000
|
+
step: 'configureAppProject'
|
|
1182
1001
|
});
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
await installNewPackages({
|
|
1193
|
-
packageManager: chosen,
|
|
1194
|
-
packages
|
|
1195
|
-
}, {
|
|
1196
|
-
output: this.output,
|
|
1197
|
-
workDir
|
|
1198
|
-
});
|
|
1199
|
-
// will refactor this later
|
|
1200
|
-
const execOptions = {
|
|
1201
|
-
cwd: workDir,
|
|
1202
|
-
encoding: 'utf8',
|
|
1203
|
-
env: getPartialEnvWithNpmPath(workDir),
|
|
1204
|
-
stdio: 'inherit'
|
|
1002
|
+
const project = selected === NEW_PROJECT ? await this.promptForProjectCreation({
|
|
1003
|
+
isUsersFirstProject: projects.length === 0,
|
|
1004
|
+
organizationId,
|
|
1005
|
+
organizations: [],
|
|
1006
|
+
planId,
|
|
1007
|
+
user
|
|
1008
|
+
}) : {
|
|
1009
|
+
displayName: projects.find((p)=>p.id === selected)?.displayName ?? '',
|
|
1010
|
+
projectId: selected
|
|
1205
1011
|
};
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
'install',
|
|
1211
|
-
'next-sanity@12'
|
|
1212
|
-
], execOptions);
|
|
1213
|
-
break;
|
|
1214
|
-
}
|
|
1215
|
-
case 'pnpm':
|
|
1216
|
-
{
|
|
1217
|
-
await execa('pnpm', [
|
|
1218
|
-
'install',
|
|
1219
|
-
'next-sanity@12'
|
|
1220
|
-
], execOptions);
|
|
1221
|
-
break;
|
|
1222
|
-
}
|
|
1223
|
-
case 'yarn':
|
|
1224
|
-
{
|
|
1225
|
-
const peerDeps = await getPeerDependencies('next-sanity@12', workDir);
|
|
1226
|
-
await installNewPackages({
|
|
1227
|
-
packageManager: 'yarn',
|
|
1228
|
-
packages: [
|
|
1229
|
-
'next-sanity@12',
|
|
1230
|
-
...peerDeps
|
|
1231
|
-
]
|
|
1232
|
-
}, {
|
|
1233
|
-
output: this.output,
|
|
1234
|
-
workDir
|
|
1235
|
-
});
|
|
1236
|
-
break;
|
|
1237
|
-
}
|
|
1238
|
-
default:
|
|
1239
|
-
{
|
|
1240
|
-
break;
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
this.log(`\n${styleText('green', 'Success!')} Your Sanity configuration files has been added to this project`);
|
|
1244
|
-
if (mcpConfigured && mcpConfigured.length > 0) {
|
|
1245
|
-
const message = await this.getPostInitMCPPrompt(mcpConfigured);
|
|
1246
|
-
this.log(`\n${message}`);
|
|
1247
|
-
this.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`);
|
|
1248
|
-
this.log(`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`);
|
|
1249
|
-
}
|
|
1250
|
-
await this.writeStagingEnvIfNeeded(workDir);
|
|
1251
|
-
this.exit(0);
|
|
1252
|
-
}
|
|
1253
|
-
async promptForDatasetImport(message) {
|
|
1254
|
-
return confirm({
|
|
1255
|
-
default: true,
|
|
1256
|
-
message: message || 'This template includes a sample dataset, would you like to use it?'
|
|
1012
|
+
const dataset = await this.getOrCreateDataset({
|
|
1013
|
+
displayName: project.displayName,
|
|
1014
|
+
projectId: project.projectId,
|
|
1015
|
+
showDefaultConfigPrompt: false
|
|
1257
1016
|
});
|
|
1017
|
+
return {
|
|
1018
|
+
datasetName: dataset.datasetName,
|
|
1019
|
+
displayName: project.displayName,
|
|
1020
|
+
projectId: project.projectId
|
|
1021
|
+
};
|
|
1258
1022
|
}
|
|
1259
1023
|
async promptForProjectCreation({ isUsersFirstProject, organizationId, organizations, planId, user }) {
|
|
1260
1024
|
const projectName = await input({
|
|
@@ -1290,37 +1054,6 @@ export class InitCommand extends SanityCommand {
|
|
|
1290
1054
|
userAction: 'create'
|
|
1291
1055
|
};
|
|
1292
1056
|
}
|
|
1293
|
-
async promptForTemplate() {
|
|
1294
|
-
const template = this.flags.template;
|
|
1295
|
-
const defaultTemplate = this.isUnattended() || template ? template || 'clean' : null;
|
|
1296
|
-
if (defaultTemplate) {
|
|
1297
|
-
return defaultTemplate;
|
|
1298
|
-
}
|
|
1299
|
-
return select({
|
|
1300
|
-
choices: [
|
|
1301
|
-
{
|
|
1302
|
-
name: 'Clean project with no predefined schema types',
|
|
1303
|
-
value: 'clean'
|
|
1304
|
-
},
|
|
1305
|
-
{
|
|
1306
|
-
name: 'Blog (schema)',
|
|
1307
|
-
value: 'blog'
|
|
1308
|
-
},
|
|
1309
|
-
{
|
|
1310
|
-
name: 'E-commerce (Shopify)',
|
|
1311
|
-
value: 'shopify'
|
|
1312
|
-
},
|
|
1313
|
-
{
|
|
1314
|
-
name: 'Movie project (schema + sample data)',
|
|
1315
|
-
value: 'moviedb'
|
|
1316
|
-
}
|
|
1317
|
-
],
|
|
1318
|
-
message: 'Select project template'
|
|
1319
|
-
});
|
|
1320
|
-
}
|
|
1321
|
-
promptForUndefinedFlag(flag) {
|
|
1322
|
-
return !this.isUnattended() && flag === undefined;
|
|
1323
|
-
}
|
|
1324
1057
|
async promptUserForNewOrganization(user) {
|
|
1325
1058
|
const name = await promptForOrganizationName(user);
|
|
1326
1059
|
const spin = spinner('Creating organization').start();
|
|
@@ -1426,72 +1159,6 @@ export class InitCommand extends SanityCommand {
|
|
|
1426
1159
|
}
|
|
1427
1160
|
}
|
|
1428
1161
|
}
|
|
1429
|
-
async writeOrOverwrite(filePath, content, workDir) {
|
|
1430
|
-
if (existsSync(filePath)) {
|
|
1431
|
-
let overwrite = this.flagOrDefault('overwrite-files', false);
|
|
1432
|
-
if (this.promptForUndefinedFlag(this.flags['overwrite-files'])) {
|
|
1433
|
-
overwrite = await confirm({
|
|
1434
|
-
default: false,
|
|
1435
|
-
message: `File ${styleText('yellow', filePath.replace(workDir, ''))} already exists. Do you want to overwrite it?`
|
|
1436
|
-
});
|
|
1437
|
-
}
|
|
1438
|
-
if (!overwrite) {
|
|
1439
|
-
return;
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
// make folder if not exists
|
|
1443
|
-
const folderPath = path.dirname(filePath);
|
|
1444
|
-
try {
|
|
1445
|
-
await mkdir(folderPath, {
|
|
1446
|
-
recursive: true
|
|
1447
|
-
});
|
|
1448
|
-
} catch {
|
|
1449
|
-
debug('Error creating folder %s', folderPath);
|
|
1450
|
-
}
|
|
1451
|
-
await writeFile(filePath, content, {
|
|
1452
|
-
encoding: 'utf8'
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
// write sanity folder files
|
|
1456
|
-
async writeSourceFiles({ fileExtension, files, folderPath, srcFolderPrefix, workDir }) {
|
|
1457
|
-
for (const [filePath, content] of Object.entries(files)){
|
|
1458
|
-
// check if file ends with full stop to indicate it's file and not directory (this only works with our template tree structure)
|
|
1459
|
-
if (filePath.includes('.') && typeof content === 'string') {
|
|
1460
|
-
await this.writeOrOverwrite(path.join(workDir, srcFolderPrefix ? 'src' : '', 'sanity', folderPath || '', `${filePath}${fileExtension}`), content, workDir);
|
|
1461
|
-
} else {
|
|
1462
|
-
await mkdir(path.join(workDir, srcFolderPrefix ? 'src' : '', 'sanity', filePath), {
|
|
1463
|
-
recursive: true
|
|
1464
|
-
});
|
|
1465
|
-
if (typeof content === 'object') {
|
|
1466
|
-
await this.writeSourceFiles({
|
|
1467
|
-
fileExtension,
|
|
1468
|
-
files: content,
|
|
1469
|
-
folderPath: filePath,
|
|
1470
|
-
srcFolderPrefix,
|
|
1471
|
-
workDir
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
/**
|
|
1478
|
-
* When running in a non-production Sanity environment (e.g. staging), write the
|
|
1479
|
-
* `SANITY_INTERNAL_ENV` variable to a `.env` file in the output directory so that
|
|
1480
|
-
* the bootstrapped project continues to target the same environment.
|
|
1481
|
-
*/ async writeStagingEnvIfNeeded(outputPath) {
|
|
1482
|
-
const sanityEnv = getSanityEnv();
|
|
1483
|
-
if (sanityEnv === 'production') return;
|
|
1484
|
-
await createOrAppendEnvVars({
|
|
1485
|
-
envVars: {
|
|
1486
|
-
INTERNAL_ENV: sanityEnv
|
|
1487
|
-
},
|
|
1488
|
-
filename: '.env',
|
|
1489
|
-
framework: null,
|
|
1490
|
-
log: false,
|
|
1491
|
-
output: this.output,
|
|
1492
|
-
outputPath
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
1162
|
}
|
|
1496
1163
|
|
|
1497
1164
|
//# sourceMappingURL=init.js.map
|