npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@farscrl/hunspell-wasm

v1.0.1

Published

WebAssembly bindings for the Hunspell spellchecker, with a build pipeline included

Readme

Hunspell compiled to WebAssembly

@farscrl/hunspell-wasm is a TypeScript API over Hunspell spellchecking, compiled to WebAssembly. Building the wasm binary, the JS bindings, tests, and publishing all live in this one repository — there's no separate build repo and no runtime download step.

import { loadModule } from '@farscrl/hunspell-wasm';
import { readFileSync } from 'node:fs';

const factory = await loadModule();

const affPath = factory.mountBuffer(readFileSync('en_US.aff'), 'en_US.aff');
const dicPath = factory.mountBuffer(readFileSync('en_US.dic'), 'en_US.dic');
const hunspell = factory.create(affPath, dicPath);

hunspell.spell('hello'); // true
hunspell.suggest('helo'); // ['hello', ...]

hunspell.dispose();
import { HUNSPELL_VERSION } from '@farscrl/hunspell-wasm';
// '1.7.3' — whatever Hunspell version is compiled into this package build

Works the same from CJS (require('@farscrl/hunspell-wasm')) and ESM (import ... from '@farscrl/hunspell-wasm'), and bundles cleanly under strict exports-map resolution (Vite, Webpack 5, Rollup, esbuild, Turbopack) — see test/bundlers for runnable proof of each.

API

loadModule(): Promise<HunspellFactory>

Loads the wasm module and returns a factory bound to it.

HunspellFactory

  • mountBuffer(buffer: Uint8Array, fileName: string): string — writes buffer into the wasm virtual filesystem and returns the path it was mounted at (pass this to create/addDictionary).
  • unmount(mountedPath: string): void — removes a previously mounted file.
  • create(affPath: string, dictPath: string): HunspellInstance — creates a hunspell instance from a mounted .aff and .dic path.

HunspellInstance

  • spell(word: string): boolean — whether word is spelled correctly.
  • suggest(word: string): string[] — spelling suggestions for word, best-first.
  • stem(word: string): string[] — morphological stems for word (affix-stripped root forms).
  • addWord(word: string): void — adds a word to the in-memory dictionary.
  • addWordWithAffix(word: string, example: string): void — adds a word, taking its affix flags from example (an existing dictionary entry).
  • removeWord(word: string): void — removes a word from the in-memory dictionary.
  • addDictionary(dictPath: string): boolean — merges an additional mounted .dic file into this instance; returns false on failure.
  • dispose(): void — frees the underlying wasm-side hunspell instance. The instance is unusable afterwards.

HUNSPELL_VERSION: string

The Hunspell version compiled into this package build (e.g. '1.7.3').

Why

Prior art in this space (hunspell-asm / docker-hunspell-wasm) splits the native build and the JS wrapper across two repos, downloads the compiled binary over wget at npm install time, and resolves the node/browser variant via the package.json browser field, which strict-ESM bundlers don't reliably honor. This repo keeps the native build and JS wrapper together, ships the wasm binary inside the npm package itself, and compiles one universal build (ENVIRONMENT=web,node) that serves both runtimes instead of swapping between two.

Repository layout

native/           Hunspell → wasm build (Docker + Emscripten)
  vendor/hunspell/   git submodule, pinned to a tagged release
  Dockerfile         FROM emscripten/emsdk:<pinned>
  build.sh           final em++ link step (flags documented inline)
src/              TypeScript wrapper
  lib/hunspell.mjs     generated by `pnpm build:wasm` (gitignored) — isomorphic web+node build
  lib/hunspell.web.mjs generated by `pnpm build:wasm` (gitignored) — browser-only build, no Node-only code at all
  lib/hunspell.d.mts   hand-written types shared by both generated glue files (checked in)
  hunspellVersion.ts   generated by `pnpm generate:version` from configure.ac (gitignored)
test/
  integration/       runs against the real compiled wasm module
  bundlers/          Vite / Turbopack / Node-ESM / Node-CJS / browser-esbuild smoke tests — the regression guard for interop
  fixtures/          small hand-written .aff/.dic, not full language dictionaries

