@gjsify/create-app 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,36 +16,61 @@ yarn create @gjsify/app my-app
16
16
  pnpm create @gjsify/app my-app
17
17
  ```
18
18
 
19
+ On a terminal it asks three questions in order — template, runtime, package
20
+ manager — each narrowing the next. Every question can be answered by a flag
21
+ instead, which is also how it is driven without a TTY.
22
+
19
23
  ### Options
20
24
 
21
25
  | | |
22
26
  |---|---|
23
27
  | `-t, --template <name>` | Template to scaffold from. Required when stdin is not a TTY; otherwise prompted for. |
28
+ | `-r, --runtime <rt>` | Runtime to set the project up for — one of the runtimes the chosen template declares. Decides which package managers are offered and which start script the next steps name. |
24
29
  | `-f, --force` | Scaffold into a non-empty directory. |
25
30
  | `--install` | Install dependencies after scaffolding. |
26
- | `-p, --package-manager <pm>` | `npm` (default), `yarn`, `pnpm` or `gjsify`. Used for `--install` and for the commands printed in the next steps. |
31
+ | `-p, --package-manager <pm>` | Must be one the chosen runtime can install for (see below). Used for `--install` and for the commands printed in the next steps. |
32
+
33
+ ### Runtime → package manager
34
+
35
+ An installer has to produce the module layout its runtime resolves against, so
36
+ the runtime decides which managers are on offer:
37
+
38
+ | Runtime | Package managers |
39
+ |---|---|
40
+ | `gjs` | `gjsify` |
41
+ | `node` | `npm`, `yarn`, `pnpm`, `gjsify` |
42
+ | `bun` | `bun` |
43
+ | `deno` | `deno` |
44
+
45
+ Where a runtime offers exactly one, nothing is asked — it is used and named.
46
+ `gjsify` is gjsify's own installer and the only one of the six that works on a
47
+ host with no Node.js at all, which is why it is what the `gjs` column installs
48
+ with.
27
49
 
28
- `gjsify` is gjsify's own installer and the only one of the four that works on a
29
- host with no Node.js at all.
50
+ Passing `-p` without `-r` settles the runtime too: `-p bun` sets the project up
51
+ for Bun, `-p gjsify` for GJS. Off a TTY, `--runtime` and `--package-manager` are
52
+ reported when they fall back to a default rather than being assumed silently —
53
+ and `--package-manager` is *required* alongside `--install`, since that is the
54
+ one flag whose default would reach your disk.
30
55
 
31
56
  ## Templates
32
57
 
33
- | Template | Runtimes | |
34
- |---|---|---|
35
- | `gtk-minimal` | GJS | `Gtk.Window` + `Gtk.Label`; no Adwaita, no Blueprint. |
36
- | `adw-canvas2d` | GJS | Adwaita app rendering through the HTML Canvas 2D API (Blueprint UI). |
37
- | `adw-webgl` | GJS | Adwaita app rendering through WebGL + three.js (Blueprint UI). |
38
- | `adw-game` | GJS | Adwaita game shell on Excalibur.js; WebGL with a Canvas 2D fallback. |
39
- | `cli` | GJS · Node · Bun · Deno | Command-line tool using yargs. |
40
- | `web-server-hono` | GJS · Node · Bun · Deno | HTTP server using Hono (Web-standard fetch-style API). |
41
- | `web-server-express` | GJS · Node · Bun · Deno | HTTP server using Express. |
42
-
43
- The GTK/Adwaita templates drive libadwaita through `gi://`, so GJS is where they
44
- belong. The other three ship two bundles — `--app gjs` and `--app node`, the
45
- latter shared by Node, Bun and Deno, whose common ABI is Node-API. Each template
46
- declares its own reach in `package.json` as `gjsify.example.runtimes`, and every
47
- build names its `--app` target explicitly rather than inheriting whichever
48
- runtime happens to invoke it.
58
+ | Template | |
59
+ |---|---|
60
+ | `gtk-minimal` | `Gtk.Window` + `Gtk.Label`; no Adwaita, no Blueprint. |
61
+ | `adw-canvas2d` | Adwaita app rendering through the HTML Canvas 2D API (Blueprint UI). |
62
+ | `adw-webgl` | Adwaita app rendering through WebGL + three.js (Blueprint UI). |
63
+ | `adw-game` | Adwaita game shell on Excalibur.js; WebGL with a Canvas 2D fallback. |
64
+ | `cli` | Command-line tool using yargs. |
65
+ | `web-server-hono` | HTTP server using Hono (Web-standard fetch-style API). |
66
+ | `web-server-express` | HTTP server using Express. |
67
+
68
+ Each template declares its own reach in `package.json` as
69
+ `gjsify.example.runtimes`, and the runtime prompt offers exactly that — no list
70
+ is kept here, because a second copy is the one that goes stale. Templates ship
71
+ two bundles, `--app gjs` and `--app node`, the latter shared by Node, Bun and
72
+ Deno, whose common ABI is Node-API; every build names its `--app` target
73
+ explicitly rather than inheriting whichever runtime happens to invoke it.
49
74
 
