Bundling projects
Bundling can be done with the ags bundle
command.
Details
Uses esbuild under the hood.
$ ags bundle --help
Bundle an app
Usage:
ags bundle [entryfile] [outfile] [flags]
Examples:
bundle app.ts my-shell -d "DATADIR='/usr/share/my-shell'"
Flags:
-d, --define stringArray replace global identifiers with constant expressions
-h, --help help for bundle
-p, --package use astal package as defined in package.json
-r, --root string root directory of the project
Currently there are 3 builtin plugins.
css: import
.css
will be inlined as a stringsass: importing
.scss
files will go through the sass transpiler and be inlined as a string that contains valid css using thesass
executable found on$PATH
scss$color: white; selector { color: $color; }
tsimport style from "./style.scss" print(style) // selector { // color: white; // }
blp: importing
.blp
files will go through blueprint and be inlined as a string that contains xml template definitionsblpusing Gtk 4.0; Label { label: _("hello"); }
tsimport ui from "./ui.blp" print(ui) // <?xml version="1.0" encoding="UTF-8"?> // <interface> // <requires lib="gtk" version="4.0"/> // <object class="GtkLabel"> // <property name="label" translatable="yes">hello</property> // </object> // </interface>
inline: importing with
inline:/path/to/file
will inline the contents of the file as a stringtxtLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.
tsimport data from "inline:./data.txt" print(ui) // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.
AGS defines SRC
which by default will point to the directory of entryfile
. It can be overriden with -d "SRC='/path/to/source'"
#!/usr/bin/ags run
App.start({
main() {
print(`source dir is ${SRC}`)
}
})
By default ags bundle
will alias the astal
package for the one defined at installation.
This behavior can be altered by using the --package
flag which will instead use the astal package as defined in package.json
.
Example
using Gtk 4.0;
using Astal 4.0;
template $Bar: Astal.Window {
// bitfields currently don't work in blueprint
// anchor: top | left | right;
exclusivity: exclusive;
CenterBox {
center-widget: Label {
label: "hello";
};
}
}
#!/usr/bin/gjs -m
import { register } from "astal/gobject"
import { App, Astal } from "astal/gtk4"
import Template from "./Bar.blp"
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
@register({ GTypeName: "Bar", Template })
class Bar extends Astal.Window {
}
App.start({
instanceName: "bar",
main() {
new Bar({
application: App,
anchor: TOP | LEFT | RIGHT,
visible: true,
})
}
})
ags bundle ./app.ts bar
chmod +x ./bar
./bar
NOTE
On Nix this still has to be done in a derivation as the bundled script is not wrapped.
Distributing
If you have no data files, you can use ags bundle
and distribute its output JS file as an executable. Optionally use a build tool like meson to also declare its runtime dependencies.
When you have data files that you cannot inline as a string, for example icons, a good practice would be to:
- Install data files to a directory, usually
/usr/share/your-project
- Define it as
DATADIR
inenv.d.ts
and at bundle time with--define
- In code you can refer to data files through this
DATADIR
variable
prefix = get_option('prefix')
pkgdatadir = prefix / get_option('datadir') / meson.project_name()
bindir = prefix / get_option('bindir')
install_data(
files('data/data.txt'),
install_dir: pkgdatadir,
)
custom_target(
command: [
find_program('ags'),
'bundle',
'--define', 'DATADIR="' + pkgdatadir + '"',
'--root', meson.project_source_root(),
meson.project_source_root() / 'app.ts',
meson.project_name(),
],
output: [meson.project_name()],
input: files('app.ts'),
install: true,
install_dir: bindir,
)
declare const DATADIR: string
const data = `${DATADIR}/data.txt`
TIP
On Nix you can use the lib.bundle function as well as meson.
Notice for Gtk4
gtk4-layer-shell
needs to be linked before wayland. When bundling a Gtk4 application you will have to use a wrapper to make it work.
#!/bin/bash
LD_PRELOAD="@LAYER_SHELL_LIBDIR@/libgtk4-layer-shell.so" @MAIN_PROGRAM@ $@
pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name()
main = meson.project_name() + '.wrapped'
custom_target(
command: [
find_program('ags'),
'bundle',
'--root', meson.project_source_root(),
meson.project_source_root() / 'app.ts',
main,
],
output: [meson.project_name()],
input: files('app.ts'),
install: true,
install_dir: pkgdatadir,
)
configure_file(
input: files('wrapper.sh'),
output: meson.project_name(),
configuration: {
'MAIN_PROGRAM': pkgdatadir / main,
'LAYER_SHELL_LIBDIR': dependency('gtk4-layer-shell-0').get_variable('libdir'),
},
install: true,
install_dir: get_option('prefix') / get_option('bindir'),
)