Skip to content

Commit

Permalink
Improve exports
Browse files Browse the repository at this point in the history
  • Loading branch information
sonnyp committed May 24, 2023
1 parent 2518d53 commit 4ea7a93
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 240 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ rules:
"off",
{ ignore: ["gi://*", "cairo", "gettext", "system", "resource://*"] },
]
import/no-named-as-default: off
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,19 @@ import "./troll/src/globals.js";
// btoa(...)
```

## resolve(base, uri)
## resolve

Arguments

- `base` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) a base uri
- `uri` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) a path or uri, can be absolute or relative
- Returns: [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) the resolved uri

Returns [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) the resolved uri

Similar to `import.meta.resolve` or `new URL(url, base)`.

```js
import { resolve } from "./troll/src/util.js";
import { resolve } from "./troll/src/main.js";

console.log(resolve(import.meta.url, "./xml.js"));
// resource:///some/path/xml.js
Expand All @@ -69,21 +72,27 @@ console.log(resolve("http://foo.example", "http://bar.example"));
// http://bar.example
```
## resolveParse(base, uri)
## resolveParse
Arguments
- `base` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) a base uri
- `uri` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type) a path or uri, can be absolute or relative
- Returns: [\<GLib.Uri\>](https://gjs-docs.gnome.org/glib20~2.0/glib.uri) the resolved uri
Returns [\<GLib.Uri\>](https://gjs-docs.gnome.org/glib20~2.0/glib.uri) the resolved uri
Same as `resolve` but returns a `GLib.Uri` instead of a `string`.
## promiseTask(target, method, finish[, ...args])
## promiseTask
Arguments
- `target` [\<GObject.object\>](https://gjs-docs.gnome.org/gobject20/gobject.object)
- `method` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)
- `finish` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)
- `args` an array of arguments to pass to `method`
- Returns: [\<Promise\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- `...args` the list of arguments to pass to `method`
Returns [\<Promise\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) resolves or rejects with the result of the finish function
Run a Gio async operation and return a promise that resolve with the result of finish method or rejects.
Expand All @@ -92,7 +101,7 @@ See also [Gio.\_promisify](https://gjs.guide/guides/gjs/asynchronous-programming
Examples
```js
import { promiseTask } from "./troll/src/util.js";
import { promiseTask } from "./troll/src/main.js";
import Gio from "gi://Gio";

