03 / Real package example / 7 min read
slugify: why is “!” still in my URL?
“Déjà Vu!” becomes “deja-vu!” — but you wanted “deja-vu”. Use REA to inspect the JavaScript package, find the setting that controls punctuation, and check the result.
On this page
THE SHORT ANSWER
lower: true only changes uppercase letters to lowercase. Add strict: true to remove punctuation too. This is an option you need to enable, not a bug in the library.
slugify("Déjà Vu!", { lower: true, strict: true })
// → "deja-vu"01. The URL we wanted
A slug is the readable last part of a URL, such as deja-vu in example.com/posts/deja-vu. This example uses the real slugify 1.6.6 npm release. The results below come from running that release locally.
Try the title below. Then turn on Remove punctuation to see the difference.
Still has a “!”? Turn on Remove punctuation.
{ lower: true, strict: false }Runs in your browser. Nothing is uploaded. REA is used in the written walkthrough.
Here is the same comparison in JavaScript:
slugify("Déjà Vu!")
// → "Deja-Vu!"
slugify("Déjà Vu!", { lower: true })
// → "deja-vu!"The accent disappeared; the exclamation mark stayed. We used REA to locate the package’s main file, read the filtering code, and check which option removes it. You can repeat those steps below.
02. Download the package and inspect it with REA
We downloaded the pinned npm tarball and unpacked it without installing or running the package. The following preparation commands work in a shell with tar:
mkdir rea-slugify-case
cd rea-slugify-case
npm pack slugify@1.6.6
tar -xzf slugify-1.6.6.tgzThen we ran REA’s JavaScript application analyzer on the extracted package directory:
If you prefer your coding agent, replace the path and paste this prompt:
Use REA to inspect /absolute/path/to/rea-slugify-case/package. I use slugify to make article URLs. Why does slugify('Déjà Vu!', { lower: true }) keep the exclamation mark? Find the entry file, show the code that allows it, and identify the option that removes it. Do not change the package.Or collect the analysis directly in your terminal:
npx -y rea-agents@6.4.0 analyze-javascript-application ./package --json > analysis.jsonREA identified the package as slugify, version 1.6.6, with an entry point of ./slugify.js. The report recorded three relevant files and zero parse failures. It also marked overall graph coverage as partial, which we retained in the published summary.
This is a readable JavaScript distribution. REA supplied the artifact inventory and analysis context; subsequent source inspection and direct checks established the behavior. No native decompilation was involved.
03. Find the option that removes punctuation
In the shipped slugify.js, lines 33–42 normalize the string, apply locale or character-map replacements, and filter characters. The default filter explicitly allows the exclamation mark.
// remove not allowed characters
.replace(options.remove || /[^\w\s$*_+~.()'"!\-:@]+/g, '')
}, '');
if (options.strict) {
slug = slug.replace(/[^A-Za-z0-9\s]/g, '');
}The strict branch runs afterward and removes characters outside letters, numbers and whitespace. Lowercasing is a separate operation at lines 57–58. That gives us a testable prediction:
slugify("Déjà Vu!", { lower: true, strict: true })
// should return "deja-vu"The same file also explains why spaces collapse into one separator, why & becomes and, and why the German locale changes Ä into AE.
04. Run the checks on the real package
We executed eight assertions against the unmodified 1.6.6 release, including the original question and a few surrounding behaviors.
Original package · Node.js 22.23.1 · macOS arm64
| Input | Options | Observed result |
|---|---|---|
| Déjà Vu! | Default | Deja-Vu! |
| Déjà Vu! | lower | deja-vu! |
| Déjà Vu! | lower + strict | deja-vu |
| hello world (padded) | Default | hello-world |
| Rock & Roll | Default | Rock-and-Roll |
| Äpfel | German locale + lower | aepfel |
| Empty string | Default | Empty string |
| 42 (a number) | Default | Throws “string argument expected” |
Read the recorded check results, or use the interactive package demo above. The demo runs the same pinned package in your browser.
05. What we tested
For this release and these inputs, the source and observed outputs agree. If you want deja-vu, use both lower: true and strict: true.
The scope matters. Eight checks do not establish every Unicode or locale behavior. The REA graph is partial; a missing relationship in it is not proof of absence. We did not reconstruct a native binary or recover unavailable source code.
Because the shipped source is readable, ordinary source inspection is also a valid route. This case demonstrates the full habit: identify the artifact, inspect one rule, then test the claim against the original implementation.
06. Download and run this example
Download the small reproduction kit. It includes the pinned dependency, all eight assertions, and the steps to collect your own REA evidence.
npm install --ignore-scripts
node verify.mjsThe assertion runner prints each result and exits with an error if a check fails. Node.js executes the npm package only at this verification step; REA’s preceding static inspection does not execute it.
Try another tutorialFind where an app saves exported files.Evidence: REA analysis summary · check results · inspected source · MIT license. Package by simov and contributors. Analysis and runtime checks performed locally on October 12, 2026. Public summaries omit local filesystem paths.
New to the tool? What is REA? · How we check our guides