50
75
  ## Operating systems
51
76
 
@@ -17,22 +17,32 @@ engine the build needs when it runs under GJS — is listed explicitly in
17
17
  ## Commands
18
18
 
19
19
  ```bash
20
- npm run dev # build + run
21
- npm run build # bundle for GJS → dist/index.gjs.js
22
- npm start # run the built bundle on GJS
23
- npm run check # type-check
24
- npm run clear # remove build output
20
+ npm run dev # watch, rebuild + relaunch on GJS
21
+ npm run build # bundle for every runtime (dist/index.gjs.js + dist/index.node.mjs)
22
+ npm start # run on GJS
23
+ npm run start:node # run on Node.js
24
+ npm run start:bun # run on Bun
25
+ npm run start:deno # run on Deno
26
+ npm run check # type-check
27
+ npm run clear # remove build output
25
28
  ```
26
29
 
27
30
  ## Runtimes
28
31
 
29
- This template targets **GJS**. It drives GTK/libadwaita through `gi://`, so GJS
30
- is where it belongs — `package.json` declares that as
31
- `gjsify.example.runtimes: ["gjs"]`, and the build passes `--app gjs` explicitly
32
- so the target does not silently follow whichever runtime happens to invoke it.
32
+ This template runs on **GJS, Node.js, Bun and Deno**, declared in `package.json`
33
+ as `gjsify.example.runtimes`. Two bundles cover all four: `--app gjs` produces
34
+ `dist/index.gjs.js`, and `--app node` produces `dist/index.node.mjs`, which
35
+ Node, Bun and Deno all consume (Node-API is their common ABI).
33
36
 
34
- gjsify itself also targets Node.js, Bun and Deno; the `cli`, `web-server-hono`
35
- and `web-server-express` templates are the ones that ship bundles for all four.
37
+ Off GJS the `gi://` imports are served by `@gjsify/node-gi`, an N-API addon that
38
+ gives Node/Bun/Deno a real GObject-Introspection host. That is why it is a
39
+ runtime dependency here rather than a build tool: the `--app node` bundle imports
40
+ it. GTK and libadwaita still have to be installed on the machine either way — the
41
+ runtime only decides who talks to them.
42
+
43
+ The build names its target explicitly. Without `--app`, `gjsify build` follows
44
+ the runtime that happens to be invoking it, so the same `npm run build` would
45
+ produce a different artifact on a contributor's machine than in CI.
36
46
 
37
47
  ## Operating systems
38
48
 
@@ -7,9 +7,14 @@
7
7
  "scripts": {
8
8
  "clear": "gjsify clear dist tsconfig.tsbuildinfo",
9
9
  "check": "gjsify tsc --noEmit",
10
- "build": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js --globals auto,dom",
10
+ "build": "gjsify run build:gjs && gjsify run build:node",
11
+ "build:gjs": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js --globals auto,dom",
12
+ "build:node": "gjsify build src/index.ts --app node --outfile dist/index.node.mjs --globals auto,dom",
11
13
  "start": "gjsify run dist/index.gjs.js",
12
- "dev": "gjsify run build && gjsify run start"
14
+ "start:node": "gjsify run dist/index.node.mjs --runtime node",
15
+ "start:bun": "gjsify run dist/index.node.mjs --runtime bun",
16
+ "start:deno": "gjsify run dist/index.node.mjs --runtime deno",
17
+ "dev": "gjsify dev --runtime gjs"
13
18
  },
