Prepend #!/usr/bin/env -S gjs -m to the outfile and chmod it 0o755. Only with --app gjs and a single --outfile.
--verbose
bool
false
Show detected globals and build details
All build options
Option
Values
Default
Description
--format
iife | esm | cjs
auto
Override output format
--library
bool
false
Build as a reusable library
--reflection, -r
bool
false
Enable TypeScript runtime types via Deepkit
--console-shim
bool
true
Inject the GJS console shim. Disable with --no-console-shim
--exclude
glob[]
[]
Glob patterns to exclude from entry points and aliases
--log-level
silent | error | warning | info | debug | verbose
warning
Bundler log level
--external
name[]
[]
Module specifiers that should NOT be bundled — they remain as runtime imports. Repeatable; comma-separated values are split. Merged with the platform’s built-in externals.
--define
KEY=VALUE[]
[]
Bundler define pass-through. VALUE is a JS expression — string literals must be JSON-quoted (--define VERSION='"1.2.3"'). Repeatable. Merged with built-in defines like global: 'globalThis'.
--alias
FROM=TO[]
[]
Layered on top of the built-in alias map. Useful for stubbing heavy deps (--alias typedoc=@gjsify/empty). Repeatable.
For --app gjs, the target is firefox140 (SpiderMonkey 140) and gi://*, cairo, system and gettext are externalised. For --app node, the target is node24.
Bundling third-party CLIs that read their own package.json at load time
Tools like typedoc and prettier read their own package.json via Path.join(fileURLToPath(import.meta.url), '../../../package.json') during top-level evaluation. When bundled, import.meta.url resolves to the bundle file, not the original main, so the lookup escapes the package and crashes. Combine --external (so Node’s runtime resolver finds the package in node_modules) with --define for any build-time version constants the bundled code expects:
--external is the right answer on Node. On GJS, gjsify run does not yet have a node_modules-style runtime resolver, so externalised packages fail with ImportError: Module not found. Bundling them is the only option on GJS today; the alternative is upstream switching to a --define-injected version constant.
The default --globals auto detects which globals your code needs from the bundled output. No configuration needed for most projects.
Mode
Usage
Description
auto
--globals auto (default)
Automatic detection from bundled output
auto,<extras>
--globals auto,dom
Auto + explicit extras for hard-to-detect cases
explicit list
--globals fetch,Buffer
Fully explicit, no auto detection
none
--globals none
Disable globals injection
Groups: node (Buffer, process, URL, …), web (fetch, streams, crypto, …), dom (document, Image, navigator, …).
How auto detection works
The CLI runs an iterative multi-pass build:
Pass 1 bundles your code in memory (no globals injected). acorn parses the output and finds free identifiers (Buffer, fetch) plus host-object member expressions (globalThis.Buffer, self.Buffer).
Pass N injects the discovered globals and rebuilds. Newly injected modules can reference more globals — the loop repeats until the set stabilises (typically 2–3 iterations, capped at 5).
The final build uses the converged set.
Because detection runs on the tree-shaken bundle, isomorphic guards (typeof document !== 'undefined') never produce false positives.
Use --verbose to see what auto mode detected:
gjsifybuildsrc/index.ts-odist/index.js--verbose
# [gjsify] --globals auto: converged after 2 iteration(s), 11 global(s):
The analyser cannot follow value-flow indirection — e.g. Excalibur stores globalThis in a variable and calls methods through it, hiding the access. Keep auto on and add extras:
Extras are seeded into pass 1 so any code reachable only through them is also visible.
Known identifiers
Any identifier below can appear in --globals (or be detected automatically). Granular subpaths mean selecting Buffer does NOT also pull in process or URL.
Run the GJS bundle of an npm-published package without persisting it in your project. Pattern follows npx / yarn dlx / pnpm dlx, but dlx here is strictly a GJS-bundle runner: it always invokes gjs -m <bundle> after resolving the package’s GJS entry. Packages without a GJS entry fail loudly.
gjsifydlx@gjsify/example-dom-canvas2d-fireworks
gjsifydlx@scope/pkg@1.2.3# version-pinned
gjsifydlx@scope/pkgmy-bin----optvalue# pick a bin from gjsify.bin, forward args
gjsifydlx./local/path# local dir (no install, no cache)
Option
Description
<spec>
Package spec (name, name@version, @scope/name@spec, or local path).
[binOrArg]
Bin name when gjsify.bin has multiple entries; otherwise treated as the first arg forwarded to the bundle.
[extraArgs..]
Extra args forwarded to gjs -m <bundle>.
--cache-max-age <minutes>
Cache TTL. Defaults to 7 days. Use 0 to bypass cache.
--registry <url>
npm registry override (writes a temp .npmrc in the cache dir).
--verbose
Pass --loglevel verbose to npm.
Cache layout: $XDG_CACHE_HOME/gjsify/dlx/<sha256>/. Cache-key is sha-256 over the sorted package specs and registries. Atomic symlink swap on first install means parallel dlx invocations of the same spec are safe.
Compile a GResource XML descriptor into a binary .gresource bundle. Thin wrapper around glib-compile-resources — useful when you want to package UI templates and assets into the app bundle without pulling in meson/autotools.
Compile gettext .po files for GNOME apps. Wraps msgfmt with the common output shapes — per-language locale tree (.mo), metainfo template substitution (.xml), and two less-common formats (.desktop, .json).