@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 @@
1
+ {"version":3,"sources":["../../src/typescript/findQueriesInSource.ts"],"sourcesContent":["import {createRequire} from 'node:module'\n\nimport {type NodePath, type TransformOptions, traverse} from '@babel/core'\nimport {type Scope} from '@babel/traverse'\nimport * as babelTypes from '@babel/types'\n\nimport {getBabelConfig} from '../getBabelConfig.js'\nimport {resolveExpression} from './expressionResolvers.js'\nimport {parseSourceFile} from './parseSource.js'\nimport {type ExtractedModule, type ExtractedQuery, QueryExtractionError} from './types.js'\n\nconst require = createRequire(import.meta.url)\n\nconst groqTagName = 'groq'\nconst defineQueryFunctionName = 'defineQuery'\nconst groqModuleName = 'groq'\nconst nextSanityModuleName = 'next-sanity'\nconst sveltekitModuleName = '@sanity/sveltekit'\n\nconst ignoreValue = '@sanity-typegen-ignore'\n\n/**\n * findQueriesInSource takes a source string and returns all GROQ queries in it.\n * @param source - The source code to search for queries\n * @param filename - The filename of the source code\n * @param babelConfig - The babel configuration to use when parsing the source\n * @param resolver - A resolver function to use when resolving module imports\n * @returns\n * @beta\n * @internal\n */\nexport function findQueriesInSource(\n source: string,\n filename: string,\n babelConfig: TransformOptions = getBabelConfig(),\n resolver: NodeJS.RequireResolve = require.resolve,\n): ExtractedModule {\n const queries: ExtractedQuery[] = []\n const errors: QueryExtractionError[] = []\n const file = parseSourceFile(source, filename, babelConfig)\n\n traverse(file, {\n // Look for variable declarations, e.g. `const myQuery = groq`... and extract the query.\n // The variable name is used as the name of the query result type\n VariableDeclarator(path) {\n const {node, scope} = path\n\n const init = node.init\n\n // Look for tagged template expressions that are called with the `groq` tag\n const isGroqTemplateTag =\n babelTypes.isTaggedTemplateExpression(init) &&\n babelTypes.isIdentifier(init.tag) &&\n init.tag.name === groqTagName\n\n // Look for strings wrapped in a defineQuery function call\n const isDefineQueryCall =\n babelTypes.isCallExpression(init) &&\n (isImportFrom(groqModuleName, defineQueryFunctionName, scope, init.callee) ||\n isImportFrom(nextSanityModuleName, defineQueryFunctionName, scope, init.callee) ||\n isImportFrom(sveltekitModuleName, defineQueryFunctionName, scope, init.callee))\n\n if (babelTypes.isIdentifier(node.id) && (isGroqTemplateTag || isDefineQueryCall)) {\n // If we find a comment leading the decleration which macthes with ignoreValue we don't add\n // the query\n if (declarationLeadingCommentContains(path, ignoreValue)) {\n return\n }\n\n const {end, id, start} = node\n const variable = {id, ...(start && {start}), ...(end && {end})}\n\n try {\n const query = resolveExpression({\n babelConfig,\n file,\n filename,\n node: init,\n resolver,\n scope,\n })\n queries.push({filename, query, variable})\n } catch (cause) {\n errors.push(new QueryExtractionError({cause, filename, variable}))\n }\n }\n },\n })\n\n return {errors, filename, queries}\n}\n\nfunction declarationLeadingCommentContains(path: NodePath, comment: string): boolean {\n /*\n * We have to consider these cases:\n *\n * // @sanity-typegen-ignore\n * const query = groq`...`\n *\n * // AST\n * VariableDeclaration {\n * declarations: [\n * VariableDeclarator: {init: tag: {name: \"groq\"}}\n * ],\n * leadingComments: ...\n * }\n *\n * // @sanity-typegen-ignore\n * const query1 = groq`...`, query2 = groq`...`\n *\n * // AST\n * VariableDeclaration {\n * declarations: [\n * VariableDeclarator: {init: tag: {name: \"groq\"}}\n * VariableDeclarator: {init: tag: {name: \"groq\"}}\n * ],\n * leadingComments: ...\n * }\n *\n * // @sanity-typegen-ignore\n * export const query = groq`...`\n *\n * // AST\n * ExportNamedDeclaration {\n * declaration: VariableDeclaration {\n * declarations: [\n * VariableDeclarator: {init: tag: {name: \"groq\"}}\n * VariableDeclarator: {init: tag: {name: \"groq\"}}\n * ],\n * },\n * leadingComments: ...\n * }\n *\n * In the case where multiple variables are under the same VariableDeclaration the leadingComments\n * will still be on the VariableDeclaration\n *\n * In the case where the variable is exported, the leadingComments are on the\n * ExportNamedDeclaration which includes the VariableDeclaration in its own declaration property\n */\n\n const variableDeclaration = path.find((node) => node.isVariableDeclaration())\n if (!variableDeclaration) return false\n\n if (\n variableDeclaration.node.leadingComments?.find(\n (commentItem) => commentItem.value.trim() === comment,\n )\n ) {\n return true\n }\n\n // If the declaration is exported, the comment lies on the parent of the export declaration\n if (\n variableDeclaration.parent.leadingComments?.find(\n (commentItem) => commentItem.value.trim() === comment,\n )\n ) {\n return true\n }\n\n return false\n}\n\nfunction isImportFrom(\n moduleName: string,\n importName: string,\n scope: Scope,\n node: babelTypes.Expression | babelTypes.V8IntrinsicIdentifier,\n) {\n if (babelTypes.isIdentifier(node)) {\n const binding = scope.getBinding(node.name)\n if (!binding) {\n return false\n }\n\n const {path} = binding\n\n // import { foo } from 'groq'\n if (babelTypes.isImportSpecifier(path.node)) {\n return (\n path.node.importKind === 'value' &&\n path.parentPath &&\n babelTypes.isImportDeclaration(path.parentPath.node) &&\n path.parentPath.node.source.value === moduleName &&\n babelTypes.isIdentifier(path.node.imported) &&\n path.node.imported.name === importName\n )\n }\n\n // const { defineQuery } = require('groq')\n if (babelTypes.isVariableDeclarator(path.node)) {\n const {init} = path.node\n return (\n babelTypes.isCallExpression(init) &&\n babelTypes.isIdentifier(init.callee) &&\n init.callee.name === 'require' &&\n babelTypes.isStringLiteral(init.arguments[0]) &&\n init.arguments[0].value === moduleName\n )\n }\n }\n\n // import * as foo from 'groq'\n // foo.defineQuery(...)\n if (babelTypes.isMemberExpression(node)) {\n const {object, property} = node\n\n if (!babelTypes.isIdentifier(object)) {\n return false\n }\n\n const binding = scope.getBinding(object.name)\n if (!binding) {\n return false\n }\n const {path} = binding\n\n return (\n babelTypes.isIdentifier(object) &&\n babelTypes.isIdentifier(property) &&\n property.name === importName &&\n babelTypes.isImportNamespaceSpecifier(path.node) &&\n path.parentPath &&\n babelTypes.isImportDeclaration(path.parentPath.node) &&\n path.parentPath.node.source.value === moduleName\n )\n }\n\n return false\n}\n"],"names":["createRequire","traverse","babelTypes","getBabelConfig","resolveExpression","parseSourceFile","QueryExtractionError","require","url","groqTagName","defineQueryFunctionName","groqModuleName","nextSanityModuleName","sveltekitModuleName","ignoreValue","findQueriesInSource","source","filename","babelConfig","resolver","resolve","queries","errors","file","VariableDeclarator","path","node","scope","init","isGroqTemplateTag","isTaggedTemplateExpression","isIdentifier","tag","name","isDefineQueryCall","isCallExpression","isImportFrom","callee","id","declarationLeadingCommentContains","end","start","variable","query","push","cause","comment","variableDeclaration","find","isVariableDeclaration","leadingComments","commentItem","value","trim","parent","moduleName","importName","binding","getBinding","isImportSpecifier","importKind","parentPath","isImportDeclaration","imported","isVariableDeclarator","isStringLiteral","arguments","isMemberExpression","object","property","isImportNamespaceSpecifier"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import path from 'node:path';
2
+ import { CodeGenerator } from '@babel/generator';
3
+ import * as t from '@babel/types';
4
+ import { formatPath } from '../utils/formatPath.js';
5
+ import { RESERVED_IDENTIFIERS } from './constants.js';
6
+ export function normalizePrintablePath(root, filename) {
7
+ const resolved = path.resolve(root, filename);
8
+ // Always use Unix-style paths for consistent output across platforms
9
+ return formatPath(path.relative(root, resolved));
10
+ }
11
+ /**
12
+ * Normalizes a glob pattern to use forward slashes (POSIX-style paths).
13
+ * Glob patterns must use forward slashes, even on Windows.
14
+ *
15
+ * @param pattern - A glob pattern string or array of patterns
16
+ * @returns The normalized pattern(s) with forward slashes
17
+ * @see https://github.com/sindresorhus/globby#api
18
+ */ export function normalizeGlobPattern(pattern) {
19
+ if (Array.isArray(pattern)) {
20
+ return pattern.map((p)=>formatPath(p));
21
+ }
22
+ return formatPath(pattern);
23
+ }
24
+ function sanitizeIdentifier(input) {
25
+ return `${input.replace(/^\d/, '_').replaceAll(/[^$\w]+(.)/g, (_, char)=>char.toUpperCase())}`;
26
+ }
27
+ /**
28
+ * Checks if a string is a valid ECMAScript IdentifierName.
29
+ * IdentifierNames start with a letter, underscore, or $, and contain only
30
+ * alphanumeric characters, underscores, or $.
31
+ */ export function isIdentifierName(input) {
32
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(input);
33
+ }
34
+ function normalizeIdentifier(input) {
35
+ const sanitized = sanitizeIdentifier(input);
36
+ return `${sanitized.charAt(0).toUpperCase()}${sanitized.slice(1)}`;
37
+ }
38
+ export function getUniqueIdentifierForName(name, currentIdentifiers) {
39
+ const desiredName = normalizeIdentifier(name);
40
+ let resultingName = desiredName;
41
+ let index = 2;
42
+ while(currentIdentifiers.has(resultingName) || RESERVED_IDENTIFIERS.has(resultingName)){
43
+ resultingName = `${desiredName}_${index}`;
44
+ index++;
45
+ }
46
+ return t.identifier(resultingName);
47
+ }
48
+ export function computeOnce(fn) {
49
+ const ref = {
50
+ computed: false,
51
+ current: undefined
52
+ };
53
+ return function() {
54
+ if (ref.computed) return ref.current;
55
+ ref.current = fn();
56
+ ref.computed = true;
57
+ return ref.current;
58
+ };
59
+ }
60
+ export function weakMapMemo(fn) {
61
+ const cache = new WeakMap();
62
+ const wrapped = function(arg) {
63
+ if (cache.has(arg)) return cache.get(arg);
64
+ const result = fn(arg);
65
+ cache.set(arg, result);
66
+ return result;
67
+ };
68
+ return wrapped;
69
+ }
70
+ export function generateCode(node) {
71
+ return `${new CodeGenerator(node).generate().code.trim()}\n\n`;
72
+ }
73
+ export function getFilterArrayUnionType(typeNode, predicate) {
74
+ if (typeNode.of.type !== 'union') {
75
+ return typeNode;
76
+ }
77
+ return {
78
+ ...typeNode,
79
+ of: {
80
+ ...typeNode.of,
81
+ of: typeNode.of.of.filter((unionTypeNode)=>predicate(unionTypeNode))
82
+ }
83
+ };
84
+ }
85
+
86
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/typescript/helpers.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {CodeGenerator} from '@babel/generator'\nimport * as t from '@babel/types'\nimport {type ArrayTypeNode, type UnionTypeNode} from 'groq-js'\n\nimport {formatPath} from '../utils/formatPath.js'\nimport {RESERVED_IDENTIFIERS} from './constants.js'\n\nexport function normalizePrintablePath(root: string, filename: string) {\n const resolved = path.resolve(root, filename)\n // Always use Unix-style paths for consistent output across platforms\n return formatPath(path.relative(root, resolved))\n}\n\n/**\n * Normalizes a glob pattern to use forward slashes (POSIX-style paths).\n * Glob patterns must use forward slashes, even on Windows.\n *\n * @param pattern - A glob pattern string or array of patterns\n * @returns The normalized pattern(s) with forward slashes\n * @see https://github.com/sindresorhus/globby#api\n */\nexport function normalizeGlobPattern(pattern: string | string[]): string | string[] {\n if (Array.isArray(pattern)) {\n return pattern.map((p) => formatPath(p))\n }\n return formatPath(pattern)\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return `${input.replace(/^\\d/, '_').replaceAll(/[^$\\w]+(.)/g, (_, char) => char.toUpperCase())}`\n}\n\n/**\n * Checks if a string is a valid ECMAScript IdentifierName.\n * IdentifierNames start with a letter, underscore, or $, and contain only\n * alphanumeric characters, underscores, or $.\n */\nexport function isIdentifierName(input: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(input)\n}\n\nfunction normalizeIdentifier(input: string): string {\n const sanitized = sanitizeIdentifier(input)\n return `${sanitized.charAt(0).toUpperCase()}${sanitized.slice(1)}`\n}\n\nexport function getUniqueIdentifierForName(name: string, currentIdentifiers: Set<string>) {\n const desiredName = normalizeIdentifier(name)\n let resultingName = desiredName\n let index = 2\n while (currentIdentifiers.has(resultingName) || RESERVED_IDENTIFIERS.has(resultingName)) {\n resultingName = `${desiredName}_${index}`\n index++\n }\n return t.identifier(resultingName)\n}\n\nexport function computeOnce<TReturn>(fn: () => TReturn): () => TReturn {\n const ref = {computed: false, current: undefined as TReturn | undefined}\n\n return function () {\n if (ref.computed) return ref.current as TReturn\n ref.current = fn()\n ref.computed = true\n return ref.current\n }\n}\n\nexport function weakMapMemo<TParam extends object, TReturn>(fn: (arg: TParam) => TReturn) {\n const cache = new WeakMap<object, TReturn>()\n\n const wrapped = function (arg: TParam) {\n if (cache.has(arg)) return cache.get(arg)!\n const result = fn(arg)\n cache.set(arg, result)\n return result\n }\n\n return wrapped\n}\n\nexport function generateCode(node: t.Node) {\n return `${new CodeGenerator(node).generate().code.trim()}\\n\\n`\n}\n\nexport function getFilterArrayUnionType(\n typeNode: ArrayTypeNode,\n predicate: (unionTypeNode: UnionTypeNode['of'][number]) => boolean,\n): ArrayTypeNode {\n if (typeNode.of.type !== 'union') {\n return typeNode\n }\n\n return {\n ...typeNode,\n of: {\n ...typeNode.of,\n of: typeNode.of.of.filter((unionTypeNode) => predicate(unionTypeNode)),\n },\n } satisfies ArrayTypeNode<UnionTypeNode>\n}\n"],"names":["path","CodeGenerator","t","formatPath","RESERVED_IDENTIFIERS","normalizePrintablePath","root","filename","resolved","resolve","relative","normalizeGlobPattern","pattern","Array","isArray","map","p","sanitizeIdentifier","input","replace","replaceAll","_","char","toUpperCase","isIdentifierName","test","normalizeIdentifier","sanitized","charAt","slice","getUniqueIdentifierForName","name","currentIdentifiers","desiredName","resultingName","index","has","identifier","computeOnce","fn","ref","computed","current","undefined","weakMapMemo","cache","WeakMap","wrapped","arg","get","result","set","generateCode","node","generate","code","trim","getFilterArrayUnionType","typeNode","predicate","of","type","filter","unionTypeNode"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAQC,aAAa,QAAO,mBAAkB;AAC9C,YAAYC,OAAO,eAAc;AAGjC,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,oBAAoB,QAAO,iBAAgB;AAEnD,OAAO,SAASC,uBAAuBC,IAAY,EAAEC,QAAgB;IACnE,MAAMC,WAAWR,KAAKS,OAAO,CAACH,MAAMC;IACpC,qEAAqE;IACrE,OAAOJ,WAAWH,KAAKU,QAAQ,CAACJ,MAAME;AACxC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASG,qBAAqBC,OAA0B;IAC7D,IAAIC,MAAMC,OAAO,CAACF,UAAU;QAC1B,OAAOA,QAAQG,GAAG,CAAC,CAACC,IAAMb,WAAWa;IACvC;IACA,OAAOb,WAAWS;AACpB;AAEA,SAASK,mBAAmBC,KAAa;IACvC,OAAO,GAAGA,MAAMC,OAAO,CAAC,OAAO,KAAKC,UAAU,CAAC,eAAe,CAACC,GAAGC,OAASA,KAAKC,WAAW,KAAK;AAClG;AAEA;;;;CAIC,GACD,OAAO,SAASC,iBAAiBN,KAAa;IAC5C,OAAO,6BAA6BO,IAAI,CAACP;AAC3C;AAEA,SAASQ,oBAAoBR,KAAa;IACxC,MAAMS,YAAYV,mBAAmBC;IACrC,OAAO,GAAGS,UAAUC,MAAM,CAAC,GAAGL,WAAW,KAAKI,UAAUE,KAAK,CAAC,IAAI;AACpE;AAEA,OAAO,SAASC,2BAA2BC,IAAY,EAAEC,kBAA+B;IACtF,MAAMC,cAAcP,oBAAoBK;IACxC,IAAIG,gBAAgBD;IACpB,IAAIE,QAAQ;IACZ,MAAOH,mBAAmBI,GAAG,CAACF,kBAAkB9B,qBAAqBgC,GAAG,CAACF,eAAgB;QACvFA,gBAAgB,GAAGD,YAAY,CAAC,EAAEE,OAAO;QACzCA;IACF;IACA,OAAOjC,EAAEmC,UAAU,CAACH;AACtB;AAEA,OAAO,SAASI,YAAqBC,EAAiB;IACpD,MAAMC,MAAM;QAACC,UAAU;QAAOC,SAASC;IAAgC;IAEvE,OAAO;QACL,IAAIH,IAAIC,QAAQ,EAAE,OAAOD,IAAIE,OAAO;QACpCF,IAAIE,OAAO,GAAGH;QACdC,IAAIC,QAAQ,GAAG;QACf,OAAOD,IAAIE,OAAO;IACpB;AACF;AAEA,OAAO,SAASE,YAA4CL,EAA4B;IACtF,MAAMM,QAAQ,IAAIC;IAElB,MAAMC,UAAU,SAAUC,GAAW;QACnC,IAAIH,MAAMT,GAAG,CAACY,MAAM,OAAOH,MAAMI,GAAG,CAACD;QACrC,MAAME,SAASX,GAAGS;QAClBH,MAAMM,GAAG,CAACH,KAAKE;QACf,OAAOA;IACT;IAEA,OAAOH;AACT;AAEA,OAAO,SAASK,aAAaC,IAAY;IACvC,OAAO,GAAG,IAAIpD,cAAcoD,MAAMC,QAAQ,GAAGC,IAAI,CAACC,IAAI,GAAG,IAAI,CAAC;AAChE;AAEA,OAAO,SAASC,wBACdC,QAAuB,EACvBC,SAAkE;IAElE,IAAID,SAASE,EAAE,CAACC,IAAI,KAAK,SAAS;QAChC,OAAOH;IACT;IAEA,OAAO;QACL,GAAGA,QAAQ;QACXE,IAAI;YACF,GAAGF,SAASE,EAAE;YACdA,IAAIF,SAASE,EAAE,CAACA,EAAE,CAACE,MAAM,CAAC,CAACC,gBAAkBJ,UAAUI;QACzD;IACF;AACF"}
@@ -0,0 +1,33 @@
1
+ import { createRequire } from 'node:module';
2
+ import createDebug from 'debug';
3
+ import { createMatchPath, loadConfig as loadTSConfig } from 'tsconfig-paths';
4
+ const require = createRequire(import.meta.url);
5
+ const debug = createDebug('sanity:codegen:moduleResolver');
6
+ /**
7
+ * This is a custom implementation of require.resolve that takes into account the paths
8
+ * configuration in tsconfig.json. This is necessary if we want to resolve paths that are
9
+ * custom defined in the tsconfig.json file.
10
+ * Resolving here is best effort and might not work in all cases.
11
+ * @beta
12
+ */ export function getResolver(cwd) {
13
+ const tsConfig = loadTSConfig(cwd);
14
+ if (tsConfig.resultType === 'failed') {
15
+ debug('Could not load tsconfig, using default resolver: %s', tsConfig.message);
16
+ return require.resolve;
17
+ }
18
+ const matchPath = createMatchPath(tsConfig.absoluteBaseUrl, tsConfig.paths, tsConfig.mainFields, tsConfig.addMatchAll);
19
+ const resolve = function(request, options) {
20
+ const found = matchPath(request);
21
+ if (found !== undefined) {
22
+ return require.resolve(found, options);
23
+ }
24
+ return require.resolve(request, options);
25
+ };
26
+ // wrap the resolve.path function to make it available.
27
+ resolve.paths = (request)=>{
28
+ return require.resolve.paths(request);
29
+ };
30
+ return resolve;
31
+ }
32
+
33
+ //# sourceMappingURL=moduleResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/typescript/moduleResolver.ts"],"sourcesContent":["import {createRequire} from 'node:module'\n\nimport createDebug from 'debug'\nimport {createMatchPath, loadConfig as loadTSConfig} from 'tsconfig-paths'\n\nconst require = createRequire(import.meta.url)\nconst debug = createDebug('sanity:codegen:moduleResolver')\n\n/**\n * This is a custom implementation of require.resolve that takes into account the paths\n * configuration in tsconfig.json. This is necessary if we want to resolve paths that are\n * custom defined in the tsconfig.json file.\n * Resolving here is best effort and might not work in all cases.\n * @beta\n */\nexport function getResolver(cwd?: string): NodeJS.RequireResolve {\n const tsConfig = loadTSConfig(cwd)\n\n if (tsConfig.resultType === 'failed') {\n debug('Could not load tsconfig, using default resolver: %s', tsConfig.message)\n return require.resolve\n }\n\n const matchPath = createMatchPath(\n tsConfig.absoluteBaseUrl,\n tsConfig.paths,\n tsConfig.mainFields,\n tsConfig.addMatchAll,\n )\n\n const resolve = function (request: string, options?: {paths?: string[]}): string {\n const found = matchPath(request)\n if (found !== undefined) {\n return require.resolve(found, options)\n }\n return require.resolve(request, options)\n }\n\n // wrap the resolve.path function to make it available.\n resolve.paths = (request: string): string[] | null => {\n return require.resolve.paths(request)\n }\n return resolve\n}\n"],"names":["createRequire","createDebug","createMatchPath","loadConfig","loadTSConfig","require","url","debug","getResolver","cwd","tsConfig","resultType","message","resolve","matchPath","absoluteBaseUrl","paths","mainFields","addMatchAll","request","options","found","undefined"],"mappings":"AAAA,SAAQA,aAAa,QAAO,cAAa;AAEzC,OAAOC,iBAAiB,QAAO;AAC/B,SAAQC,eAAe,EAAEC,cAAcC,YAAY,QAAO,iBAAgB;AAE1E,MAAMC,UAAUL,cAAc,YAAYM,GAAG;AAC7C,MAAMC,QAAQN,YAAY;AAE1B;;;;;;CAMC,GACD,OAAO,SAASO,YAAYC,GAAY;IACtC,MAAMC,WAAWN,aAAaK;IAE9B,IAAIC,SAASC,UAAU,KAAK,UAAU;QACpCJ,MAAM,uDAAuDG,SAASE,OAAO;QAC7E,OAAOP,QAAQQ,OAAO;IACxB;IAEA,MAAMC,YAAYZ,gBAChBQ,SAASK,eAAe,EACxBL,SAASM,KAAK,EACdN,SAASO,UAAU,EACnBP,SAASQ,WAAW;IAGtB,MAAML,UAAU,SAAUM,OAAe,EAAEC,OAA4B;QACrE,MAAMC,QAAQP,UAAUK;QACxB,IAAIE,UAAUC,WAAW;YACvB,OAAOjB,QAAQQ,OAAO,CAACQ,OAAOD;QAChC;QACA,OAAOf,QAAQQ,OAAO,CAACM,SAASC;IAClC;IAEA,uDAAuD;IACvDP,QAAQG,KAAK,GAAG,CAACG;QACf,OAAOd,QAAQQ,OAAO,CAACG,KAAK,CAACG;IAC/B;IACA,OAAON;AACT"}
@@ -0,0 +1,75 @@
1
+ import { parse } from '@babel/core';
2
+ // helper function to parse a source file
3
+ export function parseSourceFile(_source, _filename, babelOptions) {
4
+ let source = _source;
5
+ let filename = _filename;
6
+ if (filename.endsWith('.astro')) {
7
+ // append .ts to the filename so babel will parse it as typescript
8
+ filename += '.ts';
9
+ source = parseAstro(source);
10
+ } else if (filename.endsWith('.vue')) {
11
+ // append .ts to the filename so babel will parse it as typescript
12
+ filename += '.ts';
13
+ source = parseVue(source);
14
+ } else if (filename.endsWith('.svelte')) {
15
+ // append .ts to the filename so babel will parse it as typescript
16
+ filename += '.ts';
17
+ source = parseSvelte(source);
18
+ }
19
+ const result = parse(source, {
20
+ ...babelOptions,
21
+ filename
22
+ });
23
+ if (!result) {
24
+ throw new Error(`Failed to parse ${filename}`);
25
+ }
26
+ return result;
27
+ }
28
+ function parseAstro(source) {
29
+ // find all code fences, the js code is between --- and ---
30
+ // Handle both Unix (\n) and Windows (\r\n) line endings
31
+ const codeFences = source.match(/---\r?\n([\s\S]*?)\r?\n---/g);
32
+ if (!codeFences) {
33
+ return '';
34
+ }
35
+ return codeFences.map((codeFence)=>{
36
+ // Split on either \n or \r\n
37
+ return codeFence.split(/\r?\n/).slice(1, -1).join('\n');
38
+ }).join('\n');
39
+ }
40
+ function parseVue(source) {
41
+ // find all script tags, the js code is between <script> and </script>
42
+ const scriptRegex = /<script(?:\s+generic=["'][^"']*["'])?[^>]*>([\s\S]*?)<\/script>/g;
43
+ // const matches = [...source.matchAll(scriptRegex)]
44
+ // TODO: swap once this code runs in `ES2020`
45
+ const matches = matchAllPolyfill(source, scriptRegex);
46
+ if (matches.length === 0) {
47
+ return '';
48
+ }
49
+ return matches.map((match)=>match[1]).join('\n');
50
+ }
51
+ function parseSvelte(source) {
52
+ // find all script tags, the js code is between <script> and </script>
53
+ const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/g;
54
+ // const matches = [...source.matchAll(scriptRegex)]
55
+ // TODO: swap once this code runs in `ES2020`
56
+ const matches = matchAllPolyfill(source, scriptRegex);
57
+ if (matches.length === 0) {
58
+ return '';
59
+ }
60
+ return matches.map((match)=>match[1]).join('\n');
61
+ }
62
+ // TODO: remove once this code runs in `ES2020`
63
+ function matchAllPolyfill(str, regex) {
64
+ if (!regex.global) {
65
+ throw new Error('matchAll polyfill requires a global regex (with /g flag)');
66
+ }
67
+ const matches = [];
68
+ let match;
69
+ while((match = regex.exec(str)) !== null){
70
+ matches.push(match);
71
+ }
72
+ return matches;
73
+ }
74
+
75
+ //# sourceMappingURL=parseSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/typescript/parseSource.ts"],"sourcesContent":["import type * as babelTypes from '@babel/types'\n\nimport {parse, type TransformOptions} from '@babel/core'\n\n// helper function to parse a source file\nexport function parseSourceFile(\n _source: string,\n _filename: string,\n babelOptions: TransformOptions,\n): babelTypes.File {\n let source = _source\n let filename = _filename\n if (filename.endsWith('.astro')) {\n // append .ts to the filename so babel will parse it as typescript\n filename += '.ts'\n source = parseAstro(source)\n } else if (filename.endsWith('.vue')) {\n // append .ts to the filename so babel will parse it as typescript\n filename += '.ts'\n source = parseVue(source)\n } else if (filename.endsWith('.svelte')) {\n // append .ts to the filename so babel will parse it as typescript\n filename += '.ts'\n source = parseSvelte(source)\n }\n const result = parse(source, {\n ...babelOptions,\n filename,\n })\n\n if (!result) {\n throw new Error(`Failed to parse ${filename}`)\n }\n\n return result\n}\n\nfunction parseAstro(source: string): string {\n // find all code fences, the js code is between --- and ---\n // Handle both Unix (\\n) and Windows (\\r\\n) line endings\n const codeFences = source.match(/---\\r?\\n([\\s\\S]*?)\\r?\\n---/g)\n if (!codeFences) {\n return ''\n }\n\n return codeFences\n .map((codeFence) => {\n // Split on either \\n or \\r\\n\n return codeFence.split(/\\r?\\n/).slice(1, -1).join('\\n')\n })\n .join('\\n')\n}\n\nfunction parseVue(source: string): string {\n // find all script tags, the js code is between <script> and </script>\n const scriptRegex = /<script(?:\\s+generic=[\"'][^\"']*[\"'])?[^>]*>([\\s\\S]*?)<\\/script>/g\n // const matches = [...source.matchAll(scriptRegex)]\n // TODO: swap once this code runs in `ES2020`\n const matches = matchAllPolyfill(source, scriptRegex)\n if (matches.length === 0) {\n return ''\n }\n\n return matches.map((match) => match[1]).join('\\n')\n}\n\nfunction parseSvelte(source: string): string {\n // find all script tags, the js code is between <script> and </script>\n const scriptRegex = /<script[^>]*>([\\s\\S]*?)<\\/script>/g\n // const matches = [...source.matchAll(scriptRegex)]\n // TODO: swap once this code runs in `ES2020`\n const matches = matchAllPolyfill(source, scriptRegex)\n if (matches.length === 0) {\n return ''\n }\n\n return matches.map((match) => match[1]).join('\\n')\n}\n\n// TODO: remove once this code runs in `ES2020`\nfunction matchAllPolyfill(str: string, regex: RegExp): RegExpMatchArray[] {\n if (!regex.global) {\n throw new Error('matchAll polyfill requires a global regex (with /g flag)')\n }\n\n const matches = []\n let match\n while ((match = regex.exec(str)) !== null) {\n matches.push(match)\n }\n return matches\n}\n"],"names":["parse","parseSourceFile","_source","_filename","babelOptions","source","filename","endsWith","parseAstro","parseVue","parseSvelte","result","Error","codeFences","match","map","codeFence","split","slice","join","scriptRegex","matches","matchAllPolyfill","length","str","regex","global","exec","push"],"mappings":"AAEA,SAAQA,KAAK,QAA8B,cAAa;AAExD,yCAAyC;AACzC,OAAO,SAASC,gBACdC,OAAe,EACfC,SAAiB,EACjBC,YAA8B;IAE9B,IAAIC,SAASH;IACb,IAAII,WAAWH;IACf,IAAIG,SAASC,QAAQ,CAAC,WAAW;QAC/B,kEAAkE;QAClED,YAAY;QACZD,SAASG,WAAWH;IACtB,OAAO,IAAIC,SAASC,QAAQ,CAAC,SAAS;QACpC,kEAAkE;QAClED,YAAY;QACZD,SAASI,SAASJ;IACpB,OAAO,IAAIC,SAASC,QAAQ,CAAC,YAAY;QACvC,kEAAkE;QAClED,YAAY;QACZD,SAASK,YAAYL;IACvB;IACA,MAAMM,SAASX,MAAMK,QAAQ;QAC3B,GAAGD,YAAY;QACfE;IACF;IAEA,IAAI,CAACK,QAAQ;QACX,MAAM,IAAIC,MAAM,CAAC,gBAAgB,EAAEN,UAAU;IAC/C;IAEA,OAAOK;AACT;AAEA,SAASH,WAAWH,MAAc;IAChC,2DAA2D;IAC3D,wDAAwD;IACxD,MAAMQ,aAAaR,OAAOS,KAAK,CAAC;IAChC,IAAI,CAACD,YAAY;QACf,OAAO;IACT;IAEA,OAAOA,WACJE,GAAG,CAAC,CAACC;QACJ,6BAA6B;QAC7B,OAAOA,UAAUC,KAAK,CAAC,SAASC,KAAK,CAAC,GAAG,CAAC,GAAGC,IAAI,CAAC;IACpD,GACCA,IAAI,CAAC;AACV;AAEA,SAASV,SAASJ,MAAc;IAC9B,sEAAsE;IACtE,MAAMe,cAAc;IACpB,oDAAoD;IACpD,6CAA6C;IAC7C,MAAMC,UAAUC,iBAAiBjB,QAAQe;IACzC,IAAIC,QAAQE,MAAM,KAAK,GAAG;QACxB,OAAO;IACT;IAEA,OAAOF,QAAQN,GAAG,CAAC,CAACD,QAAUA,KAAK,CAAC,EAAE,EAAEK,IAAI,CAAC;AAC/C;AAEA,SAAST,YAAYL,MAAc;IACjC,sEAAsE;IACtE,MAAMe,cAAc;IACpB,oDAAoD;IACpD,6CAA6C;IAC7C,MAAMC,UAAUC,iBAAiBjB,QAAQe;IACzC,IAAIC,QAAQE,MAAM,KAAK,GAAG;QACxB,OAAO;IACT;IAEA,OAAOF,QAAQN,GAAG,CAAC,CAACD,QAAUA,KAAK,CAAC,EAAE,EAAEK,IAAI,CAAC;AAC/C;AAEA,+CAA+C;AAC/C,SAASG,iBAAiBE,GAAW,EAAEC,KAAa;IAClD,IAAI,CAACA,MAAMC,MAAM,EAAE;QACjB,MAAM,IAAId,MAAM;IAClB;IAEA,MAAMS,UAAU,EAAE;IAClB,IAAIP;IACJ,MAAO,AAACA,CAAAA,QAAQW,MAAME,IAAI,CAACH,IAAG,MAAO,KAAM;QACzCH,QAAQO,IAAI,CAACd;IACf;IACA,OAAOO;AACT"}
@@ -0,0 +1,23 @@
1
+ import register from '@babel/register';
2
+ import { getBabelConfig } from '../getBabelConfig.js';
3
+ /**
4
+ * Register Babel with the given options
5
+ *
6
+ * @param babelOptions - The options to use when registering Babel
7
+ * @beta
8
+ */ export function registerBabel(babelOptions) {
9
+ const options = babelOptions || getBabelConfig();
10
+ register({
11
+ ...options,
12
+ extensions: [
13
+ '.ts',
14
+ '.tsx',
15
+ '.js',
16
+ '.jsx',
17
+ '.mjs',
18
+ '.cjs'
19
+ ]
20
+ });
21
+ }
22
+
23
+ //# sourceMappingURL=registerBabel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/typescript/registerBabel.ts"],"sourcesContent":["import {type TransformOptions} from '@babel/core'\nimport register from '@babel/register'\n\nimport {getBabelConfig} from '../getBabelConfig.js'\n\n/**\n * Register Babel with the given options\n *\n * @param babelOptions - The options to use when registering Babel\n * @beta\n */\nexport function registerBabel(babelOptions?: TransformOptions): void {\n const options = babelOptions || getBabelConfig()\n\n register({...options, extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']})\n}\n"],"names":["register","getBabelConfig","registerBabel","babelOptions","options","extensions"],"mappings":"AACA,OAAOA,cAAc,kBAAiB;AAEtC,SAAQC,cAAc,QAAO,uBAAsB;AAEnD;;;;;CAKC,GACD,OAAO,SAASC,cAAcC,YAA+B;IAC3D,MAAMC,UAAUD,gBAAgBF;IAEhCD,SAAS;QAAC,GAAGI,OAAO;QAAEC,YAAY;YAAC;YAAO;YAAQ;YAAO;YAAQ;YAAQ;SAAO;IAAA;AAClF"}
@@ -0,0 +1,323 @@
1
+ import * as t from '@babel/types';
2
+ import { typeEvaluate } from 'groq-js';
3
+ import { safeParseQuery } from '../safeParseQuery.js';
4
+ import { ARRAY_OF, INTERNAL_REFERENCE_SYMBOL } from './constants.js';
5
+ import { getFilterArrayUnionType, getUniqueIdentifierForName, isIdentifierName, weakMapMemo } from './helpers.js';
6
+ export class SchemaTypeGenerator {
7
+ schema;
8
+ evaluateQuery = weakMapMemo(// eslint-disable-next-line unicorn/consistent-function-scoping
9
+ ({ query })=>{
10
+ const ast = safeParseQuery(query);
11
+ const typeNode = typeEvaluate(ast, this.schema);
12
+ const tsType = this.generateTsType(typeNode);
13
+ const stats = walkAndCountQueryTypeNodeStats(typeNode);
14
+ return {
15
+ stats,
16
+ tsType
17
+ };
18
+ });
19
+ identifiers = new Map();
20
+ tsTypes = new Map();
21
+ constructor(schema){
22
+ this.schema = schema;
23
+ const uniqueTypeNames = new Set();
24
+ for (const type of schema){
25
+ if (uniqueTypeNames.has(type.name)) {
26
+ throw new Error(`Duplicate type name "${type.name}" in schema. Type names must be unique within the same schema.`);
27
+ }
28
+ uniqueTypeNames.add(type.name);
29
+ }
30
+ for (const type of schema){
31
+ const currentIdentifierNames = new Set([
32
+ ...this.identifiers.values()
33
+ ].map((id)=>id.name));
34
+ const uniqueIdentifier = getUniqueIdentifierForName(type.name, currentIdentifierNames);
35
+ this.identifiers.set(type.name, uniqueIdentifier);
36
+ }
37
+ for (const type of schema){
38
+ this.tsTypes.set(type.name, this.generateTsType(type));
39
+ }
40
+ }
41
+ getType(typeName) {
42
+ const tsType = this.tsTypes.get(typeName);
43
+ const id = this.identifiers.get(typeName);
44
+ if (tsType && id) return {
45
+ id,
46
+ tsType
47
+ };
48
+ return undefined;
49
+ }
50
+ hasType(typeName) {
51
+ return this.tsTypes.has(typeName);
52
+ }
53
+ *[Symbol.iterator]() {
54
+ for (const { name } of this.schema){
55
+ yield {
56
+ name,
57
+ ...this.getType(name)
58
+ };
59
+ }
60
+ }
61
+ typeNames() {
62
+ return this.schema.map((schemaType)=>schemaType.name);
63
+ }
64
+ /**
65
+ * Helper function used to generate TS types for arrays of inline types, or arrays of inline types
66
+ * wrapped in the ArrayOf wrapper that adds _key prop
67
+ */ generateArrayOfTsType(typeNode) {
68
+ const typeNodes = this.generateTsType(typeNode.of);
69
+ return t.tsTypeReference(ARRAY_OF, t.tsTypeParameterInstantiation([
70
+ typeNodes
71
+ ]));
72
+ }
73
+ // Helper function used to generate TS types for array type nodes.
74
+ generateArrayTsType(typeNode) {
75
+ // if it's an array of a single inline type, wrap it in ArrayOf
76
+ if (typeNode.of.type === 'inline') {
77
+ return this.generateArrayOfTsType(typeNode);
78
+ }
79
+ // if it's not an inline object and not a union, wrap in Array
80
+ if (typeNode.of.type !== 'union') {
81
+ const typeNodes = this.generateTsType(typeNode.of);
82
+ return t.tsTypeReference(t.identifier('Array'), t.tsTypeParameterInstantiation([
83
+ typeNodes
84
+ ]));
85
+ }
86
+ // if it's not a union type or all of the union type members are non-inlines, wrap type in Array
87
+ if (typeNode.of.of.every((unionTypeNode)=>unionTypeNode.type !== 'inline')) {
88
+ const typeNodes = this.generateTsType(typeNode.of);
89
+ return t.tsTypeReference(t.identifier('Array'), t.tsTypeParameterInstantiation([
90
+ typeNodes
91
+ ]));
92
+ }
93
+ // all the union types nodes are inline
94
+ if (typeNode.of.of.every((unionMember)=>unionMember.type === 'inline')) {
95
+ return this.generateArrayOfTsType(typeNode);
96
+ }
97
+ // some of the union types are inlines, while some are not - split and recurse
98
+ const arrayOfNonInline = getFilterArrayUnionType(typeNode, (member)=>member.type !== 'inline');
99
+ const arrayOfInline = getFilterArrayUnionType(typeNode, (member)=>member.type === 'inline');
100
+ return t.tsUnionType([
101
+ this.generateArrayTsType(arrayOfNonInline),
102
+ this.generateArrayTsType(arrayOfInline)
103
+ ]);
104
+ }
105
+ // Helper function used to generate TS types for document type nodes.
106
+ generateDocumentTsType(document) {
107
+ const props = Object.entries(document.attributes).map(([key, node])=>this.generateTsObjectProperty(key, node));
108
+ return t.tsTypeLiteral(props);
109
+ }
110
+ generateInlineTsType(typeNode) {
111
+ const id = this.identifiers.get(typeNode.name);
112
+ if (!id) {
113
+ // Not found in schema, return unknown type
114
+ return t.addComment(t.tsUnknownKeyword(), 'trailing', ` Unable to locate the referenced type "${typeNode.name}" in schema`, true);
115
+ }
116
+ return t.tsTypeReference(id);
117
+ }
118
+ // Helper function used to generate TS types for object type nodes.
119
+ generateObjectTsType(typeNode) {
120
+ const props = [];
121
+ for (const [key, attribute] of Object.entries(typeNode.attributes)){
122
+ props.push(this.generateTsObjectProperty(key, attribute));
123
+ }
124
+ const rest = typeNode.rest;
125
+ if (rest) {
126
+ switch(rest.type){
127
+ case 'inline':
128
+ {
129
+ const resolved = this.generateInlineTsType(rest);
130
+ // if object rest is unknown, we can't generate a type literal for it
131
+ if (t.isTSUnknownKeyword(resolved)) return resolved;
132
+ return t.tsIntersectionType([
133
+ t.tsTypeLiteral(props),
134
+ resolved
135
+ ]);
136
+ }
137
+ case 'object':
138
+ {
139
+ for (const [key, attribute] of Object.entries(rest.attributes)){
140
+ props.push(this.generateTsObjectProperty(key, attribute));
141
+ }
142
+ break;
143
+ }
144
+ case 'unknown':
145
+ {
146
+ return t.tsUnknownKeyword();
147
+ }
148
+ default:
149
+ {
150
+ // @ts-expect-error This should never happen
151
+ throw new Error(`Type "${rest.type}" not found in schema`);
152
+ }
153
+ }
154
+ }
155
+ if (typeNode.dereferencesTo) {
156
+ const derefType = Object.assign(t.tsPropertySignature(INTERNAL_REFERENCE_SYMBOL, t.tsTypeAnnotation(t.tsLiteralType(t.stringLiteral(typeNode.dereferencesTo)))), {
157
+ computed: true,
158
+ optional: true
159
+ });
160
+ props.push(derefType);
161
+ }
162
+ return t.tsTypeLiteral(props);
163
+ }
164
+ // Helper function used to generate TS types for object properties.
165
+ generateTsObjectProperty(key, attribute) {
166
+ const type = this.generateTsType(attribute.value);
167
+ const keyNode = isIdentifierName(key) ? t.identifier(key) : t.stringLiteral(key);
168
+ const propertySignature = t.tsPropertySignature(keyNode, t.tsTypeAnnotation(type));
169
+ propertySignature.optional = attribute.optional;
170
+ return propertySignature;
171
+ }
172
+ generateTsType(typeNode) {
173
+ switch(typeNode.type){
174
+ case 'array':
175
+ {
176
+ return this.generateArrayTsType(typeNode);
177
+ }
178
+ case 'boolean':
179
+ {
180
+ if (typeNode.value !== undefined) {
181
+ return t.tsLiteralType(t.booleanLiteral(typeNode.value));
182
+ }
183
+ return t.tsBooleanKeyword();
184
+ }
185
+ case 'document':
186
+ {
187
+ return this.generateDocumentTsType(typeNode);
188
+ }
189
+ case 'inline':
190
+ {
191
+ return this.generateInlineTsType(typeNode);
192
+ }
193
+ case 'null':
194
+ {
195
+ return t.tsNullKeyword();
196
+ }
197
+ case 'number':
198
+ {
199
+ if (typeNode.value !== undefined) {
200
+ return t.tsLiteralType(t.numericLiteral(typeNode.value));
201
+ }
202
+ return t.tsNumberKeyword();
203
+ }
204
+ case 'object':
205
+ {
206
+ return this.generateObjectTsType(typeNode);
207
+ }
208
+ case 'string':
209
+ {
210
+ if (typeNode.value !== undefined) {
211
+ return t.tsLiteralType(t.stringLiteral(typeNode.value));
212
+ }
213
+ return t.tsStringKeyword();
214
+ }
215
+ case 'type':
216
+ {
217
+ return this.generateTsType(typeNode.value);
218
+ }
219
+ case 'union':
220
+ {
221
+ return this.generateUnionTsType(typeNode);
222
+ }
223
+ case 'unknown':
224
+ {
225
+ return t.tsUnknownKeyword();
226
+ }
227
+ default:
228
+ {
229
+ throw new Error(`Encountered unsupported node type "${// @ts-expect-error This should never happen
230
+ typeNode.type}" while generating schema types`);
231
+ }
232
+ }
233
+ }
234
+ // Helper function used to generate TS types for union type nodes.
235
+ generateUnionTsType(typeNode) {
236
+ if (typeNode.of.length === 0) return t.tsNeverKeyword();
237
+ if (typeNode.of.length === 1) return this.generateTsType(typeNode.of[0]);
238
+ return t.tsUnionType(typeNode.of.map((node)=>this.generateTsType(node)));
239
+ }
240
+ }
241
+ export function walkAndCountQueryTypeNodeStats(typeNode) {
242
+ switch(typeNode.type){
243
+ case 'array':
244
+ {
245
+ const acc = walkAndCountQueryTypeNodeStats(typeNode.of);
246
+ acc.allTypes += 1; // count the array type itself
247
+ return acc;
248
+ }
249
+ case 'object':
250
+ {
251
+ // if the rest is unknown, we count it as one unknown type
252
+ if (typeNode.rest && typeNode.rest.type === 'unknown') {
253
+ return {
254
+ allTypes: 2,
255
+ emptyUnions: 0,
256
+ unknownTypes: 1
257
+ } // count the object type itself as well
258
+ ;
259
+ }
260
+ const restStats = typeNode.rest ? walkAndCountQueryTypeNodeStats(typeNode.rest) : {
261
+ allTypes: 0,
262
+ emptyUnions: 0,
263
+ unknownTypes: 0
264
+ };
265
+ // count the object type itself
266
+ restStats.allTypes += 1;
267
+ const attrs = Object.values(typeNode.attributes);
268
+ let acc = restStats;
269
+ for (const attribute of attrs){
270
+ const { allTypes, emptyUnions, unknownTypes } = walkAndCountQueryTypeNodeStats(attribute.value);
271
+ acc = {
272
+ allTypes: acc.allTypes + allTypes,
273
+ emptyUnions: acc.emptyUnions + emptyUnions,
274
+ unknownTypes: acc.unknownTypes + unknownTypes
275
+ };
276
+ }
277
+ return acc;
278
+ }
279
+ case 'union':
280
+ {
281
+ if (typeNode.of.length === 0) {
282
+ return {
283
+ allTypes: 1,
284
+ emptyUnions: 1,
285
+ unknownTypes: 0
286
+ };
287
+ }
288
+ let acc = {
289
+ allTypes: 1,
290
+ emptyUnions: 0,
291
+ unknownTypes: 0
292
+ } // count the union type itself
293
+ ;
294
+ for (const type of typeNode.of){
295
+ const { allTypes, emptyUnions, unknownTypes } = walkAndCountQueryTypeNodeStats(type);
296
+ acc = {
297
+ allTypes: acc.allTypes + allTypes,
298
+ emptyUnions: acc.emptyUnions + emptyUnions,
299
+ unknownTypes: acc.unknownTypes + unknownTypes
300
+ };
301
+ }
302
+ return acc;
303
+ }
304
+ case 'unknown':
305
+ {
306
+ return {
307
+ allTypes: 1,
308
+ emptyUnions: 0,
309
+ unknownTypes: 1
310
+ };
311
+ }
312
+ default:
313
+ {
314
+ return {
315
+ allTypes: 1,
316
+ emptyUnions: 0,
317
+ unknownTypes: 0
318
+ };
319
+ }
320
+ }
321
+ }
322
+
323
+ //# sourceMappingURL=schemaTypeGenerator.js.map