14
19
  "devDependencies": {
15
20
  "@girs/adw-1": "^4.1.0",
@@ -17,21 +22,27 @@
17
22
  "@girs/gjs": "^4.1.0",
18
23
  "@girs/gobject-2.0": "^4.1.0",
19
24
  "@girs/gtk-4.0": "^4.1.0",
20
- "@gjsify/cli": "^0.40.0",
21
- "@gjsify/rolldown-native": "^0.40.0",
22
- "@gjsify/vite-plugin-blueprint": "^0.40.0",
25
+ "@gjsify/cli": "^0.42.0",
26
+ "@gjsify/dom-elements": "^0.42.0",
27
+ "@gjsify/rolldown-native": "^0.42.0",
28
+ "@gjsify/vite-plugin-blueprint": "^0.42.0",
23
29
  "@types/node": "^25.9.2",
24
30
  "typescript": "^6.0.3"
25
31
  },
26
32
  "dependencies": {
27
- "@gjsify/canvas2d": "^0.40.0"
33
+ "@gjsify/canvas2d": "^0.42.0",
34
+ "@gjsify/node-gi": "^0.42.0"
28
35
  },
29
36
  "gjsify": {
30
37
  "main": "dist/index.gjs.js",
31
38
  "example": {
32
39
  "runtimes": [
33
- "gjs"
34
- ]
40
+ "gjs",
41
+ "node",
42
+ "bun",
43
+ "deno"
44
+ ],
45
+ "node": "dist/index.node.mjs"
35
46
  }
36
47
  }
37
48
  }
@@ -16,4 +16,9 @@ app.connect('activate', () => {
16
16
  win.present();
17
17
  });
18
18
 
19
- app.run([]);
19
+ // runAsync, not the sync run(): it defers the blocking main loop to a macrotask so
20
+ // promise continuations queued before it still drain. A sync run() works today, but
21
+ // it parks them until the loop returns the moment anything here becomes asynchronous
22
+ // — ADR 0009's latent hang, which node-gi inherits on node/bun/deno (node-gtk #442).
23
+ // One lifecycle for all four runtimes, and the one a scaffolded app should grow from.
24
+ await app.runAsync([]);
@@ -17,22 +17,50 @@ engine the build needs when it runs under GJS — is listed explicitly in
17
17
  ## Commands
18
18
 
19
19
  ```bash
20
- npm run dev # build + run
21
- npm run build # bundle for GJS → dist/index.gjs.js
22
- npm start # run the built bundle on GJS
23
- npm run check # type-check
24
- npm run clear # remove build output
20
+ npm run dev # watch, rebuild + relaunch on GJS
21
+ npm run build # bundle for every runtime (dist/index.gjs.js + dist/index.node.mjs)
22
+ npm start # run on GJS
23
+ npm run start:node # run on Node.js
24
+ npm run start:bun # run on Bun
25
+ npm run start:deno # run on Deno
26
+ npm run check # type-check
27
+ npm run clear # remove build output
25
28
  ```
26
29
 
27
30
  ## Runtimes
28
31
 
29
- This template targets **GJS**. It drives GTK/libadwaita through `gi://`, so GJS
30
- is where it belongs — `package.json` declares that as
31
- `gjsify.example.runtimes: ["gjs"]`, and the build passes `--app gjs` explicitly
32
- so the target does not silently follow whichever runtime happens to invoke it.
32
+ This template runs on **GJS, Node.js, Bun and Deno**, declared in `package.json`
33
+ as `gjsify.example.runtimes`. Two bundles cover all four: `--app gjs` produces
34
+ `dist/index.gjs.js`, and `--app node` produces `dist/index.node.mjs`, which
35
+ Node, Bun and Deno all consume (Node-API is their common ABI). The GTK and
36
+ libadwaita widgets are the same on every one — off GJS they come through
37
+ `@gjsify/node-gi`, which is why it is a runtime dependency rather than a
38
+ build-time one.
33
39
 
