npm package: @gi.ts/lib
Description: This package contains the core of `gi.ts`' logic. It is written to only depend directly on `@gi.ts/parser` and contains no dependencies on a specific JavaScript environment. It is typically run on Node.js, but could be run directly on GJS or in a browser.
Latest version: 2.0.0-alpha.3

# @gi.ts/lib

This package contains the core of `gi.ts`' logic. It is written to only depend directly on `@gi.ts/parser` and contains no dependencies on a specific JavaScript environment. It is typically run on Node.js, but could be run directly on GJS or in a browser.

## How It Works

`@gi.ts/lib` is split up into 6 distinct "stages" which convert a parsed XML GIR file into an output format (most commonly `.d.ts` TypeScript definitions).

1. Loading: loads XML strings from a file source
2. Parsing: converts XML strings into a traversable tree
3. Injection: injects additional types from GJS into the GIR data
4. Transformation: applies automated and manual changes like adding inferred generics
5. Generation: converts the tree into a desired format (e.g. a `.d.ts` file)
6. Formatting: optionally makes the output look pretty (`.d.ts` uses `prettier`)

### Loading

Currently implemented by `@gi.ts/node-loader` for `@gi.ts/cli`. The loader simply loads relevant XML files from the filesystem.

### Parsing (`fromXML`)

Parsing has two stages: literally parsing XML strings and then transforming those parsed strings into a traversable tree.

#### XML string parsing

This is currently implemented by `@gi.ts/parser` using `fast-xml-parser`.

#### Tree API

In the `gir/` directory there are "parsers" for every GObject Introspection type. Each parser is prefixed with `Gir` (e.g. `GirClass`, `GirEnum`) and they all implement a static function, `fromXML` which takes a parsed XML file and constructs the relevant type.

The goal of this stage is to transform XML definitions into a tree of JavaScript objects which can be manipulated and traversed in the later stages.

Collectively, `gir/` forms the tree API of `gi.ts`.

At the heart of all of this is `GirNSRegistry`. This class holds all the state for a single "tree" of libraries. `GirNSRegistry` is also where transformers, generators, injections, and
other modifications are registered.

### Injecting Overrides

Next, `gi.ts` "injects" definitions for GJS bindings which aren't included in the autogenerated GIR files. GJS typically calls these "overrides" but we also inject core GJS class' like `ParamSpec`.

There are two types of injections: `injections/[library]` and `generators/[format]/[library]`. The injections in `injections/[library]` are written using `gi.ts`' tree API and are exposed to all output formats (e.g. `.d.ts`, `.json`, `.html`). Some injections only make sense for specific formats, however, so they are implemented alongside the generator. Currently only `.d.ts` has generator-specific injections. Some of these are implemented as generator-specific injections because it would simply be too complicated to express the TypeScript constructions using `gi.ts`' tree API.

Calling `registry.transform` on a `GirNSRegistry` triggers the injections.

### Transformation

While a `GirNSRegistry` tree is _mutable_, transformations are typically done immutably so a reproducible tree exists before and after.

Transformations are registered using `registry.registerTransformation` which accepts a visitor. The tree API frequently uses [the visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern), with some modifications.

Calling `registry.transform` on a `GirNSRegistry` _registers_ the default transformations, which are applied to the tree prior to generation.

#### Generics

There are both manual and automatic generic transformations.

##### Automatic Generification
In `generics/visitor.ts`, an automatic transformation looks for functions similar to:

`function do_something(input: string): GObject.Object;`

And transforms them to...

`function do_something<T extends GObject.Object = GObject.Object>(input: string): T;`

This is because often GObject Introspected libraries express "generics" as the base class `GObject.Object`, this transformation allows users to correctly type these calls.

A great example of this is `Gtk.Builder` and `get_object` which returns `GObject.Object`.

```ts
const builder = new Gtk.Builder(...);
const button = builder.get_object<Gtk.Button>('my_button_id');
```

##### Manual Generics

In addition to the automated transformations, `gi.ts` also adds generics to common libraries like GTK, St, Clutter, Gio, and more to ease the use of common APIs.

These changes can be found in `generics/[library].ts` for each respective library.

### Generation

To _generate_ an output for a given namespace you must acquire the relevant generator from  `GirNSRegistry` by calling `registry.getGenerator(format)`. `registry.getGenerator('dts')` and `registry.getGenerator('json')` are built-in. If you specify a different format `GirNSRegistry` uses this resolution strategy:

1. Look for a package with the name `@gi.ts/generator-[format]`
2.                 ...with the name `gi-ts-generator-[format]`
3.                 ...with the name `[format]`

It expects the given package to have a default CommonJS `export =` set to a subclass of `FormatGenerator` (see `generators/generator.ts`).

The generator for `.d.ts` is at `generators/dts.ts` and the generator for `.json` is found at `generators/json.ts`.

The `generateNamespace` function of a generator _must_ return a string (it gets printed to a file), but the individual `generate[Type]` functions can return any format the generator desires. `.json` returns Json types, `.d.ts` returns strings, and `.html` returns React render functions.

### Formatting

The final (and simplest) step is formatting. `@gi.ts/lib` comes bundled with a formatter for `.json` files based on JavaScript's `JSON.stringify` utility. Parsing for other outputs must be implemented in external packages to keep `@gi.ts/lib` from depending on Node-specific APIs. Parsing for `.d.ts` files is implemented by `@gi.ts/cli` using Prettier's API.

You can register a formatter by calling `registry.registerFormatter` on `GirNSRegistry`.

A formatter is _not_ required, if a formatter is not found `gi.ts` simply outputs whatever the generator provides from `generateNamespace`.