# Development Workflow for Automattic/harper: A Complete Contributor's Guide

> Understand the Automattic/harper development workflow. Learn how the just command runner orchestrates builds for Rust, WebAssembly, and JS/TS integrations in this contributor's guide.

- Repository: [Automattic/harper](https://github.com/Automattic/harper)
- Tags: how-to-guide
- Published: 2026-08-01

---

**The Automattic/harper development workflow relies on the `just` command runner to orchestrate incremental builds across a Rust-based grammar engine, WebAssembly bindings, and multiple JavaScript/TypeScript integrations.**

Automattic/harper is a monorepo that combines the `harper-core` Rust grammar engine with WebAssembly outputs and JavaScript ecosystem integrations including VS Code, Obsidian, WordPress, and a Tauri desktop application. The entire development workflow is managed through a centralized `justfile` that handles cross-language dependencies, ensuring you only rebuild what you have edited.

## Prerequisites and Initial Setup

Before contributing, you need the Rust toolchain, Node.js 14+, and PNPM installed on your system. The repository provides a single command to bootstrap the entire environment.

Run `just setup` from the repository root to execute the initial configuration. This recipe runs `cargo fetch` to cache Rust dependencies, installs PNPM globally, and executes `pnpm install` in every JavaScript package. This one-time setup populates all necessary caches and prepares the monorepo for development.

## Understanding the Monorepo Architecture

The repository is organized into distinct layers that depend on each other sequentially. The **harper-core** crate contains the grammar engine and linting rules written in Rust. **harper-wasm** compiles this core to WebAssembly using `wasm-pack`, producing `harper_wasm.{js,wasm}` files. **harper.js** bundles these WASM artifacts into an NPM package that provides the public `Linter` API.

Above this foundation sit the platform-specific integrations: a VS Code extension, Chrome and Firefox browser extensions, an Obsidian plugin, a WordPress plugin, and a Tauri-based desktop application. Each integration imports either [`harper.js`](https://github.com/Automattic/harper/blob/main/harper.js) or the raw WASM module, requiring the lower layers to be built first.

## The Just-Based Development Workflow

All development tasks are exposed through the `justfile` located at the repository root. View the complete list of available recipes by running `just --list`. The workflow follows six distinct phases designed to minimize rebuild times.

### Phase 1: Environment Setup

The `just setup` command installs the Rust toolchain, Node dependencies, and configures the development environment. This phase runs `cargo fetch` to download Rust crates and `pnpm install` across all packages in the `packages/` directory.

### Phase 2: Building Shared Libraries

After any change to Rust code, you must rebuild the shared libraries that JavaScript consumes. Execute `just build-wasm` to compile `harper-wasm` using `wasm-pack`, which generates the `harper_wasm.{js,wasm}` artifacts. Follow this with `just build-harperjs` to bundle the WASM module into the distributable NPM package.

Additional recipes build supporting UI components: `just build-lint-framework`, `just build-components`, and `just build-harper-editor`. These create `dist/` directories for each UI package that downstream integrations depend upon.

### Phase 3: Integration-Specific Development

To work on a specific integration, use the dedicated development recipes that start watch modes and local servers:

- **`just dev-web`** – Launches a Vite development server for the documentation site at `http://localhost:1420`
- **`just dev-vscode`** – Watches the VS Code extension source files in `packages/vscode-plugin/` and rebuilds automatically
- **`just dev-obsidian`** – Starts the Obsidian plugin development mode
- **`just dev-wp`** – Runs the WordPress plugin development environment
- **`just dev-desktop`** – Launches the Tauri desktop application from [`harper-desktop/src-tauri/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-desktop/src-tauri/src/main.rs)

### Phase 4: Testing Across the Stack

The repository enforces quality through layered testing. Run `just check-rust` to execute `cargo test` across all Rust crates, validating the grammar engine and linting rules. For JavaScript, `just check-js` runs `pnpm test` in every JS package.

Integration-specific tests include `just test-harperjs` for the core JavaScript API, `just test-vscode` for the VS Code extension, and `just test-obsidian` for the Obsidian plugin. Browser extension testing utilizes Playwright for UI automation.

### Phase 5: Linting and Formatting

Maintain code consistency using `just fmt`, which executes `cargo fmt` for Rust files and `pnpm format` for JavaScript/TypeScript. The `just precommit` recipe runs the full repository-wide linting pipeline, ensuring your changes meet project standards before submission.

### Phase 6: Release Builds

Produce production artifacts using release-specific recipes. `just build-desktop-linux` generates Linux binaries including `.deb` and `.AppImage` formats. `just build-web` creates the static documentation site, while `just build-wasm` produces the optimized WebAssembly bundle for distribution.

## Day-to-Day Development Patterns

A typical contribution follows this incremental cycle. First, clone the repository and run the setup:

```bash
git clone https://github.com/Automattic/harper.git
cd harper
just setup

```

When modifying a grammar rule in `harper-core/src/linting/weir_rules/`, rebuild only the necessary components:

```bash
just build-wasm          # Recompile Rust to WASM

just test-harperjs      # Verify JS-side integration

```

For VS Code extension development, navigate to [`packages/vscode-plugin/src/extension.ts`](https://github.com/Automattic/harper/blob/main/packages/vscode-plugin/src/extension.ts) and run:

```bash
just dev-vscode    # Watch mode for extension development

just test-vscode   # Run Playwright integration tests

```

Before committing, validate your changes against the full CI pipeline:

```bash
just check-rust
just check-js
just fmt

```

## Critical Source Files and Configuration

Several files define the core scaffold of the build system:

- **`justfile`** – Central task runner located at the repository root that orchestrates Rust builds, WASM packaging, and JavaScript workflows
- **[`harper-core/Cargo.toml`](https://github.com/Automattic/harper/blob/main/harper-core/Cargo.toml)** – Defines the core grammar engine dependencies and compilation targets
- **[`harper-wasm/Cargo.toml`](https://github.com/Automattic/harper/blob/main/harper-wasm/Cargo.toml)** – Configures the WebAssembly build target used by `wasm-pack`
- **[`packages/harper.js/package.json`](https://github.com/Automattic/harper/blob/main/packages/harper.js/package.json)** – NPM package manifest that bundles the WASM module and exposes the `Linter` class
- **[`packages/web/vite.config.ts`](https://github.com/Automattic/harper/blob/main/packages/web/vite.config.ts)** – Single source of truth for documentation routes and sidebar navigation
- **[`harper-desktop/src-tauri/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-desktop/src-tauri/src/main.rs)** – Entry point for the Tauri desktop application and highlighter process
- **[`packages/vscode-plugin/src/extension.ts`](https://github.com/Automattic/harper/blob/main/packages/vscode-plugin/src/extension.ts)** – Implements the VS Code language-server client integration
- **[`harper-core/default_config.json`](https://github.com/Automattic/harper/blob/main/harper-core/default_config.json)** – Curated default rule configuration; new rules must be registered here to be enabled by default

When adding documentation, update the corresponding markdown files under `packages/web/src/routes/docs/` and verify changes using `just dev-web`.

## Summary

- The **Automattic/harper** repository uses a **Rust + WebAssembly + JavaScript** stack managed entirely through **just** recipes
- The **`justfile`** provides incremental builds, ensuring you only recompile changed components
- **Shared libraries** (WASM and harper.js) must be built before working on integrations that consume them
- **Development recipes** like `just dev-vscode` and `just dev-desktop` start platform-specific watch modes
- **Testing** spans Rust unit tests (`just check-rust`), JavaScript tests (`just check-js`), and integration-specific Playwright suites
- **Key files** including [`harper-core/Cargo.toml`](https://github.com/Automattic/harper/blob/main/harper-core/Cargo.toml) and [`packages/web/vite.config.ts`](https://github.com/Automattic/harper/blob/main/packages/web/vite.config.ts) control build behavior and documentation routing

## Frequently Asked Questions

### Do I need to rebuild the entire repository after changing Rust code?

No. The workflow is deliberately incremental. After editing Rust code in `harper-core`, run only `just build-wasm` to regenerate the WebAssembly artifacts. The `just` system automatically handles dependencies, so JavaScript packages that import the WASM module will use the updated build without requiring a full repository recompile.

### How do I test only the VS Code extension without running the full test suite?

Use the integration-specific recipes. Run `just dev-vscode` to start the extension in watch mode, then execute `just test-vscode` to run only the Playwright-based integration tests for the VS Code plugin. This targets the code in [`packages/vscode-plugin/src/extension.ts`](https://github.com/Automattic/harper/blob/main/packages/vscode-plugin/src/extension.ts) without executing Rust or other JavaScript tests.

### What is the fastest way to verify a new grammar rule is working correctly?

After creating your rule file in `harper-core/src/linting/weir_rules/`, run `just build-wasm` followed by `just test-harperjs`. This compiles the Rust code to WASM and runs the JavaScript test suite that validates the `Linter` API behavior. For rapid iteration, you can also add unit tests in `harper-core/tests/` and run `just check-rust` for faster feedback than the full JS integration tests.

### How does the documentation site stay synchronized with code changes?

The documentation routes are defined in [`packages/web/vite.config.ts`](https://github.com/Automattic/harper/blob/main/packages/web/vite.config.ts), which serves as the single source of truth for the site's navigation structure. When adding new documentation pages under `packages/web/src/routes/docs/`, you must update the Vite configuration to include the new routes. Preview changes locally using `just dev-web` before submitting your pull request.