34
- gjsify itself also targets Node.js, Bun and Deno; the `cli`, `web-server-hono`
35
- and `web-server-express` templates are the ones that ship bundles for all four.
40
+ The build names its target explicitly. Without `--app`, `gjsify build` follows
41
+ the runtime that happens to be invoking it, so the same `npm run build` would
42
+ produce a different artifact on a contributor's machine than in CI.
43
+
44
+ `src/index.ts` awaits `app.runAsync()` rather than calling the blocking
45
+ `app.run()`. Excalibur boots through promises, and `runAsync` defers the main
46
+ loop by one macrotask so that work settles first. The blocking call still boots
47
+ the starter game on all four runtimes; `runAsync` is what a game that loads
48
+ resources before its first frame needs.
49
+
50
+ ### The `--globals` list
51
+
52
+ Both builds inject the same set, so a global cannot be present on one runtime
53
+ and missing on the other:
54
+
55
+ - `auto,dom` — what the detector finds plus the DOM group Excalibur draws into
56
+ (`document`, `HTMLCanvasElement`, `Image`, `ResizeObserver`, …).
57
+ - `XMLHttpRequest`, `ProgressEvent` — Excalibur's resource loader is XHR-based,
58
+ and the XHR implementation reports progress by constructing `ProgressEvent`.
59
+ Not detectable statically: `auto,dom` alone builds fine and then fails at
60
+ `engine.start()` with `XMLHttpRequest is not defined`.
61
+ - `PointerEvent`, `MouseEvent`, `KeyboardEvent`, `WheelEvent` — Excalibur
62
+ constructs all four when it synthesises input events. Nothing in the starter
63
+ game reads input, so the failure would first appear in *your* code.
36
64
 
37
65
  ## Operating systems
38
66
 
@@ -7,9 +7,14 @@
7
7
  "scripts": {
8
8
  "clear": "gjsify clear dist tsconfig.tsbuildinfo",
9
9
  "check": "gjsify tsc --noEmit",
10
- "build": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js --globals auto,dom",
10
+ "build": "gjsify run build:gjs && gjsify run build:node",
11
+ "build:gjs": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js --globals auto,dom,XMLHttpRequest,ProgressEvent,PointerEvent,MouseEvent,KeyboardEvent,WheelEvent",
12
+ "build:node": "gjsify build src/index.ts --app node --outfile dist/index.node.mjs --globals auto,dom,XMLHttpRequest,ProgressEvent,PointerEvent,MouseEvent,KeyboardEvent,WheelEvent",
11
13
  "start": "gjsify run dist/index.gjs.js",
12
- "dev": "gjsify run build && gjsify run start"
14
+ "start:node": "gjsify run dist/index.node.mjs --runtime node",
15
+ "start:bun": "gjsify run dist/index.node.mjs --runtime bun",
16
+ "start:deno": "gjsify run dist/index.node.mjs --runtime deno",
17
+ "dev": "gjsify dev --runtime gjs"
13
18
  },
14
19
  "devDependencies": {
15
20
  "@girs/adw-1": "^4.1.0",
@@ -17,23 +22,31 @@
17
22
  "@girs/gjs": "^4.1.0",
18
23
  "@girs/gobject-2.0": "^4.1.0",
19
24
  "@girs/gtk-4.0": "^4.1.0",
20
- "@gjsify/canvas2d": "^0.40.0",
21
- "@gjsify/cli": "^0.40.0",
22
- "@gjsify/rolldown-native": "^0.40.0",
23
- "@gjsify/vite-plugin-blueprint": "^0.40.0",
25
+ "@gjsify/canvas2d": "^0.42.0",
26
+ "@gjsify/cli": "^0.42.0",
27
+ "@gjsify/dom-elements": "^0.42.0",
28
+ "@gjsify/dom-events": "^0.42.0",
29
+ "@gjsify/fetch": "^0.42.0",
30
+ "@gjsify/rolldown-native": "^0.42.0",
31
+ "@gjsify/vite-plugin-blueprint": "^0.42.0",
24
32
  "@types/node": "^25.9.2",
25
33
  "typescript": "^6.0.3"
26
34
  },
