@sanity/cli 3.86.0 → 3.86.2-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.
@@ -2,228 +2,90 @@ import {isMainThread, parentPort, workerData as _workerData} from 'node:worker_t
2
2
 
3
3
  import {
4
4
  findQueriesInPath,
5
+ type GeneratedQueries,
6
+ type GeneratedSchema,
7
+ type GeneratedTypemap,
8
+ generateTypes,
5
9
  getResolver,
6
10
  readSchema,
7
11
  registerBabel,
8
- safeParseQuery,
9
- TypeGenerator,
10
12
  } from '@sanity/codegen'
11
13
  import createDebug from 'debug'
12
- import {typeEvaluate, type TypeNode} from 'groq-js'
14
+ import {type SchemaType} from 'groq-js'
15
+
16
+ import {
17
+ createReporter,
18
+ type WorkerChannel,
19
+ type WorkerChannelEvent,
20
+ type WorkerChannelStream,
21
+ } from '../util/workerChannel'
13
22
 
14
23
  const $info = createDebug('sanity:codegen:generate:info')
15
- const $warn = createDebug('sanity:codegen:generate:warn')
16
24
 
17
25
  export interface TypegenGenerateTypesWorkerData {
18
26
  workDir: string
19
- workspaceName?: string
20
- schemaPath: string
27
+ schemas: {
28
+ projectId: string | 'default'
29
+ dataset: string | 'default'
30
+ schemaPath: string
31
+ }[]
21
32
  searchPath: string | string[]
22
- overloadClientMethods?: boolean
33
+ overloadClientMethods: boolean
34
+ augmentGroqModule: boolean
23
35
  }
24
36
 
25
- export type TypegenGenerateTypesWorkerMessage =
26
- | {
27
- type: 'error'
28
- error: Error
29
- fatal: boolean
30
- query?: string
31
- filename?: string
32
- }
33
- | {
34
- type: 'types'
35
- filename: string
36
- types: {
37
- queryName: string
38
- query: string
39
- type: string
40
- unknownTypeNodesGenerated: number
41
- typeNodesGenerated: number
42
- emptyUnionTypeNodesGenerated: number
43
- }[]
44
- }
45
- | {
46
- type: 'schema'
47
- filename: string
48
- schema: string
49
- length: number
50
- }
51
- | {
52
- type: 'typemap'
53
- typeMap: string
54
- }
55
- | {
56
- type: 'complete'
57
- }
37
+ /** @internal */
38
+ export type TypegenWorkerChannel = WorkerChannel<{
39
+ schema: WorkerChannelEvent<GeneratedSchema>
40
+ queries: WorkerChannelStream<GeneratedQueries>
41
+ typemap: WorkerChannelEvent<GeneratedTypemap>
42
+ }>
58
43
 
59
44
  if (isMainThread || !parentPort) {
60
45
  throw new Error('This module must be run as a worker thread')
61
46
  }
62
47
 
48
+ const report = createReporter<TypegenWorkerChannel>(parentPort)
63
49
  const opts = _workerData as TypegenGenerateTypesWorkerData
64
50
 
