compasjs / compas

Unified backend tooling
https://compasjs.com
MIT License
12 stars 8 forks source link

code-gen: environment based generators #2010

Closed dirkdev98 closed 1 year ago

dirkdev98 commented 2 years ago

As started with the recent #1908 we should redo some generating based on the provided or inferred environment.

Current supported environments

See below for the current accepted options: https://github.com/compasjs/compas/issues/2010#issuecomment-1242922965

Things to solve:

API proposal;

export class Generator {
  // Like the existing `App#add` accept named types
  add(...types: TypeBuilderLike[]): this;

  // Add a way to add an existing structure, so it can be generated with different options.
  // This should include the options used for generating somehow. This way we can support consolidating types in to a single output
  addStructure(structureOrDirectory: string|Structure): this;

  // Create a new generator with a subselection of groups, which have their references resolved.
  selectGroups(groups: string[]): Generator;

  generate(options: {
    targetLanguage?: "js" | "ts";
    targetRuntime?: "node.js" | "browser" | "react-native";
    outputDirectory?: string;
    generators: {
      structure?: {
        // Enable a structure dump
      };
      openApi?: {
        openApiExtensions?: OpenApiExtensions | undefined;
        openApiRouteExtensions?: Record<string, any> | undefined;
      };
      router?: {
        targetLibrary: "koa";
        dumpApiStructure?: boolean;
      };
      database?: {
        targetDialect: "postgres";
        dumpDDL?: boolean;
      };
      validators?: {
        // Enable generating validators for the base types, some generators will include validators automatically
      };
      types?: {
        useGlobalTypes?: boolean; // Only applicable when using "js" or "ts"
        importPath?: string; // Import types from deduped types
      };
      dedupedTypes?: {
        useGlobalTypes?: boolean;
      };
      apiClient?: {
        targetLibrary?: "axios";
        validateResponses?: boolean;
        globalClients?: boolean;
        withReactHooks?: "react-query";
      };
    }
  }): OutputFile[];
}

Usage:

const app = new Generator();

app.add(...);

const publicApi = generator.selectGroups(["public", "other"]);

app.generate({
  targetLanguage: "js",
  targetRuntime: "node.js",
  outputDirectory: "./src/generated/application",
  generators: {
    router: {
      targetLibrary: "koa",
      dumpApiStructure: true,
    },
    database: {
      targetDialect: "postgres",
    }
  }
});

publicApi.generate({
  outputDirectory: "./src/generated/public-api",
  generators: {
    openApi: { /* ... */ },
  },
});

const types = new Generator();

await types.addCompasStructureFile("./src/generated/application");

types.generate({
  targetLanguage: "js",
  targetRuntime: "node.js",
  outputDirectory: "./types/generated",
  generators: {
    types: {
      useGlobalTypes: true,
    },
  }
});

This probably won't land in a single Compas release, so we may want to use @compas/code-gen/experimental for the new exports.

dirkdev98 commented 2 years ago

Current accepted options:

export class App {
  static defaultEslintIgnore: string[];

  generateOpenApi(
    options: {
      inputPath: string;
      outputFile: string;
      enabledGroups?: string[] | undefined;
      verbose?: boolean | undefined;
      openApiExtensions?: OpenApiExtensions | undefined;
      openApiRouteExtensions?: Record<string, any> | undefined;
    },
  ): Promise<void>;

  generateTypes(
    options: {
      outputDirectory: string;
      inputPaths: string[];
      dumpCompasTypes?: boolean | string[] | undefined;
      verbose?: boolean | undefined;
      fileHeader?: string | undefined;
    },
  ): Promise<void>;

  generate(
    options: {
      enabledGroups?: string[] | undefined;
      isBrowser?: boolean | undefined;
      isNode?: boolean | undefined;
      isNodeServer?: boolean | undefined;
      environment?:
        | {
            clientRuntime?: "browser" | "react-native" | undefined;
          }
        | undefined;
      enabledGenerators?:
        | ("type" | "validator" | "router" | "sql" | "apiClient" | "reactQuery")[]
        | undefined;
      useTypescript?: boolean | undefined;
      dumpStructure?: boolean | undefined;
      dumpApiStructure?: boolean | undefined;
      dumpPostgres?: boolean | undefined;
      fileHeader?: string | undefined;
      outputDirectory: string;
      declareGlobalTypes?: false | undefined;
    },
  ): Promise<void>;
}