@sanity/cli 3.93.1-next.8.cf0bf8550a → 3.94.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 (149) hide show
  1. package/lib/_chunks-cjs/cli.js +1 -1
  2. package/lib/_chunks-cjs/cli.js.map +1 -1
  3. package/package.json +10 -11
  4. package/templates/blog/README.md +1 -1
  5. package/templates/clean/README.md +1 -1
  6. package/templates/ecommerce/README.md +1 -1
  7. package/templates/get-started/README.md +1 -1
  8. package/templates/moviedb/README.md +1 -1
  9. package/templates/quickstart/README.md +1 -1
  10. package/src/CommandRunner.ts +0 -219
  11. package/src/__telemetry__/cli.telemetry.ts +0 -29
  12. package/src/__telemetry__/init.telemetry.ts +0 -95
  13. package/src/actions/codemod/codemodAction.ts +0 -109
  14. package/src/actions/codemod/mods/deskRename.ts +0 -19
  15. package/src/actions/codemod/mods/index.ts +0 -9
  16. package/src/actions/codemod/mods/partsTypeDirective.ts +0 -28
  17. package/src/actions/codemod/mods/reactIconsV3.ts +0 -54
  18. package/src/actions/codemod/types.ts +0 -10
  19. package/src/actions/init-plugin/bootstrapFromTemplate.ts +0 -196
  20. package/src/actions/init-project/bootstrapLocalTemplate.ts +0 -201
  21. package/src/actions/init-project/bootstrapRemoteTemplate.ts +0 -118
  22. package/src/actions/init-project/createAppCliConfig.ts +0 -24
  23. package/src/actions/init-project/createCliConfig.ts +0 -31
  24. package/src/actions/init-project/createPackageManifest.ts +0 -102
  25. package/src/actions/init-project/createStudioConfig.ts +0 -53
  26. package/src/actions/init-project/determineAppTemplate.ts +0 -13
  27. package/src/actions/init-project/fsUtils.ts +0 -40
  28. package/src/actions/init-project/git.ts +0 -59
  29. package/src/actions/init-project/initProject.ts +0 -1708
  30. package/src/actions/init-project/processTemplate.ts +0 -55
  31. package/src/actions/init-project/promptForDatasetName.ts +0 -68
  32. package/src/actions/init-project/prompts/index.ts +0 -51
  33. package/src/actions/init-project/prompts/nextjs.ts +0 -59
  34. package/src/actions/init-project/readPackageJson.ts +0 -17
  35. package/src/actions/init-project/templates/appQuickstart.ts +0 -23
  36. package/src/actions/init-project/templates/appSanityUi.ts +0 -25
  37. package/src/actions/init-project/templates/blog.ts +0 -5
  38. package/src/actions/init-project/templates/clean.ts +0 -5
  39. package/src/actions/init-project/templates/getStarted.ts +0 -37
  40. package/src/actions/init-project/templates/index.ts +0 -24
  41. package/src/actions/init-project/templates/moviedb.ts +0 -35
  42. package/src/actions/init-project/templates/nextjs/index.ts +0 -238
  43. package/src/actions/init-project/templates/nextjs/schemaTypes/blog.ts +0 -252
  44. package/src/actions/init-project/templates/quickstart.ts +0 -5
  45. package/src/actions/init-project/templates/shopify.ts +0 -78
  46. package/src/actions/init-project/templates/shopifyOnline.ts +0 -50
  47. package/src/actions/init-project/updateInitialTemplateMetadata.ts +0 -24
  48. package/src/actions/login/login.telemetry.ts +0 -18
  49. package/src/actions/login/login.ts +0 -350
  50. package/src/actions/login/types.ts +0 -19
  51. package/src/actions/project/createProject.ts +0 -36
  52. package/src/actions/telemetry/setTelemetryConsent.ts +0 -101
  53. package/src/actions/typegen/generate.telemetry.ts +0 -20
  54. package/src/actions/typegen/generateAction.ts +0 -210
  55. package/src/actions/versions/findSanityModuleVersions.ts +0 -185
  56. package/src/actions/yarn/events.ts +0 -73
  57. package/src/cli.ts +0 -350
  58. package/src/cliClient.ts +0 -62
  59. package/src/commands/blueprints/addBlueprintsCommand.ts +0 -155
  60. package/src/commands/blueprints/blueprintsGroup.ts +0 -10
  61. package/src/commands/blueprints/configBlueprintsCommand.ts +0 -86
  62. package/src/commands/blueprints/deployBlueprintsCommand.ts +0 -63
  63. package/src/commands/blueprints/destroyBlueprintsCommand.ts +0 -78
  64. package/src/commands/blueprints/infoBlueprintsCommand.ts +0 -55
  65. package/src/commands/blueprints/initBlueprintsCommand.ts +0 -99
  66. package/src/commands/blueprints/logsBlueprintsCommand.ts +0 -66
  67. package/src/commands/blueprints/planBlueprintsCommand.ts +0 -51
  68. package/src/commands/blueprints/stacksBlueprintsCommand.ts +0 -66
  69. package/src/commands/codemod/codemodCommand.ts +0 -32
  70. package/src/commands/debug/debugCommand.ts +0 -26
  71. package/src/commands/debug/printDebugInfo.ts +0 -243
  72. package/src/commands/docs/docsCommand.ts +0 -20
  73. package/src/commands/functions/devFunctionsCommand.ts +0 -73
  74. package/src/commands/functions/envFunctionsCommand.ts +0 -105
  75. package/src/commands/functions/functionsGroup.ts +0 -10
  76. package/src/commands/functions/logsFunctionsCommand.ts +0 -94
  77. package/src/commands/functions/testFunctionsCommand.ts +0 -93
  78. package/src/commands/help/helpCommand.ts +0 -21
  79. package/src/commands/help/showHelp.ts +0 -58
  80. package/src/commands/index.ts +0 -72
  81. package/src/commands/init/initCommand.ts +0 -123
  82. package/src/commands/install/installCommand.ts +0 -38
  83. package/src/commands/learn/learnCommand.ts +0 -20
  84. package/src/commands/login/loginCommand.ts +0 -29
  85. package/src/commands/logout/logoutCommand.ts +0 -52
  86. package/src/commands/manage/manageCommand.ts +0 -24
  87. package/src/commands/projects/listProjectsCommand.ts +0 -62
  88. package/src/commands/projects/projectsGroup.ts +0 -10
  89. package/src/commands/telemetry/disableTelemetryCommand.ts +0 -19
  90. package/src/commands/telemetry/enableTelemetryCommand.ts +0 -19
  91. package/src/commands/telemetry/telemetryGroup.ts +0 -10
  92. package/src/commands/telemetry/telemetryStatusCommand.ts +0 -89
  93. package/src/commands/typegen/generateTypesCommand.ts +0 -56
  94. package/src/commands/typegen/typegenGroup.ts +0 -10
  95. package/src/commands/versions/printVersionResult.ts +0 -51
  96. package/src/commands/versions/versionsCommand.ts +0 -13
  97. package/src/config.ts +0 -14
  98. package/src/debug.ts +0 -3
  99. package/src/index.ts +0 -6
  100. package/src/outputters/cliOutputter.ts +0 -47
  101. package/src/packageManager/getInstallCommand.ts +0 -26
  102. package/src/packageManager/getUpgradeCommand.ts +0 -52
  103. package/src/packageManager/index.ts +0 -33
  104. package/src/packageManager/installPackages.ts +0 -99
  105. package/src/packageManager/packageManagerChoice.ts +0 -204
  106. package/src/packageManager/uninstallPackages.ts +0 -44
  107. package/src/packageManager/yarnStub.ts +0 -22
  108. package/src/prompters/cliPrompter.ts +0 -9
  109. package/src/run.ts +0 -8
  110. package/src/studioDependencies.ts +0 -25
  111. package/src/types.ts +0 -387
  112. package/src/util/canLaunchBrowser.ts +0 -12
  113. package/src/util/cliWorker.ts +0 -34
  114. package/src/util/clientWrapper.ts +0 -119
  115. package/src/util/copy.ts +0 -50
  116. package/src/util/createExpiringConfig.ts +0 -80
  117. package/src/util/createTelemetryStore.ts +0 -204
  118. package/src/util/detectRuntime.ts +0 -12
  119. package/src/util/dynamicRequire.ts +0 -17
  120. package/src/util/frameworkPort.ts +0 -63
  121. package/src/util/generateCommandsDocumentation.ts +0 -62
  122. package/src/util/getCliConfig.ts +0 -172
  123. package/src/util/getCliVersion.ts +0 -22
  124. package/src/util/getLocalVersion.ts +0 -35
  125. package/src/util/getProjectDefaults.ts +0 -103
  126. package/src/util/getProviderName.ts +0 -9
  127. package/src/util/getUserConfig.ts +0 -15
  128. package/src/util/isCi.ts +0 -7
  129. package/src/util/isCommandGroup.ts +0 -7
  130. package/src/util/isInteractive.ts +0 -3
  131. package/src/util/isTrueish.ts +0 -8
  132. package/src/util/journeyConfig.ts +0 -280
  133. package/src/util/loadEnv.ts +0 -110
  134. package/src/util/mergeCommands.ts +0 -78
  135. package/src/util/neatStack.ts +0 -30
  136. package/src/util/noSuchCommandText.ts +0 -91
  137. package/src/util/parseArguments.ts +0 -76
  138. package/src/util/pathExists.ts +0 -14
  139. package/src/util/readJson.ts +0 -6
  140. package/src/util/readdirRecursive.ts +0 -26
  141. package/src/util/remoteTemplate.ts +0 -320
  142. package/src/util/resolveLatestVersions.ts +0 -24
  143. package/src/util/resolveRootDir.ts +0 -59
  144. package/src/util/telemetryDisclosure.ts +0 -45
  145. package/src/util/updateNotifier.ts +0 -158
  146. package/src/util/writeJson.ts +0 -6
  147. package/src/workers/getAndWriteJourneySchema.ts +0 -7
  148. package/src/workers/getCliConfig.ts +0 -14
  149. package/src/workers/typegenGenerate.ts +0 -228
