Automattic / Automattic/harper

WorkerLinter: default-enabled grammar rules silently inactive until getDefaultLintConfig() is called

Open
#3,490 2 comments 0 reactions 0 assignees View on GitHub
harper.js linting
Dominant language
Rust
Stars
15.4k
Forks
627
Avg merge
1d 15h
Merged PRs (30d)
106

Description

## Summary

When using `WorkerLinter` from `harper.js@2.0.0` in the browser, default-enabled grammar/style rules (e.g. `LongSentences`, `PronounVerbAgreement`, `SubjectPronoun`) do not produce lints unless `getDefaultLintConfig()` is called at least once after `setup()`. Spelling lints work regardless — only the rule-registry-driven categories silently return zero results.

`LocalLinter` does not show this behavior — defaults work without any extra calls.

## Versions

- `harper.js@2.0.0`
- Environment: Chromium-based browser via Vite-bundled SPA; should reproduce on any browser that runs Web Workers.
- WASM loaded from the `harper.js/binary` subpath.

## Reproduction

```ts
import { WorkerLinter, Dialect } from 'harper.js';
import { binary } from 'harper.js/binary';

const linter = new WorkerLinter({ binary, dialect: Dialect.American });
await linter.setup();

const LONG = "I went to the store and I bought some bread and I also bought some milk and then I walked home along the way I saw a dog and the dog was friendly so I stopped to pet it and then I continued walking home but when I got home I realized I had forgotten the eggs which was the main thing I went to the store for in the first place.";

// Returns []:
console.log((await linter.lint(LONG, { language: 'plaintext' })).length);

// Discard the return — only the side effect matters:
await linter.getDefaultLintConfig();

// Now returns 1 Readability lint ("This sentence is 72 words long."):
console.log((await linter.lint(LONG, { language: 'plaintext' })).length);
```

## Bisection

Tried each call in isolation:

- `lint()` without any config calls → 0 grammar lints
- After `getLintConfig()` → 0 grammar lints
- After `getDefaultLintConfig()` → grammar lints fire correctly
- After `setLintConfig(...)` → also arms the rules

`getDefaultLintConfig()` is documented as a read-only accessor (and the `LocalLinter` implementation behaves that way). On the worker it appears to have a side effect of materializing default rule state into the worker's active rule set.

## Workaround

Call `await linter.getDefaultLintConfig()` once after `setup()` resolves, discard the return.

## Suggestion

Either inline the default-materialization into the worker's `setup()` so its behavior matches `LocalLinter`, or rename/document the materialization call honestly (e.g. a `materializeDefaults()` method).

Happy to provide a minimal Vite reproduction repo if it'd help.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the documented WorkerLinter sequence after setup, then compare its default configuration behavior with LocalLinter and the effects of getDefaultLintConfig() and setLintConfig(). The fix is complete when default-enabled grammar rules run immediately after setup, with a regression test covering linting before any configuration accessor is called.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, rust, wasm
Domain
devtools, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.