03 / 真实软件包案例 / 阅读约 7 分钟
slugify:为什么 URL 里还有“!”?
“Déjà Vu!” 变成了“deja-vu!”,但你想要的是“deja-vu”。用 REA 分析这个 JavaScript 软件包,找到控制标点的设置,再验证结果。
本页内容
直接看答案
lower: true 只会把大写字母改成小写。再加上 strict: true 就能同时移除标点。这是需要开启的选项,并非库本身的错误。
slugify("Déjà Vu!", { lower: true, strict: true })
// → "deja-vu"01. 我们想要的 URL
slug 是 URL 末尾便于阅读的一段,例如 deja-vu (完整 URL 为 example.com/posts/deja-vu)。本示例使用真实的 slugify 1.6.6 npm 发布版本。下方结果来自在本地运行该版本。
试试下方标题,再开启 移除标点 看看差别。
还有“!”?请开启 移除标点。
{ lower: true, strict: false }在浏览器本地运行,不上传任何内容。REA 的使用见文字教程。
下面是同样的 JavaScript 对比:
slugify("Déjà Vu!")
// → "Deja-Vu!"
slugify("Déjà Vu!", { lower: true })
// → "deja-vu!"重音符号消失了,感叹号却还在。我们用 REA 定位软件包入口文件,读取过滤代码,并查明哪个选项会移除感叹号。你可以按下方步骤复现。
02. 下载软件包,用 REA 分析
我们下载了固定版本的 npm tarball,解压时没有安装或运行软件包。以下准备命令需要 shell 中有 tar:
mkdir rea-slugify-case
cd rea-slugify-case
npm pack [email protected]
tar -xzf slugify-1.6.6.tgz随后,我们对解压后的 package 目录运行 REA 的 JavaScript 应用分析器:
如果更习惯使用编程智能体,请替换路径并粘贴这段提示词:
使用 REA 分析 /absolute/path/to/rea-slugify-case/package。我用 slugify 生成文章 URL。为什么 slugify('Déjà Vu!', { lower: true }) 会保留感叹号?找到入口文件,展示允许保留它的代码,并指出移除它的选项。不要修改软件包。也可以直接在终端收集分析结果:
npx -y [email protected] analyze-javascript-application ./package --json > analysis.jsonREA 将软件包识别为 slugify,版本为 1.6.6,入口为 ./slugify.js。报告记录了三个相关文件,解析失败数为零;同时将整体图覆盖范围标记为 partial(部分覆盖),我们在发布的摘要中保留了这一点。
这是可读的 JavaScript 发行包。REA 提供了文件清单和分析上下文,随后通过源码检查和直接验证确认行为,未涉及原生代码反编译。
03. 找到移除标点的选项
在发布包的 slugify.js中,第 33–42 行完成字符串规范化、语言或字符映射替换,以及字符过滤。默认过滤规则明确允许感叹号。
// remove not allowed characters
.replace(options.remove || /[^\w\s$*_+~.()'"!\-:@]+/g, '')
}, '');
if (options.strict) {
slug = slug.replace(/[^A-Za-z0-9\s]/g, '');
}strict 分支随后执行,移除字母、数字和空白以外的字符。小写转换是第 57–58 行的独立操作。由此可以提出一个可测试的预测:
slugify("Déjà Vu!", { lower: true, strict: true })
// should return "deja-vu"同一文件也解释了为什么多个空格合并成一个分隔符,为什么 & 变成 and,以及为什么德语设置会改变 Ä 变为 AE。
04. 在真实软件包上运行验证
我们对未经修改的 1.6.6 版本执行了八项断言,包括最初的问题和几个相关行为。
原始软件包 · Node.js 22.23.1 · macOS arm64
| 输入 | 选项 | 实际结果 |
|---|---|---|
| Déjà Vu! | 默认 | Deja-Vu! |
| Déjà Vu! | lower | deja-vu! |
| Déjà Vu! | lower + strict | deja-vu |
| hello world(两侧有空格) | 默认 | hello-world |
| Rock & Roll | 默认 | Rock-and-Roll |
| Äpfel | 德语设置 + lower | aepfel |
| 空字符串 | 默认 | 空字符串 |
| 42(数字) | 默认 | 抛出“string argument expected”错误 |
查看已记录的验证结果,或使用上方交互演示。演示在浏览器中运行同一固定版本的软件包。
05. 我们测试了什么
对于这个版本和这些输入,源码与实际输出一致。如果想得到 deja-vu,请同时使用 lower: true 和 strict: true。
注意验证范围。 八项验证不能覆盖所有 Unicode 或语言设置行为。REA 的关系图并不完整;图中缺少某个关系,并不能证明关系不存在。我们没有重建原生二进制文件,也没有恢复无法获取的源码。
发布包中的源码可读,因此直接检查源码也是可行方法。这个案例演示完整的分析习惯:确认分析对象,检查一条规则,再对照原始实现验证结论。
06. 下载并运行这个示例
下载小型复现资料包,其中包含固定版本的依赖、全部八项断言,以及收集 REA 证据的步骤。
npm install --ignore-scripts
node verify.mjs断言脚本会打印每项结果,任一验证失败时以错误退出。只有到这一步,Node.js 才会执行 npm 软件包;此前 REA 的静态分析不会执行它。
试试另一篇教程查明应用把导出文件保存在哪里。