npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@gi.ts/lib

v2.0.0-alpha.3

Published

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.

Downloads

1,170

Readme

@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, 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.

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.