1. 源码简介

validate-npm-package-name 作用:检测 npm 包的名称是否符合标准

简单例子:

  1. var validate = require("validate-npm-package-name")
  2. validate("some-package")
  3. validate("example.com")
  4. validate("under_score")
  5. validate("123numeric")
  6. validate("@npm/thingy")
  7. validate("@jane/foo.js")

学习目标:
1)学习 validate-npm-package-name 源码,了解它的使用场景,输出记录文档。

源码地址:
https://github.com/npm/validate-npm-package-name

2. 源码

源码是一个 100 行左右的 js 文件,我们来解读一下它的逻辑:

  1. var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
  2. var builtins = require('builtins')
  3. var blacklist = [
  4. 'node_modules',
  5. 'favicon.ico'
  6. ]
  7. var validate = module.exports = function (name) {
  8. var warnings = []
  9. var errors = []
  10. // 验证是否 null
  11. if (name === null) {
  12. errors.push('name cannot be null')
  13. return done(warnings, errors)
  14. }
  15. // 验证是否 undefined
  16. if (name === undefined) {
  17. errors.push('name cannot be undefined')
  18. return done(warnings, errors)
  19. }
  20. // 验证是否 string 类型
  21. if (typeof name !== 'string') {
  22. errors.push('name must be a string')
  23. return done(warnings, errors)
  24. }
  25. // 验证 name 长度
  26. if (!name.length) {
  27. errors.push('name length must be greater than zero')
  28. }
  29. // 验证 name 是否以.开头
  30. if (name.match(/^\./)) {
  31. errors.push('name cannot start with a period')
  32. }
  33. // 验证 name 是否以_开头
  34. if (name.match(/^_/)) {
  35. errors.push('name cannot start with an underscore')
  36. }
  37. // 验证 name 前导或尾随是否包含空格
  38. if (name.trim() !== name) {
  39. errors.push('name cannot contain leading or trailing spaces')
  40. }
  41. // 不能是黑名单里的包
  42. blacklist.forEach(function (blacklistedName) {
  43. if (name.toLowerCase() === blacklistedName) {
  44. errors.push(blacklistedName + ' is a blacklisted name')
  45. }
  46. })
  47. // 不能是node.js 内置模块列表
  48. builtins.forEach(function (builtin) {
  49. if (name.toLowerCase() === builtin) {
  50. warnings.push(builtin + ' is a core module name')
  51. }
  52. })
  53. // 限制包名长度
  54. if (name.length > 214) {
  55. warnings.push('name can no longer contain more than 214 characters')
  56. }
  57. // 包名不能包含大写字母
  58. if (name.toLowerCase() !== name) {
  59. warnings.push('name can no longer contain capital letters')
  60. }
  61. // 包名不能包含特殊符号:~\'!()*
  62. if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) {
  63. warnings.push('name can no longer contain special characters ("~\'!()*")')
  64. }
  65. // 有些字符会引起歧义,使用 encodeURIComponent 编码
  66. if (encodeURIComponent(name) !== name) {
  67. // Maybe it's a scoped package name, like @user/package
  68. var nameMatch = name.match(scopedPackagePattern)
  69. if (nameMatch) {
  70. var user = nameMatch[1]
  71. var pkg = nameMatch[2]
  72. if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
  73. return done(warnings, errors)
  74. }
  75. }
  76. errors.push('name can only contain URL-friendly characters')
  77. }
  78. return done(warnings, errors)
  79. }
  80. // scopedPackagePattern 暴露给外面使用,方便私域定制npm发布设置校验规则
  81. validate.scopedPackagePattern = scopedPackagePattern
  82. // 结果返回函数
  83. var done = function (warnings, errors) {
  84. var result = {
  85. validForNewPackages: errors.length === 0 && warnings.length === 0,
  86. validForOldPackages: errors.length === 0,
  87. warnings: warnings,
  88. errors: errors
  89. }
  90. if (!result.warnings.length) delete result.warnings
  91. if (!result.errors.length) delete result.errors
  92. return result
  93. }

3. 总结

validate-npm-package-name 其实就是一个校验包名的函数,很简单~
它的应用场景很广泛,可以用于验证组件库包名(就不用自己写验证了~),做 cli 脚手架时创建项目也可以用到,只要是规则一致。