Skip to content

Commit 3204418

Browse files
authoredMar 17, 2025··
feat(cli): allow opening apps and studios in dashboard (#8858)
1 parent 224cdb5 commit 3204418

File tree

6 files changed

+159
-26
lines changed

6 files changed

+159
-26
lines changed
 

‎packages/@sanity/cli/src/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ export interface CliConfig {
352352
* @internal
353353
*/
354354
__experimental_appConfiguration?: {
355-
organizationId?: string
355+
organizationId: string
356356
appLocation?: string
357357
appId?: string
358358
}

‎packages/sanity/src/_internal/cli/actions/app/devAction.ts

+46-7
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,68 @@
11
import {type CliCommandArguments, type CliCommandContext} from '@sanity/cli'
2+
import chalk from 'chalk'
3+
import {hideBin} from 'yargs/helpers'
4+
import yargs from 'yargs/yargs'
25

36
import {startDevServer} from '../../server/devServer'
47
import {gracefulServerDeath} from '../../util/servers'
5-
import {getDevServerConfig, type StartDevServerCommandFlags} from '../dev/devAction'
8+
import {getCoreAppURL, getDevServerConfig, type StartDevServerCommandFlags} from '../dev/devAction'
9+
10+
function parseCliFlags(args: {argv?: string[]}) {
11+
// Using slice(1) to remove the first argument, which is the command `dev` path to the CLI
12+
return yargs(hideBin(args.argv || process.argv).slice(1))
13+
.options('host', {type: 'string', default: 'localhost'})
14+
.options('port', {type: 'number', default: 3333})
15+
.options('load-in-dashboard', {type: 'boolean', default: true}).argv
16+
}
617

718
export default async function startAppDevServer(
819
args: CliCommandArguments<StartDevServerCommandFlags>,
920
context: CliCommandContext,
1021
): Promise<void> {
11-
const flags = args.extOptions
22+
const flags = await parseCliFlags(args)
1223
const {output, workDir, cliConfig} = context
1324

25+
if (!flags.loadInDashboard) {
26+
output.warn(`Apps cannot run without the Sanity dashboard`)
27+
output.warn(`Starting dev server with the --load-in-dashboard flag set to true`)
28+
}
29+
30+
let organizationId: string | undefined
31+
if (
32+
cliConfig &&
33+
'__experimental_appConfiguration' in cliConfig &&
34+
cliConfig.__experimental_appConfiguration?.organizationId
35+
) {
36+
organizationId = cliConfig.__experimental_appConfiguration.organizationId
37+
}
38+
39+
if (!organizationId) {
40+
output.error(`Apps require an organization ID (orgId) specified in your sanity.cli.ts file`)
41+
process.exit(1)
42+
}
43+
1444
// Try to load CLI configuration from sanity.cli.(js|ts)
1545
const config = getDevServerConfig({
16-
flags: {
17-
...flags,
18-
port: flags.port || '3333',
19-
},
46+
flags,
2047
workDir,
2148
cliConfig,
2249
output,
2350
})
2451

2552
try {
26-
await startDevServer(config)
53+
const spinner = output.spinner('Starting dev server').start()
54+
await startDevServer({...config, skipStartLog: true, isApp: true})
55+
spinner.succeed()
56+
57+
output.print(`Dev server started on port ${config.httpPort}`)
58+
output.print(`View your app in the Sanity dashboard here:`)
59+
output.print(
60+
chalk.blue(
61+
chalk.underline(
62+
getCoreAppURL({organizationId, httpHost: config.httpHost, httpPort: config.httpPort}),
63+
),
64+
),
65+
)
2766
} catch (err) {
2867
gracefulServerDeath('dev', config.httpHost, config.httpPort, err)
2968
}

‎packages/sanity/src/_internal/cli/actions/dev/devAction.ts

+95-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {
66
type CliConfig,
77
type CliOutputter,
88
} from '@sanity/cli'
9+
import {type SanityProject} from '@sanity/client'
10+
import chalk from 'chalk'
11+
import {hideBin} from 'yargs/helpers'
12+
import yargs from 'yargs/yargs'
913

1014
import {type DevServerOptions, startDevServer} from '../../server/devServer'
1115
import {checkRequiredDependencies} from '../../util/checkRequiredDependencies'
@@ -14,17 +18,48 @@ import {getSharedServerConfig, gracefulServerDeath} from '../../util/servers'
1418
import {getTimer} from '../../util/timing'
1519

1620
export interface StartDevServerCommandFlags {
17-
host?: string
18-
port?: string
21+
'host'?: string
22+
'port'?: string
23+
'load-in-dashboard'?: boolean
24+
'force'?: boolean
25+
}
26+
27+
export const getCoreURL = (): string => {
28+
return process.env.SANITY_INTERNAL_ENV === 'staging'
29+
? 'https://core.sanity.work'
30+
: 'https://core.sanity.io'
31+
}
32+
33+
export const getCoreAppURL = ({
34+
organizationId,
35+
httpHost = 'localhost',
36+
httpPort = 3333,
37+
}: {
38+
organizationId: string
39+
httpHost?: string
40+
httpPort?: number
41+
}): string => {
42+
// <core-app-url>/<orgniazationId>?dev=<dev-server-url>
43+
return `${getCoreURL()}/@${organizationId}?dev=http://${httpHost}:${httpPort}`
44+
}
45+
46+
function parseCliFlags(args: {argv?: string[]}) {
47+
// Using slice(1) to remove the first argument, which is the command `dev` path to the CLI
48+
return yargs(hideBin(args.argv || process.argv).slice(1))
49+
.options('host', {type: 'string', default: 'localhost'})
50+
.options('port', {type: 'number', default: 3333})
51+
.option('load-in-dashboard', {type: 'boolean', default: false}).argv
1952
}
2053

2154
export default async function startSanityDevServer(
2255
args: CliCommandArguments<StartDevServerCommandFlags>,
2356
context: CliCommandContext,
2457
): Promise<void> {
2558
const timers = getTimer()
26-
const flags = args.extOptions
27-
const {output, workDir, cliConfig} = context
59+
const flags = await parseCliFlags(args)
60+
const {output, apiClient, workDir, cliConfig} = context
61+
62+
const {loadInDashboard} = flags
2863

2964
timers.start('checkStudioDependencyVersions')
3065
checkStudioDependencyVersions(workDir)
@@ -39,8 +74,54 @@ export default async function startSanityDevServer(
3974
// Try to load CLI configuration from sanity.cli.(js|ts)
4075
const config = getDevServerConfig({flags, workDir, cliConfig, output})
4176

77+
const projectId = cliConfig?.api?.projectId
78+
let organizationId: string | undefined | null
79+
80+
if (loadInDashboard) {
81+
if (!projectId) {
82+
output.error('Project Id is required to load in dashboard')
83+
process.exit(1)
84+
}
85+
86+
const client = apiClient({
87+
requireUser: true,
88+
requireProject: true,
89+
})
90+
91+
try {
92+
const project = await client.request<SanityProject>({uri: `/projects/${projectId}`})
93+
organizationId = project.organizationId
94+
} catch (err) {
95+
output.error('Failed to get organization Id from project Id')
96+
process.exit(1)
97+
}
98+
}
99+
42100
try {
43-
await startDevServer(config)
101+
const spinner = output.spinner('Starting dev server').start()
102+
await startDevServer({...config, skipStartLog: loadInDashboard})
103+
spinner.succeed()
104+
105+
if (loadInDashboard) {
106+
if (!organizationId) {
107+
output.error('Organization Id not found for project')
108+
process.exit(1)
109+
}
110+
111+
output.print(`Dev server started on ${config.httpPort} port`)
112+
output.print(`View your app in the Sanity dashboard here:`)
113+
output.print(
114+
chalk.blue(
115+
chalk.underline(
116+
getCoreAppURL({
117+
organizationId,
118+
httpHost: config.httpHost,
119+
httpPort: config.httpPort,
120+
}),
121+
),
122+
),
123+
)
124+
}
44125
} catch (err) {
45126
gracefulServerDeath('dev', config.httpHost, config.httpPort, err)
46127
}
@@ -52,13 +133,20 @@ export function getDevServerConfig({
52133
cliConfig,
53134
output,
54135
}: {
55-
flags: StartDevServerCommandFlags
136+
flags: Awaited<ReturnType<typeof parseCliFlags>>
56137
workDir: string
57138
cliConfig?: CliConfig
58139
output: CliOutputter
59140
}): DevServerOptions {
60141
const configSpinner = output.spinner('Checking configuration files...')
61-
const baseConfig = getSharedServerConfig({flags, workDir, cliConfig})
142+
const baseConfig = getSharedServerConfig({
143+
flags: {
144+
host: flags.host,
145+
port: flags.port,
146+
},
147+
workDir,
148+
cliConfig,
149+
})
62150
configSpinner.succeed()
63151

64152
const env = process.env // eslint-disable-line no-process-env

‎packages/sanity/src/_internal/cli/commands/dev/devCommand.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
import {type StartDevServerCommandFlags} from '../../actions/dev/devAction'
88
import {determineIsApp} from '../../util/determineIsApp'
99

10+
// TODO: Add this once we are ready to release it.
11+
// --load-in-dashboard <boolean> Load the dev server in the Sanity dashboard. [default: false]
12+
1013
const helpText = `
1114
Notes
1215
Changing the hostname or port number might require a new entry to the CORS-origins allow list.

‎packages/sanity/src/_internal/cli/server/devServer.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface DevServerOptions {
1919
vite?: UserViteConfig
2020
appLocation?: string
2121
isApp?: boolean
22+
skipStartLog?: boolean
2223
}
2324

2425
export interface DevServer {
@@ -36,6 +37,7 @@ export async function startDevServer(options: DevServerOptions): Promise<DevServ
3637
reactCompiler,
3738
appLocation,
3839
isApp,
40+
skipStartLog,
3941
} = options
4042

4143
const startTime = Date.now()
@@ -71,15 +73,16 @@ export async function startDevServer(options: DevServerOptions): Promise<DevServ
7173
debug('Listening on specified port')
7274
await server.listen()
7375

74-
const startupDuration = Date.now() - startTime
75-
const url = `http://${httpHost || 'localhost'}:${httpPort || '3333'}${basePath}`
76-
const appType = isApp ? 'Sanity application' : 'Sanity Studio'
77-
info(
78-
`${appType} ` +
79-
`using ${chalk.cyan(`vite@${require('vite/package.json').version}`)} ` +
80-
`ready in ${chalk.cyan(`${Math.ceil(startupDuration)}ms`)} ` +
81-
`and running at ${chalk.cyan(url)}`,
82-
)
83-
76+
if (!skipStartLog) {
77+
const startupDuration = Date.now() - startTime
78+
const url = `http://${httpHost || 'localhost'}:${httpPort || '3333'}${basePath}`
79+
const appType = isApp ? 'Sanity application' : 'Sanity Studio'
80+
info(
81+
`${appType} ` +
82+
`using ${chalk.cyan(`vite@${require('vite/package.json').version}`)} ` +
83+
`ready in ${chalk.cyan(`${Math.ceil(startupDuration)}ms`)} ` +
84+
`and running at ${chalk.cyan(url)}`,
85+
)
86+
}
8487
return {close: () => server.close()}
8588
}

‎packages/sanity/src/_internal/cli/util/servers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function getSharedServerConfig({
3939
workDir,
4040
cliConfig,
4141
}: {
42-
flags: {host?: string; port?: string}
42+
flags: {host?: string; port?: string | number}
4343
workDir: string
4444
cliConfig?: CliConfig
4545
}): {

0 commit comments

Comments
 (0)
Please sign in to comment.