@gapi/cli
Version:
Gapi command line interface
297 lines (294 loc) • 9.06 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) => {
const 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);
const isNotNull = interfaceName.includes('!');
const 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) => {
const 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) || [];
const 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 `export ${generateAbstractTypeDeclaration(
type,
ignoredTypes,
interfaceMap
)}`;
}
if (util_1.isEnum(type)) {
return `export ${generateEnumDeclaration(
type.description,
type.name,
type.getValues()
)}`;
}
const isInput = type instanceof graphql_1.GraphQLInputObjectType;
const f1 = type.getFields();
const f = Object.keys(f1).map((k) => f1[k]);
const 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);
const 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) => {
const typeInterface = type.replace(/interface/g, 'export interface');
typesWithExport.push(
typeInterface.includes('?:')
? typeInterface
: typeInterface.replace(/:/g, '?:')
);
});
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