【源码学习】第15期 | 如何检测npm包名的规范性?

1,618 阅读3分钟

前言

    你知道vue-create是如何检测包的规范性的吗?阅读过vue-cli源码 传送门 的童鞋可能不难发现起作用的就是validate-npm-package-name这个包,包关键源码只有100多行,下面就来一起学习探讨下~

收获清单

  • 保姆级源码调试步骤
  • 如何检测包名的规范性

环境准备

源码下载

    这里装包时要注意看包版本号是否存在npm view ** versions,否则会装不成功

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

node环境

    阅读源码前先从readme看使用方法再从package.json看执行命令、支持环境等,从package.json可以看出validate-npm-package-name支持的node环境如下,执行调试前记得先检查自己的node版本

图片.png

开启调试

    今天的源码从readme中也可以知道开启调试的命令,但更通用的是点击悬浮package.jsonscript脚本后出现的调试脚本按钮,开启调试前先找到测试文件test/index.js和主函数文件lib/index.js打好断点,基本原则是哪里不会点哪里,点到会为止~

图片.png

图片.png     调试截图 图片.png

源码分析

引入依赖及定义全局变量

// 正则匹配scope库
var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
// 列出node.js内置模块
var builtins = require('builtins')
// 黑名单
var blacklist = [

  'node_modules',

  'favicon.ico',

]

done方法输出结果

    主要是通过操作对象属性来动态返回warningerrors

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

}

    如validate('node_modules'),会得到如下图结果:

图片.png

validate函数

function validate (name) {

  var warnings = []

  var errors = []
  ...
 // 代码有删减,删减部分主要判断name是否为空,以. _等非法字符开头等情况
 // trim()主要是去首尾空格,这里是判断包名是否包含空格
  if (name.trim() !== name) {

    errors.push('name cannot contain leading or trailing spaces')

  }


  // No funny business
  // 不能是node_modules和favicon.ico

  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
  // 利用builtins判断报名是否是nodejs内置模块名
  builtins({ version: '*' }).forEach(function (builtin) {

    if (name.toLowerCase() === builtin) {

      warnings.push(builtin + ' is a core module name')

    }

  })

  
// 长度不能超过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 ("~\'!()*")')

  }

  
  // 利用把字符串作为URI 组件进行编码判断作用域包名

  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]

      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-npm-package-name主要是对包名的合法性进行检测,若非法就返回对应的错误,代码量少且易读性强,很适合刚开始阅读源码的童鞋。通过对源码的解读也了解到可以利用builtins读取nodejs的内置模块,同时对字符串的匹配正则表达式是不二之选,正则表达式可能可读性不想,因此文章的最后给大家推荐一个正则可视化工具