@gjsify/node-gi 0.13.0 → 0.21.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.
@@ -0,0 +1,60 @@
1
+ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2
+ // SPDX-FileCopyrightText: 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
3
+ //
4
+ // Adapted from GJS (refs/gjs/modules/script/mainloop.js). Copyright (c) 2012
5
+ // Giovanni Campagna. MIT OR LGPL-2.0-or-later.
6
+ // Modifications: the legacy `imports.mainloop` — a thin convenience layer over
7
+ // GLib.MainLoop — ported to @gjsify/node-gi. GJS builds the idle/timeout sources
8
+ // via GObject.source_set_closure; this port routes through GLib.idle_add /
9
+ // GLib.timeout_add directly instead (equivalent behavior, one less indirection —
10
+ // the engine marshals JS functions both as GI callbacks and as GClosures, see
11
+ // test/gclosure-in-args.test.mjs), keeping the same `imports.mainloop.*` surface.
12
+
13
+ const DEFAULT_IDLE_PRIORITY = 200; // GLib.PRIORITY_DEFAULT_IDLE
14
+
15
+ /**
16
+ * Build the `imports.mainloop` object bound to the L1 GLib namespace.
17
+ * @param {Record<string, any>} GLib
18
+ * @returns {{ run(name?: string): void, quit(name?: string): void, idle_add: Function, timeout_add: Function, timeout_add_seconds: Function, source_remove: Function }}
19
+ */
20
+ export function createMainloop(GLib) {
21
+ const _mainLoops = Object.create(null);
22
+
23
+ function run(name = '') {
24
+ if (!_mainLoops[name]) _mainLoops[name] = GLib.MainLoop.new(null, false);
25
+ _mainLoops[name].run();
26
+ }
27
+
28
+ function quit(name = '') {
29
+ if (!_mainLoops[name]) throw new Error('No main loop with this id');
30
+
31
+ const loop = _mainLoops[name];
32
+ _mainLoops[name] = null;
33
+
34
+ if (!loop.is_running()) throw new Error('Main loop was stopped already');
35
+
36
+ loop.quit();
37
+ }
38
+
39
+ // GJS signature is `idle_add(handler, priority)` — handler first — whereas the
40
+ // introspected GLib.idle_add is `(priority, handler)`; bridge the argument order.
41
+ function idle_add(handler, priority = DEFAULT_IDLE_PRIORITY) {
42
+ return GLib.idle_add(priority, handler);
43
+ }
44
+
45
+ function timeout_add(timeout, handler, priority = GLib.PRIORITY_DEFAULT) {
46
+ return GLib.timeout_add(priority, timeout, handler);
47
+ }
48
+
49
+ function timeout_add_seconds(timeout, handler, priority = GLib.PRIORITY_DEFAULT) {
50
+ return GLib.timeout_add_seconds(priority, timeout, handler);
51
+ }
52
+
53
+ function source_remove(id) {
54
+ return GLib.source_remove(id);
55
+ }
56
+
57
+ return { run, quit, idle_add, timeout_add, timeout_add_seconds, source_remove };
58
+ }
59
+
60
+ export default createMainloop;
@@ -0,0 +1,167 @@
1
+ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2
+ // SPDX-FileCopyrightText: 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
3
+ //
4
+ // Adapted from GJS (refs/gjs/modules/script/package.js). Copyright (c) 2013
5
+ // Giovanni Campagna. MIT OR LGPL-2.0-or-later.
6
+ // Modifications: the legacy `imports.package` — the GJS application bootstrap
7
+ // (`pkg.init` / `initGettext` / `initFormat`) — ported to @gjsify/node-gi. A GNOME
8
+ // app's entry point does `imports.package.init({ name, version, prefix, libdir })`
9
+ // then `pkg.initGettext()` / `pkg.initFormat()`. The GJS original's heavy
10
+ // source-tree detection + GResource registration + module-search machinery is NOT
11
+ // load-bearing for a node-gi consumer (the app carries its own dir constants and
12
+ // registers its own resources — see easy6502 app-gnome resources.ts), so this is
13
+ // the minimal, honest init: set `globalThis.pkg`, wire `_`/`C_`/`N_` and
14
+ // `String.prototype.format`, and keep `imports.searchPath` / `GLib.set_prgname`
15
+ // calls safe. Mirrors overrides/mainloop.js's `createX(deps)` shape.
16
+
17
+ /**
18
+ * Build the `imports.package` object bound to the L1 backend.
19
+ * @param {{ requireGi: Function, gettext: any, imports: Record<string, any> }} deps
20
+ */
21
+ export function createPackage({ requireGi, gettext, imports }) {
22
+ const pkg = {
23
+ // Public fields GJS source may read. Populated by init().
24
+ name: undefined,
25
+ version: undefined,
26
+ prefix: undefined,
27
+ libdir: undefined,
28
+ datadir: undefined,
29
+ pkgdatadir: undefined,
30
+ pkglibdir: undefined,
31
+ moduledir: undefined,
32
+ localedir: undefined,
33
+
34
+ /**
35
+ * @param {{ name?: string, version?: string, prefix?: string, libdir?: string, datadir?: string }} params
36
+ */
37
+ init(params = {}) {
38
+ // The accessor GJS apps use right after: `pkg.initGettext()`.
39
+ globalThis.pkg = pkg;
40
+
41
+ pkg.name = params.name;
42
+ pkg.version = params.version;
43
+ pkg.prefix = params.prefix;
44
+ pkg.libdir = params.libdir;
45
+ pkg.datadir = params.datadir ?? (params.prefix ? `${params.prefix}/share` : undefined);
46
+
47
+ // Installed-layout dirs — NOT load-bearing on node-gi (apps carry their own
48
+ // constants + register their own resources), computed for source-compat only.
49
+ if (pkg.name) {
50
+ if (pkg.datadir) {
51
+ pkg.pkgdatadir = `${pkg.datadir}/${pkg.name}`;
52
+ pkg.moduledir = pkg.pkgdatadir;
53
+ pkg.localedir = `${pkg.datadir}/locale`;
54
+ }
55
+ if (pkg.libdir) pkg.pkglibdir = `${pkg.libdir}/${pkg.name}`;
56
+ }
57
+
58
+ // Cheap + safe: keeps ARGV[0]/app-id semantics (window-manager grouping etc.).
59
+ try {
60
+ const GLib = requireGi('GLib', '2.0');
61
+ if (pkg.name && typeof GLib.set_prgname === 'function') GLib.set_prgname(pkg.name);
62
+ } catch {
63
+ /* GLib not loadable in this context — skip */
64
+ }
65
+
66
+ // GJS unshifts moduledir onto imports.searchPath (script-import machinery).
67
+ // node-gi apps use ESM + gi://, so the search path is inert here — but keep
68
+ // the array present + the unshift safe so source that reads it doesn't throw.
69
+ if (!Array.isArray(imports.searchPath)) imports.searchPath = [];
70
+ if (pkg.moduledir) imports.searchPath.unshift(pkg.moduledir);
71
+ },
72
+
73
+ initGettext() {
74
+ if (pkg.name) {
75
+ try {
76
+ gettext.bindtextdomain?.(pkg.name, pkg.localedir ?? null);
77
+ gettext.textdomain?.(pkg.name);
78
+ } catch {
79
+ /* passthrough gettext — no catalog, ignore */
80
+ }
81
+ }
82
+ // The globals GJS apps + compiled Blueprint use pervasively.
83
+ globalThis._ = (msgid) => gettext.gettext(msgid);
84
+ globalThis.C_ = (context, msgid) => gettext.pgettext(context, msgid);
85
+ globalThis.N_ = (msgid) => msgid;
86
+ },
87
+
88
+ initFormat() {
89
+ // GJS sets `String.prototype.format = imports.format.format` (backed by the
90
+ // native `_format` vprintf). node-gi has no `imports.format`, so supply an
91
+ // equivalent JS vprintf here (see makeVprintf).
92
+ const vprintf = makeVprintf();
93
+ Object.defineProperty(String.prototype, 'format', {
94
+ configurable: true,
95
+ writable: true,
96
+ enumerable: false,
97
+ value: function format(...args) {
98
+ return vprintf(this, args);
99
+ },
100
+ });
101
+ },
102
+
103
+ // Script-import machinery is not ported — node-gi apps use ESM + gi://. These
104
+ // keep the surface present for source that references them (Learn6502 does not).
105
+ require() {
106
+ /* no-op: apps import via ESM / gi:// on node-gi */
107
+ },
108
+ requireSymbol() {
109
+ return true;
110
+ },
111
+ checkSymbol() {
112
+ return true;
113
+ },
114
+ start(params = {}) {
115
+ pkg.init(params);
116
+ if (typeof params.main === 'function') params.main(globalThis.ARGV ? ['', ...globalThis.ARGV] : []);
117
+ },
118
+ run() {
119
+ /* the app drives its own main() on node-gi */
120
+ },
121
+ initSubmodule() {
122
+ /* n/a on node-gi */
123
+ },
124
+ };
125
+
126
+ return pkg;
127
+ }
128
+
129
+ // A small vprintf covering the GJS `String.prototype.format` spec
130
+ // (refs/gjs/modules/script/format.js): `%s %d %x %f`, `%.Nf` precision, `%Ns`/`%Nd`
131
+ // width, `0`-padding for numerics, and `%%` → literal `%`. Positional `%N$s` is
132
+ // supported (GJS honours it). Learn6502 only uses `%s`; the rest is fidelity.
133
+ function makeVprintf() {
134
+ return function vprintf(fmt, args) {
135
+ let auto = 0;
136
+ return String(fmt).replace(
137
+ /%(?:(\d+)\$)?(0)?(\d+)?(?:\.(\d+))?([%sdxf])/g,
138
+ (match, pos, zero, width, prec, spec) => {
139
+ if (spec === '%') return '%';
140
+ const arg = pos !== undefined ? args[Number(pos) - 1] : args[auto++];
141
+ let str;
142
+ switch (spec) {
143
+ case 's':
144
+ str = String(arg);
145
+ break;
146
+ case 'd':
147
+ str = String(Math.trunc(Number(arg)));
148
+ break;
149
+ case 'x':
150
+ str = Math.trunc(Number(arg)).toString(16);
151
+ break;
152
+ case 'f':
153
+ str = prec !== undefined ? Number(arg).toFixed(Number(prec)) : String(Number(arg));
154
+ break;
155
+ default:
156
+ return match;
157
+ }
158
+ if (width !== undefined) {
159
+ const w = Number(width);
160
+ const pad = zero && spec !== 's' ? '0' : ' ';
161
+ while (str.length < w) str = pad + str;
162
+ }
163
+ return str;
164
+ },
165
+ );
166
+ };
167
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/node-gi",
3
- "version": "0.13.0",
3
+ "version": "0.21.0",
4
4
  "description": "GObject-Introspection runtime for Node.js — load gi:// namespaces (GLib/GObject/Gio/…) with GJS-compatible semantics. Vendored + rewritten from node-gtk under MIT.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,6 +19,22 @@
19
19
  "./globals": {
20
20
  "types": "./globals.d.ts",
21
21
  "default": "./globals.js"
22
+ },
23
+ "./system": {
24
+ "types": "./system.d.ts",
25
+ "default": "./system.js"
26
+ },
27
+ "./gettext": {
28
+ "types": "./gettext.d.ts",
29
+ "default": "./gettext.js"
30
+ },
31
+ "./cairo": {
32
+ "types": "./cairo.d.ts",
33
+ "default": "./cairo.js"
34
+ },
35
+ "./gtk-runtime": {
36
+ "types": "./gtk-runtime.d.ts",
37
+ "default": "./gtk-runtime.js"
22
38
  }
23
39
  },
