- 源码地址:https://github.com/npm/validate-npm-package-name
- 参考文章:https://zhuanlan.zhihu.com/p/362147023
- 正则可看这本迷你书学习:https://juejin.cn/post/6844903501034684430
validate-npm-package-name 这个包主要介绍了合格的包名应该符合哪些规则,才可以通过正确的包名去创建项目,如我们常用的 vue-cli 创建项目等等都是使用了这些校验规则。
# 通过源码地址、拉取源码
- npm install
- npm run test
将源码包跑起来,可以看到在 test 目录 index 入口文件下的测试用例均能够 passed
# 源码分析
根目录下的 index 入口文件,导出一个 validate 函数(10 ~ 91 行)
/** | |
* name 传入的包名 | |
*/ | |
var validate = module.exports = function (name) { | |
//todos... | |
} | |
var validate = module.exports = function (name) { | |
//默认输出的错误信息 | |
var warnings = [] | |
var errors = [] | |
//不能为null | |
if (name === null) { | |
errors.push('name cannot be null') | |
return done(warnings, errors) | |
} | |
//不能为空 | |
if (name === undefined) { | |
errors.push('name cannot be undefined') | |
return done(warnings, errors) | |
} | |
//必须是个字符串 | |
if (typeof name !== 'string') { | |
errors.push('name must be a string') | |
return done(warnings, errors) | |
} | |
//必须存在长度 | |
if (!name.length) { | |
errors.push('name length must be greater than zero') | |
} | |
//不能以点开头 | |
if (name.match(/^\./)) { | |
errors.push('name cannot start with a period') | |
} | |
//不能以下划线开头 | |
if (name.match(/^_/)) { | |
errors.push('name cannot start with an underscore') | |
} | |
//不能含有空格尾随 | |
if (name.trim() !== name) { | |
errors.push('name cannot contain leading or trailing spaces') | |
} | |
// No funny business (不能大小写乱写) | |
blacklist.forEach(function (blacklistedName) { | |
if (name.toLowerCase() === blacklistedName) { | |
errors.push(blacklistedName + ' is a blacklisted name') | |
} | |
}) | |
// Generate warnings for stuff that used to be allowed | |
// core module names like http, events, util, etc | |
(不能是node的内置模块,如http,events, util等等) | |
builtins.forEach(function (builtin) { | |
//builtins是一个node的内置模块包 | |
if (name.toLowerCase() === builtin) { | |
warnings.push(builtin + ' is a core module name') | |
} | |
}) | |
//包名不能太长 | |
if (name.length > 214) { | |
warnings.push('name can no longer contain more than 214 characters') | |
} | |
// mIxeD CaSe nAMEs(不能包含大小写) | |
if (name.toLowerCase() !== name) { | |
warnings.push('name can no longer contain capital letters') | |
} | |
//不能包含()~ ! *等符号 | |
if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) { | |
warnings.push('name can no longer contain special characters ("~\'!()*")') | |
} | |
//不能包含中文 | |
if (encodeURIComponent(name) !== name) { | |
// Maybe it's a scoped package name, like @user/package | |
//类似包名 @package/user | |
var nameMatch = name.match(scopedPackagePattern) | |
if (nameMatch) { | |
var user = nameMatch[1]//package | |
var pkg = nameMatch[2]//user | |
//对user和pkg分别进行校验 | |
if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) { | |
return done(warnings, errors) | |
} | |
} | |
errors.push('name can only contain URL-friendly characters') | |
} | |
return done(warnings, errors) | |
} |
//校验后均会执行的方法 | |
/** | |
* | |
* @param {*} warnings 警告的数组 | |
* @param {*} errors 错误的数组 | |
* @returns | |
*/ | |
var done = function (warnings, errors) { | |
var result = { | |
//如果没有警告和错误 为true | |
validForNewPackages: errors.length === 0 && warnings.length === 0, | |
//只需要没有错误就行 | |
validForOldPackages: errors.length === 0, | |
warnings: warnings, | |
errors: errors | |
} | |
//删除对应的对象 | |
if (!result.warnings.length) delete result.warnings | |
if (!result.errors.length) delete result.errors | |
//返回整个校验后的对象 | |
return result | |
} |
# 源码阅读收获
通过这期的源码阅读,细致了解到了 npm 包命名的规范以及源码实现思路,进一步理解了包名的校验规则。