# Refactoring Compas code-gen: part 2

Recently we started working on refactoring the code generators of Compas.js. Compas.js is a framework based on a structure providing generated routers, validators, typed api clients and more.

## Current situation

Compas.js has a main entrypoint provided by the `@compas/code-gen` package: `App`. The idea behind that name was that you would define your whole application structure through that single `App` instance. The `App` instance then exposes three methods to generate some output, as mentioned in [Part 1 of this series](https://dirkdevisser.com/refactoring-compas-code-gen#heading-combining-generate-calls):

- `App#generate`: main entrypoint for generating
- `App#generateTypes`: generate call to combine types of various other generate calls
- `App#generateOpenApi`: convert Compas.js structure to an OpenAPI 3.x compatible specification

## `@compas/code-gen/experimental` 

Our redesign still has a single entrypoint, but now called `Generator` instead of `App`. We experienced that most of the time you need more than a single generator, for example when importing the OpenAPI spec of a service that you are using. This entrypoint will also have a single generate call `Generator#generate`. It combines the options and usecases of the previous three generate calls in to a single method, combining and redesigning the accepted options, which are discussed below. 

Using the `Generator` will look something like this (minor changes could still be done):

```js
import { Generator } from "@compas/code-gen/experimental";

// The TypeCreators are backwards compatible
import { TypeCreator } from "@compas/code-gen";

const appGenerator = new Generator();

appGenerator.add(/* ... */);

appGenerator.generate({
  outputDirectory: "./src/generated",
  generators: {
    structure: {},
    router: {
      target: { library: "koa" },
    },
  },
});

const publicGenerator = appGenerator.selectGroups("public");
const publicFiles = publicGenerator.generate({
  generators: {
    openApi: {},
  }
});

// Do something with the public files
```

## Improving targets

The current situation to define targets accepted by `App#generate` looks something like:

```ts
type GenerateOptions = {
  isBrowser?: boolean;
  isNode?: boolean;
  isNodeServer?: boolean;
  useTypescript?: boolean;
}
```

As mentioned earlier, I personally have no clue what the difference is between `isNode` and `isNodeServer`. Other problems with the current setup are that `isBrowser` currently should be used for both browser as well as react-native targets. Adding a new option to for example add support for generating Go would result in a new `useGolang` option. 

In the new setup we use a top-level `targetLanguage` option. The output of a single generate call could add imports between its generated files, so these should be using the same language. For generator specific targets, like the generate api clients, we accept a nested `target`

```
type GenerateOptions = {
  targetLanguage: "js" | "ts";
  generators: {
    apiClient: {
      target: {
        library: "axios";
        targetRuntime: "node.js" | "browser" | "react-native";
      };
    };
  };
};
```

This new target definition for in this case api client ensure that we could add support for [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), [Undici](https://undici.nodejs.org/)  or a completely different client for a different `targetLanguage` without having unrelated options top level affecting the output like `isNodeServer` would.

A drawback here is that the `targetLanguage` and the nested `target` definitions have some relation between them. This could lead to a degraded developer experience. It is then more important than ever to provide clear errors when the `targetLanguage` and `target` are not compatible, nudging the user in either a different `target` or a different `targetLanguage`.

## Scope

The flexibility of the new options give a lot of oppurtinity to directly support new targets like the aforementioned Fetch or Undici based api clients. However, we limit the scope of this refactor to only support the current outputs:

- JavaScript or Typescript output
- [Axios](https://axios-http.com/docs/intro) based api clients
- [Koa](https://koajs.com/) based router
- And PostgreSQL based SQL


For the all supported options see [the initial refactor issue on GitHub](https://github.com/compasjs/compas/issues/2010) or the [TypeScript definition on the experimental branch](https://github.com/compasjs/compas/blob/feat/experimental-code-gen/packages/code-gen/src/experimental/generator.d.ts)

