validate-npm-package-name 源码分析

·  阅读 48

一、validate-npm-package-name 作用和使用场景

作用:

检查npm包名是否符合标准,该工具提供一个接受字符串的函数,并且返回一个拥有2个属性的对象

使用场景示例:

const validate = require("validate-npm-package-name");
// 有效包名
validate("some-package");
validate("example.com");
validate("under_score");
validate("123numeric");
validate("@npm/thingy");
validate("@jane/foo.js");
// 返回结果 
{ validForNewPackages: true, validForOldPackages: true }

// 无效的包名
validate("excited!") 
validate("https") 
// 返回结果
{
  validForNewPackages: false,
  validForOldPackages: false,
  errors: [ 'name can only contain URL-friendly characters' ]
}
复制代码

二、源码分析

源码也就100行左右,可以简单阅读一下

'use strict'
// 特殊字符开头正则
var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
// node核心模块数组,例如http、fs、path、utils等
// 不同node版本会增加新的模块,例如8.4.0 新增 http2
var builtins = require('builtins')
// 黑名单
var blacklist = [
  'node_modules',
  'favicon.ico'
]
// 导出一个方法来做包名的校验,入参是包名
var validate = module.exports = function (name) {
  // 收集的警告集合
  var warnings = []
  // 收集的错误集合
  var errors = []
	// 不能为null
  if (name === null) {
    errors.push('name cannot be null')
    return done(warnings, errors)
  }
	// 不能为undefined
  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')
  }
  // 不能使用黑名单里的名字(不区分大小写)
  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内置模块命名相同
  builtins.forEach(function (builtin) {
    if (name.toLowerCase() === builtin) {
      warnings.push(builtin + ' is a core module name')
    }
  })

  // really-long-package-names-------------------------------such--length-----many---wow
  // the thisisareallyreallylongpackagenameitshouldpublishdowenowhavealimittothelengthofpackagenames-poch.
  // 名称length不能超过214
  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
    var nameMatch = name.match(scopedPackagePattern)
    if (nameMatch) {
      var user = nameMatch[1]
      var pkg = nameMatch[2]
      // 类似包名 @user/package 时要对 package 和 user 分别校验 url 安全字符
      if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
        return done(warnings, errors)
      }
    }
    // 包名不得包含任何非 url 安全字符
    errors.push('name can only contain URL-friendly characters')
  }

  return done(warnings, errors)
}
// 这行代码有点不理解,为啥把正则挂载方法的属性上?
validate.scopedPackagePattern = scopedPackagePattern
// 执行函数,返回一个对象,包含验证信息
var done = function (warnings, errors) {
  var result = {
    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
}
复制代码

三、总结

代码很简单,都是一些简单的字符串校验以及正则匹配,代码逻辑也很清晰,平铺直叙容易理解。第一次阅读源码,出乎我的意料...... 消除了我心中对于源码的敬畏感,再接再厉!

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改