What this repo is

@sindresorhus/slugify is a single-function utility package that converts arbitrary strings into URL/filename-safe slugs. It has no build step, no framework, and no runtime beyond Node.js. The entire implementation lives in two source files: index.js (the logic) and overridable-replacements.js (the default symbol map).

Data flow

When slugify(string, options) is called, the string passes through a fixed pipeline in index.js:

  1. Custom replacementsoverridable-replacements.js provides three defaults (&→ and, 🦄→ unicorn, ♥→ love). User-supplied customReplacements are merged on top via Map, with user entries winning on collision.
  2. Transliteration@sindresorhus/transliterate converts non-ASCII characters to ASCII equivalents (e.g. Déjà → Deja). Skipped entirely when transliterate: false; custom replacements still apply in that path.
  3. Decamelization — four sequential regexes in the decamelize closure split camelCase/PascalCase/acronym boundaries into spaces (e.g. fooBar → foo bar, APIs → apis).
  4. Lowercasing — applied locale-aware if locale is set.
  5. Contraction stripping — a single regex removes apostrophes from possessives/contractions (Conway's → conways), handling both straight and curly apostrophes.
  6. Non-slug character replacementbuildPatternSlug constructs a character-class negation regex that replaces everything not in [a-z\d] (plus uppercase if lowercase:false, Unicode letters/numbers if transliterate:false, and any preserveCharacters) with the separator.
  7. Separator normalizationremoveMootSeparators collapses runs of the separator and strips leading/trailing occurrences.
  8. Edge flagspreserveLeadingUnderscore and preserveTrailingDash are detected before the pipeline and reattached after.

slugifyWithCounter

slugifyWithCounter() wraps the base function with a Map-backed occurrence counter. Each call checks the lowercase slug against the map, appending -2, -3, etc. on collision. Calling .reset() clears the map. This is the right tool for generating unique HTML id attributes from repeated section headings.

Packaging

The package is pure ESM ("type": "module" in package.json). The exports field exposes a single entry point (./index.js) with types from ./index.d.ts. There is no CJS build and no bundler involved — what you see is what consumers import.