validate-npm-package-name【源码共读】

207 阅读3分钟

第一次参加若川大佬的源码共读活动,希望可以坚持下去!文末附上若川大佬主页和微信!

第一次的源码是一个很简单的工具函数。

如英文名所示,这个工具函数是检测npm包的命名是否符合规范。使用蹩脚的英文水平翻译了一下:

Give me a string and I'll tell you if it's a valid npm package name.
//给我一个字符串并且我将告诉你,它是否是一个有效的npm包名
This package exports a single synchronous function that takes a string as input and returns an object with two properties:
//这个包导出一个单一同步函数,输入一个字符串并且返回一个对象,这个对象有两个属性

源码如下:

'use strict'var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
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报错 不能为undefined
  if (name === undefined) {
    errors.push('name cannot be undefined')
    return done(warnings, errors)
  }
//传入的name不为字符串报错 必须为字符串
  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')
  }
//第一个字符不能为.
//String.match()  将String使用match(RegExp)进行匹配,并返回结果
  if (name.match(/^./)) {
    errors.push('name cannot start with a period')
  }
//第一个字符不能为_
  if (name.match(/^_/)) {
    errors.push('name cannot start with an underscore')
  }
//首位不能有空格
//String.trim() 删除字符串两端空格,并返回删除后的字符串
  if (name.trim() !== name) {
    errors.push('name cannot contain leading or trailing spaces')
  }
​
  // No funny business
  //遍历黑名单,将黑名单上的每个元素与传入的name匹配,相同则报错
  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
  //同上,遍历引入的builtin,报错改为警告
  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
  //传入的name需要为小写
  if (name.toLowerCase() !== name) {
    warnings.push('name can no longer contain capital letters')
  }
  //name中不能包含[]内的符号
  if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) {
    warnings.push('name can no longer contain special characters ("~'!()*'")')
  }
​
  if (encodeURIComponent(name) !== name) {
    //如果转义 应对@vue/core这种情况,
    var nameMatch = name.match(scopedPackagePattern)
    //若name为 '@vue/core' 经过match转换会变成 ['@vue/core','vue','core']
    if (nameMatch) {
      var user = nameMatch[1]
      var pkg = nameMatch[2]
      if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
        return done(warnings, errors)
      }
    }
​
    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
}
​

收获

第一次参加若川大佬的源码共读的活动,本来应该昨天就开始的,但是处理bug处理了好久,直到今早才处理好,所有耽搁了一天。通过这次源码让我复习与学习了很多数组与字符串的API,也认识到了自己的不足:正则表达式。希望以后每一天可以进步一点点!

String.match()  //将String使用match(RegExp)进行匹配,并返回结果
String.trim()   //删除字符串两端空格,并返回删除后的字符串
RegExp.test()   //检查传入的字符串和正则是否匹配,返回一个布尔
//将一个字符串split返回一个数组,再将数组slice,拿到数组的最后一个元素,是为了拿到 @vue/core中的core去匹配正则
if (/[~'!()*]/.test(name.split('/').slice(-1)[0]))

还了解了 encodeURIComponent() 将传入的字符转义为UTF-8编码,但 A-Z a-z 0-9 - _ . ! ~ * ' ( ) 这些字符不会被转义。

疑问

有点没明白 validate.scopedPackagePattern = scopedPackagePattern 这个语句的作用,感觉没有这个语句也可以执行。但是看了其他人的源码分析后,发现这个好像是故意暴露给引用validata的人看的。

若川大佬主页:juejin.cn/user/141582…

若川大佬微信:juejin.cn/pin/7005372…