UNPKG

node-gtk

Version:

GNOME Gtk+ bindings for NodeJS

142 lines (117 loc) 6.55 kB
# node-gtk TypeScript types — PROTOTYPE (Model B: generate-on-demand) Types are generated **on the user's machine** from the GObject-Introspection typelibs they actually have installed, using node-gtk's own runtime introspection (`require('node-gtk')._GIRepository`, the libgirepository C API exposed to JS). Because the generator reads the same typelibs and applies the same name/shape rules as `lib/bootstrap.js`, the output matches what node-gtk produces at runtime — and it matches *their* library versions, not a bundled snapshot. ## User workflow ```sh # 1. generate types for the namespaces you use (+ their dependency closure). # Output defaults to ./node_modules/.node-gtk-types (hidden, gitignored). npx node-gtk generate-types Gtk-4.0 # 2. point tsconfig at the generated shim ``` ```jsonc // tsconfig.json { "compilerOptions": { "skipLibCheck": true, "paths": { "node-gtk": ["./node_modules/.node-gtk-types/node-gtk.d.ts"] } } } ``` ```ts // 3. write code — gi.require() is typed by string-literal overloads import * as gi from 'node-gtk' const Gtk = gi.require('Gtk', '4.0') // inferred as the Gtk-4.0 namespace const win = new Gtk.ApplicationWindow({ title: 'Hi', defaultWidth: 400 }) win.on('close-request', () => false) ``` `generate-types` emits one `<Namespace>-<version>.d.ts` per namespace plus a `node-gtk.d.ts` module shim. The shim overloads `require()` so each `gi.require('Ns','ver')` resolves to the matching generated namespace; namespaces you didn't generate fall back to `any`. Because the default output lives under `node_modules`, it's treated as a generated cache — wire it into a `postinstall` script so it regenerates after install. ## Pieces (prototype) - `bin/node-gtk.js` — CLI entry (`package.json` `"bin"`); dispatches `generate-types`. - `tools/generate-types.js` — the generator. `run(argv)` / `generate(roots, outdir)`. - `examples/ts-demo/``app.ts` (valid, typechecks clean) and `app-errors.ts` (5 deliberate mistakes, all caught). Generate types into `.node-gtk-types/` first (see that dir's `.gitignore`). ## Verify the demo ```sh node bin/node-gtk.js generate-types Gtk-4.0 --outdir examples/ts-demo/.node-gtk-types node_modules/.bin/tsc -p examples/ts-demo/tsconfig.json # passes clean sed 's/app.ts/app-errors.ts/' examples/ts-demo/tsconfig.json > examples/ts-demo/tsconfig.errors.json node_modules/.bin/tsc -p examples/ts-demo/tsconfig.errors.json # 5 errors caught ``` ## Fidelity The generated `.d.ts` for the full Gtk-3.0, Gtk-4.0, and Adw/GtkSource stacks type-check with **0 errors even without `skipLibCheck`**. Modelled faithfully: - OUT/INOUT params surfaced via the return value as node-gtk does (`getStartIter(): TextIter`, `getIterAtLine(n): [boolean, TextIter]`). - Callback argument types expanded (e.g. `Gio.AsyncReadyCallback`). - 64-bit ints return `bigint` (full precision, #323/#149); params accept `number | bigint`. - Enum methods and interface constants emitted (declaration-merged). - Virtual functions emitted as the `virtual_*` override surface that `registerClass` wires into the vtable (`virtual_sizeAllocate` overrides `size_allocate`), including invoker-less lifecycle vfuncs (`virtual_dispose`, `virtual_constructed`, …) so subclass overrides are type-checked and `super.virtual_<name>()` chain-up resolves (issue #457). - GObject override conflicts reconciled as overloads, so subclass methods stay assignable to inherited ones; multiple-interface signal/method conflicts resolved with a unified, assignable-to-all declaration. - Interfaces emit both a type and a value, so constructor functions and constants work (`Gio.File.newForPath(...)`). - Relative imports use `.js` extensions, so the output works under `moduleResolution` node16/nodenext (and bundler). `skipLibCheck` is no longer required for the GTK stack, though it remains a fine default. - **JSDoc comments** from the `.gir` XML — class/method/property/signal/enum docs, with `@param`/`@returns`/`@deprecated` — so editors show GNOME's API docs on hover. The typelib doesn't carry docs, so this reads the matching `<Namespace>-<version>.gir` from `$XDG_DATA_DIRS/gir-1.0` (shipped by the library's `-dev`/`-devel` package). Best-effort: if the `.gir` is absent, types still generate without comments. Pass `--no-docs` for leaner output (~5× smaller). ## Remaining limitations - **Overriding an inherited method that collides by name** in a user subclass (e.g. a gutter renderer's `activate(iter, …)` vs `GtkWidget.activate()`) requires the override to satisfy both signatures — an inherent consequence of the GObject API reusing a name, not specific to these types. - **`virtual_*` overrides with non-primitive OUT params** are typed with those params in the return tuple (the public-method convention). At runtime a vfunc implementation receives non-primitive OUT params as objects to mutate rather than returning them; the common all-primitive case (e.g. `virtual_measure`) matches exactly. - **Interface vfuncs are not emitted** (only object/class vfuncs). Emitting `virtual_*` members on interfaces collides across multiple-interface diamonds (e.g. GTK3's Atk accessibility stack → TS2320). Overriding an interface vfunc still works at runtime; it just isn't type-checked. --- # `node-gtk create` — create a new app `node-gtk create <directory>` creates a complete, ready-to-run GTK/Adwaita application that uses node-gtk, so a new project is one command away. ```sh npx node-gtk create my-app cd my-app npm run dev ``` It generates a TypeScript + ESM project: an idiomatic Adwaita application plus its tooling — typed `gi:` imports (with `tsconfig` wired to the generated types) and npm scripts to run (`dev`/`start`), build (`build`), and regenerate types (`generate-types`, also run on `postinstall`). ### Options ``` node-gtk create <directory> [options] --name <name> Human-facing app name (default: derived from <directory>) --app-id <id> Reverse-DNS application id (default: com.example.<Name>) --no-install Don't run `npm install` after creating the project --force Create into <directory> even if it exists and is non-empty -h, --help Show this help ``` The directory basename drives the defaults: `my-cool-app` → name *"My Cool App"*, package `my-cool-app`, id `com.example.MyCoolApp`. The command lives in `tools/create-app.js`; the generated files come from the template tree in `tools/templates/app/`.