27
35
  "dependencies": {
28
- "@gjsify/webgl": "^0.40.0",
36
+ "@gjsify/node-gi": "^0.42.0",
37
+ "@gjsify/webgl": "^0.42.0",
29
38
  "excalibur": "0.32.0"
30
39
  },
31
40
  "gjsify": {
32
41
  "main": "dist/index.gjs.js",
33
42
  "example": {
34
43
  "runtimes": [
35
- "gjs"
36
- ]
44
+ "gjs",
45
+ "node",
46
+ "bun",
47
+ "deno"
48
+ ],
49
+ "node": "dist/index.node.mjs"
37
50
  }
38
51
  }
39
52
  }
@@ -16,4 +16,9 @@ app.connect('activate', () => {
16
16
  win.present();
17
17
  });
18
18
 
19
- app.run([]);
19
+ // runAsync, not the blocking run(): it defers the main loop by one macrotask, so
20
+ // promise work queued while the app starts — engine.start(), and the resources a
21
+ // real game loads before its first frame — settles before the loop takes the
22
+ // thread. The blocking run() does boot THIS starter game on all four runtimes;
23
+ // runAsync is the lifecycle GJS recommends and the one a growing game needs.
24
+ await app.runAsync([]);
@@ -17,22 +17,45 @@ engine the build needs when it runs under GJS — is listed explicitly in
17
17
  ## Commands
18
18
 
19
19
  ```bash
20
- npm run dev # build + run
21
- npm run build # bundle for GJS → dist/index.gjs.js
22
- npm start # run the built bundle on GJS
23
- npm run check # type-check
24
- npm run clear # remove build output
20
+ npm run dev # watch, rebuild + relaunch on GJS
21
+ npm run build # both bundles: dist/index.gjs.js and dist/index.node.mjs
22
+ npm start # run the built bundle on GJS
23
+ npm run start:node # …on Node.js ┐
24
+ npm run start:bun # …on Bun ├ all three share dist/index.node.mjs
25
+ npm run start:deno # …on Deno ┘
26
+ npm run check # type-check
27
+ npm run clear # remove build output
25
28
  ```
26
29
 
27
30
  ## Runtimes
28
31
 
29
- This template targets **GJS**. It drives GTK/libadwaita through `gi://`, so GJS
30
- is where it belongs — `package.json` declares that as
31
- `gjsify.example.runtimes: ["gjs"]`, and the build passes `--app gjs` explicitly
32
- so the target does not silently follow whichever runtime happens to invoke it.
32
+ This app runs on all four runtimes gjsify targets — **GJS, Node.js, Bun and
33
+ Deno** — and opens the same GTK/libadwaita window with the same three.js scene on
34
+ each. `package.json` declares that as
35
+ `gjsify.example.runtimes: ["gjs", "node", "bun", "deno"]`.
33
36
 
34
- gjsify itself also targets Node.js, Bun and Deno; the `cli`, `web-server-hono`
35
- and `web-server-express` templates are the ones that ship bundles for all four.
37
+ Two bundles, not four. `--app gjs` produces the native GJS bundle; `--app node`
38
+ produces one bundle that Node, Bun and Deno all run, with `gi://` served by
39
+ `@gjsify/node-gi`. It is a **runtime** dependency, not a build-time one: the node
40
+ bundle does not inline it, it keeps `import … from '@gjsify/node-gi/cairo'` (and
41
+ `/system`, `/gi`) as live imports, so the package has to be there when the app
42
+ runs, not only when it builds. That is why it sits in `dependencies` — move it to
43
+ `devDependencies` and the app still builds and still runs from a dev checkout,
44
+ then dies with `Cannot find module '@gjsify/node-gi/gi'` the first time someone
45
+ installs without dev deps.
46
+
47
+ `--globals auto,dom` on the `--app node` build is not decoration. Auto-detection
48
+ runs on the GJS target only; a node bundle routes the `@gjsify/*` register
49
+ subpaths to `@gjsify/empty` unless the DOM surface is requested by name. Drop
50
+ `dom` and the window still opens, but `canvas.getContext('webgl2')` throws
51
+ `WebGL2RenderingContext is not a constructor` and the canvas stays empty.
52
+
53
+ The entry point uses `await app.runAsync([])` rather than the synchronous
54
+ `app.run([])`. It is the recommended `Gio.Application` lifecycle and the form the
55
+ multi-runtime showcases use: a sync `run()` blocks the thread inside the GLib main
56
+ loop, which hangs any startup that awaits something. This scene awaits nothing, so
57
+ the sync form happens to work here too — the async one is what keeps working once
58
+ you add a step that does.
36
59
 
