diff --git a/packages/@sanity/cli/src/actions/init-project/bootstrapLocalTemplate.ts b/packages/@sanity/cli/src/actions/init-project/bootstrapLocalTemplate.ts index 6572eb537d6..73c3df6205c 100644 --- a/packages/@sanity/cli/src/actions/init-project/bootstrapLocalTemplate.ts +++ b/packages/@sanity/cli/src/actions/init-project/bootstrapLocalTemplate.ts @@ -119,6 +119,7 @@ export async function bootstrapLocalTemplate( name: packageName, dependencies, devDependencies, + scripts: template.scripts, }) // ...and a studio config (`sanity.config.[ts|js]`) diff --git a/packages/@sanity/cli/src/actions/init-project/createPackageManifest.ts b/packages/@sanity/cli/src/actions/init-project/createPackageManifest.ts index c1d81ce1c73..78370174427 100644 --- a/packages/@sanity/cli/src/actions/init-project/createPackageManifest.ts +++ b/packages/@sanity/cli/src/actions/init-project/createPackageManifest.ts @@ -29,7 +29,7 @@ export function createPackageManifest( main: 'package.json', keywords: ['sanity'], - scripts: { + scripts: data.scripts || { 'dev': 'sanity dev', 'start': 'sanity start', 'build': 'sanity build', diff --git a/packages/@sanity/cli/src/actions/init-project/initProject.ts b/packages/@sanity/cli/src/actions/init-project/initProject.ts index b419dd74311..ff1764e9aff 100644 --- a/packages/@sanity/cli/src/actions/init-project/initProject.ts +++ b/packages/@sanity/cli/src/actions/init-project/initProject.ts @@ -99,6 +99,7 @@ export interface ProjectTemplate { configTemplate?: string | ((variables: GenerateConfigOptions['variables']) => string) typescriptOnly?: boolean appLocation?: string + scripts?: Record } export interface ProjectOrganization { diff --git a/packages/@sanity/cli/src/actions/init-project/templates/coreApp.ts b/packages/@sanity/cli/src/actions/init-project/templates/coreApp.ts index d87fa06ba83..4dc274e280b 100644 --- a/packages/@sanity/cli/src/actions/init-project/templates/coreApp.ts +++ b/packages/@sanity/cli/src/actions/init-project/templates/coreApp.ts @@ -20,6 +20,12 @@ const coreAppTemplate: ProjectTemplate = { 'typescript': '^5.1.6', }, appLocation: './src/App.tsx', + scripts: { + // this will eventually run a concurrently process with another in-flight utility + dev: 'sanity app dev', + build: 'sanity app build', + start: 'sanity app start', + }, } export default coreAppTemplate diff --git a/packages/@sanity/cli/src/types.ts b/packages/@sanity/cli/src/types.ts index 893782917c9..957e789eb37 100644 --- a/packages/@sanity/cli/src/types.ts +++ b/packages/@sanity/cli/src/types.ts @@ -24,6 +24,7 @@ export interface SanityModuleInternal { export interface PackageJson { name: string version: string + scripts?: Record description?: string author?: string diff --git a/packages/sanity/src/_internal/cli/commands/app/appGroup.ts b/packages/sanity/src/_internal/cli/commands/app/appGroup.ts new file mode 100644 index 00000000000..21cf64189eb --- /dev/null +++ b/packages/sanity/src/_internal/cli/commands/app/appGroup.ts @@ -0,0 +1,10 @@ +import {type CliCommandGroupDefinition} from '@sanity/cli' + +const appGroup: CliCommandGroupDefinition = { + name: 'app', + signature: '[COMMAND]', + isGroupRoot: true, + description: 'Manages non-studio applications', +} + +export default appGroup diff --git a/packages/sanity/src/_internal/cli/commands/app/buildCommand.ts b/packages/sanity/src/_internal/cli/commands/app/buildCommand.ts new file mode 100644 index 00000000000..1fa56217a9f --- /dev/null +++ b/packages/sanity/src/_internal/cli/commands/app/buildCommand.ts @@ -0,0 +1,52 @@ +import { + type CliCommandArguments, + type CliCommandContext, + type CliCommandDefinition, +} from '@sanity/cli' + +import {type BuildSanityStudioCommandFlags} from '../../actions/build/buildAction' + +const helpText = ` +Options + --source-maps Enable source maps for built bundles (increases size of bundle) + --no-minify Skip minifying built JavaScript (speeds up build, increases size of bundle) + -y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults + +Examples + sanity app build + sanity app build --no-minify --source-maps +` + +const appBuildCommand: CliCommandDefinition = { + name: 'build', + group: 'app', + signature: '[OUTPUT_DIR]', + description: 'Builds the Sanity application configuration into a static bundle', + action: async ( + args: CliCommandArguments, + context: CliCommandContext, + overrides?: {basePath?: string}, + ) => { + const buildAction = await getBuildAction() + + return buildAction(args, context, overrides) + }, + helpText, +} + +async function getBuildAction() { + // NOTE: in dev-mode we want to include from `src` so we need to use `.ts` extension + // NOTE: this `if` statement is not included in the output bundle + if (__DEV__) { + // eslint-disable-next-line import/extensions,@typescript-eslint/consistent-type-imports + const mod: typeof import('../../actions/build/buildAction') = require('../../actions/build/buildAction.ts') + + return mod.default + } + + const mod = await import('../../actions/build/buildAction') + + return mod.default +} + +export default appBuildCommand diff --git a/packages/sanity/src/_internal/cli/commands/app/devCommand.ts b/packages/sanity/src/_internal/cli/commands/app/devCommand.ts new file mode 100644 index 00000000000..db0b4589894 --- /dev/null +++ b/packages/sanity/src/_internal/cli/commands/app/devCommand.ts @@ -0,0 +1,58 @@ +import { + type CliCommandArguments, + type CliCommandContext, + type CliCommandDefinition, +} from '@sanity/cli' + +import {type StartDevServerCommandFlags} from '../../actions/dev/devAction' + +const helpText = ` +Notes + Changing the hostname or port number might require a new entry to the CORS-origins allow list. + +Options + --port TCP port to start server on. [default: 3333] + --host The local network interface at which to listen. [default: "127.0.0.1"] + +Examples + sanity app dev --host=0.0.0.0 + sanity app dev --port=1942 +` + +const appDevCommand: CliCommandDefinition = { + name: 'dev', + group: 'app', + signature: '[--port ] [--host ]', + description: 'Starts a local dev server for your Sanity application with live reloading', + action: async ( + args: CliCommandArguments, + context: CliCommandContext, + ) => { + const devAction = await getDevAction() + + return devAction(args, context) + }, + helpText, +} + +export async function getDevAction(): Promise< + ( + args: CliCommandArguments, + context: CliCommandContext, + ) => Promise +> { + // NOTE: in dev-mode we want to include from `src` so we need to use `.ts` extension + // NOTE: this `if` statement is not included in the output bundle + if (__DEV__) { + // eslint-disable-next-line import/extensions,@typescript-eslint/consistent-type-imports + const mod: typeof import('../../actions/dev/devAction') = require('../../actions/dev/devAction.ts') + + return mod.default + } + + const mod = await import('../../actions/dev/devAction') + + return mod.default +} + +export default appDevCommand diff --git a/packages/sanity/src/_internal/cli/commands/app/startCommand.ts b/packages/sanity/src/_internal/cli/commands/app/startCommand.ts new file mode 100644 index 00000000000..84b27b2df56 --- /dev/null +++ b/packages/sanity/src/_internal/cli/commands/app/startCommand.ts @@ -0,0 +1,84 @@ +import { + type CliCommandArguments, + type CliCommandContext, + type CliCommandDefinition, +} from '@sanity/cli' + +import {type StartPreviewServerCommandFlags} from '../../actions/preview/previewAction' +import {isInteractive} from '../../util/isInteractive' +import {getDevAction} from '../dev/devCommand' + +const helpText = ` +Notes + Changing the hostname or port number might require a new CORS-entry to be added. + +Options + --port TCP port to start server on. [default: 3333] + --host The local network interface at which to listen. [default: "127.0.0.1"] + +Examples + sanity app start --host=0.0.0.0 + sanity app start --port=1942 + sanity app start some/build-output-dir +` + +const appStartCommand: CliCommandDefinition = { + name: 'start', + group: 'app', + signature: '[BUILD_OUTPUT_DIR] [--port ] [--host ]', + description: 'Previews a built Sanity application', + action: async ( + args: CliCommandArguments, + context: CliCommandContext, + ) => { + const {output, chalk, prompt} = context + const previewAction = await getPreviewAction() + + const error = (msg: string) => output.warn(chalk.red.bgBlack(msg)) + + try { + await previewAction(args, context) + } catch (err) { + if (err.name !== 'BUILD_NOT_FOUND') { + throw err + } + + error(err.message) + error('\n') + + const shouldRunDevServer = + isInteractive && + (await prompt.single({ + message: 'Do you want to start a development server instead?', + type: 'confirm', + })) + + if (shouldRunDevServer) { + const devAction = await getDevAction() + await devAction(args, context) + } else { + // Indicate that this isn't an expected exit + // eslint-disable-next-line no-process-exit + process.exit(1) + } + } + }, + helpText, +} + +async function getPreviewAction() { + // NOTE: in dev-mode we want to include from `src` so we need to use `.ts` extension + // NOTE: this `if` statement is not included in the output bundle + if (__DEV__) { + // eslint-disable-next-line import/extensions,@typescript-eslint/consistent-type-imports + const mod: typeof import('../../actions/preview/previewAction') = require('../../actions/preview/previewAction.ts') + + return mod.default + } + + const mod = await import('../../actions/preview/previewAction') + + return mod.default +} + +export default appStartCommand diff --git a/packages/sanity/src/_internal/cli/commands/index.ts b/packages/sanity/src/_internal/cli/commands/index.ts index 7de5e343395..68f4fd4af29 100644 --- a/packages/sanity/src/_internal/cli/commands/index.ts +++ b/packages/sanity/src/_internal/cli/commands/index.ts @@ -1,5 +1,9 @@ import {type CliCommandDefinition, type CliCommandGroupDefinition} from '@sanity/cli' +import appGroup from './app/appGroup' +import appBuildCommand from './app/buildCommand' +import appDevCommand from './app/devCommand' +import appStartCommand from './app/startCommand' import backupGroup from './backup/backupGroup' import disableBackupCommand from './backup/disableBackupCommand' import downloadBackupCommand from './backup/downloadBackupCommand' @@ -55,6 +59,10 @@ import listUsersCommand from './users/listUsersCommand' import usersGroup from './users/usersGroup' const commands: (CliCommandDefinition | CliCommandGroupDefinition)[] = [ + appGroup, + appDevCommand, + appBuildCommand, + appStartCommand, buildCommand, datasetGroup, deployCommand, diff --git a/packages/sanity/src/_internal/cli/commands/start/startCommand.ts b/packages/sanity/src/_internal/cli/commands/start/startCommand.ts index 7c00a348911..dc3504dc5ab 100644 --- a/packages/sanity/src/_internal/cli/commands/start/startCommand.ts +++ b/packages/sanity/src/_internal/cli/commands/start/startCommand.ts @@ -32,24 +32,20 @@ const startCommand: CliCommandDefinition = { ) => { const {output, chalk, prompt} = context const previewAction = await getPreviewAction() - const {cliConfig} = context - const isCoreApp = cliConfig && '__experimental_coreAppConfiguration' in cliConfig const warn = (msg: string) => output.warn(chalk.yellow.bgBlack(msg)) const error = (msg: string) => output.warn(chalk.red.bgBlack(msg)) - if (!isCoreApp) { - warn('╭───────────────────────────────────────────────────────────╮') - warn('│ │') - warn("│ You're running Sanity Studio v3. In this version the │") - warn('│ [start] command is used to preview static builds. |') - warn('│ │') - warn('│ To run a development server, use the [npm run dev] or |') - warn('│ [npx sanity dev] command instead. For more information, │') - warn('│ see https://www.sanity.io/help/studio-v2-vs-v3 │') - warn('│ │') - warn('╰───────────────────────────────────────────────────────────╯') - warn('') // Newline to separate from other output - } + warn('╭───────────────────────────────────────────────────────────╮') + warn('│ │') + warn("│ You're running Sanity Studio v3. In this version the │") + warn('│ [start] command is used to preview static builds. |') + warn('│ │') + warn('│ To run a development server, use the [npm run dev] or |') + warn('│ [npx sanity dev] command instead. For more information, │') + warn('│ see https://www.sanity.io/help/studio-v2-vs-v3 │') + warn('│ │') + warn('╰───────────────────────────────────────────────────────────╯') + warn('') // Newline to separate from other output try { await previewAction(args, context)