源码分析 - validate-npm-package-name

106 阅读2分钟

源码分析 - validate-npm-package-name

从包名的描述中,我们可以知道该包是用于验证 npm package name 是否合法。

示例

先看官方给的例子:

// 正确的命名
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
}
*/
// 错误的命名
validate('excited!')
// validate(' leading-space:and:weirdchars')
validate(
  'eLaBorAtE-paCkAgE-with-mixed-case-and-more-than-214-characters-----------------------------------------------------------------------------------------------------------------------------------------------------------',
)

/*
{
  validForNewPackages: false,
  validForOldPackages: false,
  errors: [
    'name cannot contain leading or trailing spaces',
    'name can only contain URL-friendly characters'
  ]
}

{
  validForNewPackages: false,
  validForOldPackages: true,
  warnings: [
    "name can no longer contain capital letters",
    "name can no longer contain more than 214 characters"
  ]
}
*/

从示例中我们可以知道, validate 函数并不是简单的返回一个 boolean,它返回一个对象包含

  • 对于旧版 package name 的验证。(validForOldPackages)
  • 对于新版 package name 的验证。(validForNewPackages)
  • 警告 / 错误的提示信息。

项目分析

该项目的源码也就 100 多行,里面涉及大量的字符串和正则的比较,这里不会逐行去分析这些比较。 更多地关注项目的结构

'use strict'

var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
var builtins = require('builtins')
var blacklist = [
  /* ... */
]

var validate = (module.exports = function (name) {
  var warnings = []
  var errors = []

  return done(warnings, errors)
})

validate.scopedPackagePattern = scopedPackagePattern

var done = function (warnings, errors) {
  var result = {
    /* ... */
  }
  return result
}

里面两个核心函数 validatedone 的结合可谓巧妙。

核心函数

var validate = (module.exports = function (name) {
  var warnings = []
  var errors = []

  // statements
  return done(warnings, errors)
})

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
}

done 函数是对输出结果进行格式化,validate 函数对输入的 name 进行一系列的验证后,将验证过程中的警告和错误推入数组中。 需要注意的是验证的先后顺序。

学习心得

事实上,我们可以将该库进行抽象,实现自己的验证库:

export function validate(somethingShouldBeValidate) {
  const warnings = []
  const errors = []

  // do validate

  return done(warnings, errors)
}

function done(warnings, errors) {
  const result = {
    isSuccess: errors.length === 0 && warnings.length === 0,
    isWarning: warnings.length !== 0,
    isError: errors.length !== 0,

    warnings,
    errors,
  }

  return result
}

验证逻辑和输出结果可以根据项目的要求而定。