HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/shop.komma.nl/node_modules/apollo-codegen-flow/src/codeGeneration.ts
import * as t from "@babel/types";
import { stripIndent } from "common-tags";
import {
  GraphQLEnumType,
  GraphQLInputObjectType,
  isEnumType,
  isInputObjectType
} from "graphql";

import {
  CompilerContext,
  Operation,
  Fragment,
  SelectionSet,
  Field
} from "apollo-codegen-core/lib/compiler";

import {
  typeCaseForSelectionSet,
  Variant
} from "apollo-codegen-core/lib/compiler/visitors/typeCase";

import { collectAndMergeFields } from "apollo-codegen-core/lib/compiler/visitors/collectAndMergeFields";

import { BasicGeneratedFile } from "apollo-codegen-core/lib/utilities/CodeGenerator";
import FlowGenerator, { ObjectProperty, FlowCompilerOptions } from "./language";
import Printer from "./printer";

class FlowGeneratedFile implements BasicGeneratedFile {
  fileContents: string;

  constructor(fileContents: string) {
    this.fileContents = fileContents;
  }
  get output() {
    return this.fileContents;
  }
}

function printEnumsAndInputObjects(
  generator: FlowAPIGenerator,
  context: CompilerContext
) {
  generator.printer.enqueue(stripIndent`
    //==============================================================
    // START Enums and Input Objects
    //==============================================================
  `);

  context.typesUsed.filter(isEnumType).forEach(enumType => {
    generator.typeAliasForEnumType(enumType);
  });

  context.typesUsed.filter(isInputObjectType).forEach(inputObjectType => {
    generator.typeAliasForInputObjectType(inputObjectType);
  });

  generator.printer.enqueue(stripIndent`
    //==============================================================
    // END Enums and Input Objects
    //==============================================================
  `);
}

export function generateSource(context: CompilerContext) {
  const generator = new FlowAPIGenerator(context);
  const generatedFiles: {
    sourcePath: string;
    fileName: string;
    content: FlowGeneratedFile;
  }[] = [];

  Object.values(context.operations).forEach(operation => {
    generator.fileHeader();
    generator.typeAliasesForOperation(operation);

    const output = generator.printer.printAndClear();

    generatedFiles.push({
      sourcePath: operation.filePath,
      fileName: `${operation.operationName}.js`,
      content: new FlowGeneratedFile(output)
    });
  });

  Object.values(context.fragments).forEach(fragment => {
    generator.fileHeader();
    generator.typeAliasesForFragment(fragment);

    const output = generator.printer.printAndClear();

    generatedFiles.push({
      sourcePath: fragment.filePath,
      fileName: `${fragment.fragmentName}.js`,
      content: new FlowGeneratedFile(output)
    });
  });

  generator.fileHeader();
  printEnumsAndInputObjects(generator, context);
  const common = generator.printer.printAndClear();

  return {
    generatedFiles,
    common
  };
}

export class FlowAPIGenerator extends FlowGenerator {
  context: CompilerContext;
  printer: Printer;
  scopeStack: string[];

  constructor(context: CompilerContext) {
    super(context.options as FlowCompilerOptions);

    this.context = context;
    this.printer = new Printer();
    this.scopeStack = [];
  }

  fileHeader() {
    this.printer.enqueue(
      stripIndent`
        /* @flow */
        /* eslint-disable */
        // @generated
        // This file was automatically generated and should not be edited.
      `
    );
  }

  public typeAliasForEnumType(enumType: GraphQLEnumType) {
    this.printer.enqueue(this.enumerationDeclaration(enumType));
  }

  public typeAliasForInputObjectType(inputObjectType: GraphQLInputObjectType) {
    const typeAlias = this.inputObjectDeclaration(inputObjectType);

    const { description } = inputObjectType;
    const exportDeclarationOptions = description
      ? { comments: ` ${description.replace("\n", " ")}` }
      : {};

    const exportedTypeAlias = this.exportDeclaration(
      typeAlias,
      exportDeclarationOptions
    );
    this.printer.enqueue(exportedTypeAlias);
  }

  public typeAliasesForOperation(operation: Operation) {
    const { operationType, operationName, variables, selectionSet } = operation;

    this.scopeStackPush(operationName);

    this.printer.enqueue(stripIndent`
      // ====================================================
      // GraphQL ${operationType} operation: ${operationName}
      // ====================================================
    `);

    // The root operation only has one variant
    // Do we need to get exhaustive variants anyway?
    const variants = this.getVariantsForSelectionSet(selectionSet);

    const variant = variants[0];
    const properties = this.getPropertiesForVariant(variant);

    const exportedTypeAlias = this.exportDeclaration(
      this.typeAliasObject(operationName, properties)
    );

    this.printer.enqueue(exportedTypeAlias);
    this.scopeStackPop();

    // Generate the variables interface if the operation has any variables
    if (variables.length > 0) {
      const interfaceName = operationName + "Variables";
      this.scopeStackPush(interfaceName);
      this.printer.enqueue(
        this.exportDeclaration(
          this.typeAliasObject(
            interfaceName,
            variables.map(variable => ({
              name: variable.name,
              annotation: this.typeAnnotationFromGraphQLType(variable.type)
            })),
            { keyInheritsNullability: true }
          )
        )
      );
      this.scopeStackPop();
    }
  }

