@sanity/cli 7.5.0 → 7.6.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.
Files changed (63) hide show
  1. package/dist/actions/build/buildApp.js +7 -183
  2. package/dist/actions/build/buildApp.js.map +1 -1
  3. package/dist/actions/build/buildStudio.js +6 -213
  4. package/dist/actions/build/buildStudio.js.map +1 -1
  5. package/dist/actions/build/shouldAutoUpdate.js +100 -22
  6. package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
  7. package/dist/actions/deploy/{createStudioUserApplication.js → createUserApplication.js} +56 -5
  8. package/dist/actions/deploy/createUserApplication.js.map +1 -0
  9. package/dist/actions/deploy/deployApp.js +232 -185
  10. package/dist/actions/deploy/deployApp.js.map +1 -1
  11. package/dist/actions/deploy/deployChecks.js +263 -0
  12. package/dist/actions/deploy/deployChecks.js.map +1 -0
  13. package/dist/actions/deploy/deployRunner.js +57 -0
  14. package/dist/actions/deploy/deployRunner.js.map +1 -0
  15. package/dist/actions/deploy/deployStudio.js +189 -198
  16. package/dist/actions/deploy/deployStudio.js.map +1 -1
  17. package/dist/actions/deploy/deploymentPlan.js +89 -0
  18. package/dist/actions/deploy/deploymentPlan.js.map +1 -0
  19. package/dist/actions/deploy/findUserApplication.js +209 -0
  20. package/dist/actions/deploy/findUserApplication.js.map +1 -0
  21. package/dist/actions/deploy/resolveDeployTarget.js +185 -0
  22. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -0
  23. package/dist/actions/dev/devAction.js +1 -1
  24. package/dist/actions/dev/devAction.js.map +1 -1
  25. package/dist/actions/dev/servers/getDevServerConfig.js +18 -6
  26. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  27. package/dist/actions/dev/servers/startAppDevServer.js +1 -1
  28. package/dist/actions/dev/servers/startAppDevServer.js.map +1 -1
  29. package/dist/actions/dev/servers/startStudioDevServer.js +2 -1
  30. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  31. package/dist/actions/manifest/extractCoreAppManifest.js +14 -0
  32. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  33. package/dist/commands/deploy.js +27 -11
  34. package/dist/commands/deploy.js.map +1 -1
  35. package/dist/commands/dev.js +2 -1
  36. package/dist/commands/dev.js.map +1 -1
  37. package/dist/commands/init.js +2 -1
  38. package/dist/commands/init.js.map +1 -1
  39. package/dist/exports/index.d.ts +9 -0
  40. package/dist/exports/index.js +1 -8
  41. package/dist/exports/index.js.map +1 -1
  42. package/dist/server/devServer.js +27 -3
  43. package/dist/server/devServer.js.map +1 -1
  44. package/dist/util/appId.js +13 -5
  45. package/dist/util/appId.js.map +1 -1
  46. package/dist/util/compareDependencyVersions.js.map +1 -1
  47. package/dist/util/determineIsApp.js +1 -1
  48. package/dist/util/determineIsApp.js.map +1 -1
  49. package/dist/util/errorMessages.js +2 -0
  50. package/dist/util/errorMessages.js.map +1 -1
  51. package/oclif.manifest.json +14 -1
  52. package/package.json +8 -8
  53. package/dist/actions/build/handlePrereleaseVersions.js +0 -44
  54. package/dist/actions/build/handlePrereleaseVersions.js.map +0 -1
  55. package/dist/actions/deploy/createStudioUserApplication.js.map +0 -1
  56. package/dist/actions/deploy/createUserApplicationForApp.js +0 -56
  57. package/dist/actions/deploy/createUserApplicationForApp.js.map +0 -1
  58. package/dist/actions/deploy/findUserApplicationForApp.js +0 -111
  59. package/dist/actions/deploy/findUserApplicationForApp.js.map +0 -1
  60. package/dist/actions/deploy/findUserApplicationForStudio.js +0 -172
  61. package/dist/actions/deploy/findUserApplicationForStudio.js.map +0 -1
  62. package/dist/actions/deploy/viewDeployment.js +0 -32
  63. package/dist/actions/deploy/viewDeployment.js.map +0 -1
@@ -1,105 +1,68 @@
1
1
  import { basename, dirname } from 'node:path';
2
2
  import { styleText } from 'node:util';
3
3
  import { createGzip } from 'node:zlib';
