【源码】validate-npm-package-name 检测 npm 包是否符合标准

1.学习目标

  • 了解validate-npm-package-name
  • 它的作用和场景

2.资料准备

git clone https://github.com/npm/validate-npm-package-name.git

3 初识–README.md

3.1 什么是validate-npm-package-name?

  • 检测 npm 包的名称是否符合标准
  • 此包导出一个同步函数,该函数以字符串作为输入,并返回一个具有两个属性的对象:
validForNewPackages :: Boolean
validForOldPackages :: Boolean

3.2. Naming Rules(命名规则)

  1. 包名称长度应大于零
  2. 包名称中的所有字符必须为小写,即不允许使用大写或混合大写的名称
  3. 包名称可以由连字符组成
  4. 包名称不得包含任何非url安全字符(因为名称最终是url的一部分)
  5. 包名称不应以开头。或_
  6. 包名称不应包含任何前导或尾随空格
  7. 包名称不应包含以下任何字符:~)(’*
  8. 包名称不能与node.js/io.js核心模块或保留/黑名单名称相同。例如,以下名称无效:
    http、流动、节点单元、图标文件
  9. 包名称长度不能超过214

3.3 例子

3.3.1 Valid Names(有效名称)

var 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
}

3.3.2 无效名称

validate("excited!")
validate(" leading-space:and:weirdchars")

// 无效 则返回如下对象
{
  validForNewPackages: false,
  validForOldPackages: false,
  errors: [
    'name cannot contain leading or trailing spaces',
    'name can only contain URL-friendly characters'
  ]
}

4. 深入–源码阅读

'use strict'
// 惰性匹配-正则表达式    链接地址:jex.im/regulex/
var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
// node 所有的node内置模块
var builtins = require('builtins')
// 黑名单
var blacklist = [
  'node_modules',
  'favicon.ico'
]

/**
 * validate:验证函数
 * name:包名
 */
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)
  }
  // 是否为string
  if (typeof name !== 'string') {
    errors.push('name must be a string')
    return done(warnings, errors)
  }
  // 验证包名的长度不能为0
  if (!name.length) {
    errors.push('name length must be greater than zero')
  }
  // 验证包名是否以.开始
  // .match(/^\./) 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \.
  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 核心模块名称,如http、events、util等
  //包名不能是node.js/io.js 核心模块 或者保留的名称
  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.
  // 包名称长度不能超过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 ("~\'!()*")')
  }
  // 包名不得包含任何非 url 安全字符
  // 关于encodeURIComponent不转义哪些字符
  // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
  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和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)
}
// scopedPackagePattern 暴露给外面使用,方便私域定制npm发布设置校验规则
validate.scopedPackagePattern = scopedPackagePattern

/**
 * 结果返回函数
 * @param {*} warnings 警告的数组
 * @param {*} errors 错误的数组
 * @returns
 */
var done = function (warnings, errors) {
  var result = {
    //一般用这个属性来判断一个包名是否合法
    validForNewPackages: errors.length === 0 && warnings.length === 0,
    //用于兼容最开始node package name 带来的遗留问题,比如报名不规范等
    validForOldPackages: errors.length === 0,
    warnings: warnings,
    errors: errors
  }
  if (!result.warnings.length) delete result.warnings
  if (!result.errors.length) delete result.errors
  return result
}

5.总结

这一期简简单单,整整齐齐,可可爱爱

上一篇:Python文本处理技巧


下一篇:关于vs c++中错误error C4996: 'scanf': This function or variable may be unsafe. Consider using s