The Problem

Packaging a webpage as a desktop app traditionally means Electron: hundreds of megabytes, high memory use, and a slow toolchain. Pake replaces that with a Rust/Tauri base, producing installers roughly 5 MB in size. The goal is a one-command path from URL to native app for macOS, Windows, and Linux.

What This Does

Pake is a CLI and GitHub Action that wraps a webpage in a Tauri shell. The TypeScript CLI (bin/cli.ts) orchestrates the build: it resolves options (bin/options/index.ts), generates icons (bin/options/icon.ts), and delegates to platform-specific builders (bin/builders/MacBuilder.ts, WinBuilder.ts, LinuxBuilder.ts). The Rust side (src-tauri/src/) is the compiled app runtime, with injected JavaScript (src-tauri/src/inject/) handling window behavior, theming, and shortcuts.

The repo ships with prebuilt icons and configs for popular sites (Twitter, DeepSeek, WeRead) and supports Docker builds via the Dockerfile.

How It Is Wired

Execution starts at start in bin/builders/BaseBuilder.ts:137, which reaches 22 functions. The core path is start -> mergeConfig -> injectCustomCode -> combineFiles, ending in a fs.writeFile call—so a run writes generated config and injected code to disk before invoking the Rust build.

The highest-traffic functions are generateLinuxPackageName (called from 9 places) and shellExec (6 places). BaseBuilder.ts is the hub: 23 functions, called from 2 files, calling into 11 others. It owns build, start, and buildAndCopy. The bin/utils/name.ts module (6 functions) is imported by 5 files and controls all package naming—a change there ripples across every platform builder.

The module graph has no circular dependencies (0 in 33 edges), so refactoring is safe. The main cost is in src-tauri/src/app/setup.rs and window.rs, where nesting depth reaches 9 levels—hard to follow but isolated from the TypeScript build pipeline.

How To Use It

Setup: Install with pnpm (the repo uses pnpm-lock.yaml and .pnpmrc):

pnpm install

Configuration: No required env vars. Optional: src-tauri/pake.json holds app defaults; src-tauri/tauri.conf.json and platform-specific tauri.*.conf.json files control window and build settings.

Running it: The CLI entry point is bin/cli.ts:

pnpm pake https://example.com --name MyApp

For Docker builds, the Dockerfile exists but no build command is documented in the repo.

Real-World Use

A team shipping an internal dashboard can package it as a native app per platform:

pnpm pake https://internal.metrics.example --name Metrics --icon ./metrics.png

CI then runs the GitHub Action (action.yml) to produce signed installers for all three OSes, with the single-app.yaml workflow handling a single-site build.

Code Health & Issues

Static analysis found 12 issues (4 high, 8 medium). The high-severity items:

  • High – Deep nesting in src-tauri/src/app/setup.rs, window.rs, lib.rs (max depth 9). Flatten with early returns.
  • High – Oversized files: tests/index.js (1352 lines), src-tauri/src/inject/event.js, find.js. Split by responsibility.

Medium issues include an unclosed file handle in icns2png.py, an empty catch {} in event.js, and duplicated code in bin/options/icon.ts and bin/utils/ico.ts.

SDLC observations from the workflow files: CI uses mutable action tags (pnpm/action-setup@v4) instead of commit SHAs, quality-and-test.yml discards exit codes and pushes to the default branch, and no dependency scan or Dependabot is configured. The Dockerfile uses rust:latest and runs as root.

The Bottom Line

Pake is a solid, well-structured tool for turning webpages into lightweight desktop apps. The TypeScript build layer is clean and testable; the Rust runtime has some readability debt but is isolated. Use it if you need fast, small native wrappers and can accept the CI hygiene gaps—pin those actions and add a non-root user before production use.