@sanity/codegen
Version:
Codegen toolkit for Sanity.io
776 lines (774 loc) • 30.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: !0 });
var fs = require("node:fs/promises"), json5 = require("json5"), z = require("zod"), groqJs = require("groq-js"), createDebug = require("debug"), glob = require("globby"), fs$1 = require("node:fs"), path = require("node:path"), node_module = require("node:module"), core = require("@babel/core"), t = require("@babel/types"), traverse = require("@babel/traverse"), tsconfigPaths = require("tsconfig-paths"), register = require("@babel/register"), generator = require("@babel/generator");
function _interopDefaultCompat(e) {
return e && typeof e == "object" && "default" in e ? e : { default: e };
}
function _interopNamespaceCompat(e) {
if (e && typeof e == "object" && "default" in e) return e;
var n = /* @__PURE__ */ Object.create(null);
return e && Object.keys(e).forEach(function(k) {
if (k !== "default") {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: !0,
get: function() {
return e[k];
}
});
}
}), n.default = e, Object.freeze(n);
}
var fs__default$1 = /* @__PURE__ */ _interopDefaultCompat(fs), json5__default = /* @__PURE__ */ _interopDefaultCompat(json5), z__namespace = /* @__PURE__ */ _interopNamespaceCompat(z), createDebug__default = /* @__PURE__ */ _interopDefaultCompat(createDebug), glob__default = /* @__PURE__ */ _interopDefaultCompat(glob), fs__default = /* @__PURE__ */ _interopDefaultCompat(fs$1), path__default = /* @__PURE__ */ _interopDefaultCompat(path), t__namespace = /* @__PURE__ */ _interopNamespaceCompat(t), traverse__default = /* @__PURE__ */ _interopDefaultCompat(traverse), register__default = /* @__PURE__ */ _interopDefaultCompat(register);
const configDefintion = z__namespace.object({
path: z__namespace.string().or(z__namespace.array(z__namespace.string())).default([
"./src/**/*.{ts,tsx,js,jsx,mjs,cjs,astro}",
"./app/**/*.{ts,tsx,js,jsx,mjs,cjs}",
"./sanity/**/*.{ts,tsx,js,jsx,mjs,cjs}"
]),
schema: z__namespace.string().default("./schema.json"),
generates: z__namespace.string().default("./sanity.types.ts"),
formatGeneratedCode: z__namespace.boolean().default(!0),
overloadClientMethods: z__namespace.boolean().default(!0)
});
async function readConfig(path2) {
try {
const content = await fs.readFile(path2, "utf-8"), json = json5__default.default.parse(content);
return configDefintion.parseAsync(json);
} catch (error) {
if (error instanceof z__namespace.ZodError)
throw new Error(
`Error in config file
${error.errors.map((err) => err.message).join(`
`)}`,
{ cause: error }
);
if (typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT")
return configDefintion.parse({});
throw error;
}
}
async function readSchema(path2) {
const content = await fs.readFile(path2, "utf-8");
return JSON.parse(content);
}
function safeParseQuery(query) {
const params = {};
for (const param of extractSliceParams(query))
params[param] = 0;
return groqJs.parse(query, { params });
}
function* extractSliceParams(query) {
const sliceRegex = /\[(\$(\w+)|\d)\.\.\.?(\$(\w+)|\d)\]/g, matches = query.matchAll(sliceRegex);
if (matches)
for (const match of matches) {
const start = match[1] === `$${match[2]}` ? match[2] : null;
start !== null && (yield start);
const end = match[3] === `$${match[4]}` ? match[4] : null;
end !== null && (yield end);
}
}
function findBabelConfig(path$1) {
const configPath = path.join(path$1, "babel.config.json");
if (fs$1.existsSync(configPath))
return configPath;
const parent = path.resolve(path.join(path$1, ".."));
if (parent && parent !== path$1)
return findBabelConfig(parent);
throw new Error("Could not find `babel.config.json` in @sanity/codegen");
}
function getBabelConfig(path2) {
return { extends: findBabelConfig(__dirname) };
}
function parseSourceFile(_source, _filename, babelOptions) {
let source = _source, filename = _filename;
filename.endsWith(".astro") ? (filename += ".ts", source = parseAstro(source)) : filename.endsWith(".vue") && (filename += ".ts", source = parseVue(source));
const result = core.parse(source, {
...babelOptions,
filename
});
if (!result)
throw new Error(`Failed to parse ${filename}`);
return result;
}
function parseAstro(source) {
const codeFences = source.match(/---\n([\s\S]*?)\n---/g);
return codeFences ? codeFences.map((codeFence) => codeFence.split(`
`).slice(1, -1).join(`
`)).join(`
`) : "";
}
function parseVue(source) {
const matches = matchAllPolyfill(source, /<script(?:\s+generic=["'][^"']*["'])?[^>]*>([\s\S]*?)<\/script>/g);
return matches.length ? matches.map((match) => match[1]).join(`
`) : "";
}
function matchAllPolyfill(str, regex) {
if (!regex.global)
throw new Error("matchAll polyfill requires a global regex (with /g flag)");
const matches = [];
let match;
for (; (match = regex.exec(str)) !== null; )
matches.push(match);
return matches;
}
const debug$2 = createDebug__default.default("sanity:codegen:findQueries:debug"), TAGGED_TEMPLATE_ALLOW_LIST = ["groq"], FUNCTION_WRAPPER_ALLOW_LIST = ["defineQuery"];
function resolveExpression({
node,
file,
scope,
filename,
resolver,
babelConfig,
params = [],
fnArguments = []
}) {
if (debug$2(
`Resolving node ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`
), t__namespace.isTaggedTemplateExpression(node) && t__namespace.isIdentifier(node.tag) && TAGGED_TEMPLATE_ALLOW_LIST.includes(node.tag.name))
return resolveExpression({
node: node.quasi,
scope,
filename,
file,
resolver,
params,
babelConfig,
fnArguments
});
if (t__namespace.isTemplateLiteral(node)) {
const resolvedExpressions = node.expressions.map(
(expression) => resolveExpression({
node: expression,
scope,
filename,
file,
resolver,
params,
babelConfig,
fnArguments
})
);
return node.quasis.map((quasi, idx) => (quasi.value.cooked || "") + (resolvedExpressions[idx] || "")).join("");
}
if (t__namespace.isLiteral(node)) {
if (node.type === "NullLiteral" || node.type === "RegExpLiteral")
throw new Error(`Unsupported literal type: ${node.type}`);
return node.value.toString();
}
if (t__namespace.isIdentifier(node))
return resolveIdentifier({
node,
scope,
filename,
file,
resolver,
fnArguments,
babelConfig,
params
});
if (t__namespace.isVariableDeclarator(node)) {
const init = node.init ?? (t__namespace.isAssignmentPattern(node.id) && node.id.right);
if (!init)
throw new Error("Unsupported variable declarator");
return resolveExpression({
node: init,
fnArguments,
scope,
filename,
file,
babelConfig,
resolver
});
}
if (t__namespace.isCallExpression(node) && t__namespace.isIdentifier(node.callee) && FUNCTION_WRAPPER_ALLOW_LIST.includes(node.callee.name))
return resolveExpression({
node: node.arguments[0],
scope,
filename,
file,
resolver,
babelConfig,
params
});
if (t__namespace.isCallExpression(node))
return resolveCallExpression({
node,
scope,
filename,
file,
resolver,
babelConfig,
params
});
if (t__namespace.isArrowFunctionExpression(node) || t__namespace.isFunctionDeclaration(node) || t__namespace.isFunctionExpression(node)) {
const newScope = new traverse.Scope(scope.path, scope);
return params.forEach((param, i) => {
newScope.push({
id: param,
init: fnArguments[i]
});
}), resolveExpression({
node: node.body,
params: node.params,
fnArguments,
scope: newScope,
filename,
file,
babelConfig,
resolver
});
}
if (t__namespace.isNewExpression(node))
return resolveExpression({
node: node.callee,
scope,
filename,
file,
babelConfig,
resolver
});
if (t__namespace.isImportDefaultSpecifier(node) || t__namespace.isImportSpecifier(node))
return resolveImportSpecifier({ node, file, filename, fnArguments, resolver, babelConfig });
if (t__namespace.isAssignmentPattern(node))
return resolveExpression({
node: node.right,
scope,
filename,
file,
resolver,
params,
babelConfig,
fnArguments
});
throw new Error(
`Unsupported expression type: ${node.type} in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`
);
}
function resolveIdentifier({
node,
scope,
filename,
file,
resolver,
babelConfig,
fnArguments,
params
}) {
const paramIndex = params.findIndex(
(param) => t__namespace.isIdentifier(param) && node.name === param.name || t__namespace.isAssignmentPattern(param) && t__namespace.isIdentifier(param.left) && node.name === param.left.name
);
let argument = fnArguments[paramIndex];
if (!argument && paramIndex >= 0 && t__namespace.isAssignmentPattern(params[paramIndex]) && (argument = params[paramIndex].right), argument && t__namespace.isLiteral(argument))
return resolveExpression({
node: argument,
scope,
filename,
file,
resolver,
params,
babelConfig,
fnArguments
});
const binding = scope.getBinding(node.name);
if (binding) {
if (t__namespace.isIdentifier(binding.path.node) && binding.path.node.name === node.name)
throw new Error(
`Could not resolve same identifier "${node.name}" in "${filename}:${node.loc?.start.line}:${node.loc?.start.column}"`
);
return resolveExpression({
node: binding.path.node,
params,
fnArguments,
scope,
filename,
babelConfig,
file,
resolver
});
}
throw new Error(
`Could not find binding for node "${node.name}" in ${filename}:${node.loc?.start.line}:${node.loc?.start.column}`
);
}
function resolveCallExpression({
node,
scope,
filename,
file,
resolver,
babelConfig,
params
}) {
const { callee } = node;
return resolveExpression({
node: callee,
scope,
filename,
file,
resolver,
babelConfig,
params,
fnArguments: node.arguments
});
}
function resolveImportSpecifier({
node,
file,
filename,
fnArguments,
resolver,
babelConfig
}) {
let importDeclaration;
if (traverse__default.default(file, {
ImportDeclaration(n) {
if (t__namespace.isImportDeclaration(n.node))
for (const specifier of n.node.specifiers) {
if (t__namespace.isImportDefaultSpecifier(specifier) && specifier.local.loc?.identifierName === node.local.name) {
importDeclaration = n.node;
break;
}
specifier.local.name === node.local.name && (importDeclaration = n.node);
}
}
}), !importDeclaration)
throw new Error(`Could not find import declaration for ${node.local.name}`);
const importName = node.local.name, importFileName = importDeclaration.source.value, importPath = importFileName.startsWith("./") || importFileName.startsWith("../") ? path__default.default.resolve(path__default.default.dirname(filename), importFileName) : importFileName, resolvedFile = resolver(importPath), source = fs__default.default.readFileSync(resolvedFile), tree = parseSourceFile(source.toString(), resolvedFile, babelConfig);
let newScope;
if (traverse__default.default(tree, {
Program(p) {
newScope = p.scope;
}
}), !newScope)
throw new Error(`Could not find scope for ${filename}`);
const binding = newScope.getBinding(importName);
if (binding)
return resolveExpression({
node: binding.path.node,
file: tree,
scope: newScope,
fnArguments,
babelConfig,
filename: resolvedFile,
resolver
});
let namedExport, newImportName;
if (traverse__default.default(tree, {
ExportDeclaration(p) {
if (p.node.type === "ExportNamedDeclaration")
for (const specifier of p.node.specifiers)
specifier.type === "ExportSpecifier" && specifier.exported.type === "Identifier" && specifier.exported.name === importName && (namedExport = p.node, newImportName = specifier.exported.name);
}
}), namedExport && newImportName)
return resolveExportSpecifier({
node: namedExport,
importName: newImportName,
filename: resolvedFile,
fnArguments,
resolver,
babelConfig
});
let result;
if (traverse__default.default(tree, {
ExportDeclaration(p) {
if (p.node.type === "ExportAllDeclaration")
try {
result = resolveExportSpecifier({
node: p.node,
importName,
filename: resolvedFile,
fnArguments,
resolver,
babelConfig
});
} catch (e) {
if (e.cause !== `noBinding:${importName}`) throw e;
}
}
}), result) return result;
throw new Error(`Could not find binding for import "${importName}" in ${importFileName}`);
}
function resolveExportSpecifier({
node,
importName,
filename,
fnArguments,
babelConfig,
resolver
}) {
if (!node.source)
throw new Error(`Could not find source for export "${importName}" in ${filename}`);
const importFileName = node.source.value, importPath = path__default.default.resolve(path__default.default.dirname(filename), importFileName), resolvedFile = resolver(importPath), source = fs__default.default.readFileSync(resolvedFile), tree = parseSourceFile(source.toString(), resolvedFile, babelConfig);
let newScope;
if (traverse__default.default(tree, {
Program(p) {
newScope = p.scope;
}
}), !newScope)
throw new Error(`Could not find scope for ${filename}`);
const binding = newScope.getBinding(importName);
if (binding)
return resolveExpression({
node: binding.path.node,
file: tree,
scope: newScope,
filename: importFileName,
babelConfig,
resolver,
fnArguments
});
throw new Error(`Could not find binding for export "${importName}" in ${importFileName}`, {
cause: `noBinding:${importName}`
});
}
const require$1 = node_module.createRequire(__filename), groqTagName = "groq", defineQueryFunctionName = "defineQuery", groqModuleName = "groq", nextSanityModuleName = "next-sanity", ignoreValue = "@sanity-typegen-ignore";
function findQueriesInSource(source, filename, babelConfig = getBabelConfig(), resolver = require$1.resolve) {
const queries = [], file = parseSourceFile(source, filename, babelConfig);
return core.traverse(file, {
// Look for variable declarations, e.g. `const myQuery = groq`... and extract the query.
// The variable name is used as the name of the query result type
VariableDeclarator(path2) {
const { node, scope } = path2, init = node.init, isGroqTemplateTag = t__namespace.isTaggedTemplateExpression(init) && t__namespace.isIdentifier(init.tag) && init.tag.name === groqTagName, isDefineQueryCall = t__namespace.isCallExpression(init) && (isImportFrom(groqModuleName, defineQueryFunctionName, scope, init.callee) || isImportFrom(nextSanityModuleName, defineQueryFunctionName, scope, init.callee));
if (t__namespace.isIdentifier(node.id) && (isGroqTemplateTag || isDefineQueryCall)) {
if (declarationLeadingCommentContains(path2, ignoreValue))
return;
const queryName = `${node.id.name}`, queryResult = resolveExpression({
node: init,
file,
scope,
babelConfig,
filename,
resolver
}), location = node.loc ? {
start: {
...node.loc?.start
},
end: {
...node.loc?.end
}
} : {};
queries.push({ name: queryName, result: queryResult, location });
}
}
}), queries;
}
function declarationLeadingCommentContains(path2, comment) {
const variableDeclaration = path2.find((node) => node.isVariableDeclaration());
return variableDeclaration ? !!(variableDeclaration.node.leadingComments?.find(
(commentItem) => commentItem.value.trim() === comment
) || variableDeclaration.parent.leadingComments?.find(
(commentItem) => commentItem.value.trim() === comment
)) : !1;
}
function isImportFrom(moduleName, importName, scope, node) {
if (t__namespace.isIdentifier(node)) {
const binding = scope.getBinding(node.name);
if (!binding)
return !1;
const { path: path2 } = binding;
if (t__namespace.isImportSpecifier(path2.node))
return path2.node.importKind === "value" && path2.parentPath && t__namespace.isImportDeclaration(path2.parentPath.node) && path2.parentPath.node.source.value === moduleName && t__namespace.isIdentifier(path2.node.imported) && path2.node.imported.name === importName;
if (t__namespace.isVariableDeclarator(path2.node)) {
const { init } = path2.node;
return t__namespace.isCallExpression(init) && t__namespace.isIdentifier(init.callee) && init.callee.name === "require" && t__namespace.isStringLiteral(init.arguments[0]) && init.arguments[0].value === moduleName;
}
}
if (t__namespace.isMemberExpression(node)) {
const { object, property } = node;
if (!t__namespace.isIdentifier(object))
return !1;
const binding = scope.getBinding(object.name);
if (!binding)
return !1;
const { path: path2 } = binding;
return t__namespace.isIdentifier(object) && t__namespace.isIdentifier(property) && property.name === importName && t__namespace.isImportNamespaceSpecifier(path2.node) && path2.parentPath && t__namespace.isImportDeclaration(path2.parentPath.node) && path2.parentPath.node.source.value === moduleName;
}
return !1;
}
const debug$1 = createDebug__default.default("sanity:codegen:moduleResolver");
function getResolver(cwd) {
const tsConfig = tsconfigPaths.loadConfig(cwd);
if (tsConfig.resultType === "failed")
return debug$1("Could not load tsconfig, using default resolver: %s", tsConfig.message), require.resolve;
const matchPath = tsconfigPaths.createMatchPath(
tsConfig.absoluteBaseUrl,
tsConfig.paths,
tsConfig.mainFields,
tsConfig.addMatchAll
), resolve = function(request, options) {
const found = matchPath(request);
return found !== void 0 ? require.resolve(found, options) : require.resolve(request, options);
};
return resolve.paths = (request) => require.resolve.paths(request), resolve;
}
const debug = createDebug__default.default("sanity:codegen:findQueries:debug");
async function* findQueriesInPath({
path: path2,
babelOptions = getBabelConfig(),
resolver = getResolver()
}) {
const queryNames = /* @__PURE__ */ new Set();
debug(`Globing ${path2}`);
const files = glob__default.default.sync(path2, {
absolute: !1,
ignore: ["**/node_modules/**"],
// we never want to look in node_modules
onlyFiles: !0
}).sort();
for (const filename of files)
if (typeof filename == "string") {
debug(`Found file "${filename}"`);
try {
const source = await fs__default$1.default.readFile(filename, "utf8"), queries = findQueriesInSource(source, filename, babelOptions, resolver);
for (const query of queries) {
if (queryNames.has(query.name))
throw new Error(
`Duplicate query name found: "${query.name}". Query names must be unique across all files.`
);
queryNames.add(query.name);
}
yield { type: "queries", filename, queries };
} catch (error) {
debug(`Error in file "${filename}"`, error), yield { type: "error", error, filename };
}
}
}
function registerBabel(babelOptions) {
const options = babelOptions || getBabelConfig();
register__default.default({ ...options, extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"] });
}
const REFERENCE_SYMBOL_NAME = "internalGroqTypeReferenceTo", ALL_SCHEMA_TYPES = "AllSanitySchemaTypes";
class TypeGenerator {
// Simple set to keep track of generated type names, to avoid conflicts
generatedTypeName = /* @__PURE__ */ new Set();
// Map between type names and their generated type names, used to resolve the correct generated type name
typeNameMap = /* @__PURE__ */ new Map();
// Map between type nodes and their generated type names, used for query mapping
typeNodeNameMap = /* @__PURE__ */ new Map();
schema;
constructor(schema) {
this.schema = schema, this.schema.forEach((s) => {
this.getTypeName(s.name, s);
});
}
/**
* Generate TypeScript types for the given schema
* @returns string
* @internal
* @beta
*/
generateSchemaTypes() {
const typeDeclarations = [], schemaNames = /* @__PURE__ */ new Set();
return this.schema.forEach((schema) => {
const typeLiteral = this.getTypeNodeType(schema), schemaName = this.typeNodeNameMap.get(schema);
if (!schemaName)
throw new Error(`Schema name not found for schema ${schema.name}`);
schemaNames.add(schemaName);
const typeAlias = t__namespace.tsTypeAliasDeclaration(t__namespace.identifier(schemaName), null, typeLiteral);
typeDeclarations.push(t__namespace.exportNamedDeclaration(typeAlias));
}), typeDeclarations.push(
t__namespace.exportNamedDeclaration(
t__namespace.tsTypeAliasDeclaration(
t__namespace.identifier(this.getTypeName(ALL_SCHEMA_TYPES)),
null,
t__namespace.tsUnionType(
[...schemaNames].map((typeName) => t__namespace.tsTypeReference(t__namespace.identifier(typeName)))
)
)
)
), typeDeclarations.map((decl) => new generator.CodeGenerator(decl).generate().code).join(`
`);
}
/**
* Takes a identifier and a type node and generates a type alias for the type node.
* @param identifierName - The name of the type to generated
* @param typeNode - The type node to generate the type for
* @returns
* @internal
* @beta
*/
generateTypeNodeTypes(identifierName, typeNode) {
const type = this.getTypeNodeType(typeNode), typeName = this.getTypeName(identifierName, typeNode), typeAlias = t__namespace.tsTypeAliasDeclaration(t__namespace.identifier(typeName), null, type);
return new generator.CodeGenerator(t__namespace.exportNamedDeclaration(typeAlias)).generate().code.trim();
}
static generateKnownTypes() {
const typeOperator = t__namespace.tsTypeOperator(t__namespace.tsSymbolKeyword(), "unique"), identifier = t__namespace.identifier(REFERENCE_SYMBOL_NAME);
identifier.typeAnnotation = t__namespace.tsTypeAnnotation(typeOperator);
const decleration = t__namespace.variableDeclaration("const", [t__namespace.variableDeclarator(identifier)]);
return decleration.declare = !0, new generator.CodeGenerator(t__namespace.exportNamedDeclaration(decleration)).generate().code.trim();
}
/**
* Takes a list of queries from the codebase and generates a type declaration
* for SanityClient to consume.
*
* Note: only types that have previously been generated with `generateTypeNodeTypes`
* will be included in the query map.
*
* @param queries - A list of queries to generate a type declaration for
* @returns
* @internal
* @beta
*/
generateQueryMap(queries) {
const typesByQuerystring = {};
for (const query of queries) {
const name = this.typeNodeNameMap.get(query.typeNode);
name && (typesByQuerystring[query.query] ??= [], typesByQuerystring[query.query].push(name));
}
const queryReturnInterface = t__namespace.tsInterfaceDeclaration(
t__namespace.identifier("SanityQueries"),
null,
[],
t__namespace.tsInterfaceBody(
Object.entries(typesByQuerystring).map(([query, types]) => t__namespace.tsPropertySignature(
t__namespace.stringLiteral(query),
t__namespace.tsTypeAnnotation(
t__namespace.tsUnionType(types.map((type) => t__namespace.tsTypeReference(t__namespace.identifier(type))))
)
))
)
), declareModule = t__namespace.declareModule(
t__namespace.stringLiteral("@sanity/client"),
t__namespace.blockStatement([queryReturnInterface])
), clientImport = t__namespace.importDeclaration([], t__namespace.stringLiteral("@sanity/client"));
return new generator.CodeGenerator(t__namespace.program([clientImport, declareModule])).generate().code.trim();
}
/**
* Since we are sanitizing identifiers we migt end up with collisions. Ie there might be a type mux.video and muxVideo, both these
* types would be sanityized into MuxVideo. To avoid this we keep track of the generated type names and add a index to the name.
* When we reference a type we also keep track of the original name so we can reference the correct type later.
*/
getTypeName(name, typeNode) {
const desiredName = uppercaseFirstLetter(sanitizeIdentifier(name));
let generatedName = desiredName, i = 2;
for (; this.generatedTypeName.has(generatedName); )
generatedName = `${desiredName}_${i++}`;
return this.generatedTypeName.add(generatedName), this.typeNameMap.set(name, generatedName), typeNode && this.typeNodeNameMap.set(typeNode, generatedName), generatedName;
}
getTypeNodeType(typeNode) {
switch (typeNode.type) {
case "string":
return typeNode.value !== void 0 ? t__namespace.tsLiteralType(t__namespace.stringLiteral(typeNode.value)) : t__namespace.tsStringKeyword();
case "number":
return typeNode.value !== void 0 ? t__namespace.tsLiteralType(t__namespace.numericLiteral(typeNode.value)) : t__namespace.tsNumberKeyword();
case "boolean":
return typeNode.value !== void 0 ? t__namespace.tsLiteralType(t__namespace.booleanLiteral(typeNode.value)) : t__namespace.tsBooleanKeyword();
case "unknown":
return t__namespace.tsUnknownKeyword();
case "document":
return this.generateDocumentType(typeNode);
case "type":
return this.getTypeNodeType(typeNode.value);
case "array":
return this.generateArrayTsType(typeNode);
case "object":
return this.generateObjectTsType(typeNode);
case "union":
return this.generateUnionTsType(typeNode);
case "inline":
return this.generateInlineTsType(typeNode);
case "null":
return t__namespace.tsNullKeyword();
default:
throw new Error(`Type "${typeNode.type}" not found in schema`);
}
}
// Helper function used to generate TS types for array type nodes.
generateArrayTsType(typeNode) {
const typeNodes = this.getTypeNodeType(typeNode.of);
return t__namespace.tsTypeReference(
t__namespace.identifier("Array"),
t__namespace.tsTypeParameterInstantiation([typeNodes])
);
}
// Helper function used to generate TS types for object properties.
generateObjectProperty(key, attribute) {
const type = this.getTypeNodeType(attribute.value), propertySignature = t__namespace.tsPropertySignature(
t__namespace.identifier(sanitizeIdentifier(key)),
t__namespace.tsTypeAnnotation(type)
);
return propertySignature.optional = attribute.optional, propertySignature;
}
// Helper function used to generate TS types for object type nodes.
generateObjectTsType(typeNode) {
const props = [];
Object.entries(typeNode.attributes).forEach(([key, attribute]) => {
props.push(this.generateObjectProperty(key, attribute));
});
const rest = typeNode.rest;
if (rest !== void 0)
switch (rest.type) {
case "unknown":
return t__namespace.tsUnknownKeyword();
case "object": {
Object.entries(rest.attributes).forEach(([key, attribute]) => {
props.push(this.generateObjectProperty(key, attribute));
});
break;
}
case "inline": {
const resolved = this.generateInlineTsType(rest);
return t__namespace.isTSUnknownKeyword(resolved) ? resolved : t__namespace.tsIntersectionType([t__namespace.tsTypeLiteral(props), resolved]);
}
default:
throw new Error(`Type "${rest.type}" not found in schema`);
}
if (typeNode.dereferencesTo !== void 0) {
const derefType = t__namespace.tsPropertySignature(
t__namespace.identifier(REFERENCE_SYMBOL_NAME),
t__namespace.tsTypeAnnotation(t__namespace.tsLiteralType(t__namespace.stringLiteral(typeNode.dereferencesTo)))
);
derefType.computed = !0, derefType.optional = !0, props.push(derefType);
}
return t__namespace.tsTypeLiteral(props);
}
generateInlineTsType(typeNode) {
const referencedTypeNode = this.schema.find((schema) => schema.name === typeNode.name);
if (referencedTypeNode === void 0) {
const generatedName2 = this.typeNameMap.get(typeNode.name);
if (generatedName2)
return t__namespace.tsTypeReference(t__namespace.identifier(generatedName2));
const missing = t__namespace.tsUnknownKeyword();
return missing.trailingComments = [
{
type: "CommentLine",
value: ` Unable to locate the referenced type "${typeNode.name}" in schema`
}
], missing;
}
const generatedName = this.typeNameMap.get(referencedTypeNode.name);
return generatedName ? t__namespace.tsTypeReference(t__namespace.identifier(generatedName)) : t__namespace.tsUnknownKeyword();
}
// Helper function used to generate TS types for union type nodes.
generateUnionTsType(typeNode) {
if (typeNode.of.length === 0)
return t__namespace.tsNeverKeyword();
if (typeNode.of.length === 1)
return this.getTypeNodeType(typeNode.of[0]);
const typeNodes = typeNode.of.map((node) => this.getTypeNodeType(node));
return t__namespace.tsUnionType(typeNodes);
}
// Helper function used to generate TS types for document type nodes.
generateDocumentType(document) {
const props = Object.entries(document.attributes).map(
([key, node]) => this.generateObjectProperty(key, node)
);
return t__namespace.tsTypeLiteral(props);
}
}
function uppercaseFirstLetter(input) {
return input.charAt(0).toUpperCase() + input.slice(1);
}
function sanitizeIdentifier(input) {
return `${input.replace(/^\d/, "_").replace(/[^$\w]+(.)/g, (_, char) => char.toUpperCase())}`;
}
exports.TypeGenerator = TypeGenerator;
exports.findQueriesInPath = findQueriesInPath;
exports.findQueriesInSource = findQueriesInSource;
exports.getResolver = getResolver;
exports.readConfig = readConfig;
exports.readSchema = readSchema;
exports.registerBabel = registerBabel;
exports.safeParseQuery = safeParseQuery;
//# sourceMappingURL=index.js.map
;