@sanity/codegen 5.7.0-next.8 → 5.7.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 (59) hide show
  1. package/bin/run.js +31 -0
  2. package/dist/_exports/index.js +11 -0
  3. package/dist/_exports/index.js.map +1 -0
  4. package/dist/actions/generatedFileWarning.js +15 -0
  5. package/dist/actions/generatedFileWarning.js.map +1 -0
  6. package/dist/actions/typegenGenerate.worker.js +54 -0
  7. package/dist/actions/typegenGenerate.worker.js.map +1 -0
  8. package/dist/actions/types.js +3 -0
  9. package/dist/actions/types.js.map +1 -0
  10. package/dist/casing.js +27 -0
  11. package/dist/casing.js.map +1 -0
  12. package/dist/commands/typegen/generate.js +237 -0
  13. package/dist/commands/typegen/generate.js.map +1 -0
  14. package/dist/getBabelConfig.js +37 -0
  15. package/dist/getBabelConfig.js.map +1 -0
  16. package/dist/index.d.ts +459 -0
  17. package/dist/readConfig.js +38 -0
  18. package/dist/readConfig.js.map +1 -0
  19. package/dist/readSchema.js +14 -0
  20. package/dist/readSchema.js.map +1 -0
  21. package/dist/safeParseQuery.js +37 -0
  22. package/dist/safeParseQuery.js.map +1 -0
  23. package/dist/typeUtils.js +37 -0
  24. package/dist/typeUtils.js.map +1 -0
  25. package/dist/typescript/constants.js +12 -0
  26. package/dist/typescript/constants.js.map +1 -0
  27. package/dist/typescript/expressionResolvers.js +356 -0
  28. package/dist/typescript/expressionResolvers.js.map +1 -0
  29. package/dist/typescript/findQueriesInPath.js +69 -0
  30. package/dist/typescript/findQueriesInPath.js.map +1 -0
  31. package/dist/typescript/findQueriesInSource.js +175 -0
  32. package/dist/typescript/findQueriesInSource.js.map +1 -0
  33. package/dist/typescript/helpers.js +86 -0
  34. package/dist/typescript/helpers.js.map +1 -0
  35. package/dist/typescript/moduleResolver.js +33 -0
  36. package/dist/typescript/moduleResolver.js.map +1 -0
  37. package/dist/typescript/parseSource.js +75 -0
  38. package/dist/typescript/parseSource.js.map +1 -0
  39. package/dist/typescript/registerBabel.js +23 -0
  40. package/dist/typescript/registerBabel.js.map +1 -0
  41. package/dist/typescript/schemaTypeGenerator.js +323 -0
  42. package/dist/typescript/schemaTypeGenerator.js.map +1 -0
  43. package/dist/typescript/typeGenerator.js +240 -0
  44. package/dist/typescript/typeGenerator.js.map +1 -0
  45. package/dist/typescript/types.js +31 -0
  46. package/dist/typescript/types.js.map +1 -0
  47. package/dist/utils/count.js +6 -0
  48. package/dist/utils/count.js.map +1 -0
  49. package/dist/utils/formatPath.js +8 -0
  50. package/dist/utils/formatPath.js.map +1 -0
  51. package/dist/utils/getMessage.js +3 -0
  52. package/dist/utils/getMessage.js.map +1 -0
  53. package/dist/utils/percent.js +8 -0
  54. package/dist/utils/percent.js.map +1 -0
  55. package/oclif.manifest.json +39 -0
  56. package/package.json +49 -23
  57. package/lib/index.d.ts +0 -433
  58. package/lib/index.js +0 -1011
  59. package/lib/index.js.map +0 -1
