Development Workflow for Automattic/harper: A Complete Contributor's Guide
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 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 athttp://localhost:1420just dev-vscode– Watches the VS Code extension source files inpackages/vscode-plugin/and rebuilds automaticallyjust dev-obsidian– Starts the Obsidian plugin development modejust dev-wp– Runs the WordPress plugin development environmentjust dev-desktop– Launches the Tauri desktop application fromharper-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:
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:
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 and run:
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:
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 workflowsharper-core/Cargo.toml– Defines the core grammar engine dependencies and compilation targetsharper-wasm/Cargo.toml– Configures the WebAssembly build target used bywasm-packpackages/harper.js/package.json– NPM package manifest that bundles the WASM module and exposes theLinterclasspackages/web/vite.config.ts– Single source of truth for documentation routes and sidebar navigationharper-desktop/src-tauri/src/main.rs– Entry point for the Tauri desktop application and highlighter processpackages/vscode-plugin/src/extension.ts– Implements the VS Code language-server client integrationharper-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
justfileprovides 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-vscodeandjust dev-desktopstart 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.tomlandpackages/web/vite.config.tscontrol 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 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, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →