author import { WriteStream } from "fs" ; import { GraphQLSchema , GraphQLUnionType } from "graphql" ; import { GeneratorConfig } from "./GeneratorConfig" ; import { InheritanceInfo } from "./InheritanceInfo" ; import { Writer } from "./Writer" ; export class CommonTypesWriter extends Writer { constructor ( schema , private inheritanceInfo , stream , config ) { super ( stream , config ) ; const info = new InheritanceInfo ( schema ) ; } protected writeCode ( ) { this . writeImplementionType ( ) ; this . writeCastMethod ( 'up' ) ; this . writeCastMethod ( 'down' ) ; this . writeWithTypeNameType ( ) ; } private writeWithTypeNameType ( ) { this . text ( WITH_TYPE_NAME_DECLARATION ) ; this . text ( "\n" ) ; } private writeImplementionType ( ) { const t = this . text . bind ( this ) ; t ( IMPLEMENTANTION_TYPE_COMMENT ) ; t ( "export type ImplementationType<T> = " ) ; this . enter ( "BLANK" , true ) ; for ( const [ type , castTypes ] of this . inheritanceInfo . downcastTypeMap ) { t ( "T extends '" ) ; t ( type . name ) ; t ( "' ? " ) ; this . enter ( "BLANK" ) ; if ( ! ( type instanceof GraphQLUnionType ) ) { t ( "'" ) ; t ( type . name ) ; t ( "'" ) ; } for ( const castType of castTypes ) { this . separator ( " | " ) ; t ( "ImplementationType<'" ) ; t ( castType . name ) ; t ( "'>" ) ; } this . leave ( ) ; t ( " :\n" ) ; } t ( "T\n" ) ; this . leave ( ) ; t ( ";" ) ; } private writeCastMethod ( prefix ) { const t = this . text . bind ( this ) ; const castTypeMap = prefix === 'up' ? this . inheritanceInfo . upcastTypeMap : this . inheritanceInfo . downcastTypeMap ; t ( prefix === 'up' ? UPCAST_FUNC_COMMENT : DOWNCAST_FUNC_COMMENT ) ; t ( `\nexport function ${ prefix }castTypes(typeName: string): string[] ` ) ; this . scope ( { type : "BLOCK" , multiLines : true , suffix : "\n" } , ( ) => { t ( "const typeNames: string[] = [];\n" ) ; t ( `${ prefix }castTypes0(typeName, typeNames);\n` ) ; t ( "return typeNames;\n" ) ; } ) ; t ( `\nfunction ${ prefix }castTypes0(typeName: string, output: string[]) ` ) ; this . scope ( { type : "BLOCK" , multiLines : true , suffix : "\n" } , ( ) => { t ( "switch (typeName)" ) ; this . scope ( { type : "BLOCK" , multiLines : true , suffix : "\n" } , ( ) => { for ( const [ type , castTypes ] of castTypeMap ) { t ( `case '${ type . name }':` ) ; this . scope ( { type : "BLANK" , multiLines : true } , ( ) => { if ( ! ( type instanceof GraphQLUnionType ) ) { t ( `output.push('${ type . name }');\n` ) ; } for ( const castType of castTypes ) { t ( `${ prefix }castTypes0('${ castType . name }', output);\n` ) ; } t ( "break;\n" ) ; } ) ; } t ( "default:" ) ; this . scope ( { type : "BLANK" , multiLines : true } , ( ) => { t ( `output.push(typeName);\n` ) ; t ( "break;\n" ) ; } ) ; } ) ; } ) ; } } const IMPLEMENTANTION_TYPE_COMMENT = `
/**
 * 
 * This 'ImplementationType' is used for inheritance, let's see an example, if graphql schema is:
 * 
 *     interface A {}
 *     interface B implements A {}
 *     type C implements B & A {}
 * 
 * Typescript code will be generated by like this:
 * 
 *     export ImplementationType<T extends string> =
 *         T extends 'A' ? 'A' | ImplementationType<'B'> :
 *         T extends 'B' ? 'B' | ImplementationType<'C'> :
 *         T
 *     ;
 * 
 * Let's see another example with abstract type, if the graphql schema is:
 * 
 *     union AbstractType = Impl1 | Imple2;
 *     type Impl1 {}
 *     type Impl2 {}
 * 
 * Typescript code will be generated by like this:
 * 
 *     export ImplementationType<T extends string> =
 *         T extends 'AbstractType' ? ImplemenationType<'Impl1'> | ImplementationType<'Impl2'> :
 *         T
 *     ;
 */
` ; const UPCAST_FUNC_COMMENT = `
/**
 * 
 * This 'upcastTypes' is used for inheritance, let's see an example, if graphql schema is:
 * 
 *     interface A {}
 *     interface B implements A {}
 *     type C implements B & A {}
 * 
 * Typescript code will be generated by like this:
 * 
 *     export function upcastTypes(typeName: string): string[] {
 *         const typeNames: string[] = [];
 *         upcastTypes0(typeName, typeNames);
 *         return typeNames;
 *     }
 * 
 *     function upcastTypes0(typeName: string, output: string[]) {
 *         switch (typeName){
 *             case 'B':
 *                 output.push('B');
 *                 upcastTypes0('A', output);
 *                 break;
 *             case 'C':
 *                 output.push('C');
 *                 upcastTypes0('B', output);
 *                 break;
 *             default:
 *                 output.push(typeName);
 *                 break;
 *         }
 *     }    
 * 
 * Let's see another example with abstract type, if the graphql schema is:
 * 
 *     union AbstractType = Impl1 | Imple2;
 *     type Impl1 {}
 *     type Impl2 {}
 * 
 * Typescript code will be generated by like this:
 * 
 *     export function upcastTypes(typeName: string): string[] {
 *         const typeNames: string[] = [];
 *         upcastTypes0(typeName, typeNames);
 *         return typeNames;
 *     }
 * 
 *     function upcastTypes0(typeName: string, output: string[]) {
 *         switch (typeName){
 *             case 'Impl1':
 *                 output.push('Impl1');
 *                 upcastTypes0('AbstractType', output);
 *                 break;
 *             case 'Impl2':
 *                 output.push('Impl2');
 *                 upcastTypes0('AbstractType', output);
 *                 break;
 *             default:
 *                 output.push(typeName);
 *                 break;
 *         }
 *     }     
 */
` ; const DOWNCAST_FUNC_COMMENT = `
/**
 * 
 * This 'downcastTypes' is used for inheritance, let's see an example, if graphql schema is:
 * 
 *     interface A {}
 *     interface B implements A {}
 *     type C implements B & A {}
 * 
 * Typescript code will be generated by like this:
 * 
 *     export function downcastTypes(typeName: string): string[] {
 *         const typeNames: string[] = [];
 *         downcastTypes0(typeName, typeNames);
 *         return typeNames;
 *     }
 * 
 *     function downcastTypes0(typeName: string, output: string[]) {
 *         switch (typeName){
 *             case 'A':
 *                 output.push('A');
 *                 downcastTypes0('B', output);
 *                 break;
 *             case 'B':
 *                 output.push('B');
 *                 downcastTypes0('C', output);
 *                 break;
 *             default:
 *                 output.push(typeName);
 *                 break;
 *         }
 *     }
 * 
 * Let's see another example with abstract type, if the graphql schema is:
 * 
 *     union AbstractType = Impl1 | Imple2;
 *     type Impl1 {}
 *     type Impl2 {}
 * 
 * Typescript code will be generated by like this:
 * 
 *     export function downcastTypes(typeName: string): string[] {
 *         const typeNames: string[] = [];
 *         downcastTypes0(typeName, typeNames);
 *         return typeNames;
 *     }
 * 
 *     function downcastTypes0(typeName: string, output: string[]) {
 *         switch (typeName){
 *             case 'AbstractType':
 *                 downcastTypes0('Impl1', output);
 *                 downcastTypes0('Impl2', output);
 *                 break;
 *             default:
 *                 output.push(typeName);
 *                 break;
 *         }
 *     }   
 */
` ; const WITH_TYPE_NAME_DECLARATION = `
export type WithTypeName<T, TypeName extends string> =
    T extends {readonly __typename: string} ?
    T :
    T & {readonly __typename: TypeName};
;
` ;