UNPKG

@gql2ts/from-query

Version:

generate typescript interfaces from a graphql schema and query

139 lines 7.2 kB
"use strict"; // 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