- 开发工具
【免费下载链接】mailcheck
Reduce misspelled email addresses in your web apps.
Mailcheck 2.0.0-beta.1 是该项目迈向 2.0 的第一个预发布版本:它在不改变既有 JavaScript 调用 API的前提下,随包内置了 TypeScript 类型声明,重写了域名匹配行为(保护.ai、.io、.app等现代真实后缀,避免被"过度纠错"),并新增了缺失域名后缀的补全能力。本文以 Releases/2.0.0-beta.1.md 发布说明为主线,结合仓库源码与测试用例,逐条解读本次改版的行为细节、升级影响与打包/测试覆盖情况,帮助你评估集成风险并规划升级路径。
一、版本概览:一次"行为收紧 + 类型补齐"的 Beta
2.0.0-beta.1 的核心变化可以归纳为三点,全部围绕"更少误报、更稳建议"展开:
- TypeScript 定义随包内置:无需再单独安装
@types/mailcheck; - 域名匹配更新:引入离线 IANA 根域快照,把"真实存在的后缀"与"可纠正的目标"区分开;
- 缺失后缀补全:
sample@gmail、sample@gmail.现在可以直接建议sample@gmail.com。
最关键的一条约束是:现有的 JavaScript API 完全不变。方法、选项、回调参数与返回值形状全部保持原样,没有需要适配的新包装器,也没有新的识别选项要配置。这意味着从 1.1.2 升级到 2.0 时,前端调用代码几乎无需改动,行为层面的变化才是需要关注的焦点。
二、邮箱建议行为:六条行为变更逐一解读
发布说明把本次行为改动总结为六个要点,下面结合 src/mailcheck.js 的实现逐一展开。
1. 现代域名后缀不再被"纠正":1438 个 IANA 后缀的离线快照
变化:地址处于.ai、.io、.app等已被 IANA 收录的真实后缀上时,不再因为"看起来很像"而被改写到其他后缀。
源码依据:源码顶部维护了一个名为validTopLevelDomains的离线快照(src/mailcheck.js),注释明确标注:
- 快照版本为2026091400(2026-09-14),数据源是 IANA 根区 TLD 列表;
- 共包含1,438 个根域后缀;
- 它仅用于识别(Recognition),不是模糊纠错目标,也不做运行时联网查询——快照更新必须通过新构建发布。
在suggest()中,从地址拆出的域名根(最后一个.之后的部分)如果命中该快照,或者命中了调用方通过topLevelDomains显式配置的后缀,就被视为knownTopLevelDomain(src/mailcheck.js),随后纠错目标会被收缩为"仅保留与当前后缀一致的域名":
var root = emailParts.domain.substring(emailParts.domain.lastIndexOf('.') + 1); var knownTopLevelDomain = validTopLevelDomains.indexOf(root) !== -1 || !!(topLevelDomains && topLevelDomains.indexOf(emailParts.topLevelDomain) !== -1); // A recognized suffix must not become a different suffix (e.g. .co -> .com). var matchingDomains = domains; if (knownTopLevelDomain && domains) { matchingDomains = []; for (var i = 0; i < domains.length; i++) { if (domains[i].substring(domains[i].indexOf('.') + 1) === emailParts.topLevelDomain) { matchingDomains.push(domains[i]); } } }测试印证:person@company.ai、person@company.io、person@company.app、person@company.dev、person@company.zip、person@company.museum等均被列入legitimate名单并断言"不产生建议"(spec/correctness.test.js)。此外spec/correctness.test.js中还有专门的用例,遍历ai/io/app/dev/me/co/om/zip/mov/xyz/tech等 30 个后缀,在两种不同的domains列表顺序下均验证"独立公司地址不被改写"。
设计意图:这是从"激进纠错"向"保守建议"的一次明确转向——宁可漏掉一个真实的拼写错误,也不要对合法地址给出错误建议。
2. 识别更多邮件服务商:Proton、HEY、Fastmail、Tuta 进入默认列表
变化:默认纠错目标列表新增了 Proton(proton.me、protonmail.com、pm.me)、HEY(hey.com)、Fastmail(fastmail.com)、Tuta(tuta.com、tutanota.com、tutamail.com)。
源码依据:defaultDomains定义于 src/mailcheck.js,本次新增的条目紧跟在原有服务商之后:
defaultDomains: ['msn.com', 'bellsouth.net', 'telus.net', 'comcast.net', 'optusnet.com.au', 'earthlink.net', 'qq.com', 'sky.com', 'icloud.com', 'mac.com', 'sympatico.ca', 'googlemail.com', 'att.net', 'xtra.co.nz', 'web.de', 'cox.net', 'gmail.com', 'ymail.com', 'aim.com', 'rogers.com', 'verizon.net', 'rocketmail.com', 'google.com', 'optonline.net', 'sbcglobal.net', 'aol.com', 'me.com', 'btinternet.com', 'charter.net', 'shaw.ca', 'proton.me', 'protonmail.com', 'pm.me', 'hey.com', 'fastmail.com', 'tuta.com', 'tutanota.com', 'tutamail.com'],测试印证:spec/correctness.test.js 中every bundled provider is left alone用例遍历Mailcheck.defaultDomains全部条目,断言Mailcheck.run({ email: 'Person@' + domain })均返回undefined(不产生建议)——即所有内置服务商的精确地址都得到保护。
3. 缺失后缀补全:sample@gmail→sample@gmail.com
变化:sample@gmail和sample@gmail.现在会建议sample@gmail.com。补全复用现有的domains列表(或默认列表),不引入新选项;且必须满足"名字精确 + 存在一个完整的全域名匹配"两个条件:
- 不基于部分名字猜测(
gmai不会补全); - 不在多个后缀之间做选择(
gmail.com与gmail.co.uk同时存在时不补全)。
源码依据:src/mailcheck.js 的补全逻辑先校验域名是否形如合法单标签(含可选的尾部点),再在domains中寻找以名字 + '.'为前缀的完整域名:
if (/^a-z0-9?\.?$/.test(emailParts.domain) && emailParts.domain.indexOf('xn--') !== 0) { if (domains && domains.indexOf(emailParts.domain) !== -1) { return false; } var prefix = emailParts.domain.replace(/\.$/, '') + '.'; var completion = false; for (var d = 0; domains && d < domains.length; d++) { if (domains[d].indexOf(prefix) === 0 && domains[d].length > prefix.length) { if (completion && completion !== domains[d]) { return false; // 多个候选 → 不补全 } completion = domains[d]; } } return completion ? { address: emailParts.address, domain: completion, full: emailParts.address + '@' + completion } : false; }测试印证(spec/correctness.test.js):
sample@gmail、Sample+Tag@GMAIL、sample@gmail.、person@icloud、person@proton分别补全为sample@gmail.com、Sample+Tag@gmail.com、sample@gmail.com、person@icloud.com、person@proton.me;- 补全结果的地址再次运行会返回
undefined(结果稳定,不会连锁纠错); - 歧义场景(
domains: ['gmail.com', 'gmail.co.uk']或反序)一律不补全; g、gm、gmai、gmial、unknowncompany等部分名/未知名均不补全;- 显式配置了单标签域名(
domains: ['gmail', 'gmail.com'])时也不扩展补全。
4. 平局不再按列表顺序取胜
变化:当两个全域名候选与输入的距离完全相等时,不再产生任何建议(此前会按列表顺序挑第一个)。
源码依据:内部辅助函数mailcheckClosestMatch(src/mailcheck.js)专门跟踪ambiguous标志——一旦发现相同距离的第二个不同候选,即标记为歧义:
if (dist < minDist) { minDist = dist; closestDomain = domains[i]; ambiguous = false; } else if (dist === minDist && domains[i] !== closestDomain) { ambiguous = true; } return { domain: minDist <= threshold && !ambiguous ? closestDomain : false, ambiguous: minDist <= threshold && ambiguous };测试印证:cat.com面对['bat.com', 'hat.com']或['hat.com', 'bat.com']均返回false;但重复项不算歧义(['bat.com', 'bat.com']仍返回bat.com);精确匹配优先(['bat.com', 'cat.com']返回cat.com)(spec/correctness.test.js)。同时mailcheckClosestMatch的注释点明了设计动机:把"歧义"与"无匹配"区分开,避免平局时回退到二级域名猜测(src/mailcheck.js)。
5. 保留本地部分(local-part)的大小写
变化:Sample.Name+Tag@gmial.com建议为Sample.Name+Tag@gmail.com——只有域名被小写化,@之前的本地部分原样保留(包括大小写与+Tag标签)。
源码依据:src/mailcheck.js 只对拆分出的domain、secondLevelDomain、topLevelDomain调用toLowerCase(),address(本地部分)从不触碰。同时run()仍保留其既有的encodeEmail()编码步骤(src/mailcheck.js),而低层suggest()则直接接收调用方传入的原始字符串。
测试印证:François、a%%^^``{{||}}、"Foo@Bar"、<script>alert("x")</script>等本地部分在run()中经编码、在suggest()中原样保留(spec/correctness.test.js、spec/correctness.test.js)。
安全提示:由于run()的 URI 编码并非 HTML 消毒器,展示用户输入时应使用textContent、jQuery 的.text()或框架的转义渲染,不要使用innerHTML(index.d.ts 的类型注释同样强调了这一点)。
6. 零距离阈值 = 仅精确匹配
变化:将domainThreshold(或通过findClosestDomain传入的阈值)设为0时,距离匹配只允许完全相等的字符串。
测试印证:Mailcheck.findClosestDomain('gmial.com', ['gmail.com'], undefined, 0)返回false,而Mailcheck.findClosestDomain('gmail.com', ['gmail.com'], undefined, 0)返回'gmail.com'(spec/correctness.test.js)。三个默认阈值domainThreshold、secondLevelThreshold、topLevelThreshold当前均为2(src/mailcheck.js),它们是公开的可变属性,也是本次 TypeScript 类型覆盖的一部分。
建议的输出方式没有变
所有建议仍然通过既有的suggested回调或run()的返回值输出;Mailcheck 永远不会自动替换输入框中的地址。渲染建议是应用层自己的职责(参考 examples/index.html 中 jQuery 示例的做法)。
三、TypeScript:类型随包内置,API 零变化
覆盖了哪些类型面
发布说明声明:类型覆盖了 options、suggestion、自定义距离函数、回调返回值,以及可变的默认值(defaultDomains、defaultSecondLevelDomains、defaultTopLevelDomains与三个阈值均为let声明,见 index.d.ts)。
类型声明分布在以下文件(均在 package.json 的types字段中登记):
- index.d.ts:
Suggestion、EmailParts、DistanceFunction、Options、RunOptions,以及run/suggest/findClosestDomain/sift4Distance/splitEmail/encodeEmail的签名; - src/mailcheck.d.ts 与 src/mailcheck.min.d.ts:直接导入源码/压缩构建时的类型入口;
- jquery.d.ts:可选的 jQuery 插件声明。
五种消费形态全部覆盖
仓库在 spec/package/ 下提供了五种消费形态的类型 fixture:
- CommonJS:spec/package/commonjs.cts 使用
import Mailcheck = require('mailcheck'),同时验证直接导入mailcheck/src/mailcheck.js与mailcheck/src/mailcheck.min.js; - ESM interop:spec/package/esm.mts 使用默认导入(需要
esModuleInterop),并验证类型导入import type { Suggestion, RunOptions } from 'mailcheck'; - 浏览器全局:spec/package/browser.ts 声明
Mailcheck全局变量; - 直接源码导入:
mailcheck/src/mailcheck.js与压缩构建均有对应类型; - 可选的 jQuery 声明:spec/package/jquery.ts 通过
import type {} from 'mailcheck/jquery'接入,类型注释提醒:该插件返回void(不可链式调用),回调参数依次为元素与建议对象。
类型层面的三个设计要点
- 回调返回值独立推断:
run()默认返回Suggestion | undefined;一旦提供suggested/empty回调,返回类型由回调的返回值联合推断(index.d.ts)。spec/package/commonjs.cts中suggested: suggestion => suggestion.full.length、empty: () => null时结果被推断为number | null; - 低层 API 语义区分:
suggest()与findClosestDomain()无匹配时返回false而非undefined,且纠错列表必须显式传入,不像run()有默认值(index.d.ts); - 编译期守护:fixture 里用
@ts-expect-error断言"不存在的validTopLevelDomains选项"、"suggest仍是五参数"等错误场景必须报错(spec/package/commonjs.cts)。
TypeScript 使用者不再需要单独的@types/mailcheck包;如果已安装该包且与内置类型冲突,应按 README.md 的建议将其移除。Mailcheck 依旧保持零运行时依赖、零运行时网络请求(package.json 的dependencies为空)。
四、升级时必须核对的行为(Breaking-ish 清单)
这四条是发布说明明确提示"升级时需检查"的行为变化,直接决定了线上表现是否可接受:
gmail.co现在会被放行:.co是真实后缀,不再被建议为gmail.com。这是刻意选择"漏掉真实拼写错误,优于给出错误建议"。测试中person@gmail.co、person@hotmail.co均列入legitimate名单(spec/correctness.test.js),person@gmil.ai同样被保护。未知或歧义的无后缀名字不再补全:
sample@unknowncompany不补全;通过domains选项传入的列表会整体替换默认列表(src/mailcheck.js 中opts.domains = opts.domains || Mailcheck.defaultDomains)。注意空数组domains: []也是有效的"替换"——它会关闭所有补全(spec/correctness.test.js 验证了替换不影响全局默认列表)。Unicode/punycode 域名与不支持的子域布局一律不猜测:
person@bücher.de、person@xn--bcher-kva.de、person@example.рф均返回undefined(spec/correctness.test.js)。代码层面通过/(^|\.)-|-(\.|$)|(^|\.)xn--/与xn--前缀检查主动放弃这类输入(src/mailcheck.js)。显式配置的复合后缀(如co.uk、com.au、co.nz)仍然受支持——它们必须在topLevelDomains中列出才被认可,本实现并非公共后缀解析器。Mailcheck 是拼写建议器,不是地址验证器:它不做投递性检查,被拒绝的建议不应阻断注册流程。类型声明同样写明 "Offline email-domain typo suggestions, not validation or deliverability checks"(index.d.ts)。
五、发布包与测试覆盖
归档内容与构建方式
发布说明提到的mailcheck-2.0.0-beta.1.tgz由仓库根目录的 cut_release.sh(转发至 script/cut-release.js)构建,归档仅包含:
- 运行时 src/mailcheck.js 与压缩构建 src/mailcheck.min.js;
- TypeScript 声明(index.d.ts、src/mailcheck.d.ts、src/mailcheck.min.d.ts、jquery.d.ts);
- 清单文件(package.json、bower.json、mailcheck.jquery.json)、README.md 与 LICENSE。
测试、构建工具和 vendored jQuery 一律不打包(PACKAGE_FILES白名单见 script/cut-release.js)。
cut_release.sh的用法(预发布与正式版通用):
./cut_release.sh 2.0.0-beta.1 # 生成 Releases/mailcheck-2.0.0-beta.1.tgz ./cut_release.sh 2.0.0 # 生成 Releases/mailcheck-2.0.0.tgz ./cut_release.sh 2.0.0-beta.1 --dry-run # 仅预览输出路径打包流程在临时副本中更新package.json、jQuery 清单与源码 banner 的版本号、用构建代码重建压缩文件、以npm pack --offline --ignore-scripts生成归档,并校验版本元数据一致性与归档文件名;绝不覆盖已存在的归档,也不执行发布(不推送、不打 tag、不创建 GitHub Release,无需 npm 登录)(script/cut-release.js)。该脚本要求开发依赖已安装,且打包前会校验package.json与源码 banner 的版本必须一致。
测试矩阵
发布说明声明的覆盖范围在仓库中均有对应落点:
| 覆盖项 | 说明 | 仓库落点 |
|---|---|---|
| 原始 Jasmine 套件 | 遗留核心规范 | spec/mailcheckSpec.js、spec/spec_runner.html |
| 回归与正确性用例 | 对源码与压缩构建同时执行 | spec/correctness.test.js |
| 包消费者测试 | 在隔离临时目录安装 tarball 后编译运行 CJS/ESM 导入 | script/test-package.js、spec/package/ |
| TypeScript 类型检查 | 严格模式下的 CJS/ESM/浏览器全局/jQuery fixture | package.json 的test:types |
| 60 个浏览器测试 | Chromium、Firefox、WebKit 三引擎 × 源码/压缩两种构建 | playwright.config.js、spec/e2e/ |
| 发布打包检查 | 构建并检查真实预发布/正式归档 | spec/release.test.js、package.json 的test:release |
浏览器测试同时覆盖了纯 JavaScript fixture与真实 jQuery 示例(examples/index.html 在测试中被本地 jQuery 替代 CDN 引用,分别以 jQuery 1.12.4 与 3.7.1 运行),并检查 blur 回调、输入不被改写、陈旧建议清除与本地部分的安全渲染(README.md)。
明确不覆盖的范围:Internet Explorer 与真实邮件投递。
发布状态
这是一个仅供集成测试的 Beta 归档,尚未发布到 npm。集成时请直接使用本仓库 src/mailcheck.min.js 或 src/mailcheck.js,或自行用cut_release.sh生成本地归档;npm install mailcheck安装到的仍是已发布的旧版本,而非本次 Beta 行为。
六、实操速查:验证与接入
在 Node 中快速验证新行为
const Mailcheck = require('mailcheck'); // 指向本仓库源码或本地归档 // 1. 现代后缀保护 Mailcheck.run({ email: 'person@company.ai' }); // undefined // 2. 缺失后缀补全 Mailcheck.run({ email: 'sample@gmail' }).full; // 'sample@gmail.com' // 3. 大小写保留 Mailcheck.run({ email: 'Sample.Name+Tag@gmial.com' }).full; // 'Sample.Name+Tag@gmail.com' // 4. 平局不取胜 Mailcheck.run({ email: 'person@cat.com', domains: ['bat.com', 'hat.com'], secondLevelDomains: [] }); // undefined完整测试入口
npm ci --ignore-scripts --no-audit --no-fund npx --no-install playwright install chromium firefox webkit # 浏览器测试依赖 npm run test:ci # 构建检查 + 全部单元/回归/类型/打包/浏览器测试各单项命令(npm test、npm run test:types、npm run test:package、npm run test:e2e、npm run test:release)的职责与 Node 版本要求(Node 20.19+)详见 README.md。
七、总结
Mailcheck 2.0.0-beta.1 是一次"正确性优先"的改版:通过 1,438 个 IANA 后缀的离线快照守住现代真实后缀、以平局弃权消除列表顺序依赖、以"精确名字 + 唯一全域名"规则约束补全,把误报率压到最低;同时用随包 TypeScript 类型完整覆盖五种消费形态,却不新增任何运行时 API。如果你正在用 Mailcheck 做表单邮箱纠错,升级前只需重点核对第五节列出的四条行为变化(尤其是gmail.co这类"宁可漏报"的新策略),其余调用代码可以原样迁移。
- 开发工具
【免费下载链接】mailcheck
Reduce misspelled email addresses in your web apps.
相关推荐
Lucide React 别名导入指南:理解图标别名、前缀/后缀命名与 IDE 自动补全定制
Lucide React 别名导入指南:理解图标别名、前缀/后缀命名与 IDE 自动补全定制 本篇指南聚焦 Lucide 图标库在 React 项目中的导入命名
前端UI组件设计系统如何在多种Linux发行版上轻松安装NonSteamLaunchers:完整指南
如何在多种Linux发行版上轻松安装NonSteamLaunchers:完整指南 NonSteamLaunchers是一款专为Linux系统设计的强大工具,它能
如何用Playnite统一管理你的所有游戏平台:一站式游戏库终极指南
如何用Playnite统一管理你的所有游戏平台:一站式游戏库终极指南 你是否厌倦了在Steam、Epic、GOG、Battle.net等多个游戏平台之间来回切换
桌面应用游戏开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考