(async () => {
Expand Down Expand Up @@ -131,9 +140,12 @@ import { once } from "./troll/src/util.js";
## build
Arguments
- `uri` [\<string\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)
- `params` [\<Object\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
- Returns: [\<Object\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
Returns [\<Object\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
A helper function to easily load, build and bind a GTK XML interface. Here is an example
Expand All @@ -144,7 +156,7 @@ A helper function to easily load, build and bind a GTK XML interface. Here is an
#!/usr/bin/env -S gjs -m

import Gtk from "gi://Gtk?version=4.0";
import { build, resolve } from "./troll/src/util.js";
import { build, resolve } from "./troll/src/main.js";

const app = new Gtk.Application({
application_id: "hello.world",
Expand Down Expand Up @@ -233,7 +245,7 @@ You can use it as a jsx pragma with [babel](https://babeljs.io/docs/en/babel-plu
```jsx
import Gtk from "gi://Gtk?version=4.0";
import gsx from "./troll/src/gsx.js";
import gsx from "./troll/src/main.js";

/** @jsx gsx.h */
/** @jsxFrag gsx.Fragment */
Expand Down Expand Up @@ -279,7 +291,7 @@ export default function MyButton() {
```js
import Gtk from "gi://Gtk?version=4.0";
import gsx from "./troll/src/gsx.js";
import gsx from "./troll/src/main.js";

const { Button, Align, Image } = Gtk;

Expand Down
2 changes: 1 addition & 1 deletion gjspack/demo/import_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"imports": {
"gi://Gtk": "gi://Gtk?version=4.0",
"lodash": "./lodash-stub.js",
"troll/": "../../"
"troll": "../../src/main.js"
}
}
2 changes: 1 addition & 1 deletion gjspack/demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import manifest from "./flatpak.json" assert { type: "json" };
// import provider from "./style.scss" assert { type: "css" };
import provider from "./style.css" assert { type: "css" };

import { build } from "troll/src/util.js";
import { build } from "troll";

console.log(manifest.id);

Expand Down
142 changes: 142 additions & 0 deletions src/async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
export class TimeoutError extends Error {
constructor(message) {
super(message);
this.name = "TimeoutError";
}
}

export function promiseTask(object, method, finish, ...args) {
return new Promise((resolve, reject) => {
object[method](...args, (self, asyncResult) => {
try {
resolve(object[finish](asyncResult));
} catch (err) {
reject(err);
}
});
});
}

function noop() {}

export class Deferred extends Promise {
constructor(def = noop) {
let res, rej;
super((resolve, reject) => {
def(resolve, reject);
res = resolve;
rej = reject;
});
this.resolve = res;
this.reject = rej;
}
}

function delay(ms) {
let timeout;
const promise = new Promise((resolve) => {
timeout = setTimeout(() => {
resolve();
}, ms);
});
promise.timeout = timeout;
return promise;
}

export function once(
object,
signal,
options = {
error: "",
timeout: -1,
},
) {
let promise;
if (object.connect && object.disconnect) {
promise = promiseSignal(object, signal, options.error);
} else {
promise = promiseEvent(object, signal, options.error);
}

if (options.timeout < 0) {
return promise;
}

const promise_timeout = timeout(options.timeout);
return Promise.race([promise, promise_timeout]).finally(() => {
clearTimeout(promise_timeout.timeout_id);
});
}

function promiseEvent(object, signal, error_signal) {
const { addListener, removeListener } = normalizeEmitter(object);

return new Promise((resolve, reject) => {
addListener(signal, listener);

function cleanup() {
removeListener(signal, listener);
if (error_signal) removeListener(error_signal, error_listener);
}

if (error_signal) {
addListener(error_signal, error_listener);
}

function error_listener(err) {
cleanup();
reject(err);
}

function listener(...params) {
cleanup();
resolve(params);
}
});
}

function promiseSignal(object, signal, error_signal) {
return new Promise((resolve, reject) => {
const handler_id = object.connect(signal, handler);
let error_handler_id;

function cleanup() {
object.disconnect(handler_id);
if (error_handler_id) object.disconnect(error_handler_id);
}

if (error_signal) {
error_handler_id = object.connect(error_signal, (self, error) => {
cleanup();
reject(error);
});
}

function handler(self, ...params) {
cleanup();
resolve(params);
}
});
}

function normalizeEmitter(emitter) {
const addListener =
emitter.on || emitter.addListener || emitter.addEventListener;
const removeListener =
emitter.off || emitter.removeListener || emitter.removeEventListener;

if (!addListener || !removeListener) {
throw new TypeError("Emitter is not compatible");
}

return {
addListener: addListener.bind(emitter),
removeListener: removeListener.bind(emitter),
};
}

function timeout(ms) {
return delay(ms).then(() => {
throw new TimeoutError(`Promise timed out after ${ms} milliseconds`);
});
}
78 changes: 78 additions & 0 deletions src/builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import GObject from "gi://GObject";
import Gtk from "gi://Gtk";
import GLib from "gi://GLib";

function noop() {}

const BuilderScope = GObject.registerClass(
{
Implements: [Gtk.BuilderScope],
},
class BuilderScope extends GObject.Object {
constructor() {
super();
this.signal_handlers = Object.create(null);
}

add_signal_handler(name, fn) {
this.signal_handlers[name] = fn;
}

// https://docs.gtk.org/gtk4/vfunc.BuilderScope.create_closure.html
vfunc_create_closure(_builder, function_name, flags, _object) {
if (flags & Gtk.BuilderClosureFlags.SWAPPED) {
throw new Error('Signal flag "swapped" is unsupported in JavaScript.');
}

const fn = this.signal_handlers[function_name];
// if (!fn) {
// throw new Error(`No function named \`${function_name}\`.`);
// }
return fn || noop;
}
},
);

export function build(uri, params = {}) {
const builder = new Gtk.Builder();

const scope = new BuilderScope();
builder.set_scope(scope);

for (const [k, v] of Object.entries(params)) {
// signals
if (typeof v === "function") {
scope.add_signal_handler(k, v);
// objects
} else if (v instanceof GObject.Object) {
builder.expose_object(k, v);
} else {
throw new Error("Only function and GObject properties are supported.");
}
}

const handler = {
get(target, prop, receiver) {
return builder.get_object(prop);
},
};

const proxy = new Proxy({}, handler);

const g_uri = GLib.Uri.parse(uri, GLib.UriFlags.NONE);
const scheme = g_uri.get_scheme();
const path = g_uri.get_path();
if (!scheme || !path) {
throw new Error(`Invalud uri \`${uri}\`.`);
}

if (scheme === "resource") {
builder.add_from_resource(path);
} else if (scheme === "file") {
builder.add_from_file(path);
} else {
throw new Error(`Unsuported scheme \`${uri}\`.`);
}

return proxy;
}
9 changes: 7 additions & 2 deletions src/gsx.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,10 @@ function registerClasses(widget, classnames) {
});
}

export { Fragment, h };
export default h;
function gsx(...args) {
return h(...args);
}
Object.assign(gsx, { h, Fragment });

export { gsx, Fragment, h };
export default gsx;
23 changes: 23 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import gsx from "./gsx.js";
import {
resolve,
parseResolve,
getPid,
getGjsVersion,
getGLibVersion,
getGIRepositoryVersion,
} from "./util.js";
import { build } from "./builder.js";
import { promiseTask } from "./async.js";

export {
gsx,
build,
resolve,
parseResolve,
getPid,
getGjsVersion,
getGLibVersion,
getGIRepositoryVersion,
promiseTask,
};
2 changes: 1 addition & 1 deletion src/std/WebSocket.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { promiseTask } from "../util.js";
import { promiseTask } from "../async.js";

import Soup from "gi://Soup?version=3.0";
import GLib from "gi://GLib";
Expand Down
2 changes: 1 addition & 1 deletion src/std/fetch.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { promiseTask } from "../util.js";
import { promiseTask } from "../async.js";
import Soup from "gi://Soup?version=3.0";
import GLib from "gi://GLib";
import Gio from "gi://Gio";
Expand Down
Loading

0 comments on commit 4ea7a93

Please sign in to comment.