简介

validate-npm-package-name,顾名思义,主要是用来校验包名是否符合规范。传入一个字符串,返回一个包含
以下两个属性的一个对象。

  1. validForNewPackages :: Boolean
  2. validForOldPackages :: Boolean

用法

  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. {
  2. validForNewPackages: true,
  3. validForOldPackages: true
  4. }

源码分析

现在进入源码部分,我们可以看到package.json中包含了一些依赖

package.json

  1. {
  2. ...
  3. "dependencies": {
  4. "builtins": "^1.0.3"
  5. },
  6. "devDependencies": {
  7. "standard": "^8.6.0",
  8. "tap": "^10.0.0"
  9. },
  10. "scripts": {
  11. "cov:test": "TAP_FLAGS='--cov' npm run test:code",
  12. "test:code": "tap ${TAP_FLAGS:-'--'} test/*.js",
  13. "test:style": "standard",
  14. "test": "npm run test:code && npm run test:style"
  15. },
  16. ...
  17. }

builtins是获取Node的内置模块,例如可以用以下代码获取Node中模块的信息

  1. const m = require('module');
  2. console.log(m);

红框内的就是内置模块的数据,内置模块允许修改,但是不支持新增,可参照Node.js的内置模块说明
image.png
standard是JavaScript代码规范,自带linter 和代码修正,可以参考StandardJs中文
tap是NodeJs的一个测试框架,支持覆盖测试等,并且拥有美观的测试输出,Node Tap

image.png

所以

  1. npm run test

主要是执行了tap和standard的操作

index.js

代码里的正则可视图

  1. /^(?:@([^/]+?)[/])?([^/]+?)$/

image.png

其他部分的直接贴注释:

  1. 'use strict'
  2. // 包匹配的正则
  3. var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
  4. // 获取NodeJs的内置模块
  5. var builtins = require('builtins')
  6. // 黑明单列表,name不允许存在在黑名单中
  7. var blacklist = [
  8. 'node_modules',
  9. 'favicon.ico'
  10. ]
  11. var validate = module.exports = function (name) {
  12. // 保存警告
  13. var warnings = []
  14. // 保存错误
  15. var errors = []
  16. // 不允许为null
  17. if (name === null) {
  18. errors.push('name cannot be null')
  19. return done(warnings, errors)
  20. }
  21. // 不允许为undefined
  22. if (name === undefined) {
  23. errors.push('name cannot be undefined')
  24. return done(warnings, errors)
  25. }
  26. // 必须是字符串类型
  27. if (typeof name !== 'string') {
  28. errors.push('name must be a string')
  29. return done(warnings, errors)
  30. }
  31. // 不能是空字符串
  32. if (!name.length) {
  33. errors.push('name length must be greater than zero')
  34. }
  35. // 不能以.开头
  36. if (name.match(/^\./)) {
  37. errors.push('name cannot start with a period')
  38. }
  39. // 不能以_开头
  40. if (name.match(/^_/)) {
  41. errors.push('name cannot start with an underscore')
  42. }
  43. // 不允许有前后空格
  44. if (name.trim() !== name) {
  45. errors.push('name cannot contain leading or trailing spaces')
  46. }
  47. // 不允许出现在黑名单中
  48. // No funny business
  49. blacklist.forEach(function (blacklistedName) {
  50. if (name.toLowerCase() === blacklistedName) {
  51. errors.push(blacklistedName + ' is a blacklisted name')
  52. }
  53. })
  54. // Generate warnings for stuff that used to be allowed
  55. // 不允许是核心模块的名字
  56. // core module names like http, events, util, etc
  57. builtins.forEach(function (builtin) {
  58. if (name.toLowerCase() === builtin) {
  59. warnings.push(builtin + ' is a core module name')
  60. }
  61. })
  62. // 长度不能超过214
  63. // really-long-package-names-------------------------------such--length-----many---wow
  64. // the thisisareallyreallylongpackagenameitshouldpublishdowenowhavealimittothelengthofpackagenames-poch.
  65. if (name.length > 214) {
  66. warnings.push('name can no longer contain more than 214 characters')
  67. }
  68. // 不允许大小写的情况
  69. // mIxeD CaSe nAMEs
  70. if (name.toLowerCase() !== name) {
  71. warnings.push('name can no longer contain capital letters')
  72. }
  73. // 不能包含特殊字符:~'!()*
  74. if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) {
  75. warnings.push('name can no longer contain special characters ("~\'!()*")')
  76. }
  77. // 合法的url字符
  78. if (encodeURIComponent(name) !== name) {
  79. // Maybe it's a scoped package name, like @user/package
  80. var nameMatch = name.match(scopedPackagePattern)
  81. if (nameMatch) {
  82. var user = nameMatch[1]
  83. var pkg = nameMatch[2]
  84. if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
  85. return done(warnings, errors)
  86. }
  87. }
  88. errors.push('name can only contain URL-friendly characters')
  89. }
  90. return done(warnings, errors)
  91. }
  92. validate.scopedPackagePattern = scopedPackagePattern
  93. // 处理的函数
  94. var done = function (warnings, errors) {
  95. var result = {
  96. validForNewPackages: errors.length === 0 && warnings.length === 0,
  97. validForOldPackages: errors.length === 0,
  98. warnings: warnings,
  99. errors: errors
  100. }
  101. if (!result.warnings.length) delete result.warnings
  102. if (!result.errors.length) delete result.errors
  103. return result
  104. }

总结

1.了解了builtinsstandardtap
2.复习了正则