The wasm glue is built twice — once with -s ENVIRONMENT=web,node (Emscripten's own runtime detection picks the right code path in either environment) and once with just -s ENVIRONMENT=web. src/loadModule.ts loads it through the #hunspell-glue entry in package.json's imports field, which resolves to the browser-only build under the "browser" condition (honored by webpack, esbuild platform: 'browser', and other browser-targeting bundlers) and to the isomorphic build otherwise. This exists because the isomorphic build's runtime-guarded import("node:module")/ require("node:fs") branches (never reached in a browser) still get resolved statically by bundlers that build for the browser, which hard-errors since those are Node builtins with no browser equivalent — see #17.

Building

Requires Docker and pnpm.

pnpm install
pnpm run build:wasm   # compiles native/vendor/hunspell → src/lib/hunspell.mjs (Docker)
pnpm build            # bundles src/ → dist/ (CJS + ESM + .d.ts) and copies the wasm glue in
pnpm test             # unit + integration tests against the real wasm module

pnpm run build:wasm only needs to be re-run when native/ changes; CI caches its output keyed on the pinned submodule commit, so everyday JS/TS work never has to rebuild it.

Bundler smoke tests

pnpm build   # the fixtures import the real dist/ output, not src/ directly
node test/bundlers/node-esm/check.mjs
node test/bundlers/node-cjs/check.cjs
pnpm --filter bundler-smoke-vite run check
pnpm --filter bundler-smoke-turbopack run check
pnpm --filter bundler-smoke-browser-esbuild run check

Not part of pnpm test — these run the actual built package through Node's ESM loader, Node's CJS require, a real Vite/Rollup bundling pass (ssr.noExternal: true, so it's forced to bundle rather than just resolve the import), a Next.js production build/serve on Turbopack, and a raw esbuild bundle pass targeting the browser — the direct regression guard for the strict-ESM interop bug this project exists to fix. The Turbopack fixture guards against emscripten-core/emscripten#26134, where a combined web,node Emscripten build can trip up bundlers that statically analyze the generated glue's guarded require("node:fs")-style calls. The browser-esbuild fixture guards against the browser-target variant of the same class of bug (#17): unlike the Vite/Turbopack fixtures, which build for a Node-capable target, it bundles with platform: 'browser' — the same mode Angular's esbuild- and webpack-based builders use — and asserts the output references no node: builtins at all. CI runs these same commands as its own bundler-smoke job.

Updating the Hunspell version

cd native/vendor/hunspell
git fetch --tags
git checkout <tag>
cd ../../..
git add native/vendor/hunspell

HUNSPELL_VERSION (and test/integration's check that it's well-formed) picks up the new version automatically on the next build — no separate bump needed. Hunspell's C API has been stable across every release so far, so bumping the submodule is normally just a patch release of this package via a changeset, not something that needs its own version matrix.

Releasing

Versioning and changelogs are managed with Changesets — every release traces back to a file committed in a PR, not a manual npm publish. Publishing never happens from a local machine, only from CI.

To release a new version:

  1. In your PR, run pnpm changeset. It asks for a bump type (patch/minor/major) and a one-line summary, then writes a file into .changeset/ — commit it along with your code change.
  2. Merge the PR to main as normal. Nothing else to do here.
  3. CI (release.yml) notices the pending changeset and opens (or updates) a "Version Packages" PR on its own — this bumps package.json's version, writes CHANGELOG.md, and removes the consumed changeset file. If multiple PRs land changesets before this one is merged, they all accumulate into the same "Version Packages" PR.
  4. Review and merge the "Version Packages" PR when you're ready to actually ship. This merge is what triggers the real release: CI rebuilds the package fresh and runs changeset publish, publishing to npm with provenance.

So day-to-day, releasing is: write a changeset in your PR, and later merge the bot's PR when you want those changes to go out. No manual version bump, changelog edit, or npm publish step.

License

MIT