apollo-codegen
Version:
Generate API code or type annotations based on a GraphQL schema and query documents
368 lines (309 loc) • 12.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.generateSource = generateSource;
exports.typeDeclarationForGraphQLType = typeDeclarationForGraphQLType;
exports.interfaceVariablesDeclarationForOperation = interfaceVariablesDeclarationForOperation;
exports.typeDeclarationForOperation = typeDeclarationForOperation;
exports.typeDeclarationForFragment = typeDeclarationForFragment;
exports.propertiesFromFields = propertiesFromFields;
exports.propertyFromField = propertyFromField;
exports.propertyDeclarations = propertyDeclarations;
var _graphql = require('graphql');
var _graphql2 = require('../utilities/graphql');
var _changeCase = require('change-case');
var _inflected = require('inflected');
var _inflected2 = _interopRequireDefault(_inflected);
var _printing = require('../utilities/printing');
var _CodeGenerator = require('../utilities/CodeGenerator');
var _CodeGenerator2 = _interopRequireDefault(_CodeGenerator);
var _language = require('./language');
var _types = require('./types');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function generateSource(context, options) {
var generator = new _CodeGenerator2.default(context);
generator.printOnNewline('/* @flow */');
generator.printOnNewline('// This file was automatically generated and should not be edited.');
typeDeclarationForGraphQLType(context.typesUsed.forEach(function (type) {
return typeDeclarationForGraphQLType(generator, type);
}));
// When an object has fragment spreads or inline fragments
// and __typename is requested at the top level, __typename
// needs to be added as a property of the fragments
var fragmentsWithTypenameField = {};
Object.values(context.operations).forEach(function (operation) {
interfaceVariablesDeclarationForOperation(generator, operation);
typeDeclarationForOperation(generator, operation, fragmentsWithTypenameField);
});
Object.values(context.fragments).forEach(function (operation) {
return typeDeclarationForFragment(generator, operation, fragmentsWithTypenameField);
});
return generator.output;
}
function typeDeclarationForGraphQLType(generator, type) {
if (type instanceof _graphql.GraphQLEnumType) {
enumerationDeclaration(generator, type);
} else if (type instanceof _graphql.GraphQLInputObjectType) {
structDeclarationForInputObjectType(generator, type);
}
}
function enumerationDeclaration(generator, type) {
var name = type.name,
description = type.description;
var values = type.getValues();
generator.printNewlineIfNeeded();
generator.printOnNewline(description && `// ${description}`);
generator.printOnNewline(`export type ${name} =`);
var nValues = values.length;
values.forEach(function (value, i) {
return generator.printOnNewline(` "${value.value}"${i === nValues - 1 ? ';' : ' |'}${(0, _printing.wrap)(' // ', value.description)}`);
});
generator.printNewline();
}
function structDeclarationForInputObjectType(generator, type) {
var interfaceName = (0, _changeCase.pascalCase)(type.name);
(0, _language.typeDeclaration)(generator, {
interfaceName
}, function () {
var properties = propertiesFromFields(generator.context, Object.values(type.getFields()));
propertyDeclarations(generator, properties, true);
});
}
function interfaceNameFromOperation(_ref) {
var operationName = _ref.operationName,
operationType = _ref.operationType;
switch (operationType) {
case 'query':
return `${(0, _changeCase.pascalCase)(operationName)}Query`;
break;
case 'mutation':
return `${(0, _changeCase.pascalCase)(operationName)}Mutation`;
break;
case 'subscription':
return `${(0, _changeCase.pascalCase)(operationName)}Subscription`;
break;
default:
throw new _graphql.GraphQLError(`Unsupported operation type "${operationType}"`);
}
}
function interfaceVariablesDeclarationForOperation(generator, _ref2) {
var operationName = _ref2.operationName,
operationType = _ref2.operationType,
variables = _ref2.variables,
fields = _ref2.fields,
fragmentsReferenced = _ref2.fragmentsReferenced,
source = _ref2.source;
if (!variables || variables.length < 1) {
return null;
}
var interfaceName = `${interfaceNameFromOperation({ operationName, operationType })}Variables`;
(0, _language.typeDeclaration)(generator, {
interfaceName
}, function () {
var properties = propertiesFromFields(generator.context, variables);
propertyDeclarations(generator, properties, true);
});
}
function typeDeclarationForOperation(generator, _ref3, fragmentsWithTypenameField) {
var operationName = _ref3.operationName,
operationType = _ref3.operationType,
variables = _ref3.variables,
fields = _ref3.fields,
fragmentSpreads = _ref3.fragmentSpreads,
inlineFragments = _ref3.inlineFragments,
fragmentsReferenced = _ref3.fragmentsReferenced,
source = _ref3.source;
if (hasTypenameField(fields)) {
console.error('__typename on operations are not yet supported');
}
var interfaceName = interfaceNameFromOperation({ operationName, operationType });
var properties = propertiesFromFields(generator.context, fields, {
typeNameSuffix: `From${operationName}`
});
(0, _language.typeDeclaration)(generator, { interfaceName }, function () {
propertyDeclarations(generator, properties, true);
});
properties.forEach(function (_ref4) {
var fragmentSpreads = _ref4.fragmentSpreads,
inlineFragments = _ref4.inlineFragments,
bareTypeName = _ref4.bareTypeName;
if (fragmentSpreads.length > 0) {
fragmentSpreads.forEach(function (fragmentSpread) {
fragmentsWithTypenameField[fragmentSpread] = true;
});
}
if (inlineFragments.length > 0) {
var fragmentName = `${(0, _changeCase.pascalCase)(bareTypeName)}From${operationName}`;
handleInlineFragments(generator, fragmentName, inlineFragments);
}
});
}
function typeDeclarationForFragment(generator, _ref5, fragmentsWithTypenameField) {
var fragmentName = _ref5.fragmentName,
typeCondition = _ref5.typeCondition,
fields = _ref5.fields,
inlineFragments = _ref5.inlineFragments,
fragmentSpreads = _ref5.fragmentSpreads,
source = _ref5.source,
possibleTypes = _ref5.possibleTypes;
var interfaceName = `${(0, _changeCase.pascalCase)(fragmentName)}Fragment`;
if (inlineFragments.length > 0) {
handleInlineFragments(generator, interfaceName, inlineFragments);
} else {
(0, _language.typeDeclaration)(generator, {
interfaceName
// extendTypes: fragmentSpreads ? fragmentSpreads.map(f => `${pascalCase(f)}Fragment`) : null,
}, function () {
if (fragmentsWithTypenameField[fragmentName]) {
addTypenameFieldIfNeeded(generator, fields, typeCondition);
}
var properties = propertiesFromFields(generator.context, fields, {
typeNameSuffix: 'Fragment'
});
propertyDeclarations(generator, properties, true);
});
}
}
function propertiesFromFields(context, fields) {
var _ref6 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
forceNullable = _ref6.forceNullable,
typeNameSuffix = _ref6.typeNameSuffix;
return fields.map(function (field) {
return propertyFromField(context, field, { forceNullable, typeNameSuffix });
});
}
function propertyFromField(context, field) {
var _ref7 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
forceNullable = _ref7.forceNullable,
typeNameSuffix = _ref7.typeNameSuffix;
var fieldName = field.name,
fieldType = field.type,
description = field.description,
fragmentSpreads = field.fragmentSpreads,
inlineFragments = field.inlineFragments;
fieldName = fieldName || field.responseName;
var propertyName = fieldName;
var property = { fieldName, fieldType, propertyName, description, inlineFragments };
var namedType = (0, _graphql.getNamedType)(fieldType);
if ((0, _graphql.isCompositeType)(namedType)) {
var typeName = void 0,
bareTypeName = void 0;
if (propertyName === '__typename') {
// Handle the __typename field specially. the fieldType is set
// to the parentType but we want the target type to be a string literal
// of the parentType.
bareTypeName = `"${fieldType}"`;
typeName = `"${fieldType}"`;
} else {
bareTypeName = (0, _changeCase.pascalCase)(_inflected2.default.singularize(propertyName));
if (inlineFragments && inlineFragments.length > 0 || fragmentSpreads && fragmentSpreads.length > 0) {
typeName = (0, _types.typeNameFromGraphQLType)(context, fieldType, `${(0, _changeCase.pascalCase)(bareTypeName)}${typeNameSuffix}`);
} else {
typeName = (0, _types.typeNameFromGraphQLType)(context, fieldType, bareTypeName);
}
}
var isArray = false;
if (fieldType instanceof _graphql.GraphQLList) {
isArray = true;
} else if (fieldType instanceof _graphql.GraphQLNonNull && fieldType.ofType instanceof _graphql.GraphQLList) {
isArray = true;
}
var isNullable = true;
if (fieldType instanceof _graphql.GraphQLNonNull && !forceNullable) {
isNullable = false;
}
return Object.assign({}, property, {
typeName, bareTypeName, fields: field.fields, isComposite: true, fragmentSpreads, inlineFragments, fieldType,
isArray, isNullable
});
} else {
var _typeName = (0, _types.typeNameFromGraphQLType)(context, fieldType);
return Object.assign({}, property, { typeName: _typeName, isComposite: false, fieldType });
}
}
function propertyDeclarations(generator, properties, inInterface) {
if (!properties) return;
properties.forEach(function (property) {
if (property.fields && property.fields.length > 0 || property.inlineFragments && property.inlineFragments.length > 0) {
if (property.inlineFragments.length > 0) {
(0, _language.propertyDeclaration)(generator, Object.assign({}, property, {
inInterface
}));
} else {
(0, _language.propertyDeclaration)(generator, Object.assign({}, property, { inInterface }), function () {
var properties = propertiesFromFields(generator.context, property.fields);
propertyDeclarations(generator, properties);
});
}
} else {
(0, _language.propertyDeclaration)(generator, Object.assign({}, property, { inInterface }));
}
});
}
function makeTypenameField(typeName) {
return {
responseName: '__typename',
fieldName: '__typename',
type: typeName
};
}
function hasTypenameField(fields) {
if (!fields) {
return false;
}
return fields.find(function (field) {
return field.fieldName === '__typename' || field.responseName === '__typename';
});
}
function removeTypenameFieldIfExists(generator, fields) {
if (hasTypenameField(fields)) {
fields = fields.filter(function (field) {
return field.fieldName !== '__typename' || field.responseName !== '__typename';
});
return true;
} else {
return false;
}
}
/**
* NOTE: Mutates `fields`
*/
function addTypenameFieldIfNeeded(generator, fields, parentTypeName) {
var removed = removeTypenameFieldIfExists();
if (generator.context.addTypename || removed) {
fields.unshift(makeTypenameField(parentTypeName));
}
}
function handleInlineFragments(generator, fragmentName, inlineFragments) {
// Keep track of these generated destination type names so we can build a union afterwards
var unionTypes = [];
inlineFragments.forEach(function (_ref8) {
var fields = _ref8.fields,
typeCondition = _ref8.typeCondition;
var typeName = `${fragmentName}On${typeCondition}`;
unionTypes.push(typeName);
addTypenameFieldIfNeeded(generator, fields, typeCondition);
var properties = propertiesFromFields(generator.context, fields, {
typeNameSuffix: 'Fragment'
});
(0, _language.typeDeclaration)(generator, {
interfaceName: typeName
}, function () {
propertyDeclarations(generator, properties, true);
});
properties.forEach(function (_ref9) {
var inlineFragments = _ref9.inlineFragments,
bareTypeName = _ref9.bareTypeName;
if (inlineFragments && inlineFragments.length > 0) {
var innerFragmentName = `${bareTypeName}Fragment`;
handleInlineFragments(generator, innerFragmentName, inlineFragments);
}
});
});
// TODO: Refactor typeDeclaration to not automatically assume bracketed type
(0, _language.typeDeclaration)(generator, { interfaceName: fragmentName, noBrackets: true }, function () {
(0, _language.unionDeclaration)(generator, unionTypes);
});
}
//# sourceMappingURL=codeGeneration.js.map
;