Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ on:
workflow_dispatch:

jobs:
build-wasm:
uses: ./.github/workflows/wasm.yaml

publish-website:
needs: build-wasm
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -22,6 +26,12 @@ jobs:
sudo docker image prune --all --force
sudo docker builder prune -a
- name: Download WASM demo artifact
uses: actions/download-artifact@v4
with:
name: wasm-demo
path: doc/wasm

- name: Setup Node.js
uses: actions/setup-node@v4
with:
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/wasm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
pull_request:
branches: [main]
workflow_dispatch:
workflow_call:

jobs:
build:
Expand Down Expand Up @@ -47,10 +48,24 @@ jobs:
- name: Install wasm-pack
run: cargo install wasm-pack

- name: Build WASM library
working-directory: ggsql-wasm/library
run: npm install && npm run build

- name: Build WASM package
working-directory: ggsql-wasm
run: wasm-pack build --target web --profile wasm --no-opt

- name: Optimise WASM binary
working-directory: ggsql-wasm
run: wasm-opt pkg/ggsql_wasm_bg.wasm -o pkg/ggsql_wasm_bg.wasm -Oz --all-features

- name: Build WASM demo
working-directory: ggsql-wasm/demo
run: npm install && npm run build

- name: Upload demo artifact
uses: actions/upload-artifact@v4
with:
name: wasm-demo
path: ggsql-wasm/demo/dist/
1 change: 1 addition & 0 deletions doc/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.quarto/
**/*.quarto_ipynb
_site
wasm
15 changes: 15 additions & 0 deletions doc/_quarto.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
project:
type: website
resources:
- wasm/**

website:
title: "ggsql"
Expand Down Expand Up @@ -48,6 +50,8 @@ website:
- text: "`LABEL`"
href: syntax/clause/label.qmd
- examples.qmd
- text: Playground
href: wasm/index.html
tools:
- icon: github
menu:
Expand Down Expand Up @@ -113,3 +117,14 @@ format:
<script src="https://cdn.jsdelivr.net/gh/posit-dev/supported-by-posit/js/badge.min.js"
data-light-bg="#94D2BD"
data-dark-bg="#001219"></script>
include-after-body:
- text: |
<script type="module">
const offset = document.querySelector('meta[name="quarto:offset"]')?.content || './';
const base = offset + 'wasm/';
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = base + 'quarto.css';
document.head.appendChild(link);
import(base + 'quarto.js');
</script>
1 change: 1 addition & 0 deletions doc/index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ A declarative visualization language that extends SQL with powerful data visuali
::: {.hero-buttons}
[Get Started](installation.qmd){.btn .btn-secondary .btn-lg}
[View Examples](examples.qmd){.btn .btn-outline-secondary .btn-lg}
[Playground](wasm/index.html){.btn .btn-outline-secondary .btn-lg}
:::

:::
Expand Down
7 changes: 5 additions & 2 deletions ggsql-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
csv = "1"
polars = { version = "0.52", default-features = false, features = ["sql", "dtype-full"] }
ggsql = { path = "../src", default-features = false, features = ["polars-sql", "vegalite"] }
polars = { version = "0.52", default-features = false, features = ["dtype-full"] }
ggsql = { path = "../src", default-features = false, features = ["vegalite", "sqlite", "builtin-data"] }
serde_json = "1"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.35", features = ["full"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1.35", default-features = false }
sqlite-wasm-rs = "0.5.2"

54 changes: 54 additions & 0 deletions ggsql-wasm/build-wasm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

SKIP_BINARY=false
SKIP_OPT=false
for arg in "$@"; do
case "$arg" in
--skip-binary) SKIP_BINARY=true ;;
--skip-opt) SKIP_OPT=true ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done

check_wasm32_support() {
local cc="${CC:-clang}"
if ! echo "int main(){return 0;}" | \
"$cc" -target wasm32-unknown-unknown -c -o /dev/null -x c - 2>/dev/null; then
echo "Error: '$cc' does not support the wasm32-unknown-unknown target." >&2
echo "Install an LLVM/clang toolchain with wasm backend support (e.g. 'sudo apt-get install llvm' on Debian/Ubuntu)." >&2
exit 1
fi
}

echo "Building WASM library..."
(cd "$SCRIPT_DIR/library" && npm install && npm run build)

if [ "$SKIP_BINARY" = false ]; then
echo "Checking wasm32 compiler support..."
check_wasm32_support

echo "Building WASM binary..."
(cd "$SCRIPT_DIR" && wasm-pack build --target web --profile wasm --no-opt)

if [ "$SKIP_OPT" = false ]; then
echo "Optimising WASM binary..."
(cd "$SCRIPT_DIR" && wasm-opt pkg/ggsql_wasm_bg.wasm -o pkg/ggsql_wasm_bg.wasm -Oz --all-features)
else
echo "Skipping wasm-opt (--skip-opt)."
fi
else
echo "Skipping WASM binary build (--skip-binary)."
fi

echo "Building WASM demo and Quarto integration..."
(cd "$SCRIPT_DIR/demo" && npm install && npm run build)

echo "Copying output to doc/wasm..."
rm -rf "$REPO_ROOT/doc/wasm"
cp -r "$SCRIPT_DIR/demo/dist" "$REPO_ROOT/doc/wasm"

echo "Done! Output is in: $REPO_ROOT/doc/wasm"
87 changes: 87 additions & 0 deletions ggsql-wasm/demo/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as esbuild from "esbuild";
import { copyFileSync, mkdirSync } from "fs";
import { dirname, join } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const isWatch = process.argv.includes("--watch");
const distDir = join(__dirname, "dist");

// Ensure dist/ directory exists
mkdirSync(distDir, { recursive: true });

// Copy static files
console.log("Copying static files...");
copyFileSync(join(__dirname, "src/index.html"), join(distDir, "index.html"));
copyFileSync(
join(__dirname, "../pkg/ggsql_wasm_bg.wasm"),
join(distDir, "ggsql_wasm_bg.wasm"),
);
copyFileSync(
join(__dirname, "node_modules/vscode-oniguruma/release/onig.wasm"),
join(distDir, "onig.wasm"),
);
copyFileSync(
join(__dirname, "../../ggsql-vscode/syntaxes/ggsql.tmLanguage.json"),
join(distDir, "ggsql.tmLanguage.json"),
);

// Build Monaco editor web worker
console.log("Building Monaco editor worker...");
await esbuild.build({
entryPoints: [
join(
__dirname,
"node_modules/monaco-editor/esm/vs/editor/editor.worker.js",
),
],
bundle: true,
outfile: join(distDir, "editor.worker.js"),
format: "iife",
});

// Shared build options
const sharedOptions = {
bundle: true,
format: "esm",
platform: "browser",
target: "es2020",
sourcemap: true,
nodePaths: [join(__dirname, "node_modules")],
loader: {
".ttf": "file",
},
};

// Build playground bundle
const playgroundOptions = {
...sharedOptions,
entryPoints: [join(__dirname, "src/main.ts")],
outfile: join(distDir, "bundle.js"),
};

// Build quarto integration bundle
const quartoOptions = {
...sharedOptions,
entryPoints: [join(__dirname, "src/quarto/main.ts")],
outfile: join(distDir, "quarto.js"),
loader: {
...sharedOptions.loader,
".css": "css",
},
};

if (isWatch) {
console.log("Starting watch mode...");
const playgroundCtx = await esbuild.context(playgroundOptions);
const quartoCtx = await esbuild.context(quartoOptions);
await Promise.all([playgroundCtx.watch(), quartoCtx.watch()]);
console.log("Watching for changes...");
} else {
console.log("Building bundles...");
await Promise.all([
esbuild.build(playgroundOptions),
esbuild.build(quartoOptions),
]);
console.log("Build complete!");
}
Loading
Loading