@sanity/cli 3.88.0 → 3.88.1-typegen-experimental.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/lib/_chunks-cjs/cli.js +58748 -56759
- package/lib/_chunks-cjs/cli.js.map +1 -1
- package/lib/_chunks-cjs/generateAction.js +113 -111
- package/lib/_chunks-cjs/generateAction.js.map +1 -1
- package/lib/_chunks-cjs/loadEnv.js +3 -3
- package/lib/_chunks-cjs/loadEnv.js.map +1 -1
- package/lib/_chunks-cjs/workerChannel.js +84 -0
- package/lib/_chunks-cjs/workerChannel.js.map +1 -0
- package/lib/workers/typegenGenerate.d.ts +144 -33
- package/lib/workers/typegenGenerate.js +83 -112
- package/lib/workers/typegenGenerate.js.map +1 -1
- package/package.json +19 -21
- package/src/actions/typegen/generate.telemetry.ts +9 -3
- package/src/actions/typegen/generateAction.ts +159 -152
- package/src/cli.ts +0 -0
- package/src/commands/blueprints/addBlueprintsCommand.ts +52 -56
- package/src/commands/blueprints/blueprintsGroup.ts +0 -1
- package/src/commands/blueprints/configBlueprintsCommand.ts +50 -74
- package/src/commands/blueprints/deployBlueprintsCommand.ts +41 -133
- package/src/commands/blueprints/destroyBlueprintsCommand.ts +76 -0
- package/src/commands/blueprints/infoBlueprintsCommand.ts +29 -51
- package/src/commands/blueprints/initBlueprintsCommand.ts +55 -73
- package/src/commands/blueprints/logsBlueprintsCommand.ts +43 -81
- package/src/commands/blueprints/planBlueprintsCommand.ts +26 -36
- package/src/commands/blueprints/stacksBlueprintsCommand.ts +43 -51
- package/src/commands/functions/devFunctionsCommand.ts +1 -2
- package/src/commands/functions/envFunctionsCommand.ts +55 -46
- package/src/commands/functions/functionsGroup.ts +1 -2
- package/src/commands/functions/logsFunctionsCommand.ts +101 -58
- package/src/commands/functions/testFunctionsCommand.ts +56 -36
- package/src/commands/index.ts +6 -4
- package/src/commands/projects/listProjectsCommand.ts +0 -0
- package/src/commands/projects/projectsGroup.ts +0 -0
- package/src/util/__tests__/workerChannel.test.ts +222 -0
- package/src/util/workerChannel.ts +312 -0
- package/src/workers/typegenGenerate.ts +181 -183
- package/templates/app-sanity-ui/src/ExampleComponent.tsx +1 -1
@@ -1,15 +1,17 @@
|
|
1
|
+
/* eslint-disable max-statements */
|
1
2
|
import {constants, mkdir, open, stat} from 'node:fs/promises'
|
2
3
|
import {dirname, join} from 'node:path'
|
4
|
+
import process from 'node:process'
|
3
5
|
import {Worker} from 'node:worker_threads'
|
4
6
|
|
5
|
-
import {readConfig} from '@sanity/codegen'
|
6
|
-
import {format as prettierFormat, resolveConfig as resolvePrettierConfig} from 'prettier'
|
7
|
+
import {DEFAULT_CONFIG, readConfig} from '@sanity/codegen'
|
7
8
|
|
8
9
|
import {type CliCommandArguments, type CliCommandContext} from '../../types'
|
9
10
|
import {getCliWorkerPath} from '../../util/cliWorker'
|
11
|
+
import {createReceiver} from '../../util/workerChannel'
|
10
12
|
import {
|
11
13
|
type TypegenGenerateTypesWorkerData,
|
12
|
-
type
|
14
|
+
type TypegenWorkerChannel,
|
13
15
|
} from '../../workers/typegenGenerate'
|
14
16
|
import {TypesGeneratedTrace} from './generate.telemetry'
|
15
17
|
|
@@ -17,7 +19,8 @@ export interface TypegenGenerateTypesCommandFlags {
|
|
17
19
|
'config-path'?: string
|
18
20
|
}
|
19
21
|
|
20
|
-
const
|
22
|
+
const DEFAULT_CONFIG_PATH = 'sanity-typegen.json'
|
23
|
+
const GENERATED_FILE_WARNING = `/**
|
21
24
|
* ---------------------------------------------------------------------------------
|
22
25
|
* This file has been generated by Sanity TypeGen.
|
23
26
|
* Command: \`sanity typegen generate\`
|
@@ -31,6 +34,20 @@ const generatedFileWarning = `/**
|
|
31
34
|
* ---------------------------------------------------------------------------------
|
32
35
|
*/\n\n`
|
33
36
|
|
37
|
+
const percentageFormatter = new Intl.NumberFormat('en-US', {
|
38
|
+
style: 'percent',
|
39
|
+
minimumFractionDigits: 1,
|
40
|
+
maximumFractionDigits: 1,
|
41
|
+
})
|
42
|
+
|
43
|
+
const percent = (value: number): string => percentageFormatter.format(Math.min(value, 1))
|
44
|
+
|
45
|
+
const count = (
|
46
|
+
amount: number,
|
47
|
+
plural: string,
|
48
|
+
singular: string = plural.slice(0, Math.max(0, plural.length - 1)),
|
49
|
+
): string => `${amount.toLocaleString('en-US')} ${amount === 1 ? singular : plural}`
|
50
|
+
|
34
51
|
export default async function typegenGenerateAction(
|
35
52
|
args: CliCommandArguments<TypegenGenerateTypesCommandFlags>,
|
36
53
|
context: CliCommandContext,
|
@@ -41,170 +58,160 @@ export default async function typegenGenerateAction(
|
|
41
58
|
const trace = telemetry.trace(TypesGeneratedTrace)
|
42
59
|
trace.start()
|
43
60
|
|
44
|
-
const
|
61
|
+
const spinner = output.spinner('Generating types…')
|
62
|
+
spinner.start()
|
45
63
|
|
64
|
+
let codegenConfig
|
65
|
+
const configPath = flags['config-path'] ?? DEFAULT_CONFIG_PATH
|
46
66
|
try {
|
47
|
-
|
48
|
-
|
49
|
-
|
67
|
+
codegenConfig = await readConfig(configPath)
|
68
|
+
spinner.info(`Using typegen configuration found at "${configPath}"`)
|
69
|
+
} catch (error) {
|
70
|
+
if (error?.code !== 'ENOENT') throw error
|
71
|
+
codegenConfig = DEFAULT_CONFIG
|
72
|
+
|
73
|
+
if (configPath !== DEFAULT_CONFIG_PATH) {
|
74
|
+
spinner.warn(
|
75
|
+
`Configuration file not found at specified path "${configPath}". Falling back to default settings.`,
|
76
|
+
)
|
50
77
|
}
|
51
|
-
} catch (err) {
|
52
|
-
if (err.code === 'ENOENT') {
|
53
|
-
// If the user has not provided a specific schema path (eg we're using the default), give some help
|
54
|
-
const hint =
|
55
|
-
codegenConfig.schema === './schema.json' ? ` - did you run "sanity schema extract"?` : ''
|
56
|
-
throw new Error(`Schema file not found: ${codegenConfig.schema}${hint}`)
|
57
|
-
}
|
58
|
-
throw err
|
59
78
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
79
|
+
const {
|
80
|
+
schemas,
|
81
|
+
augmentGroqModule,
|
82
|
+
formatGeneratedCode,
|
83
|
+
generates,
|
84
|
+
overloadClientMethods,
|
85
|
+
path: searchPath,
|
86
|
+
} = codegenConfig
|
87
|
+
|
88
|
+
spinner.start(`Loading schema${schemas.length === 1 ? '' : 's'}…`)
|
89
|
+
|
90
|
+
const outputPath = join(process.cwd(), generates)
|
91
|
+
await mkdir(dirname(outputPath), {recursive: true})
|
92
|
+
|
93
|
+
const workerData: TypegenGenerateTypesWorkerData = {
|
94
|
+
workDir,
|
95
|
+
schemas,
|
96
|
+
searchPath,
|
97
|
+
overloadClientMethods,
|
98
|
+
augmentGroqModule,
|
99
|
+
}
|
100
|
+
const worker = new Worker(await getCliWorkerPath('typegenGenerate'), {
|
101
|
+
workerData,
|
76
102
|
env: process.env,
|
77
103
|
})
|
104
|
+
const receiver = createReceiver<TypegenWorkerChannel>(worker)
|
78
105
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
constants.O_TRUNC | constants.O_CREAT | constants.O_WRONLY,
|
83
|
-
)
|
84
|
-
|
85
|
-
typeFile.write(generatedFileWarning)
|
86
|
-
|
87
|
-
const stats = {
|
88
|
-
queryFilesCount: 0,
|
89
|
-
errors: 0,
|
90
|
-
queriesCount: 0,
|
91
|
-
schemaTypesCount: 0,
|
92
|
-
unknownTypeNodesGenerated: 0,
|
93
|
-
typeNodesGenerated: 0,
|
94
|
-
emptyUnionTypeNodesGenerated: 0,
|
95
|
-
size: 0,
|
96
|
-
}
|
106
|
+
let fileHandle
|
107
|
+
let schemaStats
|
108
|
+
let queryStats
|
97
109
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
110
|
+
try {
|
111
|
+
await receiver.event.loadedSchemas()
|
112
|
+
spinner.succeed(
|
113
|
+
schemas.length === 1
|
114
|
+
? `Loaded schema from ${schemas[0].schemaPath}`
|
115
|
+
: `Loaded ${count(schemas.length, 'schemas')}`,
|
116
|
+
)
|
117
|
+
|
118
|
+
spinner.start('Generating schema types…')
|
119
|
+
fileHandle = await open(outputPath, 'w')
|
120
|
+
await fileHandle.write(GENERATED_FILE_WARNING)
|
121
|
+
const schemaResult = await receiver.event.generatedSchemaDeclarations()
|
122
|
+
schemaStats = schemaResult.schemaStats
|
123
|
+
await fileHandle.write(schemaResult.code)
|
124
|
+
|
125
|
+
const schemaTypesCount = count(schemaStats.schemaTypesCount, 'schema types')
|
126
|
+
const schemaCount = count(schemaStats.schemaCount, 'schemas')
|
127
|
+
spinner.succeed(
|
128
|
+
`Generated ${schemaTypesCount}${schemas.length > 1 ? ` from ${schemaCount}` : ''}`,
|
129
|
+
)
|
130
|
+
|
131
|
+
spinner.start('Generating query types…')
|
132
|
+
const expectedFiles = (await receiver.event.fileCount()).fileCount
|
133
|
+
const expectedFileCount = count(expectedFiles, 'files')
|
134
|
+
|
135
|
+
for await (const {
|
136
|
+
progress,
|
137
|
+
...queryResult
|
138
|
+
} of receiver.stream.generatedQueryResultDeclaration()) {
|
139
|
+
const queryCount = count(progress.queriesCount, 'queries', 'query')
|
140
|
+
const projectionCount = count(progress.projectionsCount, 'projections')
|
141
|
+
spinner.text =
|
142
|
+
`Generating query types… (${percent(progress.filesCount / expectedFiles)})\n` +
|
143
|
+
` └─ Processed ${progress.filesCount} of ${expectedFileCount}. Found ${queryCount}, ${projectionCount}.`
|
144
|
+
|
145
|
+
if (queryResult.type === 'error') {
|
146
|
+
spinner.fail(queryResult.message)
|
116
147
|
}
|
117
148
|
|
118
|
-
if (
|
119
|
-
|
120
|
-
typeMapStr += msg.typeMap
|
121
|
-
typeFile.write(typeMapStr)
|
122
|
-
stats.size += Buffer.byteLength(typeMapStr)
|
123
|
-
return
|
149
|
+
if (queryResult.type === 'declaration') {
|
150
|
+
await fileHandle.write(queryResult.code)
|
124
151
|
}
|
152
|
+
}
|
125
153
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
154
|
+
const result = await receiver.event.generationComplete()
|
155
|
+
queryStats = result.queryStats
|
156
|
+
await fileHandle.write(result.augmentedQueryResultDeclarations.code)
|
157
|
+
await fileHandle.close()
|
158
|
+
fileHandle = null
|
159
|
+
|
160
|
+
const queryTypesCount = count(queryStats.queriesCount, 'query types')
|
161
|
+
const projectionTypesCount = count(queryStats.projectionsCount, 'projection types')
|
162
|
+
const scannedFilesCount = count(queryStats.totalScannedFilesCount, 'scanned files')
|
163
|
+
spinner.succeed(
|
164
|
+
`Generated ${queryTypesCount} and ${projectionTypesCount} from ${scannedFilesCount}`,
|
165
|
+
)
|
166
|
+
|
167
|
+
if (formatGeneratedCode) {
|
168
|
+
spinner.start(`Formatting generated types with prettier…`)
|
169
|
+
|
170
|
+
try {
|
171
|
+
const prettier = await import('prettier')
|
172
|
+
const prettierConfig = await prettier.resolveConfig(outputPath)
|
173
|
+
|
174
|
+
fileHandle = await open(outputPath, constants.O_RDWR)
|
175
|
+
const code = await fileHandle.readFile({encoding: 'utf-8'})
|
176
|
+
const formattedCode = await prettier.format(code, {
|
177
|
+
...prettierConfig,
|
178
|
+
parser: 'typescript' as const,
|
179
|
+
})
|
180
|
+
await fileHandle.truncate()
|
181
|
+
await fileHandle.write(formattedCode, 0)
|
182
|
+
await fileHandle.close()
|
183
|
+
fileHandle = null
|
184
|
+
|
185
|
+
spinner.succeed('Formatted generated types with prettier')
|
186
|
+
} catch (err) {
|
187
|
+
spinner.warn(`Failed to format generated types with prettier: ${err.message}`)
|
133
188
|
}
|
189
|
+
}
|
134
190
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
} of msg.types) {
|
145
|
-
fileTypeString += `// Variable: ${queryName}\n`
|
146
|
-
fileTypeString += `// Query: ${query.replace(/(\r\n|\n|\r)/gm, '').trim()}\n`
|
147
|
-
fileTypeString += type
|
148
|
-
stats.queriesCount++
|
149
|
-
stats.typeNodesGenerated += typeNodesGenerated
|
150
|
-
stats.unknownTypeNodesGenerated += unknownTypeNodesGenerated
|
151
|
-
stats.emptyUnionTypeNodesGenerated += emptyUnionTypeNodesGenerated
|
152
|
-
}
|
153
|
-
typeFile.write(`${fileTypeString}\n`)
|
154
|
-
stats.size += Buffer.byteLength(fileTypeString)
|
155
|
-
}
|
191
|
+
// Gather final stats and report success
|
192
|
+
const outputStat = await stat(outputPath)
|
193
|
+
|
194
|
+
trace.log({
|
195
|
+
outputSize: outputStat.size,
|
196
|
+
...schemaStats,
|
197
|
+
...queryStats,
|
198
|
+
configOverloadClientMethods: overloadClientMethods,
|
199
|
+
configAugmentGroqModule: augmentGroqModule,
|
156
200
|
})
|
157
|
-
worker.addListener('error', reject)
|
158
|
-
})
|
159
201
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
})
|
167
|
-
: null
|
168
|
-
|
169
|
-
if (prettierConfig) {
|
170
|
-
const formatFile = await open(outputPath, constants.O_RDWR)
|
171
|
-
try {
|
172
|
-
const code = await formatFile.readFile()
|
173
|
-
const formattedCode = await prettierFormat(code.toString(), {
|
174
|
-
...prettierConfig,
|
175
|
-
parser: 'typescript' as const,
|
176
|
-
})
|
177
|
-
await formatFile.truncate()
|
178
|
-
await formatFile.write(formattedCode, 0)
|
179
|
-
|
180
|
-
spinner.info('Formatted generated types with Prettier')
|
181
|
-
} catch (err) {
|
182
|
-
output.warn(`Failed to format generated types with Prettier: ${err.message}`)
|
183
|
-
} finally {
|
184
|
-
await formatFile.close()
|
202
|
+
if (queryStats.errorCount > 0) {
|
203
|
+
spinner.warn(
|
204
|
+
`Encountered ${count(queryStats.errorCount, 'errors')} in ${count(queryStats.filesWithErrors, 'files')} while generating types to ${generates}`,
|
205
|
+
)
|
206
|
+
} else {
|
207
|
+
spinner.succeed(`Successfully generated types to ${generates}`)
|
185
208
|
}
|
209
|
+
} catch (err) {
|
210
|
+
trace.error(err)
|
211
|
+
throw err
|
212
|
+
} finally {
|
213
|
+
await fileHandle?.close()
|
214
|
+
await receiver.dispose()
|
215
|
+
trace.complete()
|
186
216
|
}
|
187
|
-
|
188
|
-
trace.log({
|
189
|
-
outputSize: stats.size,
|
190
|
-
queriesCount: stats.queriesCount,
|
191
|
-
schemaTypesCount: stats.schemaTypesCount,
|
192
|
-
queryFilesCount: stats.queryFilesCount,
|
193
|
-
filesWithErrors: stats.errors,
|
194
|
-
typeNodesGenerated: stats.typeNodesGenerated,
|
195
|
-
unknownTypeNodesGenerated: stats.unknownTypeNodesGenerated,
|
196
|
-
unknownTypeNodesRatio:
|
197
|
-
stats.typeNodesGenerated > 0 ? stats.unknownTypeNodesGenerated / stats.typeNodesGenerated : 0,
|
198
|
-
emptyUnionTypeNodesGenerated: stats.emptyUnionTypeNodesGenerated,
|
199
|
-
configOverloadClientMethods: codegenConfig.overloadClientMethods,
|
200
|
-
})
|
201
|
-
|
202
|
-
trace.complete()
|
203
|
-
if (stats.errors > 0) {
|
204
|
-
spinner.warn(`Encountered errors in ${stats.errors} files while generating types`)
|
205
|
-
}
|
206
|
-
|
207
|
-
spinner.succeed(
|
208
|
-
`Generated TypeScript types for ${stats.schemaTypesCount} schema types and ${stats.queriesCount} GROQ queries in ${stats.queryFilesCount} files into: ${codegenConfig.generates}`,
|
209
|
-
)
|
210
217
|
}
|
package/src/cli.ts
CHANGED
File without changes
|
@@ -2,79 +2,75 @@ import {type CliCommandDefinition} from '../../types'
|
|
2
2
|
|
3
3
|
const helpText = `
|
4
4
|
Arguments
|
5
|
-
|
5
|
+
<type> Type of Resource to add (currently only 'function' is supported)
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
Options
|
8
|
+
--name <name> Name of the Resource
|
9
|
+
--fn-type <type> Type of Function Resource to add (e.g. document-publish)
|
10
|
+
--fn-lang, --lang <ts|js> Language of the Function Resource
|
11
|
+
--js, --javascript Use JavaScript for the Function Resource
|
12
|
+
|
13
|
+
Examples:
|
14
|
+
# Add a Function Resource (TypeScript by default)
|
9
15
|
sanity blueprints add function
|
16
|
+
|
17
|
+
# Add a Function Resource with a specific name
|
18
|
+
sanity blueprints add function --name my-function
|
19
|
+
|
20
|
+
# Add a Function Resource with a specific type
|
21
|
+
sanity blueprints add function --name my-function --fn-type document-publish
|
22
|
+
|
23
|
+
# Add a Function Resource in JavaScript
|
24
|
+
sanity blueprints add function --name my-function --fn-type document-publish --js
|
10
25
|
`
|
11
26
|
|
12
|
-
|
27
|
+
export interface BlueprintsAddFlags {
|
28
|
+
'name'?: string
|
29
|
+
'fn-type'?: string
|
30
|
+
'fn-lang'?: string
|
31
|
+
'language'?: string
|
32
|
+
'lang'?: string
|
33
|
+
'js'?: boolean
|
34
|
+
'javascript'?: boolean
|
35
|
+
}
|
36
|
+
|
37
|
+
const defaultFlags: BlueprintsAddFlags = {
|
38
|
+
//
|
39
|
+
}
|
40
|
+
|
41
|
+
const addBlueprintsCommand: CliCommandDefinition<BlueprintsAddFlags> = {
|
13
42
|
name: 'add',
|
14
43
|
group: 'blueprints',
|
15
44
|
helpText,
|
16
|
-
signature:
|
45
|
+
signature:
|
46
|
+
'<type> [--name <name>] [--fn-type <document-publish>] [--fn-lang <ts|js>] [--javascript]',
|
17
47
|
description: 'Add a Resource to a Blueprint',
|
18
|
-
|
48
|
+
|
19
49
|
async action(args, context) {
|
20
|
-
const {output
|
21
|
-
const
|
50
|
+
const {output} = context
|
51
|
+
const flags = {...defaultFlags, ...args.extOptions}
|
22
52
|
|
23
53
|
const [resourceType] = args.argsWithoutOptions
|
24
54
|
|
25
55
|
if (!resourceType) {
|
26
|
-
|
27
|
-
return
|
28
|
-
}
|
29
|
-
|
30
|
-
const {blueprint: blueprintAction, resources: resourcesAction} = await import(
|
31
|
-
'@sanity/runtime-cli/actions/blueprints'
|
32
|
-
)
|
33
|
-
|
34
|
-
const existingBlueprint = blueprintAction.findBlueprintFile()
|
35
|
-
if (!existingBlueprint) {
|
36
|
-
print('No blueprint file found. Run `sanity blueprints init` first.')
|
56
|
+
output.error('Resource type is required. Available types: function')
|
37
57
|
return
|
38
58
|
}
|
39
59
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
{value: 'document-create', name: 'Document Create (Available soon)', disabled: true},
|
53
|
-
{value: 'document-delete', name: 'Document Delete (Available soon)', disabled: true},
|
54
|
-
],
|
55
|
-
})
|
56
|
-
|
57
|
-
const {filePath, resourceAdded, resource} = resourcesAction.createFunctionResource({
|
58
|
-
name: functionName,
|
59
|
-
type: functionType,
|
60
|
-
displayName: functionName,
|
61
|
-
})
|
62
|
-
|
63
|
-
print(`\nCreated function: ${filePath}`)
|
64
|
-
|
65
|
-
if (resourceAdded) {
|
66
|
-
// added to blueprint.json
|
67
|
-
print('Function Resource added to Blueprint')
|
68
|
-
} else {
|
69
|
-
// print the resource JSON for manual addition
|
70
|
-
print('\nAdd this Function Resource to your Blueprint:')
|
71
|
-
print(JSON.stringify(resource, null, 2))
|
72
|
-
}
|
73
|
-
|
74
|
-
return
|
75
|
-
}
|
60
|
+
const {blueprintAddCore} = await import('@sanity/runtime-cli/cores/blueprints')
|
61
|
+
const {success, error} = await blueprintAddCore({
|
62
|
+
bin: 'sanity',
|
63
|
+
log: (msg) => output.print(msg),
|
64
|
+
args: {type: resourceType},
|
65
|
+
flags: {
|
66
|
+
'name': flags.name,
|
67
|
+
'fn-type': flags['fn-type'],
|
68
|
+
'language': flags['fn-lang'] ?? flags.language ?? flags.lang,
|
69
|
+
'javascript': flags.js || flags.javascript,
|
70
|
+
},
|
71
|
+
})
|
76
72
|
|
77
|
-
|
73
|
+
if (!success) throw new Error(error)
|
78
74
|
},
|
79
75
|
}
|
80
76
|
|
@@ -2,30 +2,51 @@ import {type CliCommandDefinition} from '../../types'
|
|
2
2
|
|
3
3
|
const helpText = `
|
4
4
|
Options
|
5
|
-
--edit
|
5
|
+
--edit, -e Edit the configuration
|
6
|
+
--test, -t Test the configuration
|
7
|
+
--project-id <id> Project ID to use
|
6
8
|
|
7
|
-
Examples
|
9
|
+
Examples:
|
8
10
|
# View current configuration
|
9
11
|
sanity blueprints config
|
10
12
|
|
11
13
|
# Edit configuration
|
12
14
|
sanity blueprints config --edit
|
15
|
+
|
16
|
+
# Test configuration
|
17
|
+
sanity blueprints config --test
|
18
|
+
|
19
|
+
# Edit and test configuration
|
20
|
+
sanity blueprints config -et
|
13
21
|
`
|
14
22
|
|
15
|
-
|
16
|
-
|
23
|
+
export interface BlueprintsConfigFlags {
|
24
|
+
'test-config'?: boolean
|
25
|
+
'test'?: boolean
|
26
|
+
't'?: boolean
|
27
|
+
'edit'?: boolean
|
28
|
+
'e'?: boolean
|
29
|
+
'project-id'?: string
|
30
|
+
'projectId'?: string
|
31
|
+
'project'?: string
|
32
|
+
'stack-id'?: string
|
33
|
+
'stackId'?: string
|
34
|
+
'stack'?: string
|
35
|
+
}
|
36
|
+
|
37
|
+
const defaultFlags: BlueprintsConfigFlags = {
|
38
|
+
//
|
17
39
|
}
|
18
40
|
|
19
|
-
const configBlueprintsCommand: CliCommandDefinition = {
|
41
|
+
const configBlueprintsCommand: CliCommandDefinition<BlueprintsConfigFlags> = {
|
20
42
|
name: 'config',
|
21
43
|
group: 'blueprints',
|
22
44
|
helpText,
|
23
|
-
signature: '[--edit]',
|
45
|
+
signature: '[--edit] [-e] [--test] [-t] [--project-id <id>]',
|
24
46
|
description: 'View or edit local Blueprints configuration',
|
25
|
-
|
47
|
+
|
26
48
|
async action(args, context) {
|
27
|
-
const {apiClient, output
|
28
|
-
const {print} = output
|
49
|
+
const {apiClient, output} = context
|
29
50
|
const flags = {...defaultFlags, ...args.extOptions}
|
30
51
|
|
31
52
|
const client = apiClient({
|
@@ -33,79 +54,34 @@ const configBlueprintsCommand: CliCommandDefinition = {
|
|
33
54
|
requireProject: false,
|
34
55
|
})
|
35
56
|
const {token} = client.config()
|
36
|
-
const {
|
37
|
-
blueprint: blueprintAction,
|
38
|
-
projects: projectsAction,
|
39
|
-
stacks: stacksAction,
|
40
|
-
} = await import('@sanity/runtime-cli/actions/blueprints')
|
41
|
-
|
42
|
-
const config = blueprintAction.readConfigFile()
|
43
|
-
if (!config) {
|
44
|
-
print('No configuration found. Run `sanity blueprints init` first.')
|
45
|
-
return
|
46
|
-
}
|
47
57
|
|
48
|
-
|
49
|
-
print(JSON.stringify(config, null, 2))
|
50
|
-
|
51
|
-
if (!flags.edit) {
|
52
|
-
return
|
53
|
-
}
|
58
|
+
if (!token) throw new Error('No API token found. Please run `sanity login`.')
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
}
|
60
|
+
const {blueprintConfigCore} = await import('@sanity/runtime-cli/cores/blueprints')
|
61
|
+
const {getBlueprintAndStack} = await import('@sanity/runtime-cli/actions/blueprints')
|
62
|
+
const {display} = await import('@sanity/runtime-cli/utils')
|
59
63
|
|
60
|
-
const {
|
61
|
-
if (!ok) {
|
62
|
-
print(error)
|
63
|
-
return
|
64
|
-
}
|
64
|
+
const {localBlueprint, issues} = await getBlueprintAndStack({token})
|
65
65
|
|
66
|
-
if (
|
67
|
-
print
|
68
|
-
|
66
|
+
if (issues) {
|
67
|
+
// print issues and continue
|
68
|
+
output.print(display.errors.presentBlueprintIssues(issues))
|
69
69
|
}
|
70
70
|
|
71
|
-
const
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
71
|
+
const {success, error} = await blueprintConfigCore({
|
72
|
+
bin: 'sanity',
|
73
|
+
log: (message) => output.print(message),
|
74
|
+
blueprint: localBlueprint,
|
75
|
+
token,
|
76
|
+
flags: {
|
77
|
+
'project-id': flags['project-id'] ?? flags.projectId ?? flags.project,
|
78
|
+
'stack-id': flags['stack-id'] ?? flags.stackId ?? flags.stack,
|
79
|
+
'test-config': flags['test-config'] ?? flags.test ?? flags.t,
|
80
|
+
'edit': flags.edit ?? flags.e,
|
81
|
+
},
|
81
82
|
})
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
// get stacks for selected project
|
86
|
-
const {ok: stacksOk, stacks, error: stacksError} = await stacksAction.listStacks(auth)
|
87
|
-
if (!stacksOk) {
|
88
|
-
print(stacksError)
|
89
|
-
return
|
90
|
-
}
|
91
|
-
|
92
|
-
let stackId
|
93
|
-
if (stacks && stacks.length > 0) {
|
94
|
-
const stackChoices = stacks.map(({name, id}) => ({
|
95
|
-
value: id,
|
96
|
-
name: `${name} <${id}>`,
|
97
|
-
}))
|
98
|
-
|
99
|
-
stackId = await prompt.single({
|
100
|
-
type: 'list',
|
101
|
-
message: 'Select a Stack:',
|
102
|
-
choices: stackChoices,
|
103
|
-
default: config.stackId,
|
104
|
-
})
|
105
|
-
}
|
106
|
-
|
107
|
-
blueprintAction.writeConfigFile({projectId, stackId})
|
108
|
-
print('\nConfiguration updated successfully.')
|
84
|
+
if (!success) throw new Error(error)
|
109
85
|
},
|
110
86
|
}
|
111
87
|
|