@sanity/cli 7.4.2 → 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.
- package/dist/actions/build/buildApp.js +7 -183
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStudio.js +6 -213
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/build/shouldAutoUpdate.js +100 -22
- package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
- package/dist/actions/deploy/{createStudioUserApplication.js → createUserApplication.js} +56 -5
- package/dist/actions/deploy/createUserApplication.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +232 -185
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +263 -0
- package/dist/actions/deploy/deployChecks.js.map +1 -0
- package/dist/actions/deploy/deployRunner.js +57 -0
- package/dist/actions/deploy/deployRunner.js.map +1 -0
- package/dist/actions/deploy/deployStudio.js +189 -198
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +89 -0
- package/dist/actions/deploy/deploymentPlan.js.map +1 -0
- package/dist/actions/deploy/findUserApplication.js +209 -0
- package/dist/actions/deploy/findUserApplication.js.map +1 -0
- package/dist/actions/deploy/resolveDeployTarget.js +185 -0
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -0
- package/dist/actions/dev/devAction.js +1 -1
- package/dist/actions/dev/devAction.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +18 -6
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startAppDevServer.js +1 -1
- package/dist/actions/dev/servers/startAppDevServer.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +2 -1
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js +14 -0
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +11 -5
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/commands/deploy.js +27 -11
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.js +2 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +2 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/exports/index.d.ts +9 -0
- package/dist/exports/index.js +1 -8
- package/dist/exports/index.js.map +1 -1
- package/dist/server/devServer.js +27 -3
- package/dist/server/devServer.js.map +1 -1
- package/dist/util/appId.js +13 -5
- package/dist/util/appId.js.map +1 -1
- package/dist/util/compareDependencyVersions.js.map +1 -1
- package/dist/util/determineIsApp.js +1 -1
- package/dist/util/determineIsApp.js.map +1 -1
- package/dist/util/errorMessages.js +2 -0
- package/dist/util/errorMessages.js.map +1 -1
- package/oclif.manifest.json +14 -1
- package/package.json +8 -8
- package/templates/app-quickstart/.claude/skills/sanity-app-sdk/SKILL.md +56 -0
- package/templates/app-quickstart/AGENTS.md +40 -0
- package/templates/app-quickstart/README.md +22 -0
- package/templates/app-quickstart/src/ExampleComponent.css +6 -0
- package/templates/app-quickstart/src/ExampleComponent.tsx +30 -9
- package/templates/app-sanity-ui/.claude/skills/sanity-app-sdk/SKILL.md +60 -0
- package/templates/app-sanity-ui/AGENTS.md +44 -0
- package/templates/app-sanity-ui/README.md +23 -0
- package/templates/app-sanity-ui/src/ExampleComponent.tsx +8 -5
- package/dist/actions/build/handlePrereleaseVersions.js +0 -44
- package/dist/actions/build/handlePrereleaseVersions.js.map +0 -1
- package/dist/actions/deploy/createStudioUserApplication.js.map +0 -1
- package/dist/actions/deploy/createUserApplicationForApp.js +0 -56
- package/dist/actions/deploy/createUserApplicationForApp.js.map +0 -1
- package/dist/actions/deploy/findUserApplicationForApp.js +0 -111
- package/dist/actions/deploy/findUserApplicationForApp.js.map +0 -1
- package/dist/actions/deploy/findUserApplicationForStudio.js +0 -172
- package/dist/actions/deploy/findUserApplicationForStudio.js.map +0 -1
- package/dist/actions/deploy/viewDeployment.js +0 -32
- package/dist/actions/deploy/viewDeployment.js.map +0 -1
package/dist/exports/index.js
CHANGED
|
@@ -2,13 +2,6 @@ export { createCliConfig } from '../config/createCliConfig.js';
|
|
|
2
2
|
export { defineCliConfig } from '../config/defineCliConfig.js';
|
|
3
3
|
export { getCliClient } from '../util/cliClient.js';
|
|
4
4
|
export { loadEnv } from '../util/loadEnv.js';
|
|
5
|
-
|
|
6
|
-
// implementation lives in `@sanity/workbench-cli`; re-exported here so
|
|
7
|
-
// `sanity/cli` can surface it to app authors via
|
|
8
|
-
// `import {unstable_defineApp} from 'sanity/cli'`. The runtime helpers
|
|
9
|
-
// `unstable_defineView`/`unstable_defineService` are NOT here — they bundle to
|
|
10
|
-
// the browser, so they live on the browser-safe `@sanity/cli/runtime` entry to
|
|
11
|
-
// keep Node-only deps out of the frontend bundle.
|
|
12
|
-
export { unstable_defineApp } from '@sanity/workbench-cli';
|
|
5
|
+
export { unstable_defineApp, unstable_defineMediaLibrary } from '@sanity/workbench-cli';
|
|
13
6
|
|
|
14
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/index.ts"],"sourcesContent":["export type {GraphQLAPIConfig} from '../actions/graphql/types.js'\nexport {createCliConfig} from '../config/createCliConfig.js'\nexport {defineCliConfig} from '../config/defineCliConfig.js'\nexport type {CliApiConfig} from '../types.js'\nexport {type CliClientOptions, getCliClient} from '../util/cliClient.js'\nexport {loadEnv} from '../util/loadEnv.js'\nexport type {CliConfig, UserViteConfig} from '@sanity/cli-core'\n\
|
|
1
|
+
{"version":3,"sources":["../../src/exports/index.ts"],"sourcesContent":["export type {GraphQLAPIConfig} from '../actions/graphql/types.js'\nexport {createCliConfig} from '../config/createCliConfig.js'\nexport {defineCliConfig} from '../config/defineCliConfig.js'\nexport type {CliApiConfig} from '../types.js'\nexport {type CliClientOptions, getCliClient} from '../util/cliClient.js'\nexport {loadEnv} from '../util/loadEnv.js'\nexport type {CliConfig, UserViteConfig} from '@sanity/cli-core'\n\nexport {\n type DefineAppInput,\n type DefineMediaLibraryInput,\n type MediaLibraryField,\n unstable_defineApp,\n unstable_defineMediaLibrary,\n} from '@sanity/workbench-cli'\n"],"names":["createCliConfig","defineCliConfig","getCliClient","loadEnv","unstable_defineApp","unstable_defineMediaLibrary"],"mappings":"AACA,SAAQA,eAAe,QAAO,+BAA8B;AAC5D,SAAQC,eAAe,QAAO,+BAA8B;AAE5D,SAA+BC,YAAY,QAAO,uBAAsB;AACxE,SAAQC,OAAO,QAAO,qBAAoB;AAG1C,SAIEC,kBAAkB,EAClBC,2BAA2B,QACtB,wBAAuB"}
|
package/dist/server/devServer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { extendViteConfigWithUserConfig, getViteConfig, writeSanityRuntime } from '@sanity/cli-build/_internal/build';
|
|
2
3
|
import { getAppEnvironmentVariables, getStudioEnvironmentVariables } from '@sanity/cli-build/_internal/env';
|
|
3
4
|
import { getCliTelemetry } from '@sanity/cli-core';
|
|
@@ -6,7 +7,7 @@ import { serverDebug } from './serverDebug.js';
|
|
|
6
7
|
import { sanityTypegenPlugin } from './vite/plugin-typegen.js';
|
|
7
8
|
const debug = serverDebug.extend('dev');
|
|
8
9
|
export async function startDevServer(options) {
|
|
9
|
-
const { appTitle, basePath, cwd, entry, httpHost, httpPort, isApp, isWorkbenchApp, reactCompiler, reactStrictMode, schemaExtraction,
|
|
10
|
+
const { appTitle, basePath, bundledDev, cwd, entry, exposes, httpHost, httpPort, isApp, isWorkbenchApp, reactCompiler, reactStrictMode, schemaExtraction, typegen, vite: extendViteConfig, workbenchAppId } = options;
|
|
10
11
|
debug('Writing Sanity runtime files');
|
|
11
12
|
const { entries, watcher } = await writeSanityRuntime({
|
|
12
13
|
appTitle,
|
|
@@ -43,6 +44,7 @@ export async function startDevServer(options) {
|
|
|
43
44
|
basePath,
|
|
44
45
|
cwd,
|
|
45
46
|
entries,
|
|
47
|
+
exposes,
|
|
46
48
|
getEnvironmentVariables,
|
|
47
49
|
isApp,
|
|
48
50
|
isWorkbenchApp,
|
|
@@ -53,9 +55,31 @@ export async function startDevServer(options) {
|
|
|
53
55
|
host: httpHost,
|
|
54
56
|
port: httpPort
|
|
55
57
|
},
|
|
56
|
-
|
|
57
|
-
views
|
|
58
|
+
workbenchAppId
|
|
58
59
|
});
|
|
60
|
+
// Opt into Vite's experimental bundled dev mode. Set before the user-config
|
|
61
|
+
// extension below so a `vite` override in sanity.cli.ts still has final say.
|
|
62
|
+
//
|
|
63
|
+
// Bundled mode bundles the app up front from an HTML entry, defaulting to
|
|
64
|
+
// `<root>/index.html`. Sanity has no such file — it serves a virtual document
|
|
65
|
+
// rewritten to `.sanity/runtime/index.html` — so point the bundler at the real
|
|
66
|
+
// runtime HTML, otherwise the build fails with UNRESOLVED_ENTRY.
|
|
67
|
+
if (bundledDev) {
|
|
68
|
+
viteConfig = {
|
|
69
|
+
...viteConfig,
|
|
70
|
+
build: {
|
|
71
|
+
...viteConfig.build,
|
|
72
|
+
rolldownOptions: {
|
|
73
|
+
...viteConfig.build?.rolldownOptions,
|
|
74
|
+
input: path.join(cwd, '.sanity', 'runtime', 'index.html')
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
experimental: {
|
|
78
|
+
...viteConfig.experimental,
|
|
79
|
+
bundledDev: true
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
59
83
|
// Extend Vite configuration with user-provided config
|
|
60
84
|
if (extendViteConfig) {
|
|
61
85
|
viteConfig = await extendViteConfigWithUserConfig({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/devServer.ts"],"sourcesContent":["import {\n extendViteConfigWithUserConfig,\n getViteConfig,\n writeSanityRuntime,\n} from '@sanity/cli-build/_internal/build'\nimport {\n getAppEnvironmentVariables,\n getStudioEnvironmentVariables,\n} from '@sanity/cli-build/_internal/env'\nimport {CliConfig, getCliTelemetry, type UserViteConfig} from '@sanity/cli-core'\nimport {type
|
|
1
|
+
{"version":3,"sources":["../../src/server/devServer.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {\n extendViteConfigWithUserConfig,\n getViteConfig,\n writeSanityRuntime,\n} from '@sanity/cli-build/_internal/build'\nimport {\n getAppEnvironmentVariables,\n getStudioEnvironmentVariables,\n} from '@sanity/cli-build/_internal/env'\nimport {CliConfig, getCliTelemetry, type UserViteConfig} from '@sanity/cli-core'\nimport {type WorkbenchExposes} from '@sanity/workbench-cli/build'\nimport {type PluginOptions as ReactCompilerConfig} from 'babel-plugin-react-compiler'\nimport {type FSWatcher} from 'chokidar'\nimport {createServer, type InlineConfig, type ViteDevServer} from 'vite'\n\nimport {serverDebug} from './serverDebug.js'\nimport {sanityTypegenPlugin} from './vite/plugin-typegen.js'\n\nconst debug = serverDebug.extend('dev')\n\nexport interface DevServerOptions {\n basePath: string\n cwd: string\n httpPort: number\n\n reactCompiler: ReactCompilerConfig | undefined\n reactStrictMode: boolean | undefined\n\n staticPath: string\n\n appTitle?: string\n /** Enable Vite's experimental bundled dev mode (`experimental.bundledDev`). */\n bundledDev?: boolean\n entry?: string\n exposes?: WorkbenchExposes\n httpHost?: string\n isApp?: boolean\n isWorkbenchApp?: boolean\n projectName?: string\n schemaExtraction?: CliConfig['schemaExtraction']\n typegen?: CliConfig['typegen']\n vite?: UserViteConfig\n /** The workbench app's bus identity (`__SANITY_APP_ID__`). */\n workbenchAppId?: string\n}\n\ninterface DevServer {\n close(): Promise<void>\n server: ViteDevServer\n\n watcher?: FSWatcher\n}\n\nexport async function startDevServer(options: DevServerOptions): Promise<DevServer> {\n const {\n appTitle,\n basePath,\n bundledDev,\n cwd,\n entry,\n exposes,\n httpHost,\n httpPort,\n isApp,\n isWorkbenchApp,\n reactCompiler,\n reactStrictMode,\n schemaExtraction,\n typegen,\n vite: extendViteConfig,\n workbenchAppId,\n } = options\n\n debug('Writing Sanity runtime files')\n const {entries, watcher} = await writeSanityRuntime({\n appTitle,\n basePath,\n cwd,\n entry,\n isApp,\n isWorkbenchApp,\n reactStrictMode,\n watch: true,\n })\n\n debug('Resolving vite config')\n const mode = 'development'\n\n function getEnvironmentVariables() {\n return isApp\n ? getAppEnvironmentVariables({jsonEncode: true, prefix: 'process.env.'})\n : getStudioEnvironmentVariables({jsonEncode: true, prefix: 'process.env.'})\n }\n\n let viteConfig: InlineConfig = await getViteConfig({\n additionalPlugins: [\n // Add typegen when enabled\n ...(typegen?.enabled\n ? [\n sanityTypegenPlugin({\n config: typegen,\n telemetryLogger: getCliTelemetry(),\n workDir: cwd,\n }),\n ]\n : []),\n ],\n basePath,\n cwd,\n entries,\n exposes,\n getEnvironmentVariables,\n isApp,\n isWorkbenchApp,\n mode: 'development',\n reactCompiler,\n schemaExtraction,\n server: {host: httpHost, port: httpPort},\n workbenchAppId,\n })\n\n // Opt into Vite's experimental bundled dev mode. Set before the user-config\n // extension below so a `vite` override in sanity.cli.ts still has final say.\n //\n // Bundled mode bundles the app up front from an HTML entry, defaulting to\n // `<root>/index.html`. Sanity has no such file — it serves a virtual document\n // rewritten to `.sanity/runtime/index.html` — so point the bundler at the real\n // runtime HTML, otherwise the build fails with UNRESOLVED_ENTRY.\n if (bundledDev) {\n viteConfig = {\n ...viteConfig,\n build: {\n ...viteConfig.build,\n rolldownOptions: {\n ...viteConfig.build?.rolldownOptions,\n input: path.join(cwd, '.sanity', 'runtime', 'index.html'),\n },\n },\n experimental: {...viteConfig.experimental, bundledDev: true},\n }\n }\n\n // Extend Vite configuration with user-provided config\n if (extendViteConfig) {\n viteConfig = await extendViteConfigWithUserConfig(\n {command: 'serve', mode},\n viteConfig,\n extendViteConfig,\n )\n }\n\n debug('Creating vite server')\n const server = await createServer(viteConfig)\n\n debug('Listening on specified port')\n await server.listen()\n\n return {\n close: async () => {\n if (watcher) {\n await watcher.close()\n }\n await server.close()\n },\n server,\n watcher,\n }\n}\n"],"names":["path","extendViteConfigWithUserConfig","getViteConfig","writeSanityRuntime","getAppEnvironmentVariables","getStudioEnvironmentVariables","getCliTelemetry","createServer","serverDebug","sanityTypegenPlugin","debug","extend","startDevServer","options","appTitle","basePath","bundledDev","cwd","entry","exposes","httpHost","httpPort","isApp","isWorkbenchApp","reactCompiler","reactStrictMode","schemaExtraction","typegen","vite","extendViteConfig","workbenchAppId","entries","watcher","watch","mode","getEnvironmentVariables","jsonEncode","prefix","viteConfig","additionalPlugins","enabled","config","telemetryLogger","workDir","server","host","port","build","rolldownOptions","input","join","experimental","command","listen","close"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SACEC,8BAA8B,EAC9BC,aAAa,EACbC,kBAAkB,QACb,oCAAmC;AAC1C,SACEC,0BAA0B,EAC1BC,6BAA6B,QACxB,kCAAiC;AACxC,SAAmBC,eAAe,QAA4B,mBAAkB;AAIhF,SAAQC,YAAY,QAA8C,OAAM;AAExE,SAAQC,WAAW,QAAO,mBAAkB;AAC5C,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D,MAAMC,QAAQF,YAAYG,MAAM,CAAC;AAmCjC,OAAO,eAAeC,eAAeC,OAAyB;IAC5D,MAAM,EACJC,QAAQ,EACRC,QAAQ,EACRC,UAAU,EACVC,GAAG,EACHC,KAAK,EACLC,OAAO,EACPC,QAAQ,EACRC,QAAQ,EACRC,KAAK,EACLC,cAAc,EACdC,aAAa,EACbC,eAAe,EACfC,gBAAgB,EAChBC,OAAO,EACPC,MAAMC,gBAAgB,EACtBC,cAAc,EACf,GAAGjB;IAEJH,MAAM;IACN,MAAM,EAACqB,OAAO,EAAEC,OAAO,EAAC,GAAG,MAAM7B,mBAAmB;QAClDW;QACAC;QACAE;QACAC;QACAI;QACAC;QACAE;QACAQ,OAAO;IACT;IAEAvB,MAAM;IACN,MAAMwB,OAAO;IAEb,SAASC;QACP,OAAOb,QACHlB,2BAA2B;YAACgC,YAAY;YAAMC,QAAQ;QAAc,KACpEhC,8BAA8B;YAAC+B,YAAY;YAAMC,QAAQ;QAAc;IAC7E;IAEA,IAAIC,aAA2B,MAAMpC,cAAc;QACjDqC,mBAAmB;YACjB,2BAA2B;eACvBZ,SAASa,UACT;gBACE/B,oBAAoB;oBAClBgC,QAAQd;oBACRe,iBAAiBpC;oBACjBqC,SAAS1B;gBACX;aACD,GACD,EAAE;SACP;QACDF;QACAE;QACAc;QACAZ;QACAgB;QACAb;QACAC;QACAW,MAAM;QACNV;QACAE;QACAkB,QAAQ;YAACC,MAAMzB;YAAU0B,MAAMzB;QAAQ;QACvCS;IACF;IAEA,4EAA4E;IAC5E,6EAA6E;IAC7E,EAAE;IACF,0EAA0E;IAC1E,8EAA8E;IAC9E,+EAA+E;IAC/E,iEAAiE;IACjE,IAAId,YAAY;QACdsB,aAAa;YACX,GAAGA,UAAU;YACbS,OAAO;gBACL,GAAGT,WAAWS,KAAK;gBACnBC,iBAAiB;oBACf,GAAGV,WAAWS,KAAK,EAAEC,eAAe;oBACpCC,OAAOjD,KAAKkD,IAAI,CAACjC,KAAK,WAAW,WAAW;gBAC9C;YACF;YACAkC,cAAc;gBAAC,GAAGb,WAAWa,YAAY;gBAAEnC,YAAY;YAAI;QAC7D;IACF;IAEA,sDAAsD;IACtD,IAAIa,kBAAkB;QACpBS,aAAa,MAAMrC,+BACjB;YAACmD,SAAS;YAASlB;QAAI,GACvBI,YACAT;IAEJ;IAEAnB,MAAM;IACN,MAAMkC,SAAS,MAAMrC,aAAa+B;IAElC5B,MAAM;IACN,MAAMkC,OAAOS,MAAM;IAEnB,OAAO;QACLC,OAAO;YACL,IAAItB,SAAS;gBACX,MAAMA,QAAQsB,KAAK;YACrB;YACA,MAAMV,OAAOU,KAAK;QACpB;QACAV;QACAZ;IACF;AACF"}
|
package/dist/util/appId.js
CHANGED
|
@@ -13,15 +13,23 @@ function hasNewAppId(cliConfig) {
|
|
|
13
13
|
function hasDeprecatedAppId(cliConfig) {
|
|
14
14
|
return Boolean(getDeprecatedAppId(cliConfig));
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Decides which app-id problem a config has: both the deprecated `app.id` and
|
|
18
|
+
* `deployment.appId` (a conflict), only the deprecated one, or neither. Shared
|
|
19
|
+
* so the real deploy and the dry-run check reach the same verdict.
|
|
20
|
+
* @internal
|
|
21
|
+
*/ export function resolveAppIdIssue(cliConfig) {
|
|
22
|
+
if (!hasDeprecatedAppId(cliConfig)) return null;
|
|
23
|
+
return hasNewAppId(cliConfig) ? 'conflicting-config' : 'deprecated-config';
|
|
24
|
+
}
|
|
16
25
|
/**
|
|
17
26
|
* Checks if an SDK app uses the deprecated app.id config & throws a warning if so.
|
|
18
27
|
* @remarks Throws an error if an app uses both deployment.appId and app.id
|
|
19
28
|
* @internal
|
|
20
29
|
*/ export function checkForDeprecatedAppId({ cliConfig, output }) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (hasOld && hasNew) {
|
|
30
|
+
const issue = resolveAppIdIssue(cliConfig);
|
|
31
|
+
// Both configs set: a real deploy can't pick one, so stop here
|
|
32
|
+
if (issue === 'conflicting-config') {
|
|
25
33
|
output.error(`${styleText('bold', 'Found both app.id (deprecated) and deployment.appId in your application configuration.')}
|
|
26
34
|
|
|
27
35
|
Please remove app.id from your sanity.cli.js or sanity.cli.ts file.`, {
|
|
@@ -29,7 +37,7 @@ Please remove app.id from your sanity.cli.js or sanity.cli.ts file.`, {
|
|
|
29
37
|
});
|
|
30
38
|
}
|
|
31
39
|
// Just warn if only the old app ID config is found
|
|
32
|
-
if (
|
|
40
|
+
if (issue === 'deprecated-config') {
|
|
33
41
|
output.warn(`${styleText('bold', 'The `app.id` config has moved to `deployment.appId`.')}
|
|
34
42
|
|
|
35
43
|
Please update \`sanity.cli.ts\` or \`sanity.cli.js\` and move:
|
package/dist/util/appId.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/util/appId.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\n\ninterface Options {\n cliConfig: CliConfig\n output: Output\n}\n\nfunction getDeploymentAppId(cliConfig: CliConfig): string | undefined {\n const id = cliConfig?.deployment?.appId\n return id\n}\n\nfunction getDeprecatedAppId(cliConfig: CliConfig): string | undefined {\n const id = cliConfig?.app?.id\n return id\n}\n\nfunction hasNewAppId(cliConfig: CliConfig) {\n return Boolean(getDeploymentAppId(cliConfig))\n}\n\nfunction hasDeprecatedAppId(cliConfig: CliConfig) {\n return Boolean(getDeprecatedAppId(cliConfig))\n}\n\n/**\n * Checks if an SDK app uses the deprecated app.id config & throws a warning if so.\n * @remarks Throws an error if an app uses both deployment.appId and app.id\n * @internal\n */\nexport function checkForDeprecatedAppId({cliConfig, output}: Options): void {\n const
|
|
1
|
+
{"version":3,"sources":["../../src/util/appId.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\n\ninterface Options {\n cliConfig: CliConfig\n output: Output\n}\n\nfunction getDeploymentAppId(cliConfig: CliConfig): string | undefined {\n const id = cliConfig?.deployment?.appId\n return id\n}\n\nfunction getDeprecatedAppId(cliConfig: CliConfig): string | undefined {\n const id = cliConfig?.app?.id\n return id\n}\n\nfunction hasNewAppId(cliConfig: CliConfig) {\n return Boolean(getDeploymentAppId(cliConfig))\n}\n\nfunction hasDeprecatedAppId(cliConfig: CliConfig) {\n return Boolean(getDeprecatedAppId(cliConfig))\n}\n\n/** The one app-id configuration problem to surface, if any. */\nexport type AppIdIssue = 'conflicting-config' | 'deprecated-config'\n\n/**\n * Decides which app-id problem a config has: both the deprecated `app.id` and\n * `deployment.appId` (a conflict), only the deprecated one, or neither. Shared\n * so the real deploy and the dry-run check reach the same verdict.\n * @internal\n */\nexport function resolveAppIdIssue(cliConfig: CliConfig): AppIdIssue | null {\n if (!hasDeprecatedAppId(cliConfig)) return null\n return hasNewAppId(cliConfig) ? 'conflicting-config' : 'deprecated-config'\n}\n\n/**\n * Checks if an SDK app uses the deprecated app.id config & throws a warning if so.\n * @remarks Throws an error if an app uses both deployment.appId and app.id\n * @internal\n */\nexport function checkForDeprecatedAppId({cliConfig, output}: Options): void {\n const issue = resolveAppIdIssue(cliConfig)\n\n // Both configs set: a real deploy can't pick one, so stop here\n if (issue === 'conflicting-config') {\n output.error(\n `${styleText('bold', 'Found both app.id (deprecated) and deployment.appId in your application configuration.')}\n\nPlease remove app.id from your sanity.cli.js or sanity.cli.ts file.`,\n {\n exit: 1,\n },\n )\n }\n\n // Just warn if only the old app ID config is found\n if (issue === 'deprecated-config') {\n output.warn(\n `${styleText('bold', 'The `app.id` config has moved to `deployment.appId`.')}\n\nPlease update \\`sanity.cli.ts\\` or \\`sanity.cli.js\\` and move:\n${styleText('red', `app: {id: \"${getDeprecatedAppId(cliConfig)}\", ... }`)}\nto\n${styleText('green', `deployment: {appId: \"${getDeprecatedAppId(cliConfig)}\", ... }`)}\n`,\n )\n }\n}\n\n/**\n * Get an application's ID\n * @remarks Favors the current implementation (deployment.appId) but will fall back to the deprecated app.id\n * @internal\n */\nexport function getAppId(cliConfig: CliConfig): string | undefined {\n const hasNew = hasNewAppId(cliConfig)\n const hasOld = hasDeprecatedAppId(cliConfig)\n\n if (hasNew) {\n return getDeploymentAppId(cliConfig)\n }\n\n if (hasOld) {\n return getDeprecatedAppId(cliConfig)\n }\n\n return undefined\n}\n"],"names":["styleText","getDeploymentAppId","cliConfig","id","deployment","appId","getDeprecatedAppId","app","hasNewAppId","Boolean","hasDeprecatedAppId","resolveAppIdIssue","checkForDeprecatedAppId","output","issue","error","exit","warn","getAppId","hasNew","hasOld","undefined"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AASnC,SAASC,mBAAmBC,SAAoB;IAC9C,MAAMC,KAAKD,WAAWE,YAAYC;IAClC,OAAOF;AACT;AAEA,SAASG,mBAAmBJ,SAAoB;IAC9C,MAAMC,KAAKD,WAAWK,KAAKJ;IAC3B,OAAOA;AACT;AAEA,SAASK,YAAYN,SAAoB;IACvC,OAAOO,QAAQR,mBAAmBC;AACpC;AAEA,SAASQ,mBAAmBR,SAAoB;IAC9C,OAAOO,QAAQH,mBAAmBJ;AACpC;AAKA;;;;;CAKC,GACD,OAAO,SAASS,kBAAkBT,SAAoB;IACpD,IAAI,CAACQ,mBAAmBR,YAAY,OAAO;IAC3C,OAAOM,YAAYN,aAAa,uBAAuB;AACzD;AAEA;;;;CAIC,GACD,OAAO,SAASU,wBAAwB,EAACV,SAAS,EAAEW,MAAM,EAAU;IAClE,MAAMC,QAAQH,kBAAkBT;IAEhC,+DAA+D;IAC/D,IAAIY,UAAU,sBAAsB;QAClCD,OAAOE,KAAK,CACV,GAAGf,UAAU,QAAQ,0FAA0F;;mEAElD,CAAC,EAC9D;YACEgB,MAAM;QACR;IAEJ;IAEA,mDAAmD;IACnD,IAAIF,UAAU,qBAAqB;QACjCD,OAAOI,IAAI,CACT,GAAGjB,UAAU,QAAQ,wDAAwD;;;AAGnF,EAAEA,UAAU,OAAO,CAAC,WAAW,EAAEM,mBAAmBJ,WAAW,QAAQ,CAAC,EAAE;;AAE1E,EAAEF,UAAU,SAAS,CAAC,qBAAqB,EAAEM,mBAAmBJ,WAAW,QAAQ,CAAC,EAAE;AACtF,CAAC;IAEC;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASgB,SAAShB,SAAoB;IAC3C,MAAMiB,SAASX,YAAYN;IAC3B,MAAMkB,SAASV,mBAAmBR;IAElC,IAAIiB,QAAQ;QACV,OAAOlB,mBAAmBC;IAC5B;IAEA,IAAIkB,QAAQ;QACV,OAAOd,mBAAmBJ;IAC5B;IAEA,OAAOmB;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/util/compareDependencyVersions.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {getModuleUrl} from '@sanity/cli-build/_internal/build'\nimport {getLocalPackageVersion, readPackageJson} from '@sanity/cli-core'\nimport {createRequester} from '@sanity/cli-core/request'\nimport {coerce, eq, prerelease, parse as semverParse} from 'semver'\n\nconst defaultRequester = createRequester({\n middleware: {httpErrors: false, promise: {onlyBody: false}},\n})\n\ninterface
|
|
1
|
+
{"version":3,"sources":["../../src/util/compareDependencyVersions.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {\n type CompareDependencyVersions,\n type CompareDependencyVersionsResult,\n getModuleUrl,\n type UnresolvedPrerelease,\n} from '@sanity/cli-build/_internal/build'\nimport {getLocalPackageVersion, readPackageJson} from '@sanity/cli-core'\nimport {createRequester} from '@sanity/cli-core/request'\nimport {coerce, eq, prerelease, parse as semverParse} from 'semver'\n\nconst defaultRequester = createRequester({\n middleware: {httpErrors: false, promise: {onlyBody: false}},\n})\n\ninterface CompareDependencyVersionsOptions {\n /** When provided, uses the app-specific module endpoint instead of the default endpoint. */\n appId?: string\n /** Optional requester for dependency injection (primarily for testing). */\n requester?: typeof defaultRequester\n}\n\n/**\n * @internal\n *\n * Compares the versions of dependencies in the studio or app with their remote versions.\n *\n * This function reads the package.json file in the provided working directory, and compares the versions of the dependencies\n * specified in the `autoUpdatesImports` parameter with their remote versions. If the versions do not match, the dependency is\n * added to a list of failed dependencies, which is returned by the function.\n *\n * The failed dependencies are anything that does not strictly match the remote version.\n * This means that if a version is lower or greater by even a patch it will be marked as failed.\n *\n * @param packages - An array of packages with their name and version to compare against remote.\n * @param workDir - The path to the working directory containing the package.json file.\n * @param options - Optional configuration for version comparison.\n *\n * @returns A promise that resolves to an object containing `mismatched` (packages whose local and remote versions differ)\n * and `unresolvedPrerelease` (packages with prerelease versions that could not be resolved remotely).\n *\n * @throws Throws an error if the remote version of a non-prerelease package cannot be fetched, or if the local version\n * of a package cannot be parsed.\n */\nexport async function compareDependencyVersions(\n packages: {name: string; version: string}[],\n workDir: string,\n {appId, requester = defaultRequester}: CompareDependencyVersionsOptions = {},\n): Promise<CompareDependencyVersionsResult> {\n const manifest = await readPackageJson(path.join(workDir, 'package.json'), {\n skipSchemaValidation: true,\n })\n const dependencies = {...manifest?.dependencies, ...manifest?.devDependencies}\n\n const mismatched: Array<CompareDependencyVersions> = []\n const unresolvedPrerelease: Array<UnresolvedPrerelease> = []\n\n for (const pkg of packages) {\n // Skip packages that are not declared in the local package.json\n if (!dependencies[pkg.name]) {\n continue\n }\n\n const resolvedVersion = await getRemoteResolvedVersion(getModuleUrl(pkg, {appId}), requester)\n\n if (resolvedVersion === null) {\n if (prerelease(pkg.version)) {\n unresolvedPrerelease.push({pkg: pkg.name, version: pkg.version})\n continue\n }\n throw new Error(\n `Failed to resolve remote version for ${pkg.name}@${pkg.version}: package not found`,\n )\n }\n\n const packageVersion = await getLocalPackageVersion(pkg.name, workDir)\n\n const manifestVersion = dependencies[pkg.name]\n\n const installed = coerce(packageVersion ? semverParse(packageVersion) : coerce(manifestVersion))\n\n if (!installed) {\n throw new Error(`Failed to parse installed version for ${pkg.name}`)\n }\n\n if (!eq(resolvedVersion, installed.version)) {\n mismatched.push({\n installed: installed.version,\n pkg: pkg.name,\n remote: resolvedVersion,\n })\n }\n }\n\n return {mismatched, unresolvedPrerelease}\n}\n\nasync function getRemoteResolvedVersion(\n url: string,\n request: typeof defaultRequester,\n): Promise<string | null> {\n let response\n try {\n response = await request({\n maxRedirects: 0,\n method: 'HEAD',\n url,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to fetch remote version for ${url}: ${message}`, {cause: err})\n }\n\n // 302 is expected, but lets also handle 2xx\n if (response.statusCode < 400) {\n const resolved = response.headers['x-resolved-version']\n if (!resolved) {\n throw new Error(`Missing 'x-resolved-version' header on response from HEAD ${url}`)\n }\n return resolved\n }\n\n if (response.statusCode === 404) {\n return null\n }\n\n throw new Error(`Unexpected HTTP response: ${response.statusCode} ${response.statusMessage}`)\n}\n"],"names":["path","getModuleUrl","getLocalPackageVersion","readPackageJson","createRequester","coerce","eq","prerelease","parse","semverParse","defaultRequester","middleware","httpErrors","promise","onlyBody","compareDependencyVersions","packages","workDir","appId","requester","manifest","join","skipSchemaValidation","dependencies","devDependencies","mismatched","unresolvedPrerelease","pkg","name","resolvedVersion","getRemoteResolvedVersion","version","push","Error","packageVersion","manifestVersion","installed","remote","url","request","response","maxRedirects","method","err","message","String","cause","statusCode","resolved","headers","statusMessage"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAGEC,YAAY,QAEP,oCAAmC;AAC1C,SAAQC,sBAAsB,EAAEC,eAAe,QAAO,mBAAkB;AACxE,SAAQC,eAAe,QAAO,2BAA0B;AACxD,SAAQC,MAAM,EAAEC,EAAE,EAAEC,UAAU,EAAEC,SAASC,WAAW,QAAO,SAAQ;AAEnE,MAAMC,mBAAmBN,gBAAgB;IACvCO,YAAY;QAACC,YAAY;QAAOC,SAAS;YAACC,UAAU;QAAK;IAAC;AAC5D;AASA;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,eAAeC,0BACpBC,QAA2C,EAC3CC,OAAe,EACf,EAACC,KAAK,EAAEC,YAAYT,gBAAgB,EAAmC,GAAG,CAAC,CAAC;IAE5E,MAAMU,WAAW,MAAMjB,gBAAgBH,KAAKqB,IAAI,CAACJ,SAAS,iBAAiB;QACzEK,sBAAsB;IACxB;IACA,MAAMC,eAAe;QAAC,GAAGH,UAAUG,YAAY;QAAE,GAAGH,UAAUI,eAAe;IAAA;IAE7E,MAAMC,aAA+C,EAAE;IACvD,MAAMC,uBAAoD,EAAE;IAE5D,KAAK,MAAMC,OAAOX,SAAU;QAC1B,gEAAgE;QAChE,IAAI,CAACO,YAAY,CAACI,IAAIC,IAAI,CAAC,EAAE;YAC3B;QACF;QAEA,MAAMC,kBAAkB,MAAMC,yBAAyB7B,aAAa0B,KAAK;YAACT;QAAK,IAAIC;QAEnF,IAAIU,oBAAoB,MAAM;YAC5B,IAAItB,WAAWoB,IAAII,OAAO,GAAG;gBAC3BL,qBAAqBM,IAAI,CAAC;oBAACL,KAAKA,IAAIC,IAAI;oBAAEG,SAASJ,IAAII,OAAO;gBAAA;gBAC9D;YACF;YACA,MAAM,IAAIE,MACR,CAAC,qCAAqC,EAAEN,IAAIC,IAAI,CAAC,CAAC,EAAED,IAAII,OAAO,CAAC,mBAAmB,CAAC;QAExF;QAEA,MAAMG,iBAAiB,MAAMhC,uBAAuByB,IAAIC,IAAI,EAAEX;QAE9D,MAAMkB,kBAAkBZ,YAAY,CAACI,IAAIC,IAAI,CAAC;QAE9C,MAAMQ,YAAY/B,OAAO6B,iBAAiBzB,YAAYyB,kBAAkB7B,OAAO8B;QAE/E,IAAI,CAACC,WAAW;YACd,MAAM,IAAIH,MAAM,CAAC,sCAAsC,EAAEN,IAAIC,IAAI,EAAE;QACrE;QAEA,IAAI,CAACtB,GAAGuB,iBAAiBO,UAAUL,OAAO,GAAG;YAC3CN,WAAWO,IAAI,CAAC;gBACdI,WAAWA,UAAUL,OAAO;gBAC5BJ,KAAKA,IAAIC,IAAI;gBACbS,QAAQR;YACV;QACF;IACF;IAEA,OAAO;QAACJ;QAAYC;IAAoB;AAC1C;AAEA,eAAeI,yBACbQ,GAAW,EACXC,OAAgC;IAEhC,IAAIC;IACJ,IAAI;QACFA,WAAW,MAAMD,QAAQ;YACvBE,cAAc;YACdC,QAAQ;YACRJ;QACF;IACF,EAAE,OAAOK,KAAK;QACZ,MAAMC,UAAUD,eAAeV,QAAQU,IAAIC,OAAO,GAAGC,OAAOF;QAC5D,MAAM,IAAIV,MAAM,CAAC,mCAAmC,EAAEK,IAAI,EAAE,EAAEM,SAAS,EAAE;YAACE,OAAOH;QAAG;IACtF;IAEA,4CAA4C;IAC5C,IAAIH,SAASO,UAAU,GAAG,KAAK;QAC7B,MAAMC,WAAWR,SAASS,OAAO,CAAC,qBAAqB;QACvD,IAAI,CAACD,UAAU;YACb,MAAM,IAAIf,MAAM,CAAC,0DAA0D,EAAEK,KAAK;QACpF;QACA,OAAOU;IACT;IAEA,IAAIR,SAASO,UAAU,KAAK,KAAK;QAC/B,OAAO;IACT;IAEA,MAAM,IAAId,MAAM,CAAC,0BAA0B,EAAEO,SAASO,UAAU,CAAC,CAAC,EAAEP,SAASU,aAAa,EAAE;AAC9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/util/determineIsApp.ts"],"sourcesContent":["import {type CliConfig
|
|
1
|
+
{"version":3,"sources":["../../src/util/determineIsApp.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\n\n/**\n * Determine if the current project is an app (as opposed to a studio).\n *\n * For workbench apps (`unstable_defineApp`), this is driven by the resolved\n * `applicationType` — a studio is not an app, everything else is. The\n * `applicationType` is settled at config-load time (explicit, or inferred from\n * the presence of a `sanity.config.*`). Legacy configs fall back to the\n * presence of an `app` field.\n *\n * @returns `true` if the current project is an app, `false` otherwise.\n */\nexport function determineIsApp(cliConfig: CliConfig): boolean {\n const app = cliConfig?.app\n if (isWorkbenchApp(app)) {\n return app.applicationType !== 'studio'\n }\n return Boolean(cliConfig && 'app' in cliConfig)\n}\n"],"names":["isWorkbenchApp","determineIsApp","cliConfig","app","applicationType","Boolean"],"mappings":"AACA,SAAQA,cAAc,QAAO,wBAAuB;AAEpD;;;;;;;;;;CAUC,GACD,OAAO,SAASC,eAAeC,SAAoB;IACjD,MAAMC,MAAMD,WAAWC;IACvB,IAAIH,eAAeG,MAAM;QACvB,OAAOA,IAAIC,eAAe,KAAK;IACjC;IACA,OAAOC,QAAQH,aAAa,SAASA;AACvC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const NO_PROJECT_ID = `sanity.cli.ts does not contain a project identifier ("api.projectId"), which is required for the Sanity CLI to communicate with the Sanity API`;
|
|
2
2
|
export const NO_ORGANIZATION_ID = `sanity.cli.ts does not contain an organization identifier ("app.organizationId"), which is required for the Sanity CLI to communicate with the Sanity API`;
|
|
3
3
|
export const NO_MEDIA_LIBRARY_ASPECTS_PATH = `sanity.cli.ts does not contain a media library aspects path ("mediaLibrary.aspectsPath"), which is required for the Sanity CLI to manage aspects.`;
|
|
4
|
+
export const EXTERNAL_APP_NOT_SUPPORTED = `Deploying an app to an external host is not supported.`;
|
|
5
|
+
export const APP_ID_NOT_FOUND_IN_ORGANIZATION = `The \`appId\` provided in your configuration’s \`deployment\` object cannot be found in your organization`;
|
|
4
6
|
|
|
5
7
|
//# sourceMappingURL=errorMessages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/util/errorMessages.ts"],"sourcesContent":["export const NO_PROJECT_ID = `sanity.cli.ts does not contain a project identifier (\"api.projectId\"), which is required for the Sanity CLI to communicate with the Sanity API`\nexport const NO_ORGANIZATION_ID = `sanity.cli.ts does not contain an organization identifier (\"app.organizationId\"), which is required for the Sanity CLI to communicate with the Sanity API`\nexport const NO_MEDIA_LIBRARY_ASPECTS_PATH = `sanity.cli.ts does not contain a media library aspects path (\"mediaLibrary.aspectsPath\"), which is required for the Sanity CLI to manage aspects.`\n"],"names":["NO_PROJECT_ID","NO_ORGANIZATION_ID","NO_MEDIA_LIBRARY_ASPECTS_PATH"],"mappings":"AAAA,OAAO,MAAMA,gBAAgB,CAAC,8IAA8I,CAAC,CAAA;AAC7K,OAAO,MAAMC,qBAAqB,CAAC,yJAAyJ,CAAC,CAAA;AAC7L,OAAO,MAAMC,gCAAgC,CAAC,iJAAiJ,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"sources":["../../src/util/errorMessages.ts"],"sourcesContent":["export const NO_PROJECT_ID = `sanity.cli.ts does not contain a project identifier (\"api.projectId\"), which is required for the Sanity CLI to communicate with the Sanity API`\nexport const NO_ORGANIZATION_ID = `sanity.cli.ts does not contain an organization identifier (\"app.organizationId\"), which is required for the Sanity CLI to communicate with the Sanity API`\nexport const NO_MEDIA_LIBRARY_ASPECTS_PATH = `sanity.cli.ts does not contain a media library aspects path (\"mediaLibrary.aspectsPath\"), which is required for the Sanity CLI to manage aspects.`\nexport const EXTERNAL_APP_NOT_SUPPORTED = `Deploying an app to an external host is not supported.`\nexport const APP_ID_NOT_FOUND_IN_ORGANIZATION = `The \\`appId\\` provided in your configuration’s \\`deployment\\` object cannot be found in your organization`\n"],"names":["NO_PROJECT_ID","NO_ORGANIZATION_ID","NO_MEDIA_LIBRARY_ASPECTS_PATH","EXTERNAL_APP_NOT_SUPPORTED","APP_ID_NOT_FOUND_IN_ORGANIZATION"],"mappings":"AAAA,OAAO,MAAMA,gBAAgB,CAAC,8IAA8I,CAAC,CAAA;AAC7K,OAAO,MAAMC,qBAAqB,CAAC,yJAAyJ,CAAC,CAAA;AAC7L,OAAO,MAAMC,gCAAgC,CAAC,iJAAiJ,CAAC,CAAA;AAChM,OAAO,MAAMC,6BAA6B,CAAC,sDAAsD,CAAC,CAAA;AAClG,OAAO,MAAMC,mCAAmC,CAAC,yGAAyG,CAAC,CAAA"}
|
package/oclif.manifest.json
CHANGED
|
@@ -193,6 +193,12 @@
|
|
|
193
193
|
"allowNo": true,
|
|
194
194
|
"type": "boolean"
|
|
195
195
|
},
|
|
196
|
+
"dry-run": {
|
|
197
|
+
"description": "Report what would be deployed without uploading or creating anything",
|
|
198
|
+
"name": "dry-run",
|
|
199
|
+
"allowNo": false,
|
|
200
|
+
"type": "boolean"
|
|
201
|
+
},
|
|
196
202
|
"external": {
|
|
197
203
|
"description": "Register an externally hosted studio",
|
|
198
204
|
"exclusive": [
|
|
@@ -222,6 +228,13 @@
|
|
|
222
228
|
"allowNo": false,
|
|
223
229
|
"type": "boolean"
|
|
224
230
|
},
|
|
231
|
+
"title": {
|
|
232
|
+
"description": "Title for a newly created application or studio. For apps it also skips the interactive title prompt, enabling unattended creation",
|
|
233
|
+
"name": "title",
|
|
234
|
+
"hasDynamicHelp": false,
|
|
235
|
+
"multiple": false,
|
|
236
|
+
"type": "option"
|
|
237
|
+
},
|
|
225
238
|
"url": {
|
|
226
239
|
"description": "Studio URL for deployment. For external studios, the full URL. For hosted studios, the hostname (e.g. \"my-studio\" or \"my-studio.sanity.studio\")",
|
|
227
240
|
"name": "url",
|
|
@@ -5149,5 +5162,5 @@
|
|
|
5149
5162
|
]
|
|
5150
5163
|
}
|
|
5151
5164
|
},
|
|
5152
|
-
"version": "7.
|
|
5165
|
+
"version": "7.6.0"
|
|
5153
5166
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.6.0",
|
|
4
4
|
"description": "Sanity CLI tool for managing Sanity projects and organizations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -114,17 +114,17 @@
|
|
|
114
114
|
"tinyglobby": "^0.2.17",
|
|
115
115
|
"tsx": "^4.22.4",
|
|
116
116
|
"typeid-js": "^1.2.0",
|
|
117
|
-
"vite": "8.1.
|
|
117
|
+
"vite": "^8.1.3",
|
|
118
118
|
"which": "^6.0.1",
|
|
119
119
|
"yaml": "^2.9.0",
|
|
120
120
|
"zod": "^4.4.3",
|
|
121
|
-
"@sanity/cli-build": "^
|
|
122
|
-
"@sanity/cli-core": "^2.
|
|
123
|
-
"@sanity/workbench-cli": "^1.
|
|
121
|
+
"@sanity/cli-build": "^3.0.0",
|
|
122
|
+
"@sanity/cli-core": "^2.2.0",
|
|
123
|
+
"@sanity/workbench-cli": "^1.2.0"
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
126
126
|
"@eslint/compat": "^2.1.0",
|
|
127
|
-
"@sanity/pkg-utils": "^10.
|
|
127
|
+
"@sanity/pkg-utils": "^10.8.1",
|
|
128
128
|
"@sanity/ui": "^3.2.0",
|
|
129
129
|
"@swc/cli": "^0.8.1",
|
|
130
130
|
"@swc/core": "^1.15.41",
|
|
@@ -154,9 +154,9 @@
|
|
|
154
154
|
"sanity": "^6.1.0",
|
|
155
155
|
"typescript": "^5.9.3",
|
|
156
156
|
"vitest": "^4.1.9",
|
|
157
|
-
"@repo/package.config": "0.0.1",
|
|
158
157
|
"@repo/tsconfig": "3.70.0",
|
|
159
|
-
"@
|
|
158
|
+
"@repo/package.config": "0.0.1",
|
|
159
|
+
"@sanity/cli-test": "3.0.0",
|
|
160
160
|
"@sanity/eslint-config-cli": "1.1.2"
|
|
161
161
|
},
|
|
162
162
|
"peerDependencies": {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sanity-app-sdk
|
|
3
|
+
description: Build features with the Sanity App SDK (@sanity/sdk-react). Use when adding components, fetching or editing Sanity content, or working with hooks like useDocuments, useDocument, useDocumentProjection, useEditDocument, or useQuery.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sanity App SDK
|
|
7
|
+
|
|
8
|
+
## Get the maintained guide first
|
|
9
|
+
|
|
10
|
+
If the Sanity MCP server is configured, call its `get_sanity_rules` tool with the `app-sdk` rule before writing SDK code. That rule is maintained by Sanity, is more detailed, and supersedes the notes below. The notes below are a fallback for when MCP is not available.
|
|
11
|
+
|
|
12
|
+
## Picking a hook
|
|
13
|
+
|
|
14
|
+
- `useDocuments` / `usePaginatedDocuments`: lists of documents. Returns document handles, not full documents.
|
|
15
|
+
- `useDocumentProjection`: read specific fields from a handle, for display.
|
|
16
|
+
- `useDocument` plus `useEditDocument`: read and write a single document in real time.
|
|
17
|
+
- `useQuery`: raw GROQ. Use sparingly; prefer handles plus projections.
|
|
18
|
+
|
|
19
|
+
## Document handles
|
|
20
|
+
|
|
21
|
+
Fetch handles first, then spread them into other hooks:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
const {data} = useDocuments({documentType: 'article'})
|
|
25
|
+
|
|
26
|
+
// in a child component receiving one handle:
|
|
27
|
+
const {data: fields} = useDocumentProjection({...handle, projection: '{title}'})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `documentId` as the React key when rendering lists, never the array index.
|
|
31
|
+
|
|
32
|
+
## Suspense
|
|
33
|
+
|
|
34
|
+
Data hooks suspend while loading. Wrap every data-fetching component in `<Suspense>` with a fallback, keep one fetching hook per component, and always pass a `fallback` to `SanityApp`. All SDK hooks must be used inside `SanityApp`.
|
|
35
|
+
|
|
36
|
+
## Editing
|
|
37
|
+
|
|
38
|
+
Write through `useEditDocument` on change so content stays in sync with the Content Lake:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const {data: title} = useDocument({...handle, path: 'title'})
|
|
42
|
+
const editTitle = useEditDocument({...handle, path: 'title'})
|
|
43
|
+
// <input value={title ?? ''} onChange={(e) => editTitle(e.currentTarget.value)} />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Do not hold document field values in `useState` and save on submit. That pattern goes stale and loses concurrent edits.
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
Fetch these for current detail rather than relying on the notes above:
|
|
51
|
+
|
|
52
|
+
- Best practices: https://www.sanity.io/docs/app-sdk/sdk-best-practices
|
|
53
|
+
- Editing documents: https://www.sanity.io/docs/app-sdk/editing-documents
|
|
54
|
+
- Configuration: https://www.sanity.io/docs/app-sdk/sdk-configuration
|
|
55
|
+
- Deployment: https://www.sanity.io/docs/app-sdk/sdk-deployment
|
|
56
|
+
- API reference with current signatures: https://reference.sanity.io/_sanity/sdk-react/
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents working in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
A React application built with the Sanity App SDK (`@sanity/sdk-react`). It is not a Sanity Studio. The app reads and writes content in a Sanity project through SDK hooks, and runs inside the organization's Sanity Dashboard, in development and when deployed. The `sanity` CLI runs it with Vite under the hood.
|
|
8
|
+
|
|
9
|
+
## Key files
|
|
10
|
+
|
|
11
|
+
- `src/App.tsx`: entry point. The `SanityApp` component takes a `config` array with `projectId` and `dataset`. All SDK hooks must be used inside `SanityApp`.
|
|
12
|
+
- `sanity.cli.ts`: CLI config with the organization ID and app entry path.
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
- `npm run dev`: starts the dev server on port 3333, but the app only renders inside the Sanity Dashboard. The CLI prints a Dashboard URL to open. Viewing it requires a signed-in Sanity account, so a human must complete authentication in the browser.
|
|
17
|
+
- `npm run build`: production build.
|
|
18
|
+
- `npm run deploy`: deploy to the Sanity Dashboard.
|
|
19
|
+
|
|
20
|
+
Environment variables prefixed with `SANITY_APP_` are bundled into the app.
|
|
21
|
+
|
|
22
|
+
## Working with the App SDK
|
|
23
|
+
|
|
24
|
+
If the Sanity MCP server is available, call its `get_sanity_rules` tool with the `app-sdk` rule before writing SDK code. That rule is the maintained guide and supersedes the notes below.
|
|
25
|
+
|
|
26
|
+
Essentials:
|
|
27
|
+
|
|
28
|
+
- Data hooks suspend while loading. Wrap every data-fetching component in `<Suspense>`, keep one fetching hook per component, and always pass a `fallback` to `SanityApp`.
|
|
29
|
+
- Fetch lists with `useDocuments` (or `usePaginatedDocuments`). They return document handles, not full documents. Spread a handle into `useDocumentProjection` to display fields, or into `useDocument` and `useEditDocument` for real-time editing.
|
|
30
|
+
- Use `documentId` as the React key when rendering document lists, never the array index.
|
|
31
|
+
- Do not hold document field values in `useState` and save on submit. Write through `useEditDocument` on change so content stays in sync with the Content Lake.
|
|
32
|
+
- Prefer handles plus projections over raw GROQ. Reach for `useQuery` only when a complex query genuinely needs it.
|
|
33
|
+
|
|
34
|
+
## Documentation
|
|
35
|
+
|
|
36
|
+
- App SDK docs: https://www.sanity.io/docs/app-sdk
|
|
37
|
+
- Best practices: https://www.sanity.io/docs/app-sdk/sdk-best-practices
|
|
38
|
+
- Editing documents: https://www.sanity.io/docs/app-sdk/editing-documents
|
|
39
|
+
- Configuration: https://www.sanity.io/docs/app-sdk/sdk-configuration
|
|
40
|
+
- API reference with current signatures: https://reference.sanity.io/_sanity/sdk-react/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Sanity App
|
|
2
|
+
|
|
3
|
+
A custom application built with the [Sanity App SDK](https://www.sanity.io/docs/app-sdk?utm_source=readme). It is a React app that runs inside your organization's Sanity Dashboard, in development and when deployed.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `npm run dev` starts the dev server and prints a Sanity Dashboard URL where your app runs. Open it and sign in with your Sanity account.
|
|
8
|
+
- `npm run build` builds the app for production.
|
|
9
|
+
- `npm run deploy` deploys the app to the Sanity Dashboard.
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
- `src/App.tsx` is the app entry point. The `SanityApp` config sets which project and dataset the app reads content from.
|
|
14
|
+
- `sanity.cli.ts` holds your organization ID and the app entry path.
|
|
15
|
+
|
|
16
|
+
## Learn more
|
|
17
|
+
|
|
18
|
+
- [App SDK Quickstart Guide](https://www.sanity.io/docs/app-sdk/sdk-quickstart?utm_source=readme)
|
|
19
|
+
- [App SDK documentation](https://www.sanity.io/docs/app-sdk?utm_source=readme)
|
|
20
|
+
- [API reference](https://reference.sanity.io/_sanity/sdk-react/)
|
|
21
|
+
- [Deploying your app](https://www.sanity.io/docs/app-sdk/sdk-deployment?utm_source=readme)
|
|
22
|
+
- [SDK Explorer with example apps](https://sdk-explorer.sanity.io)
|
|
@@ -17,21 +17,42 @@ export function ExampleComponent() {
|
|
|
17
17
|
Welcome to your Sanity App{user?.name ? `, ${user.name}` : ''}!
|
|
18
18
|
</h1>
|
|
19
19
|
<p className="example-text">
|
|
20
|
-
This is an example component
|
|
21
|
-
|
|
20
|
+
This is an example component, rendered with the <code>useCurrentUser</code> hook from the
|
|
21
|
+
App SDK. Replace it with your own components by importing them in App.tsx.
|
|
22
22
|
</p>
|
|
23
23
|
<div className="code-hint">
|
|
24
24
|
<p>
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
A good next step is fetching content. Data hooks like <code>useDocuments</code> suspend
|
|
26
|
+
while loading, so render them inside a <code>{'<Suspense>'}</code> boundary:
|
|
27
27
|
</p>
|
|
28
|
-
<pre>{`import {
|
|
28
|
+
<pre>{`import {Suspense} from 'react'
|
|
29
|
+
import {useDocuments} from '@sanity/sdk-react'
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</
|
|
31
|
+
function DocumentList() {
|
|
32
|
+
// useDocuments returns handles you can pass to other hooks
|
|
33
|
+
const {data} = useDocuments({documentType: 'yourType'})
|
|
34
|
+
return <ul>{data.map((doc) => <li key={doc.documentId}>{doc.documentId}</li>)}</ul>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function App() {
|
|
38
|
+
return (
|
|
39
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
40
|
+
<DocumentList />
|
|
41
|
+
</Suspense>
|
|
42
|
+
)
|
|
43
|
+
}`}</pre>
|
|
34
44
|
</div>
|
|
45
|
+
<ul className="example-links">
|
|
46
|
+
<li>
|
|
47
|
+
<a href="https://www.sanity.io/docs/app-sdk">App SDK documentation</a>
|
|
48
|
+
</li>
|
|
49
|
+
<li>
|
|
50
|
+
<a href="https://reference.sanity.io/_sanity/sdk-react/">API reference</a>
|
|
51
|
+
</li>
|
|
52
|
+
<li>
|
|
53
|
+
<a href="https://sdk-explorer.sanity.io">SDK Explorer with example apps</a>
|
|
54
|
+
</li>
|
|
55
|
+
</ul>
|
|
35
56
|
</div>
|
|
36
57
|
)
|
|
37
58
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sanity-app-sdk
|
|
3
|
+
description: Build features with the Sanity App SDK (@sanity/sdk-react) and Sanity UI. Use when adding components, fetching or editing Sanity content, or working with hooks like useDocuments, useDocument, useDocumentProjection, useEditDocument, or useQuery.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sanity App SDK
|
|
7
|
+
|
|
8
|
+
## Get the maintained guide first
|
|
9
|
+
|
|
10
|
+
If the Sanity MCP server is configured, call its `get_sanity_rules` tool with the `app-sdk` rule before writing SDK code. That rule is maintained by Sanity, is more detailed, and supersedes the notes below. The notes below are a fallback for when MCP is not available.
|
|
11
|
+
|
|
12
|
+
## Picking a hook
|
|
13
|
+
|
|
14
|
+
- `useDocuments` / `usePaginatedDocuments`: lists of documents. Returns document handles, not full documents.
|
|
15
|
+
- `useDocumentProjection`: read specific fields from a handle, for display.
|
|
16
|
+
- `useDocument` plus `useEditDocument`: read and write a single document in real time.
|
|
17
|
+
- `useQuery`: raw GROQ. Use sparingly; prefer handles plus projections.
|
|
18
|
+
|
|
19
|
+
## Document handles
|
|
20
|
+
|
|
21
|
+
Fetch handles first, then spread them into other hooks:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
const {data} = useDocuments({documentType: 'article'})
|
|
25
|
+
|
|
26
|
+
// in a child component receiving one handle:
|
|
27
|
+
const {data: fields} = useDocumentProjection({...handle, projection: '{title}'})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `documentId` as the React key when rendering lists, never the array index.
|
|
31
|
+
|
|
32
|
+
## Suspense
|
|
33
|
+
|
|
34
|
+
Data hooks suspend while loading. Wrap every data-fetching component in `<Suspense>` with a fallback, keep one fetching hook per component, and always pass a `fallback` to `SanityApp`. All SDK hooks must be used inside `SanityApp`.
|
|
35
|
+
|
|
36
|
+
## Editing
|
|
37
|
+
|
|
38
|
+
Write through `useEditDocument` on change so content stays in sync with the Content Lake:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const {data: title} = useDocument({...handle, path: 'title'})
|
|
42
|
+
const editTitle = useEditDocument({...handle, path: 'title'})
|
|
43
|
+
// <input value={title ?? ''} onChange={(e) => editTitle(e.currentTarget.value)} />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Do not hold document field values in `useState` and save on submit. That pattern goes stale and loses concurrent edits.
|
|
47
|
+
|
|
48
|
+
## Sanity UI
|
|
49
|
+
|
|
50
|
+
This app wraps everything in Sanity UI's `ThemeProvider` (see `src/SanityUI.tsx`, built with `buildTheme()`). Build UI with Sanity UI primitives like `Card`, `Stack`, `Flex`, `Text`, and `Button`. See https://www.sanity.io/docs/app-sdk/sanity-ui-sdk and https://www.sanity.io/ui.
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
Fetch these for current detail rather than relying on the notes above:
|
|
55
|
+
|
|
56
|
+
- Best practices: https://www.sanity.io/docs/app-sdk/sdk-best-practices
|
|
57
|
+
- Editing documents: https://www.sanity.io/docs/app-sdk/editing-documents
|
|
58
|
+
- Configuration: https://www.sanity.io/docs/app-sdk/sdk-configuration
|
|
59
|
+
- Deployment: https://www.sanity.io/docs/app-sdk/sdk-deployment
|
|
60
|
+
- API reference with current signatures: https://reference.sanity.io/_sanity/sdk-react/
|