@gql2ts/from-query
Version:
generate typescript interfaces from a graphql schema and query
139 lines • 7.2 kB
JavaScript
;
// tslint:disable
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = (OPTIONS) => {
const printArray = OPTIONS.wrapList;
const printNullable = type => OPTIONS.printType(type, false);
const wrapNullable = ({ nullable }) => nullablePrinter => str => (nullable ? nullablePrinter(str) : str);
const typeUnion = types => types.join(' | ');
const printStringLiteral = stringLiteral => `'${stringLiteral}'`;
const printInterface = selection => typeUnion(selection.fragments.map(frag => {
const name = (frag.directives.gql2ts && frag.directives.gql2ts.arguments.interfaceName) ?
frag.directives.gql2ts.arguments.interfaceName :
OPTIONS.generateFragmentName(frag.typeDefinition.type);
return name;
}));
const quoteValue = value => `'${value}'`;
const printEnum = values => typeUnion(values.map(quoteValue));
const printType = (type, node, nameOverride) => {
if (typeof type === 'string') {
// @TODO use user input not the default
return OPTIONS.typeMap[type] || OPTIONS.typeMap.__DEFAULT;
}
const nullWrapper = wrapNullable(type)(printNullable);
switch (type.kind) {
case 'TypenameDefinition':
return nullWrapper(Array.isArray(type.type) ? typeUnion(type.type.map(printStringLiteral)) : printStringLiteral(type.type));
case 'TypeDefinition':
return nullWrapper(type.isScalar ? OPTIONS.typeMap[type.type] || OPTIONS.typeMap.__DEFAULT : nameOverride || type.type);
case 'ListTypeDefinition':
return nullWrapper(printArray(printType(type.of, node, nameOverride)));
case 'InterfaceTypeDefinition':
return nullWrapper(printInterface(node));
case 'EnumTypeDefinition':
return nullWrapper(printEnum(type.values));
default:
throw new Error('Unsupported TypeDefinition');
}
};
const getReferenceType = type => {
switch (type.kind) {
case 'TypenameDefinition':
return Array.isArray(type.type) ? typeUnion(type.type) : type.type;
case 'TypeDefinition':
return `SelectionOn${type.type}`;
case 'ListTypeDefinition':
return getReferenceType(type.of);
case 'EnumTypeDefinition':
return type.type;
default:
throw new Error('Unsupported TypeDefinition');
}
};
const hasOptionalDirective = directives => !!directives.include || !!directives.skip;
const printField = ({ name, type, node, nameOverride, optional = false }) => OPTIONS.formatInput(name, optional, printType(type, node, nameOverride));
class TypePrinter {
constructor(ir) {
this.ir = ir;
this._declarations = new Map();
}
printQuery() {
this.buildDeclarations(this.ir.name || 'AnonymousQuery', this.ir.selections);
return [...Array.from(this._declarations.entries())].map(([key, value]) => OPTIONS.exportFunction(OPTIONS.interfaceBuilder(key, OPTIONS.generateInterfaceDeclaration(value)))).join('\n\n');
}
buildDeclarations(parent, selections) {
let currentName = parent;
let count = 1;
const nextDeclaration = selections.map(selection => this.buildDeclaration(selection));
const nextDeclarationSearchKey = [...nextDeclaration].sort().join('\n');
while (this._declarations.has(currentName)) {
const preExistingDeclaration = [...this._declarations.get(currentName)].sort().join('\n');
if (preExistingDeclaration === nextDeclarationSearchKey) {
return currentName;
}
currentName = parent + count++;
}
this._declarations.set(currentName, selections.map(selection => this.buildDeclaration(selection)));
return currentName;
}
buildDeclaration(selection) {
switch (selection.kind) {
case 'Field':
const fieldName = this.buildDeclarations(getReferenceType(selection.typeDefinition), selection.selections);
return printField({
name: selection.name,
type: selection.typeDefinition,
node: selection,
nameOverride: fieldName,
optional: hasOptionalDirective(selection.directives)
});
case 'InterfaceNode':
selection.fragments.map(frag => {
let name = (frag.directives.gql2ts && frag.directives.gql2ts.arguments.interfaceName) ?
frag.directives.gql2ts.arguments.interfaceName :
frag.typeDefinition.kind === 'TypeDefinition' ?
OPTIONS.generateFragmentName(frag.typeDefinition.type) :
'InterfaceNode' + Math.random().toString().replace('.', '');
name = this.buildDeclarations(name, frag.selections);
frag.directives = frag.directives || {};
frag.directives.gql2ts = Object.assign({}, (frag.directives.gql2ts || {}), { arguments: Object.assign({}, ((frag.directives.gql2ts || { arguments: {} }).arguments), { interfaceName: name }) });
return name;
});
const selectionHasDirectives = hasOptionalDirective(selection.directives);
const fragmentsHaveDirectives = selection.fragments.some(frag => hasOptionalDirective(frag.directives));
return printField({
name: this.generateSelectionName(selection),
type: selection.typeDefinition,
node: selection,
optional: selectionHasDirectives || fragmentsHaveDirectives
});
case 'TypenameNode':
return printField({
name: selection.name,
type: selection.typeDefinition,
node: selection
});
case 'LeafNode':
return printField({
name: selection.name,
type: selection.typeDefinition,
node: selection,
optional: hasOptionalDirective(selection.directives)
});
default:
throw new Error('Unsupported Selection');
}
}
generateSelectionName(selection) {
switch (selection.kind) {
case 'Field':
return selection.name;
case 'InterfaceNode':
return selection.name;
}
}
}
const generateTypes = ir => new TypePrinter(ir).printQuery();
return generateTypes;
};
//# sourceMappingURL=generate.js.map