65
- registerBabel()
66
-
67
51
  async function main() {
68
- const schema = await readSchema(opts.schemaPath)
52
+ const schemas: {
53
+ schema: SchemaType
54
+ projectId: string | 'default'
55
+ dataset: string | 'default'
56
+ filename: string
57
+ }[] = []
58
+
59
+ for (const schemaConfig of opts.schemas) {
60
+ $info(`Reading schema from ${schemaConfig.schemaPath}...`)
61
+ const schema = await readSchema(schemaConfig.schemaPath)
62
+ schemas.push({
63
+ schema,
64
+ projectId: schemaConfig.projectId,
65
+ dataset: schemaConfig.dataset,
66
+ filename: schemaConfig.schemaPath,
67
+ })
68
+ }
69
+ $info(`Read ${schemas.length} schema definition${schemas.length === 1 ? '' : 's'} successfully.`)
69
70
 
70
- const typeGenerator = new TypeGenerator(schema)
71
- const schemaTypes = [typeGenerator.generateSchemaTypes(), TypeGenerator.generateKnownTypes()]
72
- .join('\n')
73
- .trim()
74
71
  const resolver = getResolver()
75
72
 
76
- parentPort?.postMessage({
77
- type: 'schema',
78
- schema: `${schemaTypes.trim()}\n`,
79
- filename: 'schema.json',
80
- length: schema.length,
81
- } satisfies TypegenGenerateTypesWorkerMessage)
82
-
83
- const queries = findQueriesInPath({
84
- path: opts.searchPath,
85
- resolver,
73
+ const result = generateTypes({
74
+ schemas,
75
+ queriesByFile: findQueriesInPath({path: opts.searchPath, resolver}),
76
+ augmentGroqModule: opts.augmentGroqModule,
77
+ overloadClientMethods: opts.overloadClientMethods,
86
78
  })
87
79
 
88
- const allQueries = []
89
-
90
- for await (const result of queries) {
91
- if (result.type === 'error') {
92
- parentPort?.postMessage({
93
- type: 'error',
94
- error: result.error,
95
- fatal: false,
96
- filename: result.filename,
97
- } satisfies TypegenGenerateTypesWorkerMessage)
98
- continue
99
- }
100
- $info(`Processing ${result.queries.length} queries in "${result.filename}"...`)
101
-
102
- const fileQueryTypes: {
103
- queryName: string
104
- query: string
105
- type: string
106
- typeName: string
107
- typeNode: TypeNode
108
- unknownTypeNodesGenerated: number
109
- typeNodesGenerated: number
110
- emptyUnionTypeNodesGenerated: number
111
- }[] = []
112
- for (const {name: queryName, result: query} of result.queries) {
113
- try {
114
- const ast = safeParseQuery(query)
115
- const queryTypes = typeEvaluate(ast, schema)
116
-
117
- const typeName = `${queryName}Result`
118
- const type = typeGenerator.generateTypeNodeTypes(typeName, queryTypes)
119
-
120
- const queryTypeStats = walkAndCountQueryTypeNodeStats(queryTypes)
121
- fileQueryTypes.push({
122
- queryName,
123
- query,
124
- typeName,
125
- typeNode: queryTypes,
126
- type: `${type.trim()}\n`,
127
- unknownTypeNodesGenerated: queryTypeStats.unknownTypes,
128
- typeNodesGenerated: queryTypeStats.allTypes,
129
- emptyUnionTypeNodesGenerated: queryTypeStats.emptyUnions,
130
- })
131
- } catch (err) {
132
- parentPort?.postMessage({
133
- type: 'error',
134
- error: new Error(
135
- `Error generating types for query "${queryName}" in "${result.filename}": ${err.message}`,
136
- {cause: err},
137
- ),
138
- fatal: false,
139
- query,
140
- } satisfies TypegenGenerateTypesWorkerMessage)
141
- }
142
- }
80
+ report.event.schema(await result.generatedSchema())
143
81
 
144
- if (fileQueryTypes.length > 0) {
145
- $info(`Generated types for ${fileQueryTypes.length} queries in "${result.filename}"\n`)
146
- parentPort?.postMessage({
147
- type: 'types',
148
- types: fileQueryTypes,
149
- filename: result.filename,
150
- } satisfies TypegenGenerateTypesWorkerMessage)
151
- }
152
-
153
- if (fileQueryTypes.length > 0) {
154
- allQueries.push(...fileQueryTypes)
155
- }
156
- }
157
-
158
- if (opts.overloadClientMethods && allQueries.length > 0) {
159
- const typeMap = `${typeGenerator.generateQueryMap(allQueries).trim()}\n`
160
- parentPort?.postMessage({
161
- type: 'typemap',
162
- typeMap,
163
- } satisfies TypegenGenerateTypesWorkerMessage)
82
+ for await (const {filename, results} of result.generatedQueries()) {
83
+ report.stream.queries.emit({filename, results})
164
84
  }
85
+ report.stream.queries.end()
165
86
 
166
- parentPort?.postMessage({
167
- type: 'complete',
168
- } satisfies TypegenGenerateTypesWorkerMessage)
169
- }
170
-
171
- function walkAndCountQueryTypeNodeStats(typeNode: TypeNode): {
172
- allTypes: number
173
- unknownTypes: number
174
- emptyUnions: number
175
- } {
176
- switch (typeNode.type) {
177
- case 'unknown': {
178
- return {allTypes: 1, unknownTypes: 1, emptyUnions: 0}
179
- }
180
- case 'array': {
181
- const acc = walkAndCountQueryTypeNodeStats(typeNode.of)
182
- acc.allTypes += 1 // count the array type itself
183
- return acc
184
- }
185
- case 'object': {
186
- // if the rest is unknown, we count it as one unknown type
187
- if (typeNode.rest && typeNode.rest.type === 'unknown') {
188
- return {allTypes: 2, unknownTypes: 1, emptyUnions: 0} // count the object type itself as well
189
- }
190
-
191
- const restStats = typeNode.rest
192
- ? walkAndCountQueryTypeNodeStats(typeNode.rest)
193
- : {allTypes: 1, unknownTypes: 0, emptyUnions: 0} // count the object type itself
194
-
195
- return Object.values(typeNode.attributes).reduce((acc, attribute) => {
196
- const {allTypes, unknownTypes, emptyUnions} = walkAndCountQueryTypeNodeStats(
197
- attribute.value,
198
- )
199
- return {
200
- allTypes: acc.allTypes + allTypes,
201
- unknownTypes: acc.unknownTypes + unknownTypes,
202
- emptyUnions: acc.emptyUnions + emptyUnions,
203
- }
204
- }, restStats)
205
- }
206
- case 'union': {
207
- if (typeNode.of.length === 0) {
208
- return {allTypes: 1, unknownTypes: 0, emptyUnions: 1}
209
- }
210
-
211
- return typeNode.of.reduce(
212
- (acc, type) => {
213
- const {allTypes, unknownTypes, emptyUnions} = walkAndCountQueryTypeNodeStats(type)
214
- return {
215
- allTypes: acc.allTypes + allTypes,
216
- unknownTypes: acc.unknownTypes + unknownTypes,
217
- emptyUnions: acc.emptyUnions + emptyUnions,
218
- }
219
- },
220
- {allTypes: 1, unknownTypes: 0, emptyUnions: 0}, // count the union type itself
221
- )
222
- }
223
- default: {
224
- return {allTypes: 1, unknownTypes: 0, emptyUnions: 0}
225
- }
226
- }
87
+ report.event.typemap(await result.generatedTypemap())
227
88
  }
228
89
 
90
+ registerBabel()
229
91
  main()