Fast fuzzy finder, powered by nucleo-matcher compiled to WebAssembly.
nucleois a highly performant fuzzy matcher written in rust. It aims to fill the same use case asfzfandskim. Compared tofzf,nucleohas a significantly faster matching algorithm. It is used in the helix-editor and therefore has a large user base with lots of real world testing. The core matcher implementation is considered complete and is unlikely to see major changes
npm i nucleo-matcher-wasmimport { NucleoMatcher } from 'nucleo-matcher-wasm';
const items = [
'src/components/Header.svelte',
'src/utils/helpers.ts',
'test/fixtures/data.json',
];
// Basic usage (defaults: case-insensitive, smart normalization)
const nucleo = new NucleoMatcher(items);
// With options (file path matching, smart case)
const nucleo = new NucleoMatcher(items, {
matchPaths: true,
caseMatching: 'smart',
});Usage: matchPattern(pattern, options?)
Match with fzf-like syntax (^ prefix, $ postfix, ' substring, ! negation):
nucleo.matchPattern('header');
// → [['src/components/Header.svelte', 168]]
nucleo.matchPattern('^src comp');
// → [['src/components/Header.svelte', 168]]Usage:matchLiteral(pattern, kind?, options?)
Match literally (no special syntax parsing). kind: "fuzzy" (default), "substring", "prefix", "postfix", "exact":
nucleo.matchLiteral('^src', 'fuzzy');
// Treats ^ as a literal character
nucleo.matchLiteral('test/', 'prefix');
// → [['test/fixtures/data.json', ...]]Usage: matchPatternIndices(pattern, options?) / matchLiteralIndices(pattern, kind?, options?)
Same as above but also returns matched character indices (for highlighting):
nucleo.matchPatternIndices('header');
// → [['src/components/Header.svelte', 168, [15, 16, 17, 18, 19, 20]]]Usage: setItems(items)
Replace the stored item list.
Usage: score(pattern, haystack, options?)
Score a single string without pre-loading items:
nucleo.score('hlp', 'helpers.ts');
// → 96
nucleo.score('xyz', 'helpers.ts');
// → undefinedOptions can be set in the constructor and the provided matching methods.
Note
This option can only be set in the constructor (not per-call).
Values: boolean
Default: false
Treat / and \\ as word boundaries.
Note
This option can only be set in the constructor (not per-call).
Values: boolean
Default: false
Boost matches near the start of the haystack.
Values: "ignore" | "smart" | "respect"
Default: "ignore"
Case sensitivity mode.
Values: "smart" | "never"
Default: "smart"
Unicode normalization mode
Note
This option can only be set per-call (not in the constructor).
Values: number
Default: undefined
Cap the result set to the top N matches by score. Skips marshaling discarded results across the WebAssembly boundary, and uses a bounded heap internally rather than scoring + full-sorting all candidates.
nucleo.matchPattern('hdr', { maxResults: 50 });This repository contains a benchmark script, testing nucleo-matcher-wasm against some popular fuzzy finder libraries for NodeJS. Here's the gist of it on an Apple M2 Pro with 32GB of RAM:
======================================================================
Dataset: 100 items, 500 iterations per matcher
======================================================================
Averages across all patterns:
nucleo (literal) 0.005 ms/iter
nucleo (pattern) 0.007 ms/iter
fuzzy-search 0.009 ms/iter
fzy.js 0.016 ms/iter
fuzzaldrin-plus 0.017 ms/iter
fzi 0.024 ms/iter
fuzzy 0.030 ms/iter
fuzzaldrin 0.031 ms/iter
fzf 0.045 ms/iter
fuse.js 0.174 ms/iter
fast-fuzzy 0.514 ms/iter
======================================================================
Dataset: 1,000 items, 500 iterations per matcher
======================================================================
Averages across all patterns:
nucleo (literal) 0.046 ms/iter
nucleo (pattern) 0.047 ms/iter
fuzzy-search 0.087 ms/iter
fzy.js 0.175 ms/iter
fuzzaldrin-plus 0.187 ms/iter
fzi 0.265 ms/iter
fuzzaldrin 0.296 ms/iter
fuzzy 0.307 ms/iter
fzf 0.430 ms/iter
fast-fuzzy 0.535 ms/iter
fuse.js 1.743 ms/iter
======================================================================
Dataset: 10,000 items, 50 iterations per matcher
======================================================================
Averages across all patterns:
nucleo (literal) 0.467 ms/iter
nucleo (pattern) 0.489 ms/iter
fuzzy-search 0.886 ms/iter
fzy.js 1.852 ms/iter
fuzzaldrin-plus 1.972 ms/iter
fzi 2.770 ms/iter
fuzzaldrin 2.994 ms/iter
fuzzy 3.210 ms/iter
fast-fuzzy 4.120 ms/iter
fzf 4.262 ms/iter
fuse.js 17.832 ms/iter
Try npm run bench to see how well the benchmark performs on your target environment.
Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shwasm-pack compiles the Rust crate to WebAssembly and generates JS/TS bindings.
cargo install wasm-packNode.js ≥ 18 is required for running the playground scripts and publishing to npm.
Build the WASM package targeting Node.js:
wasm-pack build --target nodejs --out-dir distThis work is licensed under Mozilla Public License 2.0 .