@@ -0,0 +1,356 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { traverse } from '@babel/core';
4
+ import { Scope } from '@babel/traverse';
5
+ import * as babelTypes from '@babel/types';
6
+ import createDebug from 'debug';
7
+ import { formatPath } from '../utils/formatPath.js';
8
+ import { parseSourceFile } from './parseSource.js';
9
+ const debug = createDebug('sanity:codegen:findQueries:debug');
10
+ const TAGGED_TEMPLATE_ALLOW_LIST = new Set([
11
+ 'groq'
12
+ ]);
13
+ const FUNCTION_WRAPPER_ALLOW_LIST = new Set([
14
+ 'defineQuery'
15
+ ]);
16
+ /**
17
+ * resolveExpression takes a node and returns the resolved value of the expression.
18
+ * @beta
19
+ * @internal
20
+ */ export function resolveExpression({ babelConfig, file, filename, fnArguments = [], node, params = [], resolver, scope }) {
21
+ debug(`Resolving node ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`);
22
+ if (babelTypes.isTaggedTemplateExpression(node) && babelTypes.isIdentifier(node.tag) && TAGGED_TEMPLATE_ALLOW_LIST.has(node.tag.name)) {
23
+ return resolveExpression({
24
+ babelConfig,
25
+ file,
26
+ filename,
27
+ fnArguments,
28
+ node: node.quasi,
29
+ params,
30
+ resolver,
31
+ scope
32
+ });
33
+ }
34
+ if (babelTypes.isTemplateLiteral(node)) {
35
+ const resolvedExpressions = node.expressions.map((expression)=>resolveExpression({
36
+ babelConfig,
37
+ file,
38
+ filename,
39
+ fnArguments,
40
+ node: expression,
41
+ params,
42
+ resolver,
43
+ scope
44
+ }));
45
+ return node.quasis.map((quasi, idx)=>{
46
+ return (quasi.value.cooked || '') + (resolvedExpressions[idx] || '');
47
+ }).join('');
48
+ }
49
+ if (babelTypes.isLiteral(node)) {
50
+ if (node.type === 'NullLiteral' || node.type === 'RegExpLiteral') {
51
+ throw new Error(`Unsupported literal type: ${node.type}`);
52
+ }
53
+ return node.value.toString();
54
+ }
55
+ if (babelTypes.isIdentifier(node)) {
56
+ return resolveIdentifier({
57
+ babelConfig,
58
+ file,
59
+ filename,
60
+ fnArguments,
61
+ node,
62
+ params,
63
+ resolver,
64
+ scope
65
+ });
66
+ }
67
+ if (babelTypes.isVariableDeclarator(node)) {
68
+ const init = node.init ?? (babelTypes.isAssignmentPattern(node.id) && node.id.right);
69
+ if (!init) {
70
+ throw new Error(`Unsupported variable declarator`);
71
+ }
72
+ return resolveExpression({
73
+ babelConfig,
74
+ file,
75
+ filename,
76
+ fnArguments,
77
+ node: init,
78
+ resolver,
79
+ scope
80
+ });
81
+ }
82
+ if (babelTypes.isCallExpression(node) && babelTypes.isIdentifier(node.callee) && FUNCTION_WRAPPER_ALLOW_LIST.has(node.callee.name)) {
83
+ return resolveExpression({
84
+ babelConfig,
85
+ file,
86
+ filename,
87
+ node: node.arguments[0],
88
+ params,
89
+ resolver,
90
+ scope
91
+ });
92
+ }
93
+ if (babelTypes.isCallExpression(node)) {
94
+ return resolveCallExpression({
95
+ babelConfig,
96
+ file,
97
+ filename,
98
+ fnArguments,
99
+ node,
100
+ params,
101
+ resolver,
102
+ scope
103
+ });
104
+ }
105
+ if (babelTypes.isArrowFunctionExpression(node) || babelTypes.isFunctionDeclaration(node) || babelTypes.isFunctionExpression(node)) {
106
+ const newScope = new Scope(scope.path, scope);
107
+ for (const [i, param] of params.entries()){
108
+ newScope.push({
109
+ id: param,
110
+ init: fnArguments[i]
111
+ });
112
+ }
113
+ return resolveExpression({
114
+ babelConfig,
115
+ file,
116
+ filename,
117
+ fnArguments,
118
+ node: node.body,
119
+ params: node.params,
120
+ resolver,
121
+ scope: newScope
122
+ });
123
+ }
124
+ if (babelTypes.isNewExpression(node)) {
125
+ return resolveExpression({
126
+ babelConfig,
127
+ file,
128
+ filename,
129
+ node: node.callee,
130
+ resolver,
131
+ scope
132
+ });
133
+ }
134
+ if (babelTypes.isImportDefaultSpecifier(node) || babelTypes.isImportSpecifier(node)) {
135
+ return resolveImportSpecifier({
136
+ babelConfig,
137
+ file,
138
+ filename,
139
+ fnArguments,
140
+ node,
141
+ resolver,
142
+ scope
143
+ });
144
+ }
145
+ if (babelTypes.isAssignmentPattern(node)) {
146
+ return resolveExpression({
147
+ babelConfig,
148
+ file,
149
+ filename,
150
+ fnArguments,
151
+ node: node.right,
152
+ params,
153
+ resolver,
154
+ scope
155
+ });
156
+ }
157
+ // Handle TypeScript type assertions (e.g., `'foo' as string`)
158
+ if (babelTypes.isTSAsExpression(node)) {
159
+ return resolveExpression({
160
+ babelConfig,
161
+ file,
162
+ filename,
163
+ fnArguments,
164
+ node: node.expression,
165
+ params,
166
+ resolver,
167
+ scope
168
+ });
169
+ }
170
+ throw new Error(`Unsupported expression type: ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`);
171
+ }
172
+ function resolveIdentifier({ babelConfig, file, filename, fnArguments, node, params, resolver, scope }) {
173
+ const paramIndex = params.findIndex((param)=>babelTypes.isIdentifier(param) && node.name === param.name || babelTypes.isAssignmentPattern(param) && babelTypes.isIdentifier(param.left) && node.name === param.left.name);
174
+ let argument = fnArguments[paramIndex];
175
+ if (!argument && paramIndex !== -1 && babelTypes.isAssignmentPattern(params[paramIndex])) {
176
+ argument = params[paramIndex].right;
177
+ }
178
+ if (argument && babelTypes.isLiteral(argument)) {
179
+ return resolveExpression({
180
+ babelConfig,
181
+ file,
182
+ filename,
183
+ fnArguments,
184
+ node: argument,
185
+ params,
186
+ resolver,
187
+ scope
188
+ });
189
+ }
190
+ const binding = scope.getBinding(node.name);
191
+ if (binding) {
192
+ if (babelTypes.isIdentifier(binding.path.node)) {
193
+ const isSame = binding.path.node.name === node.name;
194
+ if (isSame) {
195
+ throw new Error(`Could not resolve same identifier "${node.name}" in "${filename}:${node.loc?.start.line}:${node.loc?.start.column}"`);
196
+ }
197
+ }
198
+ return resolveExpression({
199
+ babelConfig,
200
+ file,
201
+ filename,
202
+ fnArguments,
203
+ node: binding.path.node,
204
+ params,
205
+ resolver,
206
+ scope
207
+ });
208
+ }
209
+ throw new Error(`Could not find binding for node "${node.name}" in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`);
210
+ }
211
+ function resolveCallExpression({ babelConfig, file, filename, node, params, resolver, scope }) {
212
+ const { callee } = node;
213
+ return resolveExpression({
214
+ babelConfig,
215
+ file,
216
+ filename,
217
+ fnArguments: node.arguments,
218
+ node: callee,
219
+ params,
220
+ resolver,
221
+ scope
222
+ });
223
+ }
224
+ function resolveImportSpecifier({ babelConfig, file, filename, fnArguments, node, resolver }) {
225
+ let importDeclaration;
226
+ traverse(file, {
227
+ ImportDeclaration (n) {
228
+ if (!babelTypes.isImportDeclaration(n.node)) {
229
+ return;
230
+ }
231
+ for (const specifier of n.node.specifiers){
232
+ if (babelTypes.isImportDefaultSpecifier(specifier) && specifier.local.loc?.identifierName === node.local.name) {
233
+ importDeclaration = n.node;
234
+ break;
235
+ }
236
+ if (specifier.local.name === node.local.name) {
237
+ importDeclaration = n.node;
238
+ }
239
+ }
240
+ }
241
+ });
242
+ if (!importDeclaration) {
243
+ throw new Error(`Could not find import declaration for ${node.local.name}`);
244
+ }
245
+ const importName = node.local.name // the name of the variable to import
246
+ ;
247
+ const importFileName = importDeclaration.source.value // the file to import from
248
+ ;
249
+ const importPath = importFileName.startsWith('./') || importFileName.startsWith('../') ? path.resolve(path.dirname(filename), importFileName) : importFileName;
250
+ const resolvedFile = resolver(formatPath(importPath));
251
+ const source = fs.readFileSync(resolvedFile);
252
+ const tree = parseSourceFile(source.toString(), resolvedFile, babelConfig);
253
+ let newScope;
254
+ traverse(tree, {
255
+ Program (p) {
256
+ newScope = p.scope;
257
+ }
258
+ });
259
+ if (!newScope) {
260
+ throw new Error(`Could not find scope for ${filename}`);
261
+ }
262
+ const binding = newScope.getBinding(importName);
263
+ if (binding) {
264
+ return resolveExpression({
265
+ babelConfig,
266
+ file: tree,
267
+ filename: resolvedFile,
268
+ fnArguments,
269
+ node: binding.path.node,
270
+ resolver,
271
+ scope: newScope
272
+ });
273
+ }
274
+ // It's not a global binding, but it might be a named export
275
+ let namedExport;
276
+ let newImportName;
277
+ traverse(tree, {
278
+ ExportDeclaration (p) {
279
+ if (p.node.type === 'ExportNamedDeclaration') {
280
+ for (const specifier of p.node.specifiers){
281
+ if (specifier.type === 'ExportSpecifier' && specifier.exported.type === 'Identifier' && specifier.exported.name === importName) {
282
+ namedExport = p.node;
283
+ newImportName = specifier.exported.name;
284
+ }
285
+ }
286
+ }
287
+ }
288
+ });
289
+ if (namedExport && newImportName) {
290
+ return resolveExportSpecifier({
291
+ babelConfig,
292
+ filename: resolvedFile,
293
+ fnArguments,
294
+ importName: newImportName,
295
+ node: namedExport,
296
+ resolver
297
+ });
298
+ }
299
+ let result;
300
+ traverse(tree, {
301
+ ExportDeclaration (p) {
302
+ if (p.node.type === 'ExportAllDeclaration') {
303
+ try {
304
+ result = resolveExportSpecifier({
305
+ babelConfig,
306
+ filename: resolvedFile,
307
+ fnArguments,
308
+ importName,
309
+ node: p.node,
310
+ resolver
311
+ });
312
+ } catch (e) {
313
+ if (e.cause !== `noBinding:${importName}`) throw e;
314
+ }
315
+ }
316
+ }
317
+ });
318
+ if (result) return result;
319
+ throw new Error(`Could not find binding for import "${importName}" in ${importFileName}`);
320
+ }
321
+ function resolveExportSpecifier({ babelConfig, filename, fnArguments, importName, node, resolver }) {
322
+ if (!node.source) {
323
+ throw new Error(`Could not find source for export "${importName}" in ${filename}`);
324
+ }
325
+ const importFileName = node.source.value;
326
+ const importPath = path.resolve(path.dirname(filename), importFileName);
327
+ const resolvedFile = resolver(formatPath(importPath));
328
+ const source = fs.readFileSync(resolvedFile);
329
+ const tree = parseSourceFile(source.toString(), resolvedFile, babelConfig);
330
+ let newScope;
331
+ traverse(tree, {
332
+ Program (p) {
333
+ newScope = p.scope;
334
+ }
335
+ });
336
+ if (!newScope) {
337
+ throw new Error(`Could not find scope for ${filename}`);
338
+ }
339
+ const binding = newScope.getBinding(importName);
340
+ if (binding) {
341
+ return resolveExpression({
342
+ babelConfig,
343
+ file: tree,
344
+ filename: resolvedFile,
345
+ fnArguments,
346
+ node: binding.path.node,
347
+ resolver,
348
+ scope: newScope
349
+ });
350
+ }
351
+ throw new Error(`Could not find binding for export "${importName}" in ${importFileName}`, {
352
+ cause: `noBinding:${importName}`
353
+ });
354
+ }
355
+
356
+ //# sourceMappingURL=expressionResolvers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/typescript/expressionResolvers.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\n\nimport {type TransformOptions, traverse} from '@babel/core'\nimport {Scope} from '@babel/traverse'\nimport * as babelTypes from '@babel/types'\nimport createDebug from 'debug'\n\nimport {formatPath} from '../utils/formatPath.js'\nimport {parseSourceFile} from './parseSource.js'\n\nconst debug = createDebug('sanity:codegen:findQueries:debug')\n\ntype resolveExpressionReturnType = string\n\nconst TAGGED_TEMPLATE_ALLOW_LIST = new Set(['groq'])\nconst FUNCTION_WRAPPER_ALLOW_LIST = new Set(['defineQuery'])\n\n/**\n * resolveExpression takes a node and returns the resolved value of the expression.\n * @beta\n * @internal\n */\nexport function resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments = [],\n node,\n params = [],\n resolver,\n scope,\n}: {\n babelConfig: TransformOptions\n file: babelTypes.File\n filename: string\n fnArguments?: babelTypes.Node[]\n node: babelTypes.Node\n params?: babelTypes.Node[]\n resolver: NodeJS.RequireResolve\n scope: Scope\n}): resolveExpressionReturnType {\n debug(\n `Resolving node ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`,\n )\n if (\n babelTypes.isTaggedTemplateExpression(node) &&\n babelTypes.isIdentifier(node.tag) &&\n TAGGED_TEMPLATE_ALLOW_LIST.has(node.tag.name)\n ) {\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: node.quasi,\n params,\n resolver,\n scope,\n })\n }\n\n if (babelTypes.isTemplateLiteral(node)) {\n const resolvedExpressions = node.expressions.map((expression) =>\n resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: expression,\n params,\n resolver,\n scope,\n }),\n )\n return node.quasis\n .map((quasi, idx) => {\n return (quasi.value.cooked || '') + (resolvedExpressions[idx] || '')\n })\n .join('')\n }\n\n if (babelTypes.isLiteral(node)) {\n if (node.type === 'NullLiteral' || node.type === 'RegExpLiteral') {\n throw new Error(`Unsupported literal type: ${node.type}`)\n }\n\n return node.value.toString()\n }\n\n if (babelTypes.isIdentifier(node)) {\n return resolveIdentifier({\n babelConfig,\n file,\n filename,\n fnArguments,\n node,\n params,\n resolver,\n scope,\n })\n }\n\n if (babelTypes.isVariableDeclarator(node)) {\n const init = node.init ?? (babelTypes.isAssignmentPattern(node.id) && node.id.right)\n if (!init) {\n throw new Error(`Unsupported variable declarator`)\n }\n\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: init,\n resolver,\n scope,\n })\n }\n\n if (\n babelTypes.isCallExpression(node) &&\n babelTypes.isIdentifier(node.callee) &&\n FUNCTION_WRAPPER_ALLOW_LIST.has(node.callee.name)\n ) {\n return resolveExpression({\n babelConfig,\n file,\n filename,\n node: node.arguments[0]!,\n params,\n resolver,\n scope,\n })\n }\n\n if (babelTypes.isCallExpression(node)) {\n return resolveCallExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node,\n params,\n resolver,\n scope,\n })\n }\n\n if (\n babelTypes.isArrowFunctionExpression(node) ||\n babelTypes.isFunctionDeclaration(node) ||\n babelTypes.isFunctionExpression(node)\n ) {\n const newScope = new Scope(scope.path, scope)\n\n for (const [i, param] of params.entries()) {\n newScope.push({\n id: param as babelTypes.LVal,\n init: fnArguments[i] as babelTypes.Expression | undefined,\n })\n }\n\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: node.body,\n params: node.params,\n resolver,\n scope: newScope,\n })\n }\n\n if (babelTypes.isNewExpression(node)) {\n return resolveExpression({\n babelConfig,\n file,\n filename,\n node: node.callee,\n resolver,\n scope,\n })\n }\n\n if (babelTypes.isImportDefaultSpecifier(node) || babelTypes.isImportSpecifier(node)) {\n return resolveImportSpecifier({babelConfig, file, filename, fnArguments, node, resolver, scope})\n }\n\n if (babelTypes.isAssignmentPattern(node)) {\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: node.right,\n params,\n resolver,\n scope,\n })\n }\n\n // Handle TypeScript type assertions (e.g., `'foo' as string`)\n if (babelTypes.isTSAsExpression(node)) {\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: node.expression,\n params,\n resolver,\n scope,\n })\n }\n\n throw new Error(\n `Unsupported expression type: ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`,\n )\n}\n\nfunction resolveIdentifier({\n babelConfig,\n file,\n filename,\n fnArguments,\n node,\n params,\n resolver,\n scope,\n}: {\n babelConfig: TransformOptions\n file: babelTypes.File\n filename: string\n fnArguments: babelTypes.Node[]\n node: babelTypes.Identifier\n params: babelTypes.Node[]\n resolver: NodeJS.RequireResolve\n scope: Scope\n}): resolveExpressionReturnType {\n const paramIndex = params.findIndex(\n (param) =>\n (babelTypes.isIdentifier(param) && node.name === param.name) ||\n (babelTypes.isAssignmentPattern(param) &&\n babelTypes.isIdentifier(param.left) &&\n node.name === param.left.name),\n )\n let argument = fnArguments[paramIndex]\n if (!argument && paramIndex !== -1 && babelTypes.isAssignmentPattern(params[paramIndex])) {\n argument = params[paramIndex].right\n }\n if (argument && babelTypes.isLiteral(argument)) {\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: argument,\n params,\n resolver,\n scope,\n })\n }\n const binding = scope.getBinding(node.name)\n if (binding) {\n if (babelTypes.isIdentifier(binding.path.node)) {\n const isSame = binding.path.node.name === node.name\n if (isSame) {\n throw new Error(\n `Could not resolve same identifier \"${node.name}\" in \"${filename}:${node.loc?.start.line}:${node.loc?.start.column}\"`,\n )\n }\n }\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments,\n node: binding.path.node,\n params,\n resolver,\n scope,\n })\n }\n\n throw new Error(\n `Could not find binding for node \"${node.name}\" in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`,\n )\n}\n\nfunction resolveCallExpression({\n babelConfig,\n file,\n filename,\n node,\n params,\n resolver,\n scope,\n}: {\n babelConfig: TransformOptions\n file: babelTypes.File\n filename: string\n fnArguments: babelTypes.Node[]\n node: babelTypes.CallExpression\n params: babelTypes.Node[]\n resolver: NodeJS.RequireResolve\n scope: Scope\n}): resolveExpressionReturnType {\n const {callee} = node\n return resolveExpression({\n babelConfig,\n file,\n filename,\n fnArguments: node.arguments,\n node: callee,\n params,\n resolver,\n scope,\n })\n}\n\nfunction resolveImportSpecifier({\n babelConfig,\n file,\n filename,\n fnArguments,\n node,\n resolver,\n}: {\n babelConfig: TransformOptions\n file: babelTypes.File\n filename: string\n fnArguments: babelTypes.Node[]\n node: babelTypes.ExportSpecifier | babelTypes.ImportDefaultSpecifier | babelTypes.ImportSpecifier\n resolver: NodeJS.RequireResolve\n scope: Scope\n}): resolveExpressionReturnType {\n let importDeclaration: babelTypes.ImportDeclaration | undefined\n traverse(file, {\n ImportDeclaration(n) {\n if (!babelTypes.isImportDeclaration(n.node)) {\n return\n }\n for (const specifier of n.node.specifiers) {\n if (\n babelTypes.isImportDefaultSpecifier(specifier) &&\n specifier.local.loc?.identifierName === node.local.name\n ) {\n importDeclaration = n.node\n break\n }\n if (specifier.local.name === node.local.name) {\n importDeclaration = n.node\n }\n }\n },\n })\n\n if (!importDeclaration) {\n throw new Error(`Could not find import declaration for ${node.local.name}`)\n }\n\n const importName = node.local.name // the name of the variable to import\n const importFileName = importDeclaration.source.value // the file to import from\n\n const importPath =\n importFileName.startsWith('./') || importFileName.startsWith('../')\n ? path.resolve(path.dirname(filename), importFileName)\n : importFileName\n\n const resolvedFile = resolver(formatPath(importPath))\n const source = fs.readFileSync(resolvedFile)\n const tree = parseSourceFile(source.toString(), resolvedFile, babelConfig)\n\n let newScope: Scope | undefined\n traverse(tree, {\n Program(p) {\n newScope = p.scope\n },\n })\n if (!newScope) {\n throw new Error(`Could not find scope for ${filename}`)\n }\n\n const binding = newScope.getBinding(importName)\n if (binding) {\n return resolveExpression({\n babelConfig,\n file: tree,\n filename: resolvedFile,\n fnArguments,\n node: binding.path.node,\n resolver,\n scope: newScope,\n })\n }\n\n // It's not a global binding, but it might be a named export\n let namedExport: babelTypes.ExportNamedDeclaration | undefined\n let newImportName: string | undefined\n traverse(tree, {\n ExportDeclaration(p) {\n if (p.node.type === 'ExportNamedDeclaration') {\n for (const specifier of p.node.specifiers) {\n if (\n specifier.type === 'ExportSpecifier' &&\n specifier.exported.type === 'Identifier' &&\n specifier.exported.name === importName\n ) {\n namedExport = p.node\n newImportName = specifier.exported.name\n }\n }\n }\n },\n })\n\n if (namedExport && newImportName) {\n return resolveExportSpecifier({\n babelConfig,\n filename: resolvedFile,\n fnArguments,\n importName: newImportName,\n node: namedExport,\n resolver,\n })\n }\n\n let result: resolveExpressionReturnType | undefined\n traverse(tree, {\n ExportDeclaration(p) {\n if (p.node.type === 'ExportAllDeclaration') {\n try {\n result = resolveExportSpecifier({\n babelConfig,\n filename: resolvedFile,\n fnArguments,\n importName,\n node: p.node,\n resolver,\n })\n } catch (e) {\n if ((e as Error).cause !== `noBinding:${importName}`) throw e\n }\n }\n },\n })\n if (result) return result\n\n throw new Error(`Could not find binding for import \"${importName}\" in ${importFileName}`)\n}\n\nfunction resolveExportSpecifier({\n babelConfig,\n filename,\n fnArguments,\n importName,\n node,\n resolver,\n}: {\n babelConfig: TransformOptions\n filename: string\n fnArguments: babelTypes.Node[]\n importName: string\n node: babelTypes.ExportAllDeclaration | babelTypes.ExportNamedDeclaration\n resolver: NodeJS.RequireResolve\n}): resolveExpressionReturnType {\n if (!node.source) {\n throw new Error(`Could not find source for export \"${importName}\" in ${filename}`)\n }\n\n const importFileName = node.source.value\n const importPath = path.resolve(path.dirname(filename), importFileName)\n const resolvedFile = resolver(formatPath(importPath))\n const source = fs.readFileSync(resolvedFile)\n const tree = parseSourceFile(source.toString(), resolvedFile, babelConfig)\n\n let newScope: Scope | undefined\n traverse(tree, {\n Program(p) {\n newScope = p.scope\n },\n })\n if (!newScope) {\n throw new Error(`Could not find scope for ${filename}`)\n }\n\n const binding = newScope.getBinding(importName)\n if (binding) {\n return resolveExpression({\n babelConfig,\n file: tree,\n filename: resolvedFile,\n fnArguments,\n node: binding.path.node,\n resolver,\n scope: newScope,\n })\n }\n\n throw new Error(`Could not find binding for export \"${importName}\" in ${importFileName}`, {\n cause: `noBinding:${importName}`,\n })\n}\n"],"names":["fs","path","traverse","Scope","babelTypes","createDebug","formatPath","parseSourceFile","debug","TAGGED_TEMPLATE_ALLOW_LIST","Set","FUNCTION_WRAPPER_ALLOW_LIST","resolveExpression","babelConfig","file","filename","fnArguments","node","params","resolver","scope","type","loc","start","line","column","isTaggedTemplateExpression","isIdentifier","tag","has","name","quasi","isTemplateLiteral","resolvedExpressions","expressions","map","expression","quasis","idx","value","cooked","join","isLiteral","Error","toString","resolveIdentifier","isVariableDeclarator","init","isAssignmentPattern","id","right","isCallExpression","callee","arguments","resolveCallExpression","isArrowFunctionExpression","isFunctionDeclaration","isFunctionExpression","newScope","i","param","entries","push","body","isNewExpression","isImportDefaultSpecifier","isImportSpecifier","resolveImportSpecifier","isTSAsExpression","paramIndex","findIndex","left","argument","binding","getBinding","isSame","importDeclaration","ImportDeclaration","n","isImportDeclaration","specifier","specifiers","local","identifierName","importName","importFileName","source","importPath","startsWith","resolve","dirname","resolvedFile","readFileSync","tree","Program","p","namedExport","newImportName","ExportDeclaration","exported","resolveExportSpecifier","result","e","cause"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import fs from 'node:fs/promises';
2
+ import createDebug from 'debug';
3
+ import glob from 'globby';
4
+ import { getBabelConfig } from '../getBabelConfig.js';
5
+ import { findQueriesInSource } from './findQueriesInSource.js';
6
+ import { normalizeGlobPattern } from './helpers.js';
7
+ import { getResolver } from './moduleResolver.js';
8
+ import { QueryExtractionError } from './types.js';
9
+ const debug = createDebug('sanity:codegen:findQueries:debug');
10
+ /**
11
+ * findQueriesInPath takes a path or array of paths and returns all GROQ queries in the files.
12
+ * @param path - The path or array of paths to search for queries
13
+ * @param babelOptions - The babel configuration to use when parsing the source
14
+ * @param resolver - A resolver function to use when resolving module imports
15
+ * @returns An async generator that yields the results of the search
16
+ * @beta
17
+ * @internal
18
+ */ export function findQueriesInPath({ babelOptions = getBabelConfig(), path, resolver = getResolver() }) {
19
+ const queryNames = new Set();
20
+ // Holds all query names found in the source files
21
+ debug(`Globing ${path}`);
22
+ // Normalize glob patterns to use forward slashes on all platforms
23
+ const normalizedPath = normalizeGlobPattern(path);
24
+ const files = glob.sync(normalizedPath, {
25
+ absolute: false,
26
+ ignore: [
27
+ '**/node_modules/**'
28
+ ],
29
+ onlyFiles: true
30
+ }).toSorted();
31
+ async function* getQueries() {
32
+ for (const filename of files){
33
+ if (typeof filename !== 'string') {
34
+ continue;
35
+ }
36
+ debug(`Found file "${filename}"`);
37
+ try {
38
+ const source = await fs.readFile(filename, 'utf8');
39
+ const pluckedModuleResult = findQueriesInSource(source, filename, babelOptions, resolver);
40
+ // Check and error on duplicate query names, because we can't generate types with the same name.
41
+ for (const { variable } of pluckedModuleResult.queries){
42
+ if (queryNames.has(variable.id.name)) {
43
+ throw new Error(`Duplicate query name found: "${variable.id.name}". Query names must be unique across all files.`);
44
+ }
45
+ queryNames.add(variable.id.name);
46
+ }
47
+ yield pluckedModuleResult;
48
+ } catch (cause) {
49
+ debug(`Error in file "${filename}"`, cause);
50
+ yield {
51
+ errors: [
52
+ new QueryExtractionError({
53
+ cause,
54
+ filename
55
+ })
56
+ ],
57
+ filename,
58
+ queries: []
59
+ };
60
+ }
61
+ }
62
+ }
63
+ return {
64
+ files,
65
+ queries: getQueries()
66
+ };
67
+ }
68
+
69
+ //# sourceMappingURL=findQueriesInPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/typescript/findQueriesInPath.ts"],"sourcesContent":["import fs from 'node:fs/promises'\n\nimport {type TransformOptions} from '@babel/core'\nimport createDebug from 'debug'\nimport glob from 'globby'\n\nimport {getBabelConfig} from '../getBabelConfig.js'\nimport {findQueriesInSource} from './findQueriesInSource.js'\nimport {normalizeGlobPattern} from './helpers.js'\nimport {getResolver} from './moduleResolver.js'\nimport {type ExtractedModule, QueryExtractionError} from './types.js'\n\nconst debug = createDebug('sanity:codegen:findQueries:debug')\n\ninterface FindQueriesInPathOptions {\n path: string | string[]\n\n babelOptions?: TransformOptions\n resolver?: NodeJS.RequireResolve\n}\n\n/**\n * findQueriesInPath takes a path or array of paths and returns all GROQ queries in the files.\n * @param path - The path or array of paths to search for queries\n * @param babelOptions - The babel configuration to use when parsing the source\n * @param resolver - A resolver function to use when resolving module imports\n * @returns An async generator that yields the results of the search\n * @beta\n * @internal\n */\nexport function findQueriesInPath({\n babelOptions = getBabelConfig(),\n path,\n resolver = getResolver(),\n}: FindQueriesInPathOptions): {files: string[]; queries: AsyncIterable<ExtractedModule>} {\n const queryNames = new Set()\n // Holds all query names found in the source files\n debug(`Globing ${path}`)\n\n // Normalize glob patterns to use forward slashes on all platforms\n const normalizedPath = normalizeGlobPattern(path)\n\n const files = glob\n .sync(normalizedPath, {\n absolute: false,\n ignore: ['**/node_modules/**'], // we never want to look in node_modules\n onlyFiles: true,\n })\n .toSorted()\n\n async function* getQueries(): AsyncGenerator<ExtractedModule> {\n for (const filename of files) {\n if (typeof filename !== 'string') {\n continue\n }\n\n debug(`Found file \"${filename}\"`)\n try {\n const source = await fs.readFile(filename, 'utf8')\n const pluckedModuleResult = findQueriesInSource(source, filename, babelOptions, resolver)\n // Check and error on duplicate query names, because we can't generate types with the same name.\n for (const {variable} of pluckedModuleResult.queries) {\n if (queryNames.has(variable.id.name)) {\n throw new Error(\n `Duplicate query name found: \"${variable.id.name}\". Query names must be unique across all files.`,\n )\n }\n queryNames.add(variable.id.name)\n }\n\n yield pluckedModuleResult\n } catch (cause) {\n debug(`Error in file \"${filename}\"`, cause)\n\n yield {\n errors: [new QueryExtractionError({cause, filename})],\n filename,\n queries: [],\n }\n }\n }\n }\n\n return {files, queries: getQueries()}\n}\n"],"names":["fs","createDebug","glob","getBabelConfig","findQueriesInSource","normalizeGlobPattern","getResolver","QueryExtractionError","debug","findQueriesInPath","babelOptions","path","resolver","queryNames","Set","normalizedPath","files","sync","absolute","ignore","onlyFiles","toSorted","getQueries","filename","source","readFile","pluckedModuleResult","variable","queries","has","id","name","Error","add","cause","errors"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AAGjC,OAAOC,iBAAiB,QAAO;AAC/B,OAAOC,UAAU,SAAQ;AAEzB,SAAQC,cAAc,QAAO,uBAAsB;AACnD,SAAQC,mBAAmB,QAAO,2BAA0B;AAC5D,SAAQC,oBAAoB,QAAO,eAAc;AACjD,SAAQC,WAAW,QAAO,sBAAqB;AAC/C,SAA8BC,oBAAoB,QAAO,aAAY;AAErE,MAAMC,QAAQP,YAAY;AAS1B;;;;;;;;CAQC,GACD,OAAO,SAASQ,kBAAkB,EAChCC,eAAeP,gBAAgB,EAC/BQ,IAAI,EACJC,WAAWN,aAAa,EACC;IACzB,MAAMO,aAAa,IAAIC;IACvB,kDAAkD;IAClDN,MAAM,CAAC,QAAQ,EAAEG,MAAM;IAEvB,kEAAkE;IAClE,MAAMI,iBAAiBV,qBAAqBM;IAE5C,MAAMK,QAAQd,KACXe,IAAI,CAACF,gBAAgB;QACpBG,UAAU;QACVC,QAAQ;YAAC;SAAqB;QAC9BC,WAAW;IACb,GACCC,QAAQ;IAEX,gBAAgBC;QACd,KAAK,MAAMC,YAAYP,MAAO;YAC5B,IAAI,OAAOO,aAAa,UAAU;gBAChC;YACF;YAEAf,MAAM,CAAC,YAAY,EAAEe,SAAS,CAAC,CAAC;YAChC,IAAI;gBACF,MAAMC,SAAS,MAAMxB,GAAGyB,QAAQ,CAACF,UAAU;gBAC3C,MAAMG,sBAAsBtB,oBAAoBoB,QAAQD,UAAUb,cAAcE;gBAChF,gGAAgG;gBAChG,KAAK,MAAM,EAACe,QAAQ,EAAC,IAAID,oBAAoBE,OAAO,CAAE;oBACpD,IAAIf,WAAWgB,GAAG,CAACF,SAASG,EAAE,CAACC,IAAI,GAAG;wBACpC,MAAM,IAAIC,MACR,CAAC,6BAA6B,EAAEL,SAASG,EAAE,CAACC,IAAI,CAAC,+CAA+C,CAAC;oBAErG;oBACAlB,WAAWoB,GAAG,CAACN,SAASG,EAAE,CAACC,IAAI;gBACjC;gBAEA,MAAML;YACR,EAAE,OAAOQ,OAAO;gBACd1B,MAAM,CAAC,eAAe,EAAEe,SAAS,CAAC,CAAC,EAAEW;gBAErC,MAAM;oBACJC,QAAQ;wBAAC,IAAI5B,qBAAqB;4BAAC2B;4BAAOX;wBAAQ;qBAAG;oBACrDA;oBACAK,SAAS,EAAE;gBACb;YACF;QACF;IACF;IAEA,OAAO;QAACZ;QAAOY,SAASN;IAAY;AACtC"}
@@ -0,0 +1,175 @@
1
+ import { createRequire } from 'node:module';
2
+ import { traverse } from '@babel/core';
3
+ import * as babelTypes from '@babel/types';
4
+ import { getBabelConfig } from '../getBabelConfig.js';
5
+ import { resolveExpression } from './expressionResolvers.js';
6
+ import { parseSourceFile } from './parseSource.js';
7
+ import { QueryExtractionError } from './types.js';
8
+ const require = createRequire(import.meta.url);
9
+ const groqTagName = 'groq';
10
+ const defineQueryFunctionName = 'defineQuery';
11
+ const groqModuleName = 'groq';
12
+ const nextSanityModuleName = 'next-sanity';
13
+ const sveltekitModuleName = '@sanity/sveltekit';
14
+ const ignoreValue = '@sanity-typegen-ignore';
15
+ /**
16
+ * findQueriesInSource takes a source string and returns all GROQ queries in it.
17
+ * @param source - The source code to search for queries
18
+ * @param filename - The filename of the source code
19
+ * @param babelConfig - The babel configuration to use when parsing the source
20
+ * @param resolver - A resolver function to use when resolving module imports
21
+ * @returns
22
+ * @beta
23
+ * @internal
24
+ */ export function findQueriesInSource(source, filename, babelConfig = getBabelConfig(), resolver = require.resolve) {
25
+ const queries = [];
26
+ const errors = [];
27
+ const file = parseSourceFile(source, filename, babelConfig);
28
+ traverse(file, {
29
+ // Look for variable declarations, e.g. `const myQuery = groq`... and extract the query.
30
+ // The variable name is used as the name of the query result type
31
+ VariableDeclarator (path) {
32
+ const { node, scope } = path;
33
+ const init = node.init;
34
+ // Look for tagged template expressions that are called with the `groq` tag
35
+ const isGroqTemplateTag = babelTypes.isTaggedTemplateExpression(init) && babelTypes.isIdentifier(init.tag) && init.tag.name === groqTagName;
36
+ // Look for strings wrapped in a defineQuery function call
37
+ const isDefineQueryCall = babelTypes.isCallExpression(init) && (isImportFrom(groqModuleName, defineQueryFunctionName, scope, init.callee) || isImportFrom(nextSanityModuleName, defineQueryFunctionName, scope, init.callee) || isImportFrom(sveltekitModuleName, defineQueryFunctionName, scope, init.callee));
38
+ if (babelTypes.isIdentifier(node.id) && (isGroqTemplateTag || isDefineQueryCall)) {
39
+ // If we find a comment leading the decleration which macthes with ignoreValue we don't add
40
+ // the query
41
+ if (declarationLeadingCommentContains(path, ignoreValue)) {
42
+ return;
43
+ }
44
+ const { end, id, start } = node;
45
+ const variable = {
46
+ id,
47
+ ...start && {
48
+ start
49
+ },
50
+ ...end && {
51
+ end
52
+ }
53
+ };
54
+ try {
55
+ const query = resolveExpression({
56
+ babelConfig,
57
+ file,
58
+ filename,
59
+ node: init,
60
+ resolver,
61
+ scope
62
+ });
63
+ queries.push({
64
+ filename,
65
+ query,
66
+ variable
67
+ });
68
+ } catch (cause) {
69
+ errors.push(new QueryExtractionError({
70
+ cause,
71
+ filename,
72
+ variable
73
+ }));
74
+ }
75
+ }
76
+ }
77
+ });
78
+ return {
79
+ errors,
80
+ filename,
81
+ queries
82
+ };
83
+ }
84
+ function declarationLeadingCommentContains(path, comment) {
85
+ /*
86
+ * We have to consider these cases:
87
+ *
88
+ * // @sanity-typegen-ignore
89
+ * const query = groq`...`
90
+ *
91
+ * // AST
92
+ * VariableDeclaration {
93
+ * declarations: [
94
+ * VariableDeclarator: {init: tag: {name: "groq"}}
95
+ * ],
96
+ * leadingComments: ...
97
+ * }
98
+ *
99
+ * // @sanity-typegen-ignore
100
+ * const query1 = groq`...`, query2 = groq`...`
101
+ *
102
+ * // AST
103
+ * VariableDeclaration {
104
+ * declarations: [
105
+ * VariableDeclarator: {init: tag: {name: "groq"}}
106
+ * VariableDeclarator: {init: tag: {name: "groq"}}
107
+ * ],
108
+ * leadingComments: ...
109
+ * }
110
+ *
111
+ * // @sanity-typegen-ignore
112
+ * export const query = groq`...`
113
+ *
114
+ * // AST
115
+ * ExportNamedDeclaration {
116
+ * declaration: VariableDeclaration {
117
+ * declarations: [
118
+ * VariableDeclarator: {init: tag: {name: "groq"}}
119
+ * VariableDeclarator: {init: tag: {name: "groq"}}
120
+ * ],
121
+ * },
122
+ * leadingComments: ...
123
+ * }
124
+ *
125
+ * In the case where multiple variables are under the same VariableDeclaration the leadingComments
126
+ * will still be on the VariableDeclaration
127
+ *
128
+ * In the case where the variable is exported, the leadingComments are on the
129
+ * ExportNamedDeclaration which includes the VariableDeclaration in its own declaration property
130
+ */ const variableDeclaration = path.find((node)=>node.isVariableDeclaration());
131
+ if (!variableDeclaration) return false;
132
+ if (variableDeclaration.node.leadingComments?.find((commentItem)=>commentItem.value.trim() === comment)) {
133
+ return true;
134
+ }
135
+ // If the declaration is exported, the comment lies on the parent of the export declaration
136
+ if (variableDeclaration.parent.leadingComments?.find((commentItem)=>commentItem.value.trim() === comment)) {
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ function isImportFrom(moduleName, importName, scope, node) {
142
+ if (babelTypes.isIdentifier(node)) {
143
+ const binding = scope.getBinding(node.name);
144
+ if (!binding) {
145
+ return false;
146
+ }
147
+ const { path } = binding;
148
+ // import { foo } from 'groq'
149
+ if (babelTypes.isImportSpecifier(path.node)) {
150
+ return path.node.importKind === 'value' && path.parentPath && babelTypes.isImportDeclaration(path.parentPath.node) && path.parentPath.node.source.value === moduleName && babelTypes.isIdentifier(path.node.imported) && path.node.imported.name === importName;
151
+ }
152
+ // const { defineQuery } = require('groq')
153
+ if (babelTypes.isVariableDeclarator(path.node)) {
154
+ const { init } = path.node;
155
+ return babelTypes.isCallExpression(init) && babelTypes.isIdentifier(init.callee) && init.callee.name === 'require' && babelTypes.isStringLiteral(init.arguments[0]) && init.arguments[0].value === moduleName;
156
+ }
157
+ }
158
+ // import * as foo from 'groq'
159
+ // foo.defineQuery(...)
160
+ if (babelTypes.isMemberExpression(node)) {
161
+ const { object, property } = node;
162
+ if (!babelTypes.isIdentifier(object)) {
163
+ return false;
164
+ }
165
+ const binding = scope.getBinding(object.name);
166
+ if (!binding) {
167
+ return false;
168
+ }
169
+ const { path } = binding;
170
+ return babelTypes.isIdentifier(object) && babelTypes.isIdentifier(property) && property.name === importName && babelTypes.isImportNamespaceSpecifier(path.node) && path.parentPath && babelTypes.isImportDeclaration(path.parentPath.node) && path.parentPath.node.source.value === moduleName;
171
+ }
172
+ return false;
173
+ }
174
+
175
+ //# sourceMappingURL=findQueriesInSource.js.map