# GeoLibre Build System Tools: A Complete Guide to npm Workspaces, Vite, and Tauri

> Explore GeoLibre's build system: npm workspaces, Vite, and Tauri. Learn how this polyglot GIS platform generates web, desktop, and Python outputs from a single monorepo.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-16

---

**GeoLibre uses npm workspaces, Vite, Cargo, and Tauri to build a polyglot GIS platform that outputs web bundles, native desktop binaries, and Python embeddings from a single monorepo.**

The [opengeos/GeoLibre](https://github.com/opengeos/GeoLibre) repository is a multi-language geographic information system (GIS) platform that orchestrates JavaScript, Rust, and Python through a unified build pipeline. This analysis breaks down every build tool, configuration file, and custom script that powers the development workflow.

---

## JavaScript and TypeScript Build Tools

GeoLibre's frontend and worker layers rely on a modern JavaScript toolchain centered on npm workspaces and Vite.

### npm Workspaces

The root [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) declares a monorepo structure using **npm workspaces**:

```json
{
  "workspaces": ["apps/*", "packages/*", "workers/*"]
}

```

This configuration, found at [[`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json)](https://github.com/opengeos/GeoLibre/blob/main/package.json#L6-L9), enables single-command dependency management across three distinct code groups. Running `npm install` hoists shared dependencies and establishes a unified dependency graph for the entire project.

### Vite and Rollup

**Vite** serves as the primary build orchestrator. The core configuration lives in [[`apps/geolibre-desktop/vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts)](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts#L1-L8):

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'

export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      // Manual chunking rules for code-splitting
    }
  }
})

