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

no-nepali-profanity

v0.2.0

Published

Profanity matcher for English, Romanized Nepali and Devanagari Nepali: leetspeak, stretched and spelled-out letters, Nepali postpositions, with care not to block real names.

Readme

no-nepali-profanity

A small, dependency-free profanity matcher for English, Romanized (Latin) Nepali and Devanagari Nepali, plus the Hindi slang common in Nepal. Built for moderating user-written text — names, comments, reviews — on Nepali sites, where false positives on real names are more damaging than a missed swear.

Zero runtime dependencies. TypeScript, ships pre-built ESM (dist/) with .d.ts typings, published as a Node package (type: module). No browser/CDN bundle is shipped; it targets server-side or build-time moderation.

Documentation: mukhxadnahunna.com/js

Install

npm install no-nepali-profanity

Requires Node 18+ (uses String.prototype.normalize and Unicode property regex escapes). ESM-only:

import { containsProfanity, findProfanity, tokenize } from "no-nepali-profanity";

TypeScript works out of the box — the .d.ts ship with the package.

Usage

Seven functions, one lexicon module. All examples below are verified against the current build.

import { containsProfanity, findProfanity, tokenize } from "no-nepali-profanity";

// boolean check — fastest
containsProfanity("Great teacher!");        // false
containsProfanity("muji");                  // true
containsProfanity("मुजीको कक्षा");           // true  (Devanagari + postposition)

// which words — returns normalised matches as they appeared
findProfanity("f.u.c.k this sh1t");        // ["fuck", "shit"]
findProfanity("f u c k this");              // ["fuck"]
findProfanity("Randip Thapa");              // []
findProfanity("*ss teacher");               // ["*ss"]

// debugging — see what the matcher actually splits into
tokenize("Great teacher!");                 // ["great", "teacher"]

API

| Signature | Returns | Notes | |---|---|---| | containsProfanity(text, options?) | boolean | Yes/no from findProfanity. | | findProfanity(text, options?) | string[] | Matching words normalised + lowercased (leet decoded, case-folded), deduplicated. Empty when clean. | | check(text, options?) | { text, hasProfanity, words, matches, censor() } | Scans once; inspect the result and censor it without scanning again. | | findProfanityMatches(text, options?) | { text, normalized, start, end }[] | Every occurrence with its position in the original text, sorted by position. | | censor(text, options?) | string | The text with each match masked: "you muji" → "you ****". Options: mask (default "*"), replace(match). | | createFilter(options?) | { check, containsProfanity, findProfanity, findProfanityMatches, censor } | Builds the tables once for fixed options. | | tokenize(text) | string[] | Raw tokens the matcher sees. Useful for debugging why a word is (or isn't) caught. | | lexicon | module | Tagged entries (WORDS, STEMS, PHRASES, INFIXES), the ALLOWED list, flat per-script lists (LATIN_WORDS, DEVANAGARI_WORDS…) and suffixes. |

Censoring

censor("you muji");                                   // "you ****"
censor("F.U.C.K this Sh1t!");                          // "******* this ****!"
censor("you muji", { mask: "#" });                    // "you ####"
censor("you muji", { replace: () => "[censored]" });  // "you [censored]"
findProfanityMatches("you muji");                     // [{ text: "muji", normalized: "muji", start: 4, end: 8 }]

Check and censor in one pass:

const result = check("you muji");
result.hasProfanity;   // true
result.words;          // ["muji"]
result.censor();       // "you ****"

check("you muji").censor();   // "you ****"

Options

findProfanity("fuck muji मुजी", { languages: ["romanized"] });      // ["muji"]
containsProfanity("you idiot", { strictness: "lenient" });         // false
findProfanity("damn it", { strictness: "strict" });   // ["damn"]
  • languages: any of "english", "romanized", "devanagari". Default: all three.
  • strictness: "lenient" (severe words only), "standard" (default, adds milder insults like idiot, murkha) or "strict" (adds entries that are also ordinary words, like damn, and the stems rand, cond, kand, lund; names they would hit, like Randip, are on a built-in allow list).
  • extraWords: more words to flag. allowWords: words never to flag, such as names on your site.

What it catches

  • Case and Unicode forms: IDIOT, full-width letters.
  • Leetspeak: sh1t, @ss (0 1 3 4 5 7 @ $).
  • ! for i between letters: sh!t, b!tch. Sentence-final Great teacher! is left alone.
  • * for a hidden letter: f*ck, sh*t, *ss, and markdown emphasis like *sh*t* still reads as the word while *is* stays clean.
  • Stretched letters: fuuuuck, for words of 4+ letters.
  • Spelled-out letters: f.u.c.k, f u c k.
  • Nepali postpositions and plurals glued on: mujiko, randiharu, मुजीको, …हरू.
  • Devanagari spelling variants: nukta, chandrabindu vs anusvara, zero-width joiners.
  • Stems where no ordinary word starts the same way: fucking, bitches, machiknee.
  • Multi-word phrases: chaak ko pwal, pesa garne, sasto manche (Latin and Devanagari).

What it deliberately doesn't

  • Short words match exactly, so as, class, assignment and Assam are fine.
  • Name collisions: shit is a whole word only, because Shitij / शितिज is a name. Names like मुजी-adjacent Randip / राण्डीप, Putali / पुतली, Asha / आशा are checked in the test suite.
  • No caste names, surnames or ordinary words that are only offensive in context (e.g. kami, kukur). A word list can't tell a slur from someone's name; that needs human moderation.
  • No judgement of context, sarcasm or meaning. This is a first-pass filter, not a moderator.

Development

npm install
npm test         # vitest
npm run build    # tsc -> dist/
npm run typecheck

The word lists live in src/lexicon.ts, apart from the matching logic in src/index.ts. A helper exports the lists as CSV for review: scripts/export-csv.mjs → words.csv. Native-speaker review of the Nepali lists is the most valuable contribution.

License

MIT