# Automattic Harper Project Structure: Monorepo Layout and Component Architecture

> Explore the Automattic Harper project structure a Rust monorepo. Discover its core grammar engine, LSP server, CLI tool, WebAssembly bindings, and editor plugins within this detailed guide.

- Repository: [Automattic/harper](https://github.com/Automattic/harper)
- Tags: architecture
- Published: 2026-07-26

---

**Harper is organized as a Rust-centric monorepo with a core grammar engine at its center, surrounded by language-specific front-ends including an LSP server, CLI tool, WebAssembly bindings, and multiple editor plugins.**

The Automattic Harper repository is an open-source grammar and spell checking engine distributed as a monorepo. It houses the Rust-based core engine, language-specific front-ends, a documentation website, and multiple editor integrations all managed through unified Rust and JavaScript workspace configurations.

## Core Rust Workspace Components

The heart of the project resides in several interconnected Rust crates defined in the root [`Cargo.toml`](https://github.com/Automattic/harper/blob/main/Cargo.toml) workspace.

### harper-core: The Grammar Engine

**`harper-core`** contains the grammar-checking engine written in Rust. It parses text, runs spelling and style rules, and exposes a library used by all other components. The public API surface including `Document`, `LintGroup`, and rule registration is defined in [`harper-core/src/lib.rs`](https://github.com/Automattic/harper/blob/main/harper-core/src/lib.rs).

### harper-ls: Language Server Protocol Implementation

**`harper-ls`** wraps the core engine in an LSP implementation, enabling real-time diagnostics in editors. The entry point is located at [`harper-ls/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-ls/src/main.rs), which bridges editor buffers to the core linting functionality.

### harper-cli: Command-Line Interface

**`harper-cli`** provides a debugging and linting tool for scripts or CI pipelines. Running `harper-cli lint path/to/file.txt` executes the driver in [`harper-cli/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-cli/src/main.rs), which constructs a `Document` using the merged dictionary (user dictionary + curated wordlist) and outputs lint results as JSON. The linting logic implementation resides in [`harper-cli/src/lint.rs`](https://github.com/Automattic/harper/blob/main/harper-cli/src/lint.rs).

### Supporting Crates: Comments and Brill Tagging

**`harper-comments`** provides language-specific parsers for linting comment blocks in source files, with its library root at [`harper-comments/src/lib.rs`](https://github.com/Automattic/harper/blob/main/harper-comments/src/lib.rs). **`harper-brill`** implements a statistical POS-tagger used by the core for advanced linguistic analysis, located at [`harper-brill/src/lib.rs`](https://github.com/Automattic/harper/blob/main/harper-brill/src/lib.rs).

## JavaScript and WebAssembly Layer

The repository includes a JavaScript ecosystem coordinated through [`pnpm-workspace.yaml`](https://github.com/Automattic/harper/blob/main/pnpm-workspace.yaml), allowing packages to depend on the compiled WebAssembly artifact (`harper-wasm`) produced by the core.

### harper.js: WebAssembly Bridge

**[`harper.js`](https://github.com/Automattic/harper/blob/main/harper.js)** compiles the core to WebAssembly and provides a thin API for browser or Node.js usage. The JavaScript entry point at [`packages/harper.js/src/index.ts`](https://github.com/Automattic/harper/blob/main/packages/harper.js/src/index.ts) loads the compiled WebAssembly module:

```javascript
import { lint } from 'harper.js';

(async () => {
  const text = "She *realy* enjoys writting.";
  const result = await lint(text);
  console.log(result.lints);
})();

```

### Web Documentation and Demo

**`packages/web`** hosts the SvelteKit website (writewithharper.com) that serves documentation, live demos, and generated API references. The routing and sidebar layout are configured in [`packages/web/vite.config.ts`](https://github.com/Automattic/harper/blob/main/packages/web/vite.config.ts).

## Editor Integrations and Desktop Application

Beyond the core libraries, the monorepo contains multiple front-end implementations.

### Editor Plugins

Individual extensions plug the LSP or WebAssembly client into host applications:

- **VS Code**: `packages/vscode-plugin/` with extension logic in [`packages/vscode-plugin/src/extension.ts`](https://github.com/Automattic/harper/blob/main/packages/vscode-plugin/src/extension.ts)
- **Chrome**: `packages/chrome-plugin/`
- **Firefox**: `packages/firefox-plugin/`
- **Obsidian**: `packages/obsidian-plugin/`
- **WordPress**: `packages/wordpress-plugin/`

The VS Code extension ships `harper-ls` as a binary and spawns the server ([`harper-ls/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-ls/src/main.rs)) when documents open, displaying diagnostics via the LSP client.

### harper-desktop: Tauri Application

**`harper-desktop`** bundles a desktop application using Tauri, combining a Svelte-based web UI with a native overlay highlighter. The application entry point is [`harper-desktop/src-tauri/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-desktop/src-tauri/src/main.rs).

## Repository Layout

The project organizes code into distinct directories under the repository root:

```

/ (repo root)
├─ Cargo.toml                 # workspace definition for all Rust crates

├─ pnpm-workspace.yaml       # workspace definition for all JS packages

├─ harper-core/              # core engine (Rust)

├─ harper-ls/                # LSP server (Rust)

├─ harper-cli/               # CLI front‑end (Rust)

├─ harper-comments/          # comment parsers (Rust)

├─ harper-brill/             # statistical tagger (Rust)

├─ harper-desktop/           # Tauri desktop app (Rust + Svelte)

├─ packages/
│   ├─ harper.js/            # WebAssembly + JS wrapper

│   ├─ harper-editor/       # Shared Svelte editor components

│   ├─ components/          # UI building blocks

│   ├─ web/                 # Documentation site (SvelteKit)

│   ├─ chrome-plugin/       # Chrome extension source

│   ├─ firefox-plugin/      # Firefox extension source

│   ├─ obsidian-plugin/     # Obsidian plugin source

│   ├─ wordpress-plugin/    # WordPress plugin source

│   └─ vscode-plugin/       # VS Code extension source

└─ ... (CI config, Dockerfiles, etc.)

```

## Architecture and Data Flow

The Harper project structure supports a consistent diagnostic pipeline across all front-ends. According to the source code, the architecture follows this flow:

1. **Input** (plain text, file, or editor buffer) enters the system
2. **Document construction** creates a `harper-core::Document` using the merged dictionary (user dictionary + curated wordlist)
3. **LintGroup** runs rule pipelines including Weir rules, Brill-tagging, and spelling checks
4. **Results** emit as `Lint` objects, serialized to JSON for consumption by the LSP, CLI, or browser UI

All front-ends (CLI, LSP, Web, Desktop) ultimately call the same core library, guaranteeing consistent diagnostics across platforms.

## Usage Examples

### Linting via CLI

To lint a file from the command line:

```bash
harper-cli lint path/to/file.txt

```

This command runs [`harper-cli/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-cli/src/main.rs), building a `Document` with the current dictionary and printing lint JSON to stdout.

### Integrating the LSP in VS Code

The VS Code extension in [`packages/vscode-plugin/src/extension.ts`](https://github.com/Automattic/harper/blob/main/packages/vscode-plugin/src/extension.ts) connects to the language server spawned from [`harper-ls/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-ls/src/main.rs), synchronizing documents and displaying diagnostics according to the LSP specification.

### Using the JavaScript API

For browser or Node.js applications, the wrapper loads the Wasm module:

```javascript
import { lint } from 'harper.js';

(async () => {
  const text = "She *realy* enjoys writting.";
  const result = await lint(text);
  console.log(result.lints);
})();

```

## Summary

- Harper uses a **monorepo structure** combining Rust workspaces ([`Cargo.toml`](https://github.com/Automattic/harper/blob/main/Cargo.toml)) and JavaScript packages ([`pnpm-workspace.yaml`](https://github.com/Automattic/harper/blob/main/pnpm-workspace.yaml))
- The **core engine** (`harper-core`) provides the grammar and spell checking logic exposed through [`harper-core/src/lib.rs`](https://github.com/Automattic/harper/blob/main/harper-core/src/lib.rs)
- **Front-end diversity** includes an LSP server (`harper-ls`), CLI tool (`harper-cli`), WebAssembly bindings ([`harper.js`](https://github.com/Automattic/harper/blob/main/harper.js)), and multiple editor plugins
- **Consistent architecture** ensures all interfaces use the same `Document` construction and `LintGroup` pipelines, with results serialized from `Lint` objects
- Key entry points include [`harper-ls/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-ls/src/main.rs) for editor integration, [`harper-cli/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-cli/src/main.rs) for scripting, and [`packages/harper.js/src/index.ts`](https://github.com/Automattic/harper/blob/main/packages/harper.js/src/index.ts) for web usage

## Frequently Asked Questions

### What is the relationship between harper-core and harper-ls?

**Harper-ls** is a Language Server Protocol implementation that wraps **harper-core**. While [`harper-core/src/lib.rs`](https://github.com/Automattic/harper/blob/main/harper-core/src/lib.rs) defines the public API for document parsing and lint generation, [`harper-ls/src/main.rs`](https://github.com/Automattic/harper/blob/main/harper-ls/src/main.rs) adapts these capabilities to the LSP specification, enabling real-time diagnostics in editors like VS Code and Neovim.

### How does the JavaScript package access the Rust core functionality?

The **[`harper.js`](https://github.com/Automattic/harper/blob/main/harper.js)** package compiles the Rust core to WebAssembly (Wasm) and provides a JavaScript/TypeScript wrapper. The entry point at [`packages/harper.js/src/index.ts`](https://github.com/Automattic/harper/blob/main/packages/harper.js/src/index.ts) loads the compiled `harper-wasm` artifact and exposes functions like `lint()`, allowing browser and Node.js applications to use the engine without native binaries.

### Where is the logic for linting comments in source code?

Comment parsing logic resides in **`harper-comments`**, located at [`harper-comments/src/lib.rs`](https://github.com/Automattic/harper/blob/main/harper-comments/src/lib.rs). This crate provides language-specific parsers that extract comment blocks from source files before passing them to the core linting engine, enabling spell checking within code comments.

### What file controls the workspace configuration for all Rust components?

The **[`Cargo.toml`](https://github.com/Automattic/harper/blob/main/Cargo.toml)** file in the repository root declares the Rust workspace. Adding a new crate to this workspace automatically integrates it into the build pipeline and makes it available to other Rust components like `harper-cli` and `harper-ls`.