37
60
  ## Operating systems
38
61
 
@@ -7,9 +7,14 @@
7
7
  "scripts": {
8
8
  "clear": "gjsify clear dist tsconfig.tsbuildinfo",
9
9
  "check": "gjsify tsc --noEmit",
10
- "build": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js",
10
+ "build": "gjsify run build:gjs && gjsify run build:node",
11
+ "build:gjs": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js",
12
+ "build:node": "gjsify build src/index.ts --app node --outfile dist/index.node.mjs --globals auto,dom",
11
13
  "start": "gjsify run dist/index.gjs.js",
12
- "dev": "gjsify run build && gjsify run start"
14
+ "start:node": "gjsify run dist/index.node.mjs --runtime node",
15
+ "start:bun": "gjsify run dist/index.node.mjs --runtime bun",
16
+ "start:deno": "gjsify run dist/index.node.mjs --runtime deno",
17
+ "dev": "gjsify dev --runtime gjs"
13
18
  },
14
19
  "devDependencies": {
15
20
  "@girs/adw-1": "^4.1.0",
@@ -17,23 +22,29 @@
17
22
  "@girs/gjs": "^4.1.0",
18
23
  "@girs/gobject-2.0": "^4.1.0",
19
24
  "@girs/gtk-4.0": "^4.1.0",
20
- "@gjsify/cli": "^0.40.0",
21
- "@gjsify/rolldown-native": "^0.40.0",
22
- "@gjsify/vite-plugin-blueprint": "^0.40.0",
25
+ "@gjsify/cli": "^0.42.0",
26
+ "@gjsify/dom-elements": "^0.42.0",
27
+ "@gjsify/rolldown-native": "^0.42.0",
28
+ "@gjsify/vite-plugin-blueprint": "^0.42.0",
23
29
  "@types/node": "^25.9.2",
24
30
  "@types/three": "^0.185.0",
25
31
  "typescript": "^6.0.3"
26
32
  },
27
33
  "dependencies": {
28
- "@gjsify/webgl": "^0.40.0",
34
+ "@gjsify/node-gi": "^0.42.0",
35
+ "@gjsify/webgl": "^0.42.0",
29
36
  "three": "0.185.1"
30
37
  },
31
38
  "gjsify": {
32
39
  "main": "dist/index.gjs.js",
33
40
  "example": {
34
41
  "runtimes": [
35
- "gjs"
36
- ]
42
+ "gjs",
43
+ "node",
44
+ "bun",
45
+ "deno"
46
+ ],
47
+ "node": "dist/index.node.mjs"
37
48
  }
38
49
  }
39
50
  }
@@ -16,4 +16,11 @@ app.connect('activate', () => {
16
16
  win.present();
17
17
  });
18
18
 
19
- app.run([]);
19
+ // runAsync, not the sync run(): it is the recommended Gio.Application lifecycle
20
+ // and what every multi-runtime showcase here uses, because sync run() blocks the
21
+ // thread inside the GLib main loop — which is what hangs a startup that awaits
22
+ // anything, and the documented hazard on the `--app node` reverse bridge.
23
+ // Measured at 0.40.0: this scene starts nothing async, so the sync form happens
24
+ // to work on all four today. Kept anyway, so the template does not hand a
25
+ // scaffolded app the form that breaks the moment it grows an async startup step.
26
+ await app.runAsync([]);
@@ -17,7 +17,7 @@ engine the build needs when it runs under GJS — is listed explicitly in
17
17
  ## Commands
18
18
 
19
19
  ```bash
20
- npm run dev # build for GJS + run
20
+ npm run dev # watch, rebuild + relaunch on GJS
21
21
  npm run build # bundle for every runtime (dist/index.gjs.js + dist/index.node.mjs)
22
22
  npm start # run on GJS
23
23
  npm run start:node # run on Node.js
@@ -14,13 +14,13 @@
14
14
  "start:node": "gjsify run dist/index.node.mjs --runtime node",
15
15
  "start:bun": "gjsify run dist/index.node.mjs --runtime bun",
16
16
  "start:deno": "gjsify run dist/index.node.mjs --runtime deno",
17
- "dev": "gjsify run build:gjs && gjsify run start"
17
+ "dev": "gjsify dev --runtime gjs"
18
18
  },
19
19
  "devDependencies": {
20
- "@gjsify/cli": "^0.40.0",
21
- "@gjsify/node-globals": "^0.40.0",
22
- "@gjsify/rolldown-native": "^0.40.0",
23
- "@gjsify/runtime": "^0.40.0",
20
+ "@gjsify/cli": "^0.42.0",
21
+ "@gjsify/node-globals": "^0.42.0",
22
+ "@gjsify/rolldown-native": "^0.42.0",
23
+ "@gjsify/runtime": "^0.42.0",
24
24
  "@types/node": "^25.9.2",
25
25
  "@types/yargs": "^17.0.35",
26
26
  "typescript": "^6.0.3"
@@ -31,7 +31,20 @@ yargs(hideBin(process.argv))
31
31
  console.log(`${c.dim}pid :${c.reset} ${process.pid}`);
32
32
  },
33
33
  )
34
- .demandCommand(1, 'Pass --help to see available commands.')
34
+ // The `$0` default command. Without it, `demandCommand(1)` made the scripts
35
+ // the scaffolder itself prints — `npm run dev`, `npm start`, neither of which
36
+ // passes an argument — exit 1 with "Pass --help to see available commands."
37
+ // on a project created seconds earlier.
38
+ .command(
39
+ '$0',
40
+ 'Show what this starter does',
41
+ () => {},
42
+ () => {
43
+ console.log(`${c.dim}runtime :${c.reset} ${c.green}${runtimeName}${c.reset}`);
44
+ console.log('');
45
+ console.log(`Try ${c.cyan}greet <name>${c.reset}, ${c.cyan}info${c.reset} or ${c.cyan}--help${c.reset}.`);
46
+ },
47
+ )
35
48
  .strict()
36
49
  .help()
37
50
  .parse();
@@ -17,22 +17,43 @@ engine the build needs when it runs under GJS — is listed explicitly in
17
17
  ## Commands
18
18
 
19
19
  ```bash
20
- npm run dev # build + run
21
- npm run build # bundle for GJS → dist/index.gjs.js
22
- npm start # run the built bundle on GJS
23
- npm run check # type-check
24
- npm run clear # remove build output
20
+ npm run dev # watch, rebuild + relaunch on GJS
21
+ npm run build # bundle both targets
22
+ npm run build:gjs # → dist/index.gjs.js
23
+ npm run build:node # → dist/index.node.mjs
24
+ npm start # run on GJS
25
+ npm run start:node # run on Node.js
26
+ npm run start:bun # run on Bun
27
+ npm run start:deno # run on Deno
28
+ npm run check # type-check
29
+ npm run clear # remove build output
25
30
  ```
26
31
 
27
32
  ## Runtimes
28
33
 
29
- This template targets **GJS**. It drives GTK/libadwaita through `gi://`, so GJS
30
- is where it belongs — `package.json` declares that as
31
- `gjsify.example.runtimes: ["gjs"]`, and the build passes `--app gjs` explicitly
32
- so the target does not silently follow whichever runtime happens to invoke it.
34
+ One `src/index.ts` runs on all four runtimes gjsify targets, out of two bundles:
33
35
 
