function buildFileWriteRequest()

in src/pluggable-objc-file-creation.ts [826:1029]


function buildFileWriteRequest<T>(
  request: ObjCGenerationRequest<T>,
  typeInfoProvider: ObjCGenerationTypeInfoProvider<T>,
  plugins: List.List<ObjCGenerationPlugIn<T>>,
): Either.Either<Error.Error[], FileWriter.FileWriteRequest> {
  const classFileFromTypeInfo: (
    typeInformation: T,
    typeName: string,
    comments: string[],
    file?: Code.File,
  ) => Either.Either<Error.Error, Code.File> =
    classFileCreationFunctionWithBaseClassAndPlugins(
      request.baseClassName,
      request.baseClassLibraryName,
      request.diagnosticIgnores,
      request.path,
      plugins,
      typeInfoProvider,
    );

  const outputPath: File.AbsoluteFilePath = Maybe.match(
    function (file: File.AbsoluteFilePath): File.AbsoluteFilePath {
      return file;
    },
    function (): File.AbsoluteFilePath {
      const fullPathAsString: string = File.getAbsolutePathString(request.path);
      return File.getAbsoluteFilePath(path.dirname(fullPathAsString));
    },
    request.outputPath,
  );

  // filter down to the plugins that we want to emit if we are filtered
  const filteredPlugins = List.filter(function (p) {
    return OutputControl.ShouldEmitPluginFile(
      request.outputFlags,
      p.requiredIncludesToRun[0],
    );
  }, plugins);

  // In single-file output mode, everything goes into the same file, and
  // we don't listen to any of the output control flags for not outputting
  // the base file.
  if (request.outputFlags.singleFile) {
    const baseFileOrError = classFileFromTypeInfo(
      request.typeInformation,
      typeInfoProvider.typeNameForType(request.typeInformation),
      typeInfoProvider.commentsForType(request.typeInformation),
    )
      .map(function (file: Code.File) {
        return transformFileWithPlugins(
          file,
          request.typeInformation,
          filteredPlugins,
        );
      })
      .mbind(function (file: Code.File) {
        // gather additional type files, then merge them into our base file
        const extraTypes = typeInfoProvider.additionalTypesForType(
          request.typeInformation,
        );
        return extraTypes.reduce(function (
          prev: Either.Either<Error.Error, Code.File>,
          type: T,
        ) {
          return prev.mbind(function (prevFile: Code.File) {
            return classFileFromTypeInfo(
              type,
              typeInfoProvider.typeNameForType(type),
              typeInfoProvider.commentsForType(type),
              prevFile,
            )
              .map(function (file: Code.File) {
                return transformFileWithPlugins(file, type, filteredPlugins);
              })
              .map(function (file: Code.File) {
                // plugins can add headers we don't want, as it's tough to know whether you
                // are the main type, or an additional type when generating the file, so for
                // now I am just going to filter the headers out.
                var newFile = file;
                newFile.imports = file.imports.filter(function (
                  value: ObjC.Import,
                ) {
                  return (
                    value.file != typeInfoProvider.typeNameForType(type) + '.h'
                  );
                });
                newFile.forwardDeclarations = file.forwardDeclarations.concat([
                  ObjC.ForwardDeclaration.ForwardClassDeclaration(
                    typeInfoProvider.typeNameForType(type),
                  ),
                ]);
                return newFile;
              });
          });
        },
        Either.Right<Error.Error, Code.File>(file));
      });

    const fileWriteRequestOrError: Either.Either<
      Error.Error,
      FileWriter.FileWriteRequest
    > = Either.mbind(function (file: Code.File) {
      const emptyRequest = Either.Right<
        Error.Error,
        FileWriter.FileWriteRequest
      >({
        name: typeInfoProvider.typeNameForType(request.typeInformation),
        requests: List.of<FileWriter.Request>(),
      });

      const result = fileCreationRequestContainingAdditionalFile(
        request.outputFlags,
        outputPath,
        emptyRequest,
        file,
      );
      return result;
    }, baseFileOrError);

    return fileCreationRequestContainingArrayOfPossibleError(
      fileWriteRequestOrError,
    );
  } else {
    const typeInfos = [request.typeInformation].concat(
      typeInfoProvider.additionalTypesForType(request.typeInformation),
    );

    // each type info will manifest as its own file, regardless of whether
    // the single-file output flag is set.
    const allFileRequests = typeInfos.map(function (type: T) {
      // build base file if we are allowed to
      const classFile: Either.Either<Error.Error, Code.File[]> =
        OutputControl.ShouldEmitObject(request.outputFlags)
          ? classFileFromTypeInfo(
              type,
              typeInfoProvider.typeNameForType(type),
              typeInfoProvider.commentsForType(type),
            ).map(function (file: Code.File) {
              return [file];
            })
          : Either.Right<Error.Error, Code.File[]>([]);

      // add files from plugins, or merge them into our base file
      // We'll end up with an array of files. If single file is set,
      // we will only have one entry in the array.
      const filesToWrite: Either.Either<Error.Error, Code.File[]> =
        classFile.map(function (files: Code.File[]) {
          const additionalFiles: Code.File[] = List.foldl<
            ObjCGenerationPlugIn<T>,
            Code.File[]
          >(
            (soFar, plugin) => buildAdditionalFiles(type, soFar, plugin),
            [],
            filteredPlugins,
          );
          return files.concat(additionalFiles);
        });

      // create file write requests for each file
      const completeFileCreationRequest: Either.Either<
        Error.Error,
        FileWriter.FileWriteRequest
      > = Either.mbind(function (files: Code.File[]) {
        return files.reduce(
          function (
            soFar: Either.Either<Error.Error, FileWriter.FileWriteRequest>,
            currentFile: Code.File,
          ) {
            return fileCreationRequestContainingAdditionalFile(
              request.outputFlags,
              outputPath,
              soFar,
              currentFile,
            );
          },
          Either.Right<Error.Error, FileWriter.FileWriteRequest>({
            name: typeInfoProvider.typeNameForType(type),
            requests: List.of<FileWriter.Request>(),
          }),
        );
      }, filesToWrite);

      return fileCreationRequestContainingArrayOfPossibleError(
        completeFileCreationRequest,
      );
    });

    // Unify all write requests
    return allFileRequests.reduce(function (
      soFar: Either.Either<Error.Error[], FileWriter.FileWriteRequest>,
      current: Either.Either<Error.Error[], FileWriter.FileWriteRequest>,
    ) {
      return Either.map(function (
        request: FileWriter.FileWriteRequest,
      ): FileWriter.FileWriteRequest {
        return {
          name: request.name,
          requests: List.append(request.requests, current.right!.requests),
        };
      },
      soFar);
    });
  }
}