24
40
  "files": [
@@ -26,8 +42,17 @@
26
42
  "index.d.ts",
27
43
  "gi.js",
28
44
  "gi.d.ts",
45
+ "gtk-runtime.js",
46
+ "gtk-runtime.d.ts",
29
47
  "globals.js",
30
48
  "globals.d.ts",
49
+ "system.js",
50
+ "system.d.ts",
51
+ "gettext.js",
52
+ "gettext.d.ts",
53
+ "cairo.js",
54
+ "cairo.d.ts",
55
+ "overrides",
31
56
  "src",
32
57
  "binding.gyp",
33
58
  "prebuilds"
@@ -38,16 +63,32 @@
38
63
  "node": "polyfill",
39
64
  "browser": "none",
40
65
  "nativescript": "none"
41
- }
66
+ },
67
+ "tier": 2
42
68
  },
43
69
  "scripts": {
44
70
  "install": "node-gyp rebuild",
45
71
  "build": "node-gyp rebuild",
46
72
  "build:native": "node-gyp rebuild",
73
+ "build:prebuild": "node-gyp rebuild && node scripts/stage-prebuild.mjs",
47
74
  "rebuild": "node-gyp rebuild",
48
75
  "clean": "node-gyp clean",
49
- "test": "node --test",
50
- "test:node": "node --test"
76
+ "test": "NODE_GI_NATIVE=build node --test",
77
+ "test:node": "NODE_GI_NATIVE=build node --test",
78
+ "test:gc": "NODE_GI_NATIVE=build node --test --expose-gc",
79
+ "test:dbus": "dbus-run-session -- env NODE_GI_NATIVE=build node --test --expose-gc test/dbus.test.mjs test/dbus-async.test.mjs",
80
+ "test:bun": "node scripts/cross-runtime.mjs bun",
81
+ "test:conformance": "node scripts/conformance.mjs",
82
+ "test:gimarshalling": "node scripts/gimarshalling.mjs",
83
+ "test:deno": "node scripts/cross-runtime.mjs deno",
84
+ "test:cairo": "NODE_GI_NATIVE=build node --test test/cairo.test.mjs",
85
+ "test:cairo-canvas2d": "NODE_GI_NATIVE=build node --test test/cairo-canvas2d.test.mjs",
86
+ "test:gtk": "NODE_GI_NATIVE=build node --test test/gtk-smoke.test.mjs",
87
+ "test:gtk-cairo": "NODE_GI_NATIVE=build node --test test/cairo-drawfunc.test.mjs",
88
+ "test:adw": "NODE_GI_NATIVE=build node --test test/adw-smoke.test.mjs",
89
+ "test:gtk-template": "NODE_GI_NATIVE=build node --test test/gtk-template.test.mjs",
90
+ "test:gtk-template-callbacks": "NODE_GI_NATIVE=build node --test test/gtk-template-callbacks.test.mjs",
91
+ "test:gtk-template-signals": "NODE_GI_NATIVE=build node --test test/gtk-template-signals.test.mjs"
51
92
  },
52
93
  "dependencies": {
53
94
  "node-addon-api": "^8.0.0"