4
- import { CLIError } from '@oclif/core/errors';
5
4
  import { formatSchemaValidation, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
6
- import { exitCodes, getLocalPackageVersion } from '@sanity/cli-core';
5
+ import { exitCodes } from '@sanity/cli-core';
7
6
  import { spinner } from '@sanity/cli-core/ux';
8
7
  import { getWorkbench } from '@sanity/workbench-cli/deploy';
9
8
  import { pack } from 'tar-fs';
10
9
  import { createDeployment } from '../../services/userApplications.js';
11
10
  import { getAppId } from '../../util/appId.js';
12
11
  import { NO_PROJECT_ID } from '../../util/errorMessages.js';
13
- import { getErrorMessage } from '../../util/getErrorMessage.js';
14
12
  import { buildStudio } from '../build/buildStudio.js';
15
- import { shouldAutoUpdate } from '../build/shouldAutoUpdate.js';
16
- import { checkDir } from './checkDir.js';
17
- import { createStudioUserApplication } from './createStudioUserApplication.js';
13
+ import { createStudioUserApplication } from './createUserApplication.js';
14
+ import { checkAutoUpdates, checkBuild, checkPackageVersion, checkStudioTarget, verifyOutputDir } from './deployChecks.js';
18
15
  import { deployDebug } from './deployDebug.js';
16
+ import { listDeploymentFiles } from './deploymentPlan.js';
17
+ import { runDeploy } from './deployRunner.js';
19
18
  import { deployStudioSchemasAndManifests } from './deployStudioSchemasAndManifests.js';
20
- import { findUserApplicationForStudio } from './findUserApplicationForStudio.js';
21
- import { normalizeUrl, validateUrl } from './urlUtils.js';
22
- export async function deployStudio(options) {
23
- const { cliConfig, flags, output, projectRoot, sourceDir } = options;
24
- const workbench = getWorkbench(cliConfig);
25
- const workDir = projectRoot.directory;
26
- const configPath = projectRoot.path;
27
- const appId = getAppId(cliConfig);
19
+ import { findUserApplicationForStudio } from './findUserApplication.js';
20
+ export function deployStudio(options) {
21
+ return runDeploy(options, {
22
+ listFiles: ({ flags, projectRoot, sourceDir })=>flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),
23
+ run: runStudioDeployment,
24
+ type: 'studio'
25
+ });
26
+ }
27
+ /** Validates the deploy, extracts and uploads the schema, and ships the build. */ async function runStudioDeployment(options, reporter) {
28
+ const { cliConfig, flags, output, sourceDir } = options;
29
+ const workDir = options.projectRoot.directory;
30
+ const isExternal = !!flags.external;
31
+ const isWorkbenchApp = getWorkbench(cliConfig) !== null;
28
32
  const projectId = cliConfig.api?.projectId;
29
- const installedSanityVersion = await getLocalPackageVersion('sanity', workDir);
30
- const isAutoUpdating = shouldAutoUpdate({
33
+ const dryRun = !!flags['dry-run'];
34
+ const isAutoUpdating = checkAutoUpdates(reporter, {
31
35
  cliConfig,
32
- flags,
33
- output
36
+ flags
34
37
  });
35
- const isExternal = !!flags.external;
36
- const urlType = isExternal ? 'external' : 'internal';
37
- // Federated (`unstable_defineApp`) applications integrate through Sanity's
38
- // build and hosting pipeline, which `--external` skips. Fail before any
39
- // prompts or API calls.
40
- if (isExternal && workbench) {
41
- output.error('Deploying a federated application to an external host is not yet supported. ' + 'Remove the `--external` flag to deploy to Sanity hosting.', {
42
- exit: exitCodes.USAGE_ERROR
38
+ if (isExternal && isWorkbenchApp) {
39
+ // A federated app deploys through Sanity's build/hosting pipeline, which
40
+ // --external skips.
41
+ reporter.report({
42
+ exitCode: exitCodes.USAGE_ERROR,
43
+ message: 'Deploying a federated application to an external host is not yet supported',
44
+ solution: 'Remove the --external flag to deploy to Sanity hosting',
45
+ status: 'fail'
43
46
  });
44
- return;
45
47
  }
46
- // Resolve the app host from --url flag (takes precedence) or studioHost config
47
- const appHost = resolveAppHost({
48
- flags,
49
- isExternal,
50
- output,
51
- studioHost: cliConfig.studioHost
48
+ const version = await checkPackageVersion(reporter, {
49
+ moduleName: 'sanity',
50
+ workDir
52
51
  });
53
- if (!installedSanityVersion) {
54
- output.error(`Failed to find installed sanity version`, {
55
- exit: 1
56
- });
57
- return;
58
- }
59
- if (!projectId) {
60
- output.error(NO_PROJECT_ID, {
61
- exit: 1
62
- });
63
- return;
64
- }
65
- let spin = spinner('Verifying local content');
66
- try {
67
- let userApplication = await findUserApplicationForStudio({
68
- appHost,
69
- appId,
70
- output,
71
- projectId,
72
- unattended: !!flags.yes,
73
- urlType
74
- });
75
- if (!userApplication) {
76
- if (flags.yes) {
77
- const flagHint = isExternal ? 'Use --url to specify the external studio URL' : 'Use --url to specify the studio hostname';
78
- output.error(`Cannot prompt for ${isExternal ? 'external studio URL' : 'studio hostname'} in unattended mode. ${flagHint}.`, {
79
- exit: exitCodes.USAGE_ERROR
80
- });
81
- return;
82
- }
83
- if (isExternal) {
84
- output.log('Your project has not been registered with an external studio URL.');
85
- output.log('Please enter the full URL where your studio is hosted.');
86
- } else {
87
- output.log('Your project has not been assigned a studio hostname.');
88
- output.log('To deploy your Sanity Studio to our hosted sanity.studio service,');
89
- output.log('you will need one. Please enter the subdomain you want to use.');
90
- }
91
- userApplication = await createStudioUserApplication({
92
- projectId,
93
- urlType
94
- });
95
- deployDebug('Created user application', userApplication);
96
- }
97
- deployDebug('Found user application', userApplication);
98
- // Always build the project, unless --no-build is passed or --external is passed
99
- const shouldBuild = flags.build && !isExternal;
100
- if (shouldBuild) {
101
- deployDebug(`Building studio`);
102
- await buildStudio({
52
+ reporter.report(projectId ? {
53
+ message: `Project: ${projectId}`,
54
+ status: 'pass'
55
+ } : {
56
+ message: NO_PROJECT_ID,
57
+ solution: 'Add `api.projectId` to sanity.cli.ts',
58
+ status: 'fail'
59
+ });
60
+ const application = await resolveStudioApplication(options, {
61
+ dryRun,
62
+ reporter
63
+ });
64
+ await checkBuild(reporter, {
65
+ build: ()=>buildStudio({
103
66
  autoUpdatesEnabled: isAutoUpdating,
104
67
  calledFromDeploy: true,
105
68
  cliConfig,
@@ -107,136 +70,164 @@ export async function deployStudio(options) {
107
70
  outDir: sourceDir,
108
71
  output,
109
72
  workDir
110
- });
111
- }
112
- let studioManifest = null;
113
- try {
114
- studioManifest = await deployStudioSchemasAndManifests({
115
- configPath,
116
- isExternal,
117
- outPath: `${sourceDir}/static`,
118
- projectId,
119
- schemaRequired: flags['schema-required'],
120
- verbose: flags.verbose,
121
- workDir
122
- });
123
- } catch (error) {
124
- deployDebug('Error deploying studio schemas and manifests', error);
125
- if (error instanceof SchemaExtractionError) {
126
- output.error(formatSchemaValidation(error.validation || []), {
127
- exit: 1
128
- });
129
- }
130
- output.error(`Error deploying studio schemas and manifests: ${error}`, {
131
- exit: 1
132
- });
73
+ }),
74
+ skipReason: studioBuildSkipReason({
75
+ build: flags.build,
76
+ isExternal
77
+ }),
78
+ successMessage: 'Studio built'
79
+ });
80
+ if (!isExternal) {
81
+ await verifyOutputDir({
82
+ isWorkbenchApp,
83
+ reporter,
84
+ sourceDir
85
+ });
86
+ }
87
+ // Dry run stops here everything below mutates.
88
+ if (dryRun) return;
89
+ // A real deploy has already exited if anything failed; landing here without a
90
+ // resolved application or version means the deploy target was never resolved.
91
+ if (!application || !version) return;
92
+ const studioManifest = await uploadStudioSchema(options, {
93
+ isExternal
94
+ });
95
+ await shipStudioDeployment({
96
+ application,
97
+ isAutoUpdating,
98
+ isExternal,
99
+ options,
100
+ studioManifest,
101
+ version
102
+ });
103
+ }
104
+ /**
105
+ * Finds the application a real deploy targets, registering a studio host when
106
+ * none is configured. A dry run resolves and reports the target read-only instead.
107
+ */ async function resolveStudioApplication(options, { dryRun, reporter }) {
108
+ const { cliConfig, flags, output } = options;
109
+ const isExternal = !!flags.external;
110
+ // Sets the title on a newly registered studio; blank falls back to undefined
111
+ const title = flags.title?.trim() || undefined;
112
+ if (dryRun) {
113
+ await checkStudioTarget(reporter, {
114
+ appId: getAppId(cliConfig),
115
+ isExternal,
116
+ projectId: cliConfig.api?.projectId,
117
+ studioHost: cliConfig.studioHost,
118
+ title,
119
+ urlFlag: flags.url
120
+ });
121
+ return null;
122
+ }
123
+ const projectId = cliConfig.api?.projectId ?? '';
124
+ let application = await findUserApplicationForStudio({
125
+ appId: getAppId(cliConfig),
126
+ isExternal,
127
+ output,
128
+ projectId,
129
+ studioHost: cliConfig.studioHost,
130
+ title,
131
+ unattended: !!flags.yes,
132
+ urlFlag: flags.url
133
+ });
134
+ if (!application) {
135
+ if (isExternal) {
136
+ output.log('Your project has not been registered with an external studio URL.');
137
+ output.log('Please enter the full URL where your studio is hosted.');
138
+ } else {
139
+ output.log('Your project has not been assigned a studio hostname.');
140
+ output.log('To deploy your Sanity Studio to our hosted sanity.studio service,');
141
+ output.log('you will need one. Please enter the subdomain you want to use.');
133
142
  }
134
- if (!studioManifest) {
135
- output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {
143
+ application = await createStudioUserApplication({
144
+ projectId,
145
+ title,
146
+ urlType: isExternal ? 'external' : 'internal'
147
+ });
148
+ deployDebug('Created user application', application);
149
+ }
150
+ deployDebug('Found user application', application);
151
+ return application;
152
+ }
153
+ /** Extracts the studio schema and manifest and uploads them to the schema store. */ async function uploadStudioSchema(options, { isExternal }) {
154
+ const { cliConfig, flags, output, projectRoot, sourceDir } = options;
155
+ let studioManifest = null;
156
+ try {
157
+ studioManifest = await deployStudioSchemasAndManifests({
158
+ configPath: projectRoot.path,
159
+ isExternal,
160
+ outPath: `${sourceDir}/static`,
161
+ projectId: cliConfig.api?.projectId ?? '',
162
+ schemaRequired: flags['schema-required'],
163
+ verbose: flags.verbose,
164
+ workDir: projectRoot.directory
165
+ });
166
+ } catch (error) {
167
+ deployDebug('Error deploying studio schemas and manifests', error);
168
+ if (error instanceof SchemaExtractionError) {
169
+ output.error(formatSchemaValidation(error.validation || []), {
136
170
  exit: 1
137
171
  });
138
172
  }
139
- let tarball;
140
- if (!isExternal) {
141
- // Ensure that the directory exists, is a directory and seems to have valid content
142
- spin = spin.start();
143
- try {
144
- await (workbench ? workbench.checkBuiltOutput(sourceDir) : checkDir(sourceDir));
145
- spin.succeed();
146
- } catch (err) {
147
- spin.fail();
148
- deployDebug('Error checking directory', err);
149
- output.error(getErrorMessage(err), {
150
- exit: 1
151
- });
152
- }
153
- // Create a tarball of the given directory
154
- const parentDir = dirname(sourceDir);
155
- const base = basename(sourceDir);
156
- tarball = pack(parentDir, {
157
- entries: [
158
- base
159
- ]
160
- }).pipe(createGzip());
161
- }
162
- spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start();
163
- const { location } = await createDeployment({
164
- applicationId: userApplication.id,
173
+ output.error(`Error deploying studio schemas and manifests: ${error}`, {
174
+ exit: 1
175
+ });
176
+ }
177
+ if (!studioManifest) {
178
+ output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {
179
+ exit: 1
180
+ });
181
+ }
182
+ return studioManifest;
183
+ }
184
+ async function shipStudioDeployment({ application, isAutoUpdating, isExternal, options, studioManifest, version }) {
185
+ const { cliConfig, output, sourceDir } = options;
186
+ let tarball;
187
+ if (!isExternal) {
188
+ tarball = pack(dirname(sourceDir), {
189
+ entries: [
190
+ basename(sourceDir)
191
+ ]
192
+ }).pipe(createGzip());
193
+ }
194
+ const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start();
195
+ let location;
196
+ try {
197
+ ;
198
+ ({ location } = await createDeployment({
199
+ applicationId: application.id,
165
200
  isApp: false,
166
201
  isAutoUpdating,
167
202
  manifest: studioManifest,
168
- projectId,
203
+ projectId: cliConfig.api?.projectId,
169
204
  tarball,
170
- version: installedSanityVersion
171
- });
172
- spin.succeed();
173
- if (isExternal) {
174
- output.log(`\nSuccess! Studio registered`);
175
- } else {
176
- output.log(`\nSuccess! Studio deployed to ${styleText('cyan', location)}`);
177
- }
178
- if (!appId) {
179
- const example = `Example:
205
+ version
206
+ }));
207
+ } catch (error) {
208
+ spin.fail();
209
+ throw error;
210
+ }
211
+ spin.succeed();
212
+ output.log(isExternal ? `\nSuccess! Studio registered` : `\nSuccess! Studio deployed to ${styleText('cyan', location)}`);
213
+ if (getAppId(cliConfig)) return;
214
+ const example = `Example:
180
215
  export default defineCliConfig({
181
216
  //…
182
217
  deployment: {
183
- ${styleText('cyan', `appId: '${userApplication.id}'`)},
218
+ ${styleText('cyan', `appId: '${application.id}'`)},
184
219
  },
185
220
  //…
186
221
  })`;
187
- output.log(`\nAdd ${styleText('cyan', `appId: '${userApplication.id}'`)}`);
188
- output.log(`to the \`deployment\` section in sanity.cli.js or sanity.cli.ts`);
189
- output.log(`to avoid prompting for application id on next deploy.`);
190
- output.log(`\n${example}`);
191
- }
192
- } catch (error) {
193
- // if the error is a CLIError, we can just output the message and preserve its exit code
194
- if (error instanceof CLIError) {
195
- output.error(error.message, {
196
- exit: error.oclif?.exit ?? exitCodes.RUNTIME_ERROR
197
- });
198
- return;
199
- }
200
- spin.fail();
201
- deployDebug('Error deploying studio', error);
202
- output.error(`Error deploying studio: ${error}`, {
203
- exit: 1
204
- });
205
- }
222
+ output.log(`\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`);
223
+ output.log(`to the \`deployment\` section in sanity.cli.js or sanity.cli.ts`);
224
+ output.log(`to avoid prompting for application id on next deploy.`);
225
+ output.log(`\n${example}`);
206
226
  }
207
- function resolveAppHost({ flags, isExternal, output, studioHost }) {
208
- const url = flags.url;
209
- if (!url) {
210
- return studioHost;
211
- }
212
- if (isExternal) {
213
- const normalized = normalizeUrl(url);
214
- const validation = validateUrl(normalized);
215
- if (validation !== true) {
216
- output.error(validation, {
217
- exit: exitCodes.USAGE_ERROR
218
- });
219
- return undefined;
220
- }
221
- return normalized;
222
- }
223
- // For internal deploys, strip protocol prefix and .sanity.studio suffix if present
224
- const hostname = url.replace(/^https?:\/\//i, '').replace(/\.sanity\.studio\/?$/i, '');
225
- // If the result still looks like a URL (contains dots), the user likely meant --external
226
- if (hostname.includes('.')) {
227
- output.error(`"${hostname}" does not look like a sanity.studio hostname. Did you mean to use --external?`, {
228
- exit: exitCodes.USAGE_ERROR
229
- });
230
- return undefined;
231
- }
232
- // Validate hostname characters (alphanumeric and hyphens only)
233
- if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {
234
- output.error(`Invalid studio hostname "${hostname}". Hostnames can only contain letters, numbers, and hyphens.`, {
235
- exit: exitCodes.USAGE_ERROR
236
- });
237
- return undefined;
238
- }
239
- return hostname;
227
+ function studioBuildSkipReason({ build, isExternal }) {
228
+ if (isExternal) return 'Build skipped for externally hosted studios';
229
+ if (!build) return 'Build skipped (--no-build) — validating existing output directory';
230
+ return;
240
231
  }
241
232
 
242
233
  //# sourceMappingURL=deployStudio.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, getLocalPackageVersion, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {getWorkbench} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {getErrorMessage} from '../../util/getErrorMessage.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {shouldAutoUpdate} from '../build/shouldAutoUpdate.js'\nimport {checkDir} from './checkDir.js'\nimport {createStudioUserApplication} from './createStudioUserApplication.js'\nimport {deployDebug} from './deployDebug.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplicationForStudio.js'\nimport {type DeployAppOptions} from './types.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\nexport async function deployStudio(options: DeployAppOptions) {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n const workbench = getWorkbench(cliConfig)\n\n const workDir = projectRoot.directory\n const configPath = projectRoot.path\n\n const appId = getAppId(cliConfig)\n const projectId = cliConfig.api?.projectId\n const installedSanityVersion = await getLocalPackageVersion('sanity', workDir)\n const isAutoUpdating = shouldAutoUpdate({cliConfig, flags, output})\n\n const isExternal = !!flags.external\n const urlType: 'external' | 'internal' = isExternal ? 'external' : 'internal'\n\n // Federated (`unstable_defineApp`) applications integrate through Sanity's\n // build and hosting pipeline, which `--external` skips. Fail before any\n // prompts or API calls.\n if (isExternal && workbench) {\n output.error(\n 'Deploying a federated application to an external host is not yet supported. ' +\n 'Remove the `--external` flag to deploy to Sanity hosting.',\n {exit: exitCodes.USAGE_ERROR},\n )\n return\n }\n\n // Resolve the app host from --url flag (takes precedence) or studioHost config\n const appHost = resolveAppHost({flags, isExternal, output, studioHost: cliConfig.studioHost})\n\n if (!installedSanityVersion) {\n output.error(`Failed to find installed sanity version`, {exit: 1})\n return\n }\n\n if (!projectId) {\n output.error(NO_PROJECT_ID, {exit: 1})\n return\n }\n\n let spin = spinner('Verifying local content')\n\n try {\n let userApplication = await findUserApplicationForStudio({\n appHost,\n appId,\n output,\n projectId,\n unattended: !!flags.yes,\n urlType,\n })\n\n if (!userApplication) {\n if (flags.yes) {\n const flagHint = isExternal\n ? 'Use --url to specify the external studio URL'\n : 'Use --url to specify the studio hostname'\n output.error(\n `Cannot prompt for ${isExternal ? 'external studio URL' : 'studio hostname'} in unattended mode. ${flagHint}.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n return\n }\n\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n userApplication = await createStudioUserApplication({projectId, urlType})\n\n deployDebug('Created user application', userApplication)\n }\n\n deployDebug('Found user application', userApplication)\n\n // Always build the project, unless --no-build is passed or --external is passed\n const shouldBuild = flags.build && !isExternal\n if (shouldBuild) {\n deployDebug(`Building studio`)\n await buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n })\n }\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests({\n configPath,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId,\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir,\n })\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n let tarball: Gzip | undefined\n\n if (!isExternal) {\n // Ensure that the directory exists, is a directory and seems to have valid content\n spin = spin.start()\n try {\n await (workbench ? workbench.checkBuiltOutput(sourceDir) : checkDir(sourceDir))\n spin.succeed()\n } catch (err) {\n spin.fail()\n deployDebug('Error checking directory', err)\n output.error(getErrorMessage(err), {exit: 1})\n }\n\n // Create a tarball of the given directory\n const parentDir = dirname(sourceDir)\n const base = basename(sourceDir)\n tarball = pack(parentDir, {entries: [base]}).pipe(createGzip())\n }\n\n spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n\n const {location} = await createDeployment({\n applicationId: userApplication.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId,\n tarball,\n version: installedSanityVersion,\n })\n\n spin.succeed()\n\n if (isExternal) {\n output.log(`\\nSuccess! Studio registered`)\n } else {\n output.log(`\\nSuccess! Studio deployed to ${styleText('cyan', location)}`)\n }\n\n if (!appId) {\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${userApplication.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${userApplication.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n }\n } catch (error) {\n // if the error is a CLIError, we can just output the message and preserve its exit code\n if (error instanceof CLIError) {\n output.error(error.message, {exit: error.oclif?.exit ?? exitCodes.RUNTIME_ERROR})\n return\n }\n\n spin.fail()\n deployDebug('Error deploying studio', error)\n output.error(`Error deploying studio: ${error}`, {exit: 1})\n }\n}\n\nfunction resolveAppHost({\n flags,\n isExternal,\n output,\n studioHost,\n}: {\n flags: DeployAppOptions['flags']\n isExternal: boolean\n output: Output\n studioHost: string | undefined\n}): string | undefined {\n const url = flags.url\n if (!url) {\n return studioHost\n }\n\n if (isExternal) {\n const normalized = normalizeUrl(url)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n output.error(validation, {exit: exitCodes.USAGE_ERROR})\n return undefined\n }\n return normalized\n }\n\n // For internal deploys, strip protocol prefix and .sanity.studio suffix if present\n const hostname = url.replace(/^https?:\\/\\//i, '').replace(/\\.sanity\\.studio\\/?$/i, '')\n\n // If the result still looks like a URL (contains dots), the user likely meant --external\n if (hostname.includes('.')) {\n output.error(\n `\"${hostname}\" does not look like a sanity.studio hostname. Did you mean to use --external?`,\n {exit: exitCodes.USAGE_ERROR},\n )\n return undefined\n }\n\n // Validate hostname characters (alphanumeric and hyphens only)\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {\n output.error(\n `Invalid studio hostname \"${hostname}\". Hostnames can only contain letters, numbers, and hyphens.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n return undefined\n }\n\n return hostname\n}\n"],"names":["basename","dirname","styleText","createGzip","CLIError","formatSchemaValidation","SchemaExtractionError","exitCodes","getLocalPackageVersion","spinner","getWorkbench","pack","createDeployment","getAppId","NO_PROJECT_ID","getErrorMessage","buildStudio","shouldAutoUpdate","checkDir","createStudioUserApplication","deployDebug","deployStudioSchemasAndManifests","findUserApplicationForStudio","normalizeUrl","validateUrl","deployStudio","options","cliConfig","flags","output","projectRoot","sourceDir","workbench","workDir","directory","configPath","path","appId","projectId","api","installedSanityVersion","isAutoUpdating","isExternal","external","urlType","error","exit","USAGE_ERROR","appHost","resolveAppHost","studioHost","spin","userApplication","unattended","yes","flagHint","log","shouldBuild","build","autoUpdatesEnabled","calledFromDeploy","outDir","studioManifest","outPath","schemaRequired","verbose","validation","tarball","start","checkBuiltOutput","succeed","err","fail","parentDir","base","entries","pipe","location","applicationId","id","isApp","manifest","version","example","message","oclif","RUNTIME_ERROR","url","normalized","undefined","hostname","replace","includes","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {getWorkbench} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n type CheckReporter,\n checkStudioTarget,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles} from './deploymentPlan.js'\nimport {runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: CheckReporter,\n): Promise<void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const isWorkbenchApp = getWorkbench(cliConfig) !== null\n const projectId = cliConfig.api?.projectId\n const dryRun = !!flags['dry-run']\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n if (isExternal && isWorkbenchApp) {\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips.\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const version = await checkPackageVersion(reporter, {\n moduleName: 'sanity',\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n const application = await resolveStudioApplication(options, {dryRun, reporter})\n\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved application or version means the deploy target was never resolved.\n if (!application || !version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: CheckReporter},\n): Promise<UserApplication | null> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId: getAppId(cliConfig),\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return null\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n let application = await findUserApplicationForStudio({\n appId: getAppId(cliConfig),\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n application = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', application)\n }\n\n deployDebug('Found user application', application)\n return application\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests({\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n })\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<void> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}`,\n )\n\n if (getAppId(cliConfig)) return\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","getWorkbench","pack","createDeployment","getAppId","NO_PROJECT_ID","buildStudio","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","deployStudio","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","isWorkbenchApp","projectId","api","dryRun","isAutoUpdating","report","exitCode","USAGE_ERROR","message","solution","status","version","moduleName","application","resolveStudioApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","studioManifest","uploadStudioSchema","shipStudioDeployment","title","trim","undefined","appId","studioHost","urlFlag","url","unattended","yes","log","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","location","applicationId","id","isApp","manifest","fail","succeed","example"],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import { readdir, stat } from 'node:fs/promises';
2
+ import { join, relative, sep } from 'node:path';
3
+ import { styleText } from 'node:util';
4
+ import { logSymbols } from '@sanity/cli-core/ux';
5
+ /**
6
+ * Lists the files a deploy would pack from `sourceDir`, as paths relative to
7
+ * `fromDir`. A missing directory yields an empty list rather than throwing.
8
+ */ export async function listDeploymentFiles(sourceDir, fromDir) {
9
+ const walk = async (dir)=>{
10
+ let entries;
11
+ try {
12
+ entries = await readdir(dir, {
13
+ withFileTypes: true
14
+ });
15
+ } catch {
16
+ return [];
17
+ }
18
+ const nested = await Promise.all(entries.map((entry)=>{
19
+ const full = join(dir, entry.name);
20
+ return entry.isDirectory() ? walk(full) : Promise.resolve([
21
+ full
22
+ ]);
23
+ }));
24
+ return nested.flat();
25
+ };
26
+ const absolute = await walk(sourceDir);
27
+ const files = await Promise.all(absolute.map(async (file)=>({
28
+ // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\`).
29
+ path: relative(fromDir, file).split(sep).join('/'),
30
+ size: (await stat(file)).size
31
+ })));
32
+ return files.toSorted((a, b)=>a.path.localeCompare(b.path));
33
+ }
34
+ export function renderDeploymentPlan(plan, output) {
35
+ const label = plan.type === 'coreApp' ? 'application' : 'studio';
36
+ const problems = plan.checks.filter((check)=>check.status === 'fail');
37
+ const warnings = plan.checks.filter((check)=>check.status === 'warn');
38
+ const totalBytes = plan.files.reduce((sum, file)=>sum + file.size, 0);
39
+ output.log('\nDry run — no changes made.\n');
40
+ // Only pass/skip here; problems and warnings render below with their fixes.
41
+ for (const check of plan.checks){
42
+ if (check.status === 'pass' || check.status === 'skip') {
43
+ output.log(` ${statusIcon(check.status)} ${check.message}`);
44
+ }
45
+ }
46
+ output.log(problems.length === 0 ? styleText('green', `\nThis ${label} can be deployed.`) : styleText('red', `\nThis ${label} can't be deployed.`));
47
+ renderIssues(output, 'Problems to fix:', problems);
48
+ renderIssues(output, 'Warnings:', warnings);
49
+ // A blocked deploy uploads nothing, so only list files for a deployable plan.
50
+ if (problems.length === 0 && plan.files.length > 0) {
51
+ output.log(`\nFiles to deploy (${formatMB(totalBytes)}):`);
52
+ for (const file of plan.files){
53
+ output.log(` ${file.path} (${formatMB(file.size)})`);
54
+ }
55
+ }
56
+ }
57
+ function renderIssues(output, title, checks) {
58
+ if (checks.length === 0) return;
59
+ output.log(`\n${title}`);
60
+ for (const check of checks){
61
+ const fix = check.solution ? `: ${check.solution}` : '';
62
+ output.log(` ${statusIcon(check.status)} ${check.message}${fix}`);
63
+ }
64
+ }
65
+ function formatMB(bytes) {
66
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
67
+ }
68
+ function statusIcon(status) {
69
+ switch(status){
70
+ case 'fail':
71
+ {
72
+ return logSymbols.error;
73
+ }
74
+ case 'skip':
75
+ {
76
+ return logSymbols.info;
77
+ }
78
+ case 'warn':
79
+ {
80
+ return logSymbols.warning;
81
+ }
82
+ default:
83
+ {
84
+ return logSymbols.success;
85
+ }
86
+ }
87
+ }
88
+
89
+ //# sourceMappingURL=deploymentPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {type DeployCheck} from './deployChecks.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n files: DeploymentFile[]\n type: 'coreApp' | 'studio'\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n const totalBytes = plan.files.reduce((sum, file) => sum + file.size, 0)\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(` ${statusIcon(check.status)} ${check.message}`)\n }\n }\n\n output.log(\n problems.length === 0\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (problems.length === 0 && plan.files.length > 0) {\n output.log(`\\nFiles to deploy (${formatMB(totalBytes)}):`)\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction renderIssues(output: Output, title: string, checks: DeployCheck[]): void {\n if (checks.length === 0) return\n\n output.log(`\\n${title}`)\n for (const check of checks) {\n const fix = check.solution ? `: ${check.solution}` : ''\n output.log(` ${statusIcon(check.status)} ${check.message}${fix}`)\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n\nfunction statusIcon(status: DeployCheck['status']): string {\n switch (status) {\n case 'fail': {\n return logSymbols.error\n }\n case 'skip': {\n return logSymbols.info\n }\n case 'warn': {\n return logSymbols.warning\n }\n default: {\n return logSymbols.success\n }\n }\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","logSymbols","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","renderDeploymentPlan","plan","output","label","type","problems","checks","filter","check","status","warnings","totalBytes","reduce","sum","log","statusIcon","message","length","renderIssues","formatMB","title","fix","solution","bytes","toFixed","error","info","warning","success"],"mappings":"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"}