@sanity/codegen 4.19.0 → 4.19.1-next.10

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/index.mjs DELETED
@@ -1,771 +0,0 @@
1
- import fs$1, { readFile } from "node:fs/promises";
2
- import json5 from "json5";
3
- import * as z from "zod";
4
- import { parse } from "groq-js";
5
- import createDebug from "debug";
6
- import glob from "globby";
7
- import fs, { existsSync } from "node:fs";
8
- import path, { join, resolve } from "node:path";
9
- import { createRequire } from "node:module";
10
- import { parse as parse$1, traverse as traverse$1 } from "@babel/core";
11
- import * as t from "@babel/types";
12
- import traverse, { Scope } from "@babel/traverse";
13
- import { loadConfig, createMatchPath } from "tsconfig-paths";
14
- import register from "@babel/register";
15
- import { CodeGenerator } from "@babel/generator";
16
- const configDefinition = z.object({
17
- path: z.string().or(z.array(z.string())).default([
18
- "./src/**/*.{ts,tsx,js,jsx,mjs,cjs,astro}",
19
- "./app/**/*.{ts,tsx,js,jsx,mjs,cjs}",
20
- "./sanity/**/*.{ts,tsx,js,jsx,mjs,cjs}"
21
- ]),
22
- schema: z.string().default("./schema.json"),
23
- generates: z.string().default("./sanity.types.ts"),
24
- formatGeneratedCode: z.boolean().default(!0),
25
- overloadClientMethods: z.boolean().default(!0)
26
- });
27
- async function readConfig(path2) {
28
- try {
29
- const content = await readFile(path2, "utf-8"), json = json5.parse(content);
30
- return configDefinition.parseAsync(json);
31
- } catch (error) {
32
- if (error instanceof z.ZodError)
33
- throw new Error(
34
- `Error in config file
35
- ${error.errors.map((err) => err.message).join(`
36
- `)}`,
37
- { cause: error }
38
- );
39
- if (typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT")
40
- return configDefinition.parse({});
41
- throw error;
42
- }
43
- }
44
- async function readSchema(path2) {
45
- const content = await readFile(path2, "utf-8");
46
- return JSON.parse(content);
47
- }
48
- function safeParseQuery(query) {
49
- const params = {};
50
- for (const param of extractSliceParams(query))
51
- params[param] = 0;
52
- return parse(query, { params });
53
- }
54
- function* extractSliceParams(query) {
55
- const sliceRegex = /\[(\$(\w+)|\d)\.\.\.?(\$(\w+)|\d)\]/g, matches = query.matchAll(sliceRegex);
56
- if (matches)
57
- for (const match of matches) {
58
- const start = match[1] === `$${match[2]}` ? match[2] : null;
59
- start !== null && (yield start);
60
- const end = match[3] === `$${match[4]}` ? match[4] : null;
61
- end !== null && (yield end);
62
- }
63
- }
64
- function findBabelConfig(path2) {
65
- const configPath = join(path2, "babel.config.json");
66
- if (existsSync(configPath))
67
- return configPath;
68
- const parent = resolve(join(path2, ".."));
69
- if (parent && parent !== path2)
70
- return findBabelConfig(parent);
71
- throw new Error("Could not find `babel.config.json` in @sanity/codegen");
72
- }
73
- function getBabelConfig(path2) {
74
- return { extends: findBabelConfig(__dirname) };
75
- }
76
- function parseSourceFile(_source, _filename, babelOptions) {
77
- let source = _source, filename = _filename;
78
- filename.endsWith(".astro") ? (filename += ".ts", source = parseAstro(source)) : filename.endsWith(".vue") && (filename += ".ts", source = parseVue(source));
79
- const result = parse$1(source, {
80
- ...babelOptions,
81
- filename
82
- });
83
- if (!result)
84
- throw new Error(`Failed to parse ${filename}`);
85
- return result;
86
- }
87
- function parseAstro(source) {
88
- const codeFences = source.match(/---\n([\s\S]*?)\n---/g);
89
- return codeFences ? codeFences.map((codeFence) => codeFence.split(`
90
- `).slice(1, -1).join(`
91
- `)).join(`
92
- `) : "";
93
- }
94
- function parseVue(source) {
95
- const matches = matchAllPolyfill(source, /<script(?:\s+generic=["'][^"']*["'])?[^>]*>([\s\S]*?)<\/script>/g);
96
- return matches.length ? matches.map((match) => match[1]).join(`
97
- `) : "";
98
- }
99
- function matchAllPolyfill(str, regex) {
100
- if (!regex.global)
101
- throw new Error("matchAll polyfill requires a global regex (with /g flag)");
102
- const matches = [];
103
- let match;
104
- for (; (match = regex.exec(str)) !== null; )
105
- matches.push(match);
106
- return matches;
107
- }
108
- const debug$2 = createDebug("sanity:codegen:findQueries:debug"), TAGGED_TEMPLATE_ALLOW_LIST = ["groq"], FUNCTION_WRAPPER_ALLOW_LIST = ["defineQuery"];
109
- function resolveExpression({
110
- node,
111
- file,
112
- scope,
113
- filename,
114
- resolver,
115
- babelConfig,
116
- params = [],
117
- fnArguments = []
118
- }) {
119
- if (debug$2(
120
- `Resolving node ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`
121
- ), t.isTaggedTemplateExpression(node) && t.isIdentifier(node.tag) && TAGGED_TEMPLATE_ALLOW_LIST.includes(node.tag.name))
122
- return resolveExpression({
123
- node: node.quasi,
124
- scope,
125
- filename,
126
- file,
127
- resolver,
128
- params,
129
- babelConfig,
130
- fnArguments
131
- });
132
- if (t.isTemplateLiteral(node)) {
133
- const resolvedExpressions = node.expressions.map(
134
- (expression) => resolveExpression({
135
- node: expression,
136
- scope,
137
- filename,
138
- file,
139
- resolver,
140
- params,
141
- babelConfig,
142
- fnArguments
143
- })
144
- );
145
- return node.quasis.map((quasi, idx) => (quasi.value.cooked || "") + (resolvedExpressions[idx] || "")).join("");
146
- }
147
- if (t.isLiteral(node)) {
148
- if (node.type === "NullLiteral" || node.type === "RegExpLiteral")
149
- throw new Error(`Unsupported literal type: ${node.type}`);
150
- return node.value.toString();
151
- }
152
- if (t.isIdentifier(node))
153
- return resolveIdentifier({
154
- node,
155
- scope,
156
- filename,
157
- file,
158
- resolver,
159
- fnArguments,
160
- babelConfig,
161
- params
162
- });
163
- if (t.isVariableDeclarator(node)) {
164
- const init = node.init ?? (t.isAssignmentPattern(node.id) && node.id.right);
165
- if (!init)
166
- throw new Error("Unsupported variable declarator");
167
- return resolveExpression({
168
- node: init,
169
- fnArguments,
170
- scope,
171
- filename,
172
- file,
173
- babelConfig,
174
- resolver
175
- });
176
- }
177
- if (t.isCallExpression(node) && t.isIdentifier(node.callee) && FUNCTION_WRAPPER_ALLOW_LIST.includes(node.callee.name))
178
- return resolveExpression({
179
- node: node.arguments[0],
180
- scope,
181
- filename,
182
- file,
183
- resolver,
184
- babelConfig,
185
- params
186
- });
187
- if (t.isCallExpression(node))
188
- return resolveCallExpression({
189
- node,
190
- scope,
191
- filename,
192
- file,
193
- resolver,
194
- babelConfig,
195
- params
196
- });
197
- if (t.isArrowFunctionExpression(node) || t.isFunctionDeclaration(node) || t.isFunctionExpression(node)) {
198
- const newScope = new Scope(scope.path, scope);
199
- return params.forEach((param, i) => {
200
- newScope.push({
201
- id: param,
202
- init: fnArguments[i]
203
- });
204
- }), resolveExpression({
205
- node: node.body,
206
- params: node.params,
207
- fnArguments,
208
- scope: newScope,
209
- filename,
210
- file,
211
- babelConfig,
212
- resolver
213
- });
214
- }
215
- if (t.isNewExpression(node))
216
- return resolveExpression({
217
- node: node.callee,
218
- scope,
219
- filename,
220
- file,
221
- babelConfig,
222
- resolver
223
- });
224
- if (t.isImportDefaultSpecifier(node) || t.isImportSpecifier(node))
225
- return resolveImportSpecifier({ node, file, filename, fnArguments, resolver, babelConfig });
226
- if (t.isAssignmentPattern(node))
227
- return resolveExpression({
228
- node: node.right,
229
- scope,
230
- filename,
231
- file,
232
- resolver,
233
- params,
234
- babelConfig,
235
- fnArguments
236
- });
237
- throw new Error(
238
- `Unsupported expression type: ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`
239
- );
240
- }
241
- function resolveIdentifier({
242
- node,
243
- scope,
244
- filename,
245
- file,
246
- resolver,
247
- babelConfig,
248
- fnArguments,
249
- params
250
- }) {
251
- const paramIndex = params.findIndex(
252
- (param) => t.isIdentifier(param) && node.name === param.name || t.isAssignmentPattern(param) && t.isIdentifier(param.left) && node.name === param.left.name
253
- );
254
- let argument = fnArguments[paramIndex];
255
- if (!argument && paramIndex >= 0 && t.isAssignmentPattern(params[paramIndex]) && (argument = params[paramIndex].right), argument && t.isLiteral(argument))
256
- return resolveExpression({
257
- node: argument,
258
- scope,
259
- filename,
260
- file,
261
- resolver,
262
- params,
263
- babelConfig,
264
- fnArguments
265
- });
266
- const binding = scope.getBinding(node.name);
267
- if (binding) {
268
- if (t.isIdentifier(binding.path.node) && binding.path.node.name === node.name)
269
- throw new Error(
270
- `Could not resolve same identifier "${node.name}" in "${filename}:${node.loc?.start.line}:${node.loc?.start.column}"`
271
- );
272
- return resolveExpression({
273
- node: binding.path.node,
274
- params,
275
- fnArguments,
276
- scope,
277
- filename,
278
- babelConfig,
279
- file,
280
- resolver
281
- });
282
- }
283
- throw new Error(
284
- `Could not find binding for node "${node.name}" in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`
285
- );
286
- }
287
- function resolveCallExpression({
288
- node,
289
- scope,
290
- filename,
291
- file,
292
- resolver,
293
- babelConfig,
294
- params
295
- }) {
296
- const { callee } = node;
297
- return resolveExpression({
298
- node: callee,
299
- scope,
300
- filename,
301
- file,
302
- resolver,
303
- babelConfig,
304
- params,
305
- fnArguments: node.arguments
306
- });
307
- }
308
- function resolveImportSpecifier({
309
- node,
310
- file,
311
- filename,
312
- fnArguments,
313
- resolver,
314
- babelConfig
315
- }) {
316
- let importDeclaration;
317
- if (traverse(file, {
318
- ImportDeclaration(n) {
319
- if (t.isImportDeclaration(n.node))
320
- for (const specifier of n.node.specifiers) {
321
- if (t.isImportDefaultSpecifier(specifier) && specifier.local.loc?.identifierName === node.local.name) {
322
- importDeclaration = n.node;
323
- break;
324
- }
325
- specifier.local.name === node.local.name && (importDeclaration = n.node);
326
- }
327
- }
328
- }), !importDeclaration)
329
- throw new Error(`Could not find import declaration for ${node.local.name}`);
330
- const importName = node.local.name, importFileName = importDeclaration.source.value, importPath = importFileName.startsWith("./") || importFileName.startsWith("../") ? path.resolve(path.dirname(filename), importFileName) : importFileName, resolvedFile = resolver(importPath), source = fs.readFileSync(resolvedFile), tree = parseSourceFile(source.toString(), resolvedFile, babelConfig);
331
- let newScope;
332
- if (traverse(tree, {
333
- Program(p) {
334
- newScope = p.scope;
335
- }
336
- }), !newScope)
337
- throw new Error(`Could not find scope for ${filename}`);
338
- const binding = newScope.getBinding(importName);
339
- if (binding)
340
- return resolveExpression({
341
- node: binding.path.node,
342
- file: tree,
343
- scope: newScope,
344
- fnArguments,
345
- babelConfig,
346
- filename: resolvedFile,
347
- resolver
348
- });
349
- let namedExport, newImportName;
350
- if (traverse(tree, {
351
- ExportDeclaration(p) {
352
- if (p.node.type === "ExportNamedDeclaration")
353
- for (const specifier of p.node.specifiers)
354
- specifier.type === "ExportSpecifier" && specifier.exported.type === "Identifier" && specifier.exported.name === importName && (namedExport = p.node, newImportName = specifier.exported.name);
355
- }
356
- }), namedExport && newImportName)
357
- return resolveExportSpecifier({
358
- node: namedExport,
359
- importName: newImportName,
360
- filename: resolvedFile,
361
- fnArguments,
362
- resolver,
363
- babelConfig
364
- });
365
- let result;
366
- if (traverse(tree, {
367
- ExportDeclaration(p) {
368
- if (p.node.type === "ExportAllDeclaration")
369
- try {
370
- result = resolveExportSpecifier({
371
- node: p.node,
372
- importName,
373
- filename: resolvedFile,
374
- fnArguments,
375
- resolver,
376
- babelConfig
377
- });
378
- } catch (e) {
379
- if (e.cause !== `noBinding:${importName}`) throw e;
380
- }
381
- }
382
- }), result) return result;
383
- throw new Error(`Could not find binding for import "${importName}" in ${importFileName}`);
384
- }
385
- function resolveExportSpecifier({
386
- node,
387
- importName,
388
- filename,
389
- fnArguments,
390
- babelConfig,
391
- resolver
392
- }) {
393
- if (!node.source)
394
- throw new Error(`Could not find source for export "${importName}" in ${filename}`);
395
- const importFileName = node.source.value, importPath = path.resolve(path.dirname(filename), importFileName), resolvedFile = resolver(importPath), source = fs.readFileSync(resolvedFile), tree = parseSourceFile(source.toString(), resolvedFile, babelConfig);
396
- let newScope;
397
- if (traverse(tree, {
398
- Program(p) {
399
- newScope = p.scope;
400
- }
401
- }), !newScope)
402
- throw new Error(`Could not find scope for ${filename}`);
403
- const binding = newScope.getBinding(importName);
404
- if (binding)
405
- return resolveExpression({
406
- node: binding.path.node,
407
- file: tree,
408
- scope: newScope,
409
- filename: resolvedFile,
410
- babelConfig,
411
- resolver,
412
- fnArguments
413
- });
414
- throw new Error(`Could not find binding for export "${importName}" in ${importFileName}`, {
415
- cause: `noBinding:${importName}`
416
- });
417
- }
418
- const require$1 = createRequire(__filename), groqTagName = "groq", defineQueryFunctionName = "defineQuery", groqModuleName = "groq", nextSanityModuleName = "next-sanity", ignoreValue = "@sanity-typegen-ignore";
419
- function findQueriesInSource(source, filename, babelConfig = getBabelConfig(), resolver = require$1.resolve) {
420
- const queries = [], file = parseSourceFile(source, filename, babelConfig);
421
- return traverse$1(file, {
422
- // Look for variable declarations, e.g. `const myQuery = groq`... and extract the query.
423
- // The variable name is used as the name of the query result type
424
- VariableDeclarator(path2) {
425
- const { node, scope } = path2, init = node.init, isGroqTemplateTag = t.isTaggedTemplateExpression(init) && t.isIdentifier(init.tag) && init.tag.name === groqTagName, isDefineQueryCall = t.isCallExpression(init) && (isImportFrom(groqModuleName, defineQueryFunctionName, scope, init.callee) || isImportFrom(nextSanityModuleName, defineQueryFunctionName, scope, init.callee));
426
- if (t.isIdentifier(node.id) && (isGroqTemplateTag || isDefineQueryCall)) {
427
- if (declarationLeadingCommentContains(path2, ignoreValue))
428
- return;
429
- const queryName = `${node.id.name}`, queryResult = resolveExpression({
430
- node: init,
431
- file,
432
- scope,
433
- babelConfig,
434
- filename,
435
- resolver
436
- }), location = node.loc ? {
437
- start: {
438
- ...node.loc?.start
439
- },
440
- end: {
441
- ...node.loc?.end
442
- }
443
- } : {};
444
- queries.push({ name: queryName, result: queryResult, location });
445
- }
446
- }
447
- }), queries;
448
- }
449
- function declarationLeadingCommentContains(path2, comment) {
450
- const variableDeclaration = path2.find((node) => node.isVariableDeclaration());
451
- return variableDeclaration ? !!(variableDeclaration.node.leadingComments?.find(
452
- (commentItem) => commentItem.value.trim() === comment
453
- ) || variableDeclaration.parent.leadingComments?.find(
454
- (commentItem) => commentItem.value.trim() === comment
455
- )) : !1;
456
- }
457
- function isImportFrom(moduleName, importName, scope, node) {
458
- if (t.isIdentifier(node)) {
459
- const binding = scope.getBinding(node.name);
460
- if (!binding)
461
- return !1;
462
- const { path: path2 } = binding;
463
- if (t.isImportSpecifier(path2.node))
464
- return path2.node.importKind === "value" && path2.parentPath && t.isImportDeclaration(path2.parentPath.node) && path2.parentPath.node.source.value === moduleName && t.isIdentifier(path2.node.imported) && path2.node.imported.name === importName;
465
- if (t.isVariableDeclarator(path2.node)) {
466
- const { init } = path2.node;
467
- return t.isCallExpression(init) && t.isIdentifier(init.callee) && init.callee.name === "require" && t.isStringLiteral(init.arguments[0]) && init.arguments[0].value === moduleName;
468
- }
469
- }
470
- if (t.isMemberExpression(node)) {
471
- const { object, property } = node;
472
- if (!t.isIdentifier(object))
473
- return !1;
474
- const binding = scope.getBinding(object.name);
475
- if (!binding)
476
- return !1;
477
- const { path: path2 } = binding;
478
- return t.isIdentifier(object) && t.isIdentifier(property) && property.name === importName && t.isImportNamespaceSpecifier(path2.node) && path2.parentPath && t.isImportDeclaration(path2.parentPath.node) && path2.parentPath.node.source.value === moduleName;
479
- }
480
- return !1;
481
- }
482
- const debug$1 = createDebug("sanity:codegen:moduleResolver");
483
- function getResolver(cwd) {
484
- const tsConfig = loadConfig(cwd);
485
- if (tsConfig.resultType === "failed")
486
- return debug$1("Could not load tsconfig, using default resolver: %s", tsConfig.message), require.resolve;
487
- const matchPath = createMatchPath(
488
- tsConfig.absoluteBaseUrl,
489
- tsConfig.paths,
490
- tsConfig.mainFields,
491
- tsConfig.addMatchAll
492
- ), resolve2 = function(request, options) {
493
- const found = matchPath(request);
494
- return found !== void 0 ? require.resolve(found, options) : require.resolve(request, options);
495
- };
496
- return resolve2.paths = (request) => require.resolve.paths(request), resolve2;
497
- }
498
- const debug = createDebug("sanity:codegen:findQueries:debug");
499
- async function* findQueriesInPath({
500
- path: path2,
501
- babelOptions = getBabelConfig(),
502
- resolver = getResolver()
503
- }) {
504
- const queryNames = /* @__PURE__ */ new Set();
505
- debug(`Globing ${path2}`);
506
- const files = glob.sync(path2, {
507
- absolute: !1,
508
- ignore: ["**/node_modules/**"],
509
- // we never want to look in node_modules
510
- onlyFiles: !0
511
- }).sort();
512
- for (const filename of files)
513
- if (typeof filename == "string") {
514
- debug(`Found file "${filename}"`);
515
- try {
516
- const source = await fs$1.readFile(filename, "utf8"), queries = findQueriesInSource(source, filename, babelOptions, resolver);
517
- for (const query of queries) {
518
- if (queryNames.has(query.name))
519
- throw new Error(
520
- `Duplicate query name found: "${query.name}". Query names must be unique across all files.`
521
- );
522
- queryNames.add(query.name);
523
- }
524
- yield { type: "queries", filename, queries };
525
- } catch (error) {
526
- debug(`Error in file "${filename}"`, error), yield { type: "error", error, filename };
527
- }
528
- }
529
- }
530
- function registerBabel(babelOptions) {
531
- const options = babelOptions || getBabelConfig();
532
- register({ ...options, extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"] });
533
- }
534
- const REFERENCE_SYMBOL_NAME = "internalGroqTypeReferenceTo", ALL_SCHEMA_TYPES = "AllSanitySchemaTypes";
535
- class TypeGenerator {
536
- // Simple set to keep track of generated type names, to avoid conflicts
537
- generatedTypeName = /* @__PURE__ */ new Set();
538
- // Map between type names and their generated type names, used to resolve the correct generated type name
539
- typeNameMap = /* @__PURE__ */ new Map();
540
- // Map between type nodes and their generated type names, used for query mapping
541
- typeNodeNameMap = /* @__PURE__ */ new Map();
542
- schema;
543
- constructor(schema) {
544
- this.schema = schema, this.schema.forEach((s) => {
545
- this.getTypeName(s.name, s);
546
- });
547
- }
548
- /**
549
- * Generate TypeScript types for the given schema
550
- * @returns string
551
- * @internal
552
- * @beta
553
- */
554
- generateSchemaTypes() {
555
- const typeDeclarations = [], schemaNames = /* @__PURE__ */ new Set();
556
- return this.schema.forEach((schema) => {
557
- const typeLiteral = this.getTypeNodeType(schema), schemaName = this.typeNodeNameMap.get(schema);
558
- if (!schemaName)
559
- throw new Error(`Schema name not found for schema ${schema.name}`);
560
- schemaNames.add(schemaName);
561
- const typeAlias = t.tsTypeAliasDeclaration(t.identifier(schemaName), null, typeLiteral);
562
- typeDeclarations.push(t.exportNamedDeclaration(typeAlias));
563
- }), typeDeclarations.push(
564
- t.exportNamedDeclaration(
565
- t.tsTypeAliasDeclaration(
566
- t.identifier(this.getTypeName(ALL_SCHEMA_TYPES)),
567
- null,
568
- t.tsUnionType(
569
- [...schemaNames].map((typeName) => t.tsTypeReference(t.identifier(typeName)))
570
- )
571
- )
572
- )
573
- ), typeDeclarations.map((decl) => new CodeGenerator(decl).generate().code).join(`
574
-
575
- `);
576
- }
577
- /**
578
- * Takes a identifier and a type node and generates a type alias for the type node.
579
- * @param identifierName - The name of the type to generated
580
- * @param typeNode - The type node to generate the type for
581
- * @returns
582
- * @internal
583
- * @beta
584
- */
585
- generateTypeNodeTypes(identifierName, typeNode) {
586
- const type = this.getTypeNodeType(typeNode), typeName = this.getTypeName(identifierName, typeNode), typeAlias = t.tsTypeAliasDeclaration(t.identifier(typeName), null, type);
587
- return new CodeGenerator(t.exportNamedDeclaration(typeAlias)).generate().code.trim();
588
- }
589
- static generateKnownTypes() {
590
- const typeOperator = t.tsTypeOperator(t.tsSymbolKeyword(), "unique"), identifier = t.identifier(REFERENCE_SYMBOL_NAME);
591
- identifier.typeAnnotation = t.tsTypeAnnotation(typeOperator);
592
- const decleration = t.variableDeclaration("const", [t.variableDeclarator(identifier)]);
593
- return decleration.declare = !0, new CodeGenerator(t.exportNamedDeclaration(decleration)).generate().code.trim();
594
- }
595
- /**
596
- * Takes a list of queries from the codebase and generates a type declaration
597
- * for SanityClient to consume.
598
- *
599
- * Note: only types that have previously been generated with `generateTypeNodeTypes`
600
- * will be included in the query map.
601
- *
602
- * @param queries - A list of queries to generate a type declaration for
603
- * @returns
604
- * @internal
605
- * @beta
606
- */
607
- generateQueryMap(queries) {
608
- const typesByQuerystring = {};
609
- for (const query of queries) {
610
- const name = this.typeNodeNameMap.get(query.typeNode);
611
- name && (typesByQuerystring[query.query] ??= [], typesByQuerystring[query.query].push(name));
612
- }
613
- const queryReturnInterface = t.tsInterfaceDeclaration(
614
- t.identifier("SanityQueries"),
615
- null,
616
- [],
617
- t.tsInterfaceBody(
618
- Object.entries(typesByQuerystring).map(([query, types]) => t.tsPropertySignature(
619
- t.stringLiteral(query),
620
- t.tsTypeAnnotation(
621
- t.tsUnionType(types.map((type) => t.tsTypeReference(t.identifier(type))))
622
- )
623
- ))
624
- )
625
- ), declareModule = t.declareModule(
626
- t.stringLiteral("@sanity/client"),
627
- t.blockStatement([queryReturnInterface])
628
- ), clientImport = t.importDeclaration([], t.stringLiteral("@sanity/client"));
629
- return new CodeGenerator(t.program([clientImport, declareModule])).generate().code.trim();
630
- }
631
- /**
632
- * Since we are sanitizing identifiers we migt end up with collisions. Ie there might be a type mux.video and muxVideo, both these
633
- * types would be sanityized into MuxVideo. To avoid this we keep track of the generated type names and add a index to the name.
634
- * When we reference a type we also keep track of the original name so we can reference the correct type later.
635
- */
636
- getTypeName(name, typeNode) {
637
- const desiredName = uppercaseFirstLetter(sanitizeIdentifier(name));
638
- let generatedName = desiredName, i = 2;
639
- for (; this.generatedTypeName.has(generatedName); )
640
- generatedName = `${desiredName}_${i++}`;
641
- return this.generatedTypeName.add(generatedName), this.typeNameMap.set(name, generatedName), typeNode && this.typeNodeNameMap.set(typeNode, generatedName), generatedName;
642
- }
643
- getTypeNodeType(typeNode) {
644
- switch (typeNode.type) {
645
- case "string":
646
- return typeNode.value !== void 0 ? t.tsLiteralType(t.stringLiteral(typeNode.value)) : t.tsStringKeyword();
647
- case "number":
648
- return typeNode.value !== void 0 ? t.tsLiteralType(t.numericLiteral(typeNode.value)) : t.tsNumberKeyword();
649
- case "boolean":
650
- return typeNode.value !== void 0 ? t.tsLiteralType(t.booleanLiteral(typeNode.value)) : t.tsBooleanKeyword();
651
- case "unknown":
652
- return t.tsUnknownKeyword();
653
- case "document":
654
- return this.generateDocumentType(typeNode);
655
- case "type":
656
- return this.getTypeNodeType(typeNode.value);
657
- case "array":
658
- return this.generateArrayTsType(typeNode);
659
- case "object":
660
- return this.generateObjectTsType(typeNode);
661
- case "union":
662
- return this.generateUnionTsType(typeNode);
663
- case "inline":
664
- return this.generateInlineTsType(typeNode);
665
- case "null":
666
- return t.tsNullKeyword();
667
- default:
668
- throw new Error(`Type "${typeNode.type}" not found in schema`);
669
- }
670
- }
671
- // Helper function used to generate TS types for array type nodes.
672
- generateArrayTsType(typeNode) {
673
- const typeNodes = this.getTypeNodeType(typeNode.of);
674
- return t.tsTypeReference(
675
- t.identifier("Array"),
676
- t.tsTypeParameterInstantiation([typeNodes])
677
- );
678
- }
679
- // Helper function used to generate TS types for object properties.
680
- generateObjectProperty(key, attribute) {
681
- const type = this.getTypeNodeType(attribute.value), propertySignature = t.tsPropertySignature(
682
- t.identifier(sanitizeIdentifier(key)),
683
- t.tsTypeAnnotation(type)
684
- );
685
- return propertySignature.optional = attribute.optional, propertySignature;
686
- }
687
- // Helper function used to generate TS types for object type nodes.
688
- generateObjectTsType(typeNode) {
689
- const props = [];
690
- Object.entries(typeNode.attributes).forEach(([key, attribute]) => {
691
- props.push(this.generateObjectProperty(key, attribute));
692
- });
693
- const rest = typeNode.rest;
694
- if (rest !== void 0)
695
- switch (rest.type) {
696
- case "unknown":
697
- return t.tsUnknownKeyword();
698
- case "object": {
699
- Object.entries(rest.attributes).forEach(([key, attribute]) => {
700
- props.push(this.generateObjectProperty(key, attribute));
701
- });
702
- break;
703
- }
704
- case "inline": {
705
- const resolved = this.generateInlineTsType(rest);
706
- return t.isTSUnknownKeyword(resolved) ? resolved : t.tsIntersectionType([t.tsTypeLiteral(props), resolved]);
707
- }
708
- default:
709
- throw new Error(`Type "${rest.type}" not found in schema`);
710
- }
711
- if (typeNode.dereferencesTo !== void 0) {
712
- const derefType = t.tsPropertySignature(
713
- t.identifier(REFERENCE_SYMBOL_NAME),
714
- t.tsTypeAnnotation(t.tsLiteralType(t.stringLiteral(typeNode.dereferencesTo)))
715
- );
716
- derefType.computed = !0, derefType.optional = !0, props.push(derefType);
717
- }
718
- return t.tsTypeLiteral(props);
719
- }
720
- generateInlineTsType(typeNode) {
721
- const referencedTypeNode = this.schema.find((schema) => schema.name === typeNode.name);
722
- if (referencedTypeNode === void 0) {
723
- const generatedName2 = this.typeNameMap.get(typeNode.name);
724
- if (generatedName2)
725
- return t.tsTypeReference(t.identifier(generatedName2));
726
- const missing = t.tsUnknownKeyword();
727
- return missing.trailingComments = [
728
- {
729
- type: "CommentLine",
730
- value: ` Unable to locate the referenced type "${typeNode.name}" in schema`
731
- }
732
- ], missing;
733
- }
734
- const generatedName = this.typeNameMap.get(referencedTypeNode.name);
735
- return generatedName ? t.tsTypeReference(t.identifier(generatedName)) : t.tsUnknownKeyword();
736
- }
737
- // Helper function used to generate TS types for union type nodes.
738
- generateUnionTsType(typeNode) {
739
- if (typeNode.of.length === 0)
740
- return t.tsNeverKeyword();
741
- if (typeNode.of.length === 1)
742
- return this.getTypeNodeType(typeNode.of[0]);
743
- const typeNodes = typeNode.of.map((node) => this.getTypeNodeType(node));
744
- return t.tsUnionType(typeNodes);
745
- }
746
- // Helper function used to generate TS types for document type nodes.
747
- generateDocumentType(document) {
748
- const props = Object.entries(document.attributes).map(
749
- ([key, node]) => this.generateObjectProperty(key, node)
750
- );
751
- return t.tsTypeLiteral(props);
752
- }
753
- }
754
- function uppercaseFirstLetter(input) {
755
- return input.charAt(0).toUpperCase() + input.slice(1);
756
- }
757
- function sanitizeIdentifier(input) {
758
- return `${input.replace(/^\d/, "_").replace(/[^$\w]+(.)/g, (_, char) => char.toUpperCase())}`;
759
- }
760
- export {
761
- TypeGenerator,
762
- configDefinition,
763
- findQueriesInPath,
764
- findQueriesInSource,
765
- getResolver,
766
- readConfig,
767
- readSchema,
768
- registerBabel,
769
- safeParseQuery
770
- };
771
- //# sourceMappingURL=index.mjs.map