leo-profanity

A zero-dependency profanity filter for JavaScript and TypeScript with multi-language dictionary support.

Library
npm
v1.9.0
74stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
36/100Needs Attention
Development Activity8
Maintenance20
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture62
Code Quality76
Innovation70
Learning Curve55

leo-profanity is a lightweight profanity-filter library for Node.js and browser JavaScript, built around a bundled English bad-word dictionary sourced from Shutterstock’s public list. It exposes a small, chainable API for detecting (check), redacting (clean), and extracting (badWordsUsed) profane words in a string, with configurable replacement characters and partial-masking (keep the first N letters visible).

Beyond the default English list, the library supports French and Russian dictionaries as optional peer packages, lets consumers register entirely custom dictionaries per language, and includes a whitelist mechanism to exempt specific words (e.g. common false positives) from filtering. Internally it maintains a Set-backed word index for O(1) lookups, making it suitable for filtering user-generated content such as chat messages, comments, and usernames.

What You Get

  • A default English profanity dictionary (250+ words) ready to use out of the box, with optional French and Russian dictionaries installable as peer packages.
  • Three core methods — check() to detect profanity, clean() to redact it, and badWordsUsed() to list which words matched.
  • A whitelist API (addWhitelist/removeWhitelist) to exempt specific words from filtering, avoiding common false positives.
  • Full TypeScript type definitions and support for both CommonJS require() and browser <script> usage.

Common Use Cases

  • Filtering profanity from chat messages and comments in real-time messaging apps before they’re displayed to other users.
  • Sanitizing usernames and profile fields at signup to block obscene or offensive submissions.
  • Moderating user-generated content in forums, review sites, and community platforms without a hosted moderation service.
  • Building multi-language community platforms that need locale-specific profanity dictionaries (English, French, Russian) side by side.

Under The Hood

Architecture The library is a single-file module (src/index.js) exposing a singleton object literal with methods for list management, whitelist handling, dictionary loading, and text processing (sanitize → proceed → clean/check/badWordsUsed). There are no classes or dependency injection — it’s a flat, mutable singleton pattern where a words array and _wordsSet Set are kept in sync via an internal _syncSet() call, and a single shared _whitelist Set applies to all consumers in the same process. Data flow is straightforward: sanitize() lowercases and strips commas/periods, proceed() splits the string, checks each token against _wordsSet while skipping whitelisted entries, and rebuilds the string with replacements while collecting matched words. Because dictionary and whitelist state is process-global rather than instantiable per-consumer, mutating it from one call site (e.g. add() or addWhitelist()) affects every other call site sharing the same process — that shared singleton state, not any file boundary, is the core abstraction to be careful with if the design ever changes.

Tech Stack Vanilla JavaScript (CommonJS), no runtime framework, and zero required dependencies for the core English filter. Optional peer dependencies (french-badwords-list, russian-bad-words) are loaded via try/catch require() calls to add language dictionaries without breaking installs that skip them. TypeScript consumers get hand-written type definitions (src/index.d.ts) shipped alongside the JS source rather than generated via tsc. Testing runs on Mocha + Chai with nyc for coverage and Stryker for mutation testing; linting uses standard/ts-standard for zero-config style enforcement. CI runs on GitHub Actions with semantic-release automating npm publishing. No bundler or build step is required since the package ships plain CommonJS directly. Requires Node.js 18+.

Code Quality The test suite is extensive, covering nearly every public and private method — word list mutation, sanitization, the check/clean/badWordsUsed pipeline, whitelist add/remove/clear/get, and dictionary load/add/remove — including edge cases like empty strings, duplicate adds, non-existent words, and chaining return values. Coverage is enforced at an 80% threshold across statements, branches, functions, and lines via nyc, and mutation testing via Stryker adds a correctness signal beyond simple line coverage. Error handling favors defensive guards over exceptions (falsy/empty input returns empty results rather than throwing), and optional dictionary requires are wrapped in try/catch so missing optional peer packages don’t break the main import. Naming is consistent and linting is enforced without a custom config, though there’s no runtime type safety beyond the hand-authored TypeScript declarations.

API Design The public API is small and ergonomic — a single default export exposes check(), clean(), badWordsUsed(), add()/remove(), whitelist methods, and dictionary management from one object, with mutating methods consistently returning this for chaining. clean() supports both legacy positional arguments and a newer options-object form, preserving backward compatibility while improving readability for new callers. Getting started requires zero configuration — require('leo-profanity').clean(str) works immediately with the bundled English dictionary — and TypeScript users get full autocomplete via the shipped .d.ts with no separate @types package needed. Documentation covers every public method with runnable examples, including an unusually transparent “Algorithm” section that documents the library’s own false-positive and false-negative tradeoffs rather than overselling its accuracy.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search