Automattic / Automattic/harper
WorkerLinter: default-enabled grammar rules silently inactive until getDefaultLintConfig() is called
- 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
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