gapi-cli
Version:
Gapi command line interface
182 lines (179 loc) • 8.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const graphql_1 = require("graphql");
const util_1 = require("../../util/dist");
const language_typescript_1 = require("../../language-typescript/dist");
const run = (schemaInput, optionsInput) => {
const { generateEnumName, generateInterfaceName, generateTypeName, printType, formatInput, wrapList, formatEnum, typeBuilder, generateInterfaceDeclaration: gID, interfaceBuilder, addSemicolon, } = optionsInput.formats;
const TYPE_MAP = Object.assign({}, language_typescript_1.DEFAULT_TYPE_MAP);
function isScalar(type) {
return type instanceof graphql_1.GraphQLScalarType || !!type._scalarConfig;
}
const generateRootDataName = schema => {
let rootNamespaces = [];
const queryType = schema.getQueryType();
const mutationType = schema.getMutationType();
const subscriptionType = schema.getSubscriptionType();
if (queryType) {
rootNamespaces.push(generateInterfaceName(queryType.name));
}
if (mutationType) {
rootNamespaces.push(generateInterfaceName(mutationType.name));
}
if (subscriptionType) {
rootNamespaces.push(generateInterfaceName(subscriptionType.name));
}
return rootNamespaces.join(' | ');
};
const generateRootTypes = schema => ` export interface IGraphQLResponseRoot {
data?: ${generateRootDataName(schema)};
errors?: Array<IGraphQLResponseError>;
}
export interface IGraphQLResponseError {
message: string; // Required for all errors
locations?: Array<IGraphQLResponseErrorLocation>;
[propName: string]: any; // 7.2.2 says 'GraphQL servers may provide additional entries to error'
}
export interface IGraphQLResponseErrorLocation {
line: number;
column: number;
}`;
const generateDescription = description => description ? `/**
description: ${description}
*/` : '';
const wrapWithDescription = (declaration, description) => ` ${generateDescription(description)}
${declaration}`;
const generateTypeDeclaration = (description, name, possibleTypes) => wrapWithDescription(addSemicolon(typeBuilder(name, possibleTypes)) + '\n\n', description);
const typeNameDeclaration = name => addSemicolon(`__typename?: "${name}"`);
const generateInterfaceDeclaration = ({ name, description }, declaration, fields, additionalInfo, isInput) => {
if (!isInput && !optionsInput.ignoreTypeNameDeclaration) {
fields = [typeNameDeclaration(name), ...fields];
}
return additionalInfo + wrapWithDescription(interfaceBuilder(declaration, gID(fields.map(f => ` ${f}`), ' ')), description);
};
const generateEnumDeclaration = (description, name, enumValues) => wrapWithDescription(typeBuilder(generateEnumName(name), addSemicolon(formatEnum(enumValues))), description);
/**
* TODO
* - add support for custom types (via optional json file or something)
* - allow this to return metadata for Non Null types
*/
const resolveInterfaceName = type => {
if (util_1.isList(type)) {
return wrapList(resolveInterfaceName((type).ofType));
}
else if (util_1.isNonNullable(type)) {
return `!${resolveInterfaceName((type).ofType)}`;
}
else if (isScalar(type)) {
return TYPE_MAP[type.name] || TYPE_MAP.__DEFAULT;
}
else if (graphql_1.isAbstractType(type)) {
return generateTypeName(type.name);
}
else if (util_1.isEnum(type)) {
return generateEnumName(type.name);
}
else {
return generateInterfaceName(type.name);
}
};
const fieldToDefinition = (field, isInput, supportsNullability) => {
let interfaceName = resolveInterfaceName(field.type);
let isNotNull = interfaceName.includes('!');
let showNullabiltyAttribute = !isNotNull && supportsNullability;
if (isNotNull) {
/**
* should probably refactor this at some point to have
* `resolveInterfaceName` return better metadata
* global regex replace is ugly
*/
interfaceName = interfaceName.replace(/\!/g, '');
}
return formatInput(field.name, isInput && (showNullabiltyAttribute || !isNotNull), printType(interfaceName, !showNullabiltyAttribute));
};
const findRootType = type => {
if (util_1.isList(type) || util_1.isNonNullable(type)) {
return findRootType(type.ofType);
}
return type;
};
const filterField = (field, ignoredTypes) => {
let nestedType = findRootType(field.type);
return !ignoredTypes.has(nestedType.name);
};
function isUnion(type) {
return type instanceof graphql_1.GraphQLUnionType;
}
const generateAbstractTypeDeclaration = (type, ignoredTypes, interfaceMap) => {
const poss = (isUnion(type)) ? type.getTypes() : interfaceMap.get(type) || [];
let possibleTypes = poss
.filter(t => !ignoredTypes.has(t.name))
.map(t => generateInterfaceName(t.name));
return generateTypeDeclaration(type.description, generateTypeName(type.name), possibleTypes.join(' | '));
};
const typeToInterface = (type, ignoredTypes, supportsNullability, interfaceMap) => {
if (isScalar(type)) {
return null;
}
if (isUnion(type)) {
return generateAbstractTypeDeclaration(type, ignoredTypes, interfaceMap);
}
if (util_1.isEnum(type)) {
return generateEnumDeclaration(type.description, type.name, type.getValues());
}
let isInput = type instanceof graphql_1.GraphQLInputObjectType;
const f1 = type.getFields();
let f = Object.keys(f1).map(k => f1[k]);
let fields = f
.filter(field => filterField(field, ignoredTypes))
.map(field => [generateDescription(field.description), fieldToDefinition(field, isInput, supportsNullability)])
.reduce((acc, val) => [...acc, ...val.filter(x => x)], [])
.filter(field => field);
let interfaceDeclaration = generateInterfaceName(type.name);
let additionalInfo = '';
if (graphql_1.isAbstractType(type)) {
additionalInfo = generateAbstractTypeDeclaration(type, ignoredTypes, interfaceMap);
}
return generateInterfaceDeclaration(type, interfaceDeclaration, fields, additionalInfo, isInput);
};
const typesToInterfaces = (schema, options) => {
const ignoredTypes = new Set(options.ignoredTypes);
const interfaces = [];
interfaces.push(generateRootTypes(schema)); // add root entry point & errors
const supportsNullability = !options.legacy;
const types = schema.getTypeMap();
const typeArr = Object.keys(types).map(k => types[k]);
const interfaceMap = new Map();
typeArr.forEach(type => {
if (type instanceof graphql_1.GraphQLObjectType) {
type.getInterfaces().forEach(iface => {
if (interfaceMap.has(iface)) {
interfaceMap.set(iface, [...(interfaceMap.get(iface)), type]);
}
else {
interfaceMap.set(iface, [type]);
}
});
}
});
const typesWithExport = [];
const typeInterfaces = typeArr
.filter(type => !type.name.startsWith('__')) // remove introspection types
.filter(type => // remove ignored types
!ignoredTypes.has(type.name))
.map(type => // convert to interface
typeToInterface(type, ignoredTypes, supportsNullability, interfaceMap))
.filter(type => type); // remove empty ones
typeInterfaces.forEach(type => typesWithExport.push(type.replace(/interface/g, "export interface")));
return interfaces
.concat(typesWithExport) // add typeInterfaces to return object
.join('\n\n'); // add newlines between interfaces
};
return typesToInterfaces(schemaInput, optionsInput);
};
exports.schemaToInterfaces = (schema, options = {}, formatters = {}) => run(util_1.schemaFromInputs(schema), Object.assign({}, options, { formats: Object.assign({}, language_typescript_1.DEFAULT_OPTIONS, formatters) }));
exports.generateNamespace = (namespace, schema, options = {}, overrides = {}) => {
const formatters = Object.assign({}, language_typescript_1.DEFAULT_OPTIONS, overrides);
return formatters.postProcessor(formatters.generateNamespace(namespace, exports.schemaToInterfaces(schema, options, formatters)));
};
//# sourceMappingURL=index.js.map