Automattic Harper Project Structure: Monorepo Layout and Component Architecture
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 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.
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, 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, 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.
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. harper-brill implements a statistical POS-tagger used by the core for advanced linguistic analysis, located at harper-brill/src/lib.rs.
JavaScript and WebAssembly Layer
The repository includes a JavaScript ecosystem coordinated through pnpm-workspace.yaml, allowing packages to depend on the compiled WebAssembly artifact (harper-wasm) produced by the core.
harper.js: WebAssembly Bridge
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 loads the compiled WebAssembly module:
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.
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 inpackages/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) 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.
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:
- Input (plain text, file, or editor buffer) enters the system
- Document construction creates a
harper-core::Documentusing the merged dictionary (user dictionary + curated wordlist) - LintGroup runs rule pipelines including Weir rules, Brill-tagging, and spelling checks
- Results emit as
Lintobjects, 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:
harper-cli lint path/to/file.txt
This command runs 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 connects to the language server spawned from 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:
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) and JavaScript packages (pnpm-workspace.yaml) - The core engine (
harper-core) provides the grammar and spell checking logic exposed throughharper-core/src/lib.rs - Front-end diversity includes an LSP server (
harper-ls), CLI tool (harper-cli), WebAssembly bindings (harper.js), and multiple editor plugins - Consistent architecture ensures all interfaces use the same
Documentconstruction andLintGrouppipelines, with results serialized fromLintobjects - Key entry points include
harper-ls/src/main.rsfor editor integration,harper-cli/src/main.rsfor scripting, andpackages/harper.js/src/index.tsfor 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 defines the public API for document parsing and lint generation, 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 package compiles the Rust core to WebAssembly (Wasm) and provides a JavaScript/TypeScript wrapper. The entry point at 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. 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 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.
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 →