34
- gjsify itself also targets Node.js, Bun and Deno; the `cli`, `web-server-hono`
35
- and `web-server-express` templates are the ones that ship bundles for all four.
36
+ | Bundle | Built with | Runs on | `gi://` resolves through |
37
+ |---|---|---|---|
38
+ | `dist/index.gjs.js` | `--app gjs` | GJS | the interpreter itself |
39
+ | `dist/index.node.mjs` | `--app node` | Node.js, Bun, Deno | [`@gjsify/node-gi`](https://www.npmjs.com/package/@gjsify/node-gi) |
40
+
41
+ Node, Bun and Deno share the one `--app node` bundle — Node-API is their common
42
+ ABI, so none of them needs a target of its own. That is also why
43
+ `@gjsify/node-gi` sits in `dependencies` rather than `devDependencies`: the built
44
+ bundle imports it while it runs, not while it builds.
45
+
46
+ Both builds name `--app` explicitly so the target cannot silently follow
47
+ whichever runtime happens to invoke the build. `package.json` then declares the
48
+ result as `gjsify.example.runtimes`, which is what `gjsify run --runtime <name>`
49
+ checks against — asking for a runtime this project has no bundle for fails with a
50
+ message instead of crashing somewhere inside one.
51
+
52
+ Neither build names `--globals`. The default is `auto`, and this source touches
53
+ no web API — only `gi://` and `process`, which `auto` shims on GJS and which is
54
+ native on the other three. Templates that draw through a Canvas or WebGL request
55
+ that surface explicitly (`--globals auto,dom`); asking for it here would inject
56
+ registers nothing ever calls.
36
57
 
37
58
  ## Operating systems
38
59
 
@@ -7,27 +7,37 @@
7
7
  "scripts": {
8
8
  "clear": "gjsify clear dist tsconfig.tsbuildinfo",
9
9
  "check": "gjsify tsc --noEmit",
10
- "build": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js",
10
+ "build": "gjsify run build:gjs && gjsify run build:node",
11
+ "build:gjs": "gjsify build src/index.ts --app gjs --outfile dist/index.gjs.js",
12
+ "build:node": "gjsify build src/index.ts --app node --outfile dist/index.node.mjs",
11
13
  "start": "gjsify run dist/index.gjs.js",
12
- "dev": "gjsify run build && gjsify run start"
14
+ "start:node": "gjsify run dist/index.node.mjs --runtime node",
15
+ "start:bun": "gjsify run dist/index.node.mjs --runtime bun",
16
+ "start:deno": "gjsify run dist/index.node.mjs --runtime deno",
17
+ "dev": "gjsify dev --runtime gjs"
13
18
  },
14
19
  "devDependencies": {
15
- "@gjsify/cli": "^0.40.0",
16
- "@gjsify/rolldown-native": "^0.40.0",
20
+ "@gjsify/cli": "^0.42.0",
21
+ "@gjsify/rolldown-native": "^0.42.0",
17
22
  "@types/node": "^25.9.2",
18
23
  "typescript": "^6.0.3"
19
24
  },
20
25
  "dependencies": {
21
26
  "@girs/gio-2.0": "^4.1.0",
22
27
  "@girs/glib-2.0": "^4.1.0",
23
- "@girs/gtk-4.0": "^4.1.0"
28
+ "@girs/gtk-4.0": "^4.1.0",
29
+ "@gjsify/node-gi": "^0.42.0"
24
30
  },
25
31
  "gjsify": {
26
32
  "main": "dist/index.gjs.js",
27
33
  "example": {
28
34
  "runtimes": [
29
- "gjs"
30
- ]
35
+ "gjs",
36
+ "node",
37
+ "bun",
38
+ "deno"
39
+ ],
40
+ "node": "dist/index.node.mjs"
31
41
  }
32
42
  }
33
43
  }
@@ -17,7 +17,7 @@ engine the build needs when it runs under GJS — is listed explicitly in
17
17
  ## Commands
18
18
 
19
19
  ```bash
20
- npm run dev # build for GJS + run
20
+ npm run dev # watch, rebuild + relaunch on GJS
21
21
  npm run build # bundle for every runtime (dist/index.gjs.js + dist/index.node.mjs)
22
22
  npm start # run on GJS
23
23
  npm run start:node # run on Node.js