-
-
Notifications
You must be signed in to change notification settings - Fork 2
Extending CmpStr
This page explains how to extend the CmpStr npm package with custom metrics, phonetic algorithms, or phonetic mappings — without modifying the package source code.
CmpStr is explicitly designed to be runtime-extensible. New functionality can be registered dynamically after importing the package and is immediately available throughout the entire API.
If you have ideas for improvements or new features that may benefit others, feel free to open an issue on GitHub. Relevant contributions may be included in future releases.
If you want to contribute code directly, fork the repository and submit a pull request.
See the contribution guidelines here: CONTRIBUTING.md
To implement custom metrics or phonetic algorithms, use the dedicated development entry point. It exposes all required base classes, registries, and internal utilities alongside default exports:
// CommonJS
const { ... } = require( 'cmpstr/root' );
// ES Modules / TypeScript
import { ... } from 'cmpstr/root';This entry point is intentionally separated from the public API to keep the default bundle lightweight while still enabling advanced extensions.
- MetricRegistry: Central registry for managing metric implementations.
- Metric: Abstract base class for string similarity metrics.
- MetricCls: Type definition for metric constructors.
- PhoneticRegistry: Registry for phonetic algorithm implementations.
- PhoneticMappingRegistry: Registry for phonetic character mappings.
- Phonetic: Abstract base class for phonetic algorithms.
- PhoneticCls: Type definition for phonetic algorithm constructors.
- DeepMerge: Deep object merge utilities
- Filter: Filter management and hooks
- Hasher: Internally used, modified implementation of the FNV1a hash algorithm
- HashTable: Optimized hash table implementation
- Pool: Object pooling for memory optimization
- Profiler: Lightweight execution time profiler
- StructuredData: Processing structured data (objects)
Custom metrics allow you to integrate entirely new similarity calculations into CmpStr.
Create a class extending the abstract Metric base class.
At minimum, you must implement the compute() method, which receives two strings and their lengths, and returns the MetricCompute<R> object with a normalized similarity score (res between 0 and 1).
import {
type MetricInput, type MetricOptions, type MetricCompute,
Metric, MetricRegistry
} from 'cmpstr/root';
class MyMetric extends Metric {
constructor (
a: MetricInput, b: MetricInput,
opt: MetricOptions = {}
) {
super( 'myMetric', a, b, opt, true );
}
protected override compute (
a: string, b: string,
m: number, n: number,
maxLen: number
) : MetricCompute< R > {
// Example implementation
return { res: a === b ? 1 : 0 };
}
}You may optionally override helper methods such as preCompute() to short-circuit trivial cases or add internal helper functions.
Register your metric using a unique identifier:
MetricRegistry.add( 'myMetric', MyMetric );The metric can now be used like any built-in metric:
import { CmpStr } from 'cmpstr';
const cmp = CmpStr.create( { metric: 'myMetric' } );
const result = cmp.test( 'foo', 'bar' );- Set the
symmetricflag (fifth argument ofsuper()) totrueifmetric(a, b) === metric(b, a). - Metric options can be exposed via the
constructor. - Reviewing built-in metrics (e.g. Levenshtein, Dice, Cosine) is recommended for advanced patterns.
Phonetic algorithms are implemented by extending the Phonetic base class.
Most phonetic logic is handled via mappings (see below).
Only override algorithm methods if you need custom behavior.
At a minimum, set up the constructor to call the parent with your algorithm name and options. Override the default static property to set default options for your algorithm, e.g., mapping, delimiter, length, padding, and deduplication.
import {
type PhoneticOptions,
Phonetic, PhoneticRegistry, PhoneticMappingRegistry
} from 'cmpstr/root';
class MyPhonetic extends Phonetic {
protected static override default: PhoneticOptions = {
map: 'en', delimiter: ' ', length: 4, pad: '0', dedupe: true
};
constructor ( opt: PhoneticOptions = {} ) {
super( 'myPhonetic', opt );
}
}Mostly, the work of phonetic algorithms is done via character mappings, which you will need to define and register separately. You can also override methods like encode() or adjustCode() if your algorithm requires special handling before or after applying the mappings.
Key methods you may override:
If your algorithm requires special handling, you can override these methods:
-
encode ( word: string ) : string
Main method to encode a string into its phonetic representation. -
adjustCode ( code: string, chars: string[] ) : string
Optional method to adjust the final phonetic code (e.g., truncation, padding). -
mapChar ( char: string, i: number, chars: string[], charLen: number, lastCode: string | null, map: Record<string, string> ) : string | undefined
Method to map individual characters using the provided mapping.
Register your phonetic algorithm with a unique name:
PhoneticRegistry.add( 'myPhonetic', MyPhonetic );The phonetic algorithm can now be used like any built-in one:
import { CmpStr } from 'cmpstr';
const cmp = CmpStr.create( {
metric: 'dice',
processors: {
phonetic: { algo: 'myPhonetic' }
}
} );
const result = cmp.test( 'foo', 'bar' );Phonetic mappings are essential for language support and algorithm variants. Mappings define how characters are converted to codes, which regex patterns are applied, and may include context-sensitive rules and mapping-specific options.
import { PhoneticMappingRegistry } from 'cmpstr/root';A mapping can include:
- map — Object mapping characters to codes.
-
patterns — Array of
{ pattern: RegExp, replace: string, all?: boolean }for regex-based replacements. - ruleset — Array of context-sensitive rules (see below).
- ignore — Array of characters to skip.
-
options — Mapping-specific options (e.g.,
length,pad,dedupe).
Example:
PhoneticMappingRegistry.add( 'myPhonetic', 'en', {
map: { a: '1', b: '2', c: '3' },
patterns: [
{ pattern: /ph/g, replace: 'f' },
{ pattern: /gh/g, replace: 'g' }
],
ruleset: [
// 'c' before 'e', 'i', 'y' → 'S'
{ char: 'c', next: [ 'e', 'i', 'y' ], code: 'S' },
// 'g' after 'n' → removed
{ char: 'g', prev: [ 'n' ], code: '' }
],
ignore: [ 'h', 'w' ],
options: { length: 6, pad: '0', dedupe: true }
} );Pattern and rule system:
- Patterns are applied before character mapping, allowing for global or context-based replacements.
- Ruleset enables context-sensitive mapping (e.g.,
cbeforeebecomesS). - Options in the mapping are merged with algorithm and user options (see
Phoneticbase class for details).
Specify the mapping ID in your phonetic options:
const cmp = CmpStr.create().setProcessors( {
phonetic: { algo: 'myPhonetic', opt: { map: 'en' } }
} );Mappings can be added to built-in algorithms as well (e.g., for a new language):
PhoneticMappingRegistry.add( 'soundex', 'fr', {
map: { a: '0', b: '1', ... }, // ...patterns, ruleset, options
} );Rules in the ruleset array can match based on:
-
char— The character to match. -
prev,next— Required previous/next character(s). -
prevNot,nextNot— Excluded previous/next character(s). -
position—start,middle,end. -
leading,trailing— Multi-character context at start/end. -
match— Array of characters for n-gram matching. -
code— The code to use if the rule matches.
The arguments char and code are mandatory; others are optional. A rule matches if all specified conditions are met. See the Phonetic base class and built-in mappings for detailed examples.
CmpStr 3.2 / API Reference • FAQ • npm Package • jsDelivr • DevDocs
CmpStr is a lightweight, fast and well performing package for calculating string similarity.
Getting Started
Installation & Setup
Quick Start
API Reference
Documentation
Similarity Metrics
Phonetic Algorithms
Normalization & Filtering
Comparison Modes
Structured Data
Asynchronous API
Diff & Text Analysis
Extending CmpStr
Project Management