@@ -1,80 +0,0 @@
1
- import type ConfigStore from 'configstore'
2
-
3
- export interface ExpiringConfigOptions<Type> {
4
- /** Config store */
5
- store: ConfigStore
6
- /** Config key */
7
- key: string
8
- /** TTL (milliseconds) */
9
- ttl: number
10
- /** Fetch value */
11
- fetchValue: () => Type | Promise<Type>
12
- /** Subscribe to revalidate event */
13
- onRevalidate?: () => void
14
- /** Subscribe to fetch event */
15
- onFetch?: () => void
16
- /** Subscribe to cache hit event */
17
- onCacheHit?: () => void
18
- }
19
-
20
- export interface ExpiringConfigApi<Type> {
21
- /**
22
- * Attempt to get the cached value. If there is no cached value, or the cached value has expired,
23
- * fetch, cache, and return the value.
24
- */
25
- get: () => Promise<Type>
26
- /**
27
- * Delete the cached value.
28
- */
29
- delete: () => void
30
- }
31
-
32
- /**
33
- * Create a config in the provided config store that expires after the provided TTL.
34
- */
35
- export function createExpiringConfig<Type>({
36
- key,
37
- ttl,
38
- store,
39
- fetchValue,
40
- onRevalidate = () => null,
41
- onFetch = () => null,
42
- onCacheHit = () => null,
43
- }: ExpiringConfigOptions<Type>): ExpiringConfigApi<Type> {
44
- let currentFetch: Promise<Type> | null = null
45
- return {
46
- async get() {
47
- const {value, updatedAt} = store.get(key) ?? {}
48
-
49
- if (value && updatedAt) {
50
- const hasExpired = Date.now() - updatedAt > ttl
51
-
52
- if (!hasExpired) {
53
- onCacheHit()
54
- return value
55
- }
56
-
57
- onRevalidate()
58
- }
59
-
60
- if (currentFetch) {
61
- return currentFetch
62
- }
63
- onFetch()
64
-
65
- currentFetch = Promise.resolve(fetchValue())
66
- const nextValue = await currentFetch
67
- currentFetch = null
68
-
69
- store.set(key, {
70
- value: nextValue,
71
- updatedAt: Date.now(),
72
- })
73
-
74
- return nextValue
75
- },
76
- delete() {
77
- store.delete(key)
78
- },
79
- }
80
- }
@@ -1,204 +0,0 @@
1
- import {appendFile} from 'node:fs/promises'
2
-
3
- import {type SanityClient} from '@sanity/client'
4
- import {
5
- type ConsentStatus,
6
- createBatchedStore,
7
- createSessionId,
8
- type TelemetryEvent,
9
- } from '@sanity/telemetry'
10
-
11
- import {debug as baseDebug} from '../debug'
12
- import {getClientWrapper, getCliToken} from './clientWrapper'
13
- import {createExpiringConfig} from './createExpiringConfig'
14
- import {getUserConfig} from './getUserConfig'
15
- import {isCi} from './isCi'
16
- import {isTrueish} from './isTrueish'
17
-
18
- const debug = baseDebug.extend('telemetry')
19
-
20
- const FIVE_MINUTES = 1000 * 60 * 5
21
- const LOG_FILE_NAME = 'telemetry-events.ndjson'
22
-
23
- export const TELEMETRY_CONSENT_CONFIG_KEY = 'telemetryConsent'
24
-
25
- const VALID_API_STATUSES = ['granted', 'denied', 'unset'] as const
26
- type ValidApiConsentStatus = (typeof VALID_API_STATUSES)[number]
27
-
28
- function isValidApiConsentStatus(status: string): status is ValidApiConsentStatus {
29
- return VALID_API_STATUSES.includes(status as ValidApiConsentStatus)
30
- }
31
-
32
- function parseApiConsentStatus(value: unknown): ValidApiConsentStatus {
33
- if (typeof value === 'string' && isValidApiConsentStatus(value)) {
34
- return value
35
- }
36
- throw new Error(`Invalid consent status. Must be one of: ${VALID_API_STATUSES.join(', ')}`)
37
- }
38
-
39
- function createTelemetryClient(token: string) {
40
- const getClient = getClientWrapper(null, 'sanity.cli.js')
41
- return getClient({requireUser: false, requireProject: false}).config({
42
- apiVersion: '2023-12-18',
43
- token,
44
- useCdn: false,
45
- useProjectHostname: false,
46
- })
47
- }
48
-
49
- let _client: SanityClient | null = null
50
- function getCachedClient(token: string) {
51
- if (!_client) {
52
- _client = createTelemetryClient(token)
53
- }
54
- return _client
55
- }
56
-
57
- interface Env {
58
- DO_NOT_TRACK?: string
59
- SANITY_TELEMETRY_INSPECT?: string
60
- }
61
-
62
- interface Options {
63
- env: Env | NodeJS.ProcessEnv
64
- }
65
-
66
- export type ConsentInformation =
67
- | {
68
- status: Extract<ConsentStatus, 'granted'>
69
- reason?: never
70
- }
71
- | {
72
- status: Extract<ConsentStatus, 'undetermined'>
73
- reason: 'unauthenticated' | 'fetchError'
74
- }
75
- | {
76
- status: Extract<ConsentStatus, 'denied'>
77
- reason?: 'localOverride'
78
- }
79
- | {
80
- status: Extract<ConsentStatus, 'unset'>
81
- reason?: never
82
- }
83
-
84
- export function resolveConsent({env}: Options): Promise<ConsentInformation> {
85
- debug('Resolving consent…')
86
- if (isCi) {
87
- debug('CI environment detected, treating telemetry consent as denied')
88
- return Promise.resolve({status: 'denied'})
89
- }
90
- if (isTrueish(env.DO_NOT_TRACK)) {
91
- debug('DO_NOT_TRACK is set, consent is denied')
92
- return Promise.resolve({
93
- status: 'denied',
94
- reason: 'localOverride',
95
- })
96
- }
97
-
98
- const token = getCliToken()
99
- if (!token) {
100
- debug('User is not logged in, consent is undetermined')
101
- return Promise.resolve({
102
- status: 'undetermined',
103
- reason: 'unauthenticated',
104
- })
105
- }
106
-
107
- const client = getCachedClient(token)
108
-
109
- function fetchConsent(): Promise<{
110
- status: ValidApiConsentStatus
111
- }> {
112
- const telemetryConsentConfig = createExpiringConfig<{
113
- status: ValidApiConsentStatus
114
- }>({
115
- store: getUserConfig(),
116
- key: TELEMETRY_CONSENT_CONFIG_KEY,
117
- ttl: FIVE_MINUTES,
118
- fetchValue: () =>
119
- client.request({uri: '/intake/telemetry-status', tag: 'telemetry-consent.cli'}),
120
- onRevalidate() {
121
- debug('Revalidating cached telemetry consent status...')
122
- },
123
- onFetch() {
124
- debug('Fetching telemetry consent status...')
125
- },
126
- onCacheHit() {
127
- debug('Retrieved telemetry consent status from cache')
128
- },
129
- })
130
- return telemetryConsentConfig.get()
131
- }
132
-
133
- return fetchConsent()
134
- .then<ConsentInformation>((response) => {
135
- debug('User consent status is %s', response.status)
136
- return {status: parseApiConsentStatus(response.status)}
137
- })
138
- .catch((err) => {
139
- debug('Failed to fetch user consent status, treating it as "undetermined": %s', err.stack)
140
- return {
141
- status: 'undetermined',
142
- reason: 'fetchError',
143
- }
144
- })
145
- }
146
- export function createTelemetryStore<UserProperties>({
147
- env,
148
- projectId,
149
- }: {
150
- projectId?: string
151
- env: {[key: string]: string | undefined}
152
- }) {
153
- debug('Initializing telemetry')
154
-
155
- // Note: if this function throws/rejects the events will be put back on the buffer
156
- async function sendEvents(batch: TelemetryEvent[]) {
157
- const token = getCliToken()
158
- if (!token) {
159
- // Note: since the telemetry store checks for consent before sending events, and this token
160
- // check is also done during consent checking, this would normally never happen
161
- debug('No user token found. Something is not quite right')
162
- return Promise.reject(new Error('User is not logged in'))
163
- }
164
- const inspectEvents = isTrueish(env.SANITY_TELEMETRY_INSPECT)
165
- if (inspectEvents) {
166
- // eslint-disable-next-line no-console
167
- console.info(`SANITY_TELEMETRY_INSPECT is set, appending events to "${LOG_FILE_NAME}"`)
168
- await appendFile(LOG_FILE_NAME, `${batch.map((entry) => JSON.stringify(entry)).join('\n')}\n`)
169
- }
170
- const client = getCachedClient(token)
171
- debug('Submitting %s telemetry events', batch.length)
172
- try {
173
- return await client.request({
174
- uri: '/intake/batch',
175
- method: 'POST',
176
- json: true,
177
- body: {projectId, batch},
178
- })
179
- } catch (err) {
180
- const statusCode = err.response && err.response.statusCode
181
- debug(
182
- 'Failed to send telemetry events%s: %s',
183
- statusCode ? ` (HTTP ${statusCode})` : '',
184
- err.stack,
185
- )
186
- // note: we want to throw - the telemetry store implements error handling already
187
- throw err
188
- }
189
- }
190
-
191
- const sessionId = createSessionId()
192
- debug('session id: %s', sessionId)
193
-
194
- const store = createBatchedStore<UserProperties>(sessionId, {
195
- resolveConsent: () => resolveConsent({env}),
196
- sendEvents,
197
- })
198
-
199
- process.once('SIGINT', () => store.flush().finally(() => process.exit(0)))
200
- process.once('beforeExit', () => store.flush())
201
- process.once('unhandledRejection', () => store.flush())
202
- process.once('uncaughtException', () => store.flush())
203
- return store
204
- }
@@ -1,12 +0,0 @@
1
- export function detectRuntime() {
2
- if ('Deno' in globalThis) {
3
- return 'deno'
4
- }
5
- if ('Bun' in globalThis) {
6
- return 'bun'
7
- }
8
- // Consider using a more reliable way of detecting that we're actually in Node.js
9
- // I first attempted using https://www.npmjs.com/package/is-really-node, but it fails
10
- // due to using top level await.
11
- return 'node'
12
- }
@@ -1,17 +0,0 @@
1
- // Prevent webpack from bundling in webpack context,
2
- // use regular node require for unbundled context
3
-
4
- /* eslint-disable camelcase, no-undef */
5
- declare const __webpack_require__: boolean
6
- declare const __non_webpack_require__: typeof require
7
-
8
- const requireFunc: typeof require =
9
- typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
10
- /* eslint-enable camelcase, no-undef */
11
-
12
- export function dynamicRequire<T = any>(request: string): T {
13
- const mod = requireFunc(request)
14
- return mod.__esModule && mod.default ? mod.default : mod
15
- }
16
-
17
- dynamicRequire.resolve = requireFunc.resolve
@@ -1,63 +0,0 @@
1
- const FALLBACK_PORT = 3000
2
-
3
- const portMap: Record<string, number> = {
4
- 'nextjs': 3000,
5
- 'blitzjs': 3000,
6
- 'gatsby': 8000,
7
- 'remix': 3000,
8
- 'astro': 4321,
9
- 'hexo': 4000,
10
- 'eleventy': 8080,
11
- 'docusaurus': 3000,
12
- 'docusaurus-2': 3000,
13
- 'preact': 8080,
14
- 'solidstart': 3000,
15
- 'solidstart-1': 3000,
16
- 'dojo': 3000,
17
- 'ember': 4200,
18
- 'vue': 8080,
19
- 'scully': 1668,
20
- 'ionic-angular': 4200,
21
- 'angular': 4200,
22
- 'polymer': 8081,
23
- 'svelte': 5000,
24
- 'sveltekit': 5173,
25
- 'sveltekit-1': 5173,
26
- 'ionic-react': 3000,
27
- 'create-react-app': 3000,
28
- 'gridsome': 8080,
29
- 'umijs': 8000,
30
- 'saber': 3000,
31
- 'stencil': 3333,
32
- 'nuxtjs': 3000,
33
- 'redwoodjs': 8910,
34
- 'hugo': 1313,
35
- 'jekyll': 4000,
36
- 'brunch': 3333,
37
- 'middleman': 4567,
38
- 'zola': 1111,
39
- 'hydrogen': 3000,
40
- 'vite': 5173,
41
- 'vitepress': 5173,
42
- 'vuepress': 8080,
43
- 'parcel': 1234,
44
- 'fasthtml': 8000,
45
- 'sanity': 3333,
46
- 'sanity-v3': 3333,
47
- 'storybook': 6006,
48
- }
49
-
50
- /**
51
- * Returns the default development port for a given framework.
52
- * Contains default ports for all frameworks supported by `@vercel/frameworks`.
53
- * Falls back to port 3000 if framework is not found or not specified.
54
- *
55
- * @see https://github.com/vercel/vercel/blob/main/packages/frameworks/src/frameworks.ts
56
- * for the complete list of supported frameworks
57
- *
58
- * @param frameworkSlug - The framework identifier from `@vercel/frameworks`
59
- * @returns The default port number for the framework
60
- */
61
- export function getDefaultPortForFramework(frameworkSlug?: string | null): number {
62
- return portMap[frameworkSlug ?? ''] ?? FALLBACK_PORT
63
- }
@@ -1,62 +0,0 @@
1
- import {padEnd} from 'lodash'
2
-
3
- import {type CliCommandDefinition, type CliCommandGroupDefinition} from '../types'
4
- import {getNoSuchCommandText} from './noSuchCommandText'
5
-
6
- /**
7
- * Generate documentation for all commands within a given group
8
- */
9
- export function generateCommandsDocumentation(
10
- commandGroups: Record<string, (CliCommandDefinition | CliCommandGroupDefinition)[]>,
11
- group = 'default',
12
- ): string {
13
- const commandGroup = commandGroups[group]
14
- const commands = commandGroup && commandGroup.filter((cmd) => !cmd.hideFromHelp)
15
-
16
- if (!commands || commands.length === 0) {
17
- throw new Error(getNoSuchCommandText(group))
18
- }
19
-
20
- // Find the maximum length of a command name, so we can pad the descriptions
21
- const cmdLength = commands.reduce((max, cmd) => Math.max(cmd.name.length, max), 0)
22
- const prefix = group === 'default' ? '' : ` ${group}`
23
-
24
- const rows = [
25
- `usage: npx sanity${prefix} [--default] [-v|--version] [-d|--debug] [-h|--help] <command> [<args>]`,
26
- '',
27
- 'Commands:',
28
- ]
29
- .concat(commands.map((cmd) => ` ${padEnd(cmd.name, cmdLength + 1)} ${cmd.description}`))
30
- .concat([
31
- '',
32
- `See 'npx sanity help${prefix} <command>' for specific information on a subcommand.`,
33
- ])
34
-
35
- return rows.join('\n')
36
- }
37
-
38
- /**
39
- * Generate documentation for a single command within the given group
40
- */
41
- export function generateCommandDocumentation(
42
- command: CliCommandDefinition,
43
- group?: string | null,
44
- subCommand?: string,
45
- ): string {
46
- if (!command) {
47
- throw new Error(
48
- subCommand
49
- ? `"${subCommand}" is not a subcommand of "${group}". See 'npx sanity help ${group}'`
50
- : getNoSuchCommandText(group || command),
51
- )
52
- }
53
-
54
- const cmdParts = [group || command.name, subCommand].filter(Boolean).join(' ')
55
- return [
56
- `usage: npx sanity ${cmdParts} ${command.signature}`,
57
- '',
58
- ` ${command.description}`,
59
- '',
60
- (command.helpText || '').trim(),
61
- ].join('\n')
62
- }
@@ -1,172 +0,0 @@
1
- /* eslint-disable no-sync */
2
-
3
- /**
4
- * Reads the Sanity CLI config from one of the following files (in preferred order):
5
- * - sanity.cli.js
6
- * - sanity.cli.ts
7
- *
8
- * Note: There are two ways of using this:
9
- * a) `getCliConfig(cwd)`
10
- * b) `getCliConfig(cwd, {forked: true})`
11
- *
12
- * Approach a is generally a bit faster as it avoids the forking startup time, while
13
- * approach b could be considered "safer" since any side-effects of running the config
14
- * file will not bleed into the current CLI process directly.
15
- */
16
- import fs from 'node:fs'
17
- import path from 'node:path'
18
- import {Worker} from 'node:worker_threads'
19
-
20
- import {type CliConfig, type SanityJson} from '../types'
21
- import {getCliWorkerPath} from './cliWorker'
22
- import {dynamicRequire} from './dynamicRequire'
23
-
24
- export type CliMajorVersion = 2 | 3
25
-
26
- export type CliConfigResult =
27
- | {config: SanityJson; path: string; version: 2}
28
- | {config: CliConfig; path: string; version: 3}
29
- | {config: null; path: string; version: CliMajorVersion}
30
-
31
- export async function getCliConfig(
32
- cwd: string,
33
- {forked}: {forked?: boolean} = {},
34
- ): Promise<CliConfigResult | null> {
35
- if (forked) {
36
- try {
37
- return await getCliConfigForked(cwd)
38
- } catch {
39
- // Intentional noop - try unforked variant
40
- }
41
- }
42
-
43
- const {unregister} = __DEV__
44
- ? {unregister: () => undefined}
45
- : // eslint-disable-next-line @typescript-eslint/no-require-imports
46
- require('esbuild-register/dist/node').register({supported: {'dynamic-import': true}})
47
-
48
- try {
49
- const v3Config = getSanityCliConfig(cwd)
50
- if (v3Config) {
51
- return v3Config
52
- }
53
-
54
- return getSanityJsonConfig(cwd)
55
- } catch (err) {
56
- throw err
57
- } finally {
58
- unregister()
59
- }
60
- }
61
-
62
- export function getCliConfigSync(cwd: string): CliConfigResult | null {
63
- const v3Config = getSanityCliConfig(cwd)
64
- return v3Config ? v3Config : getSanityJsonConfig(cwd)
65
- }
66
-
67
- async function getCliConfigForked(cwd: string): Promise<CliConfigResult | null> {
68
- const workerPath = await getCliWorkerPath('getCliConfig')
69
- return new Promise((resolve, reject) => {
70
- const worker = new Worker(workerPath, {
71
- workerData: cwd,
72
- env: process.env,
73
- })
74
- worker.on('message', (message) => {
75
- if (message.type === 'config') {
76
- resolve(message.config)
77
- } else {
78
- const error = new Error(message.error)
79
- ;(error as any).type = message.errorType
80
- reject(new Error(message.error))
81
- }
82
- })
83
- worker.on('error', reject)
84
- worker.on('exit', (code) => {
85
- if (code !== 0) {
86
- reject(new Error(`Worker stopped with exit code ${code}`))
87
- }
88
- })
89
- })
90
- }
91
-
92
- function getSanityJsonConfig(cwd: string): CliConfigResult | null {
93
- const configPath = path.join(cwd, 'sanity.json')
94
-
95
- if (!fs.existsSync(configPath)) {
96
- return null
97
- }
98
-
99
- return {
100
- config: loadJsonConfig(configPath),
101
- path: configPath,
102
- version: 2,
103
- }
104
- }
105
-
106
- function getSanityCliConfig(cwd: string): CliConfigResult | null {
107
- const jsConfigPath = path.join(cwd, 'sanity.cli.js')
108
- const tsConfigPath = path.join(cwd, 'sanity.cli.ts')
109
-
110
- const [js, ts] = [fs.existsSync(jsConfigPath), fs.existsSync(tsConfigPath)]
111
-
112
- if (!js && !ts) {
113
- return null
114
- }
115
-
116
- if (!js && ts) {
117
- return {
118
- config: importConfig(tsConfigPath),
119
- path: tsConfigPath,
120
- version: 3,
121
- }
122
- }
123
-
124
- if (js && ts) {
125
- warn('Found both `sanity.cli.js` and `sanity.cli.ts` - using sanity.cli.js')
126
- }
127
-
128
- return {
129
- config: importConfig(jsConfigPath),
130
- path: jsConfigPath,
131
- version: 3,
132
- }
133
- }
134
-
135
- function loadJsonConfig(filePath: string): SanityJson | null {
136
- try {
137
- const content = fs.readFileSync(filePath, 'utf8')
138
- return JSON.parse(content)
139
- } catch (err) {
140
- console.error(`Error reading "${filePath}": ${err.message}`)
141
- return null
142
- }
143
- }
144
-
145
- function importConfig(filePath: string): CliConfig | null {
146
- try {
147
- const config = dynamicRequire<CliConfig | {default: CliConfig} | null>(filePath)
148
- if (config === null || typeof config !== 'object') {
149
- throw new Error('Module export is not a configuration object')
150
- }
151
-
152
- return 'default' in config ? config.default : config
153
- } catch (err) {
154
- // If attempting to import `defineCliConfig` or similar from `sanity/cli`,
155
- // accept the fact that it might not be installed. Instead, let the CLI
156
- // give a warning about the `sanity` module not being installed
157
- if (err.code === 'MODULE_NOT_FOUND' && err.message.includes('sanity/cli')) {
158
- return null
159
- }
160
-
161
- console.error(`Error reading "${filePath}": ${err.message}`)
162
- return null
163
- }
164
- }
165
-
166
- function warn(warning: string) {
167
- if (typeof process.send === 'function') {
168
- process.send({type: 'warning', warning})
169
- } else {
170
- console.warn(warning)
171
- }
172
- }
@@ -1,22 +0,0 @@
1
- import fs from 'node:fs/promises'
2
- import path from 'node:path'
3
-
4
- import pkgDir from 'pkg-dir'
5
-
6
- export async function getCliVersion(): Promise<string> {
7
- const cliPath = pkgDir.sync(__dirname)
8
-
9
- if (!cliPath) {
10
- throw new Error('Unable to resolve root of @sanity/cli module')
11
- }
12
-
13
- let data: string | undefined
14
- try {
15
- data = await fs.readFile(path.join(cliPath, 'package.json'), 'utf-8')
16
- } catch (err) {
17
- throw new Error(`Unable to read @sanity/cli/package.json: ${err.message}`)
18
- }
19
-
20
- const pkg = JSON.parse(data)
21
- return pkg.version
22
- }
@@ -1,35 +0,0 @@
1
- import {readFileSync} from 'node:fs'
2
- import {join as joinPath, normalize as normalizePath} from 'node:path'
3
-
4
- import resolveFrom from 'resolve-from'
5
-
6
- export function getLocalVersion(moduleId: string, workDir: string): string | undefined {
7
- const fromPath = workDir || process.cwd()
8
- const modulePath = resolveFrom.silent(fromPath, joinPath(moduleId, 'package.json'))
9
- if (modulePath) {
10
- return tryGetVersion(modulePath)
11
- }
12
-
13
- // In the case of packages with an `exports` key, we may not be able to resolve `package.json`.
14
- // If this happens, try to resolve the module itself and look for the last occurence of the
15
- // package name, then append `package.json` to that path
16
- const pathSegment = normalizePath(moduleId)
17
- const parentPath = resolveFrom.silent(fromPath, moduleId)
18
- if (!parentPath) {
19
- return undefined
20
- }
21
-
22
- const moduleRoot = parentPath.slice(0, parentPath.lastIndexOf(pathSegment) + pathSegment.length)
23
- const manifestPath = joinPath(moduleRoot, 'package.json')
24
- return tryGetVersion(manifestPath)
25
- }
26
-
27
- function tryGetVersion(modulePath: string): string | undefined {
28
- try {
29
- const fileContent = readFileSync(modulePath, 'utf8')
30
- const manifest = JSON.parse(fileContent)
31
- return manifest.version
32
- } catch {
33
- return undefined
34
- }
35
- }