  public typeAliasesForFragment(fragment: Fragment) {
    const { fragmentName, selectionSet } = fragment;

    this.scopeStackPush(fragmentName);

    this.printer.enqueue(stripIndent`
      // ====================================================
      // GraphQL fragment: ${fragmentName}
      // ====================================================
    `);

    const variants = this.getVariantsForSelectionSet(selectionSet);

    if (variants.length === 1) {
      const properties = this.getPropertiesForVariant(variants[0]);

      const name = this.annotationFromScopeStack(this.scopeStack).id.name;
      const exportedTypeAlias = this.exportDeclaration(
        this.typeAliasObject(name, properties)
      );

      this.printer.enqueue(exportedTypeAlias);
    } else {
      const unionMembers: t.FlowTypeAnnotation[] = [];
      variants.forEach(variant => {
        this.scopeStackPush(variant.possibleTypes[0].toString());
        const properties = this.getPropertiesForVariant(variant);

        const name = this.annotationFromScopeStack(this.scopeStack).id.name;
        const exportedTypeAlias = this.exportDeclaration(
          this.typeAliasObject(name, properties)
        );

        this.printer.enqueue(exportedTypeAlias);

        unionMembers.push(this.annotationFromScopeStack(this.scopeStack));

        this.scopeStackPop();
      });

      this.printer.enqueue(
        this.exportDeclaration(
          this.typeAliasGenericUnion(
            this.annotationFromScopeStack(this.scopeStack).id.name,
            unionMembers
          )
        )
      );
    }

    this.scopeStackPop();
  }

  private getVariantsForSelectionSet(selectionSet: SelectionSet) {
    return this.getTypeCasesForSelectionSet(selectionSet).exhaustiveVariants;
  }

  private getTypeCasesForSelectionSet(selectionSet: SelectionSet) {
    return typeCaseForSelectionSet(
      selectionSet,
      this.context.options.mergeInFieldsFromFragmentSpreads
    );
  }

  private getPropertiesForVariant(variant: Variant): ObjectProperty[] {
    const fields = collectAndMergeFields(
      variant,
      this.context.options.mergeInFieldsFromFragmentSpreads
    );

    return fields.map(field => {
      const fieldName = field.alias !== undefined ? field.alias : field.name;
      this.scopeStackPush(fieldName);

      let res;
      if (field.selectionSet) {
        const generatedTypeName = this.annotationFromScopeStack(
          this.scopeStack
        );
        res = this.handleFieldSelectionSetValue(generatedTypeName, field);
      } else {
        res = this.handleFieldValue(field, variant);
      }

      this.scopeStackPop();
      return res;
    });
  }

  private handleFieldSelectionSetValue(
    generatedTypeName: t.GenericTypeAnnotation,
    field: Field
  ) {
    const { selectionSet } = field;

    const annotation = this.typeAnnotationFromGraphQLType(
      field.type,
      generatedTypeName.id.name
    );

    const typeCase = this.getTypeCasesForSelectionSet(
      selectionSet as SelectionSet
    );
    const variants = typeCase.exhaustiveVariants;

    let exportedTypeAlias;
    if (variants.length === 1) {
      const variant = variants[0];
      const properties = this.getPropertiesForVariant(variant);
      exportedTypeAlias = this.exportDeclaration(
        this.typeAliasObject(
          this.annotationFromScopeStack(this.scopeStack).id.name,
          properties
        )
      );
    } else {
      const propertySets = variants.map(variant => {
        this.scopeStackPush(variant.possibleTypes[0].toString());
        const properties = this.getPropertiesForVariant(variant);
        this.scopeStackPop();
        return properties;
      });

      exportedTypeAlias = this.exportDeclaration(
        this.typeAliasObjectUnion(generatedTypeName.id.name, propertySets)
      );
    }

    this.printer.enqueue(exportedTypeAlias);

    return {
      name: field.alias ? field.alias : field.name,
      description: field.description,
      annotation: annotation
    };
  }

  private handleFieldValue(field: Field, variant: Variant) {
    let res;
    if (field.name === "__typename") {
      const annotations = variant.possibleTypes.map(type => {
        const annotation = t.stringLiteralTypeAnnotation(type.toString());
        return annotation;
      });

      res = {
        name: field.alias ? field.alias : field.name,
        description: field.description,
        annotation: t.unionTypeAnnotation(annotations)
      };
    } else {
      // TODO: Double check that this works
      res = {
        name: field.alias ? field.alias : field.name,
        description: field.description,
        annotation: this.typeAnnotationFromGraphQLType(field.type)
      };
    }

    return res;
  }

  public get output(): string {
    return this.printer.print();
  }

  scopeStackPush(name: string) {
    this.scopeStack.push(name);
  }

  scopeStackPop() {
    const popped = this.scopeStack.pop();
    return popped;
  }
}