@verifyhash/slugify-lite
v0.1.1
Published
Zero-dependency, zero-network string-to-URL-slug library for Node.js: Latin-1/accent transliteration via a pinned in-repo map, custom separator, word-boundary maxLength truncation, and custom replacement overrides.
Downloads
41
Maintainers
Readme
slugify-lite
A tiny, zero-dependency, zero-network Node.js library that turns an arbitrary
string into a clean URL slug: "Héllo, World!" → "hello-world". One CommonJS
file (index.js), no install step, no I/O, no runtime require of anything —
drop it into any project and require it.
Install
npm install @verifyhash/slugify-litePublished as @verifyhash/slugify-lite;
source lives in the verifyhash/libs monorepo.
Zero runtime dependencies — you can also vendor the folder directly.
Who it's for
JavaScript/Node developers who need slugs for URLs, filenames, anchor IDs, or
database keys and want a small, auditable, single-file helper instead of
pulling in a larger dependency graph. It covers the everyday Western-European
case (accented Latin letters like é, ü, ñ, ß, ø, æ) with a pinned,
in-repo character map — no lookup tables downloaded at runtime, no transitive
packages. If you already reach for slugify
or @sindresorhus/slugify
but want zero deps and full control over the map, this is that.
Install / use
No install — copy index.js, or require it directly:
const slugify = require('./index.js');
slugify('Héllo, World!'); // 'hello-world'
slugify('Crème brûlée'); // 'creme-brulee'
slugify('Straße'); // 'strasse' (ß -> ss)
// custom separator
slugify('Hello World', { separator: '_' }); // 'hello_world'
// preserve case
slugify('CamelCase API', { lowercase: false }); // 'CamelCase-API'
// truncate to a max length on a WORD boundary (never mid-word)
slugify('The quick brown fox', { maxLength: 15 }); // 'the-quick-brown'
slugify('The quick brown fox', { maxLength: 13 }); // 'the-quick'
// custom replacements, merged OVER the built-in map (raw-char match)
slugify('100% cotton & wool', {
replacements: { '%': ' percent ', '&': ' and ' }
}); // '100-percent-cotton-and-wool'API
slugify(input, options?) → string
input is coerced with String(); null/undefined return ''.
| option | type | default | meaning |
| -------------- | --------- | ------- | ----------------------------------------------------------------------------------------- |
| separator | string | '-' | Character(s) used to join words. May be multi-character or ''; matched literally. |
| lowercase | boolean | true | Lowercase the result. Set false to preserve case (transliteration still happens). |
| maxLength | number | — | Max output length. Truncated on a word boundary — never mid-word, never a trailing separator. |
| replacements | object | — | Single-char → string map, merged over the built-in map. Matched against the raw character (before lowercasing). |
Pipeline, in order: (1) transliterate each character (custom replacements
win over the built-in map, then the built-in map, else the char passes through);
(2) lowercase unless lowercase: false; (3) collapse every run of non-ASCII-
alphanumeric characters ([^A-Za-z0-9]+) to a single separator, which strips
punctuation, whitespace, and any unmapped characters; (4) trim leading/trailing
separators; (5) if maxLength is set, keep whole words while the running slug
(words joined by separator) stays <= maxLength.
The module also exposes slugify.slugify, slugify.default (both the same
function), and slugify.charMap — a frozen copy of the built-in map for
inspection.
Limits (please read — this is the honest part)
- It only romanizes what the built-in map covers. The map is a small,
hand-maintained table of common accented/Latin-1 and a few Latin-Extended
characters (roughly
à á â ã ä å ā ă ą æ ç ć č ð è é ê ë ē ę ě ñ ń ň ø ō œ ß þ ù ú û ü ū ů ý ÿ ž …plus their uppercase forms). It is not Unicode-complete and does not do context-aware romanization. - Non-Latin scripts are stripped, not transliterated. Cyrillic (
Москва), Greek, Arabic, Hebrew, Hangul, and CJK (日本語) are not in the map, so step 3 removes them.slugify('日本語 test')→'test', andslugify('日本語')→''. If you need Chinese→pinyin, Cyrillic→Latin, or Greek→Latin romanization, use a dedicated transliteration library — that is deliberately out of scope here. - Emoji and symbols are dropped unless you supply a
replacementsentry for them (e.g.{ '&': ' and ' }). maxLengthnever splits a word. If the very first word is longer thanmaxLength, the result is''(there is no whole word that fits). It counts the separator length between words, but does not hyphenate or ellipsize.- Custom
replacementsmatch the raw character, before lowercasing — so to overrideøsupply the lowercase key'ø'; an uppercase'Ø'in the input is handled by the built-in uppercase map entry unless you add'Ø'yourself. - No collision avoidance / uniqueness. Two different inputs can slug to the
same string (
'Café!'and'café'both →'cafe'). If you need unique slugs, dedupe at the call site.
Running the tests
One command, Node's built-in assert only — no test runner, no dependencies:
node test/index.test.jsIt prints slugify-lite: all N tests passed. and exits 0 on success. The
suite covers accented→ASCII transliteration (including ß→ss, æ→ae),
punctuation/whitespace collapsing, custom separators, non-Latin/CJK/emoji
passthrough behavior, maxLength word-boundary truncation, custom replacement
overrides, empty/whitespace-only input, and leading/trailing separator trimming.
License
MIT — see LICENSE.