```

Vite wraps **Rollup** for production bundling. The configuration defines `manualChunks` to control code splitting and asset boundaries. This setup handles hot module replacement (HMR) during development and optimized static asset generation for deployment.

### esbuild for Minification

GeoLibre plugs **esbuild** directly into the Vite pipeline through a custom plugin. In [[`vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/vite.config.ts)](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts#L13-L31), the `selectiveJsMinifyPlugin` uses esbuild's `transform` API to minify specific production chunks:

```typescript
const selectiveJsMinifyPlugin = {
  name: 'selective-js-minify',
  renderChunk(code, chunk) {
    if (shouldMinify(chunk.fileName)) {
      return esbuild.transform(code, { minify: true })
    }
    return null
  }
}

```

This selective approach avoids minifying already-optimized vendor bundles while compressing application code.

### tsx for ESM-native Testing

**tsx** enables TypeScript execution without compilation. Listed under devDependencies in [[`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json)](https://github.com/opengeos/GeoLibre/blob/main/package.json#L41-L48), it supports native ESM test runs:

```bash
node --import tsx --test src/**/*.test.ts

```

This eliminates the need for a separate TypeScript compilation step in test workflows.

### Playwright for End-to-End Testing

**Playwright** combined with **@axe-core/playwright** provides accessibility-aware browser testing. The `test:e2e` npm script executes against the built web application, validating runtime behavior and WCAG compliance.

---

## Rust Build Tools for Tauri Desktop

GeoLibre produces native desktop applications through the Tauri framework, which binds a Rust backend to the Vite-generated frontend.

### Cargo Package Management

**Cargo** manages the Rust crate defined in [[`apps/geolibre-desktop/src-tauri/Cargo.toml`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src-tauri/Cargo.toml)](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src-tauri/Cargo.toml#L1-L30):

```toml
[package]
name = "geolibre-desktop"
version = "0.1.0"

[features]
native-duckdb = ["duckdb"]
mas = ["tauri/macos-private-api"]

[profile.release]
opt-level = "s"
lto = true

```

This manifest declares feature flags for optional DuckDB integration and macOS App Store compatibility, plus release optimizations for binary size (`opt-level = "s"`) and link-time optimization.

### Tauri v2 CLI

**Tauri v2** bridges the Rust shell with the web frontend. The build process invokes the Tauri CLI through a Node.js wrapper script:

```bash
npm run tauri:build

```

This command compiles the Rust binary, bundles the Vite output, and generates platform-specific installers for Windows, macOS, and Linux.

---

## Python Build Tools for Backend Services

GeoLibre includes a FastAPI sidecar service managed through modern Python tooling.

### uv for Dependency Locking

**uv** replaces pip for dependency resolution and locking. The [`backend/geolibre_server/uv.lock`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/uv.lock) file pins exact versions of all transitive dependencies. CI validates lockfile consistency with:

```bash
uv lock --check

```

This ensures reproducible builds across environments without the performance cost of traditional pip resolution.

### pytest for Test Execution

**pytest** and **pytest-cov** run backend unit tests with coverage enforcement:

```bash
pytest --cov=geolibre_server --cov-fail-under=55

```

The `--cov-fail-under=55` threshold blocks merges that reduce test coverage below 55%.

---

## Custom Build Scripts

GeoLibre extends standard tooling with Node.js scripts in the `scripts/` directory.

### lite-build.mjs for Cloudflare Deployment

[`scripts/lite-build.mjs`](https://github.com/opengeos/GeoLibre/blob/main/scripts/lite-build.mjs#L1-L12) wraps the standard build with Cloudflare-specific constraints:

```javascript
// Enforces 25 MiB per-file limit for Cloudflare Pages
const MAX_FILE_SIZE = 25 * 1024 * 1024

async function validateBuild(outputDir) {
  const files = await glob(`${outputDir}/**/*.{js,wasm}`)
  for (const file of files) {
    const stats = await stat(file)
    if (stats.size > MAX_FILE_SIZE) {
      throw new Error(`${file} exceeds ${MAX_FILE_SIZE} bytes`)
    }
  }
}

```

Lines [73-81](https://github.com/opengeos/GeoLibre/blob/main/scripts/lite-build.mjs#L73-L81) implement the size validation logic, failing the build if any asset exceeds Cloudflare's limit. The script also offloads DuckDB-WASM to jsDelivr CDN to reduce bundle size.

### tauri-build.mjs for Flagged Builds

[`scripts/tauri-build.mjs`](https://github.com/opengeos/GeoLibre/blob/main/scripts/tauri-build.mjs) orchestrates Tauri compilation with optional feature flags:

```bash

# Standard release

node scripts/tauri-build.mjs

# With native DuckDB (bundles WASM locally)

node scripts/tauri-build.mjs --native-duckdb

# macOS App Store variant

node scripts/tauri-build.mjs --mas

```

---

## CI and Automation Integration

GeoLibre's build tools chain together in CI through npm script composition.

### Unified CI Command

The root [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) defines a comprehensive CI pipeline at [lines 28-30](https://github.com/opengeos/GeoLibre/blob/main/package.json#L28-L30):

```bash
npm run ci

```

This executes: lint → build → test:frontend:coverage → test:worker → test:backend:coverage → check:rust

### Rust Check Integration

The `check:rust` script invokes Cargo validation:

```bash
cargo check --manifest-path apps/geolibre-desktop/src-tauri/Cargo.toml

```

This catches compilation errors without producing a full release binary.

### Pre-commit Hooks

**pre-commit** runs the full npm build before accepting commits, ensuring compiled assets in the repository remain synchronized with source changes. The [`.pre-commit-config.yaml`](https://github.com/opengeos/GeoLibre/blob/main/.pre-commit-config.yaml) references a local `npm-build` hook.

---

## Build Outputs

GeoLibre's build system produces three distinct artifacts from one source tree:

| Output | Toolchain | Location |
|--------|-----------|----------|
| **Web bundle** | Vite + Rollup | `apps/geolibre-desktop/dist/` |
| **Desktop binary** | Tauri + Cargo | `apps/geolibre-desktop/src-tauri/target/` |
| **Python wheel** | npm workspaces + setuptools | `python/` (Jupyter embedding) |

---

## Summary

- **npm workspaces** unify dependency management across apps, packages, and workers in a single monorepo structure defined in the root [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json).
- **Vite** with Rollup and esbuild handles development serving, HMR, and optimized production bundling with custom chunking rules in [`vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/vite.config.ts).
- **Cargo** and **Tauri v2** compile the Rust desktop shell with feature flags for platform-specific and optional native dependencies.
- **uv** and **pytest** manage the Python FastAPI backend with locked dependencies and coverage-gated testing.
- **Custom scripts** (`lite-build.mjs`, `tauri-build.mjs`) enforce deployment constraints and orchestrate complex build variants.
- The **CI pipeline** (`npm run ci`) validates the entire stack through chained npm scripts including Rust compilation checks.

---

## Frequently Asked Questions

### What is the primary JavaScript bundler in GeoLibre?

**Vite** is the primary bundler. It provides the development server, handles hot module replacement, and orchestrates Rollup for production builds. The configuration in [`apps/geolibre-desktop/vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts) includes custom plugins for selective esbuild minification and manual chunk splitting.

### How does GeoLibre manage dependencies across multiple languages?

GeoLibre uses **npm workspaces** for JavaScript/TypeScript, **Cargo** for Rust, and **uv** for Python. Each tool maintains its own lockfile ([`package-lock.json`](https://github.com/opengeos/GeoLibre/blob/main/package-lock.json), `Cargo.lock`, `uv.lock`) to ensure reproducible builds. The CI pipeline validates all three ecosystems through a single `npm run ci` command.

### Can GeoLibre build a lightweight web version without desktop features?

Yes. The `npm run lite:build` command executes `scripts/lite-build.mjs`, which produces a Cloudflare-compatible bundle. This variant offloads DuckDB-WASM to jsDelivr CDN and enforces a 25 MiB per-file size limit, making it suitable for edge deployment without the Rust desktop shell.

### What testing frameworks does GeoLibre use?

GeoLibre employs **three testing stacks**: Node.js test runner with tsx for ESM-native TypeScript tests, **Playwright** with axe-core for browser-based end-to-end testing, and **pytest** with coverage thresholds for the Python backend. Each integrates into the `npm run ci` pipeline with distinct coverage requirements.