跟着川哥一起学 validate-npm-package-name

1,683 阅读1分钟

validate-npm-package-name

介绍

包如其名 validate npm package name,校验 npm 包的名字是否符合规范

规范来自哪里?

你会发现这个包是属于 npm 组织下的, 所以这其实就是官方规范。

依赖

dependencies

  1. builtins

根据当前的 node 环境,返回内置模块里面的核心模块, 注意:不是全部的内置模块

项目里的版本属于旧版是一个 json 文件 (意思是后出来的核心模块不管了? 建议官方升级)

devDependencies

  1. standard

代码检查以及格式化工具,开箱即用

  1. tap

一款 node 的 测试框架

源码

'use strict';

// 正则啊 学了忘 忘了学 太难了
// ?: 非捕获分组  只匹配, 不捕获,节省内存
// ^ 不放在开头表示非的意思  [^/1234] 表示不包含 '/' | '1' | '2' | '3' | '4'
// scoped 可有可无
var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$');
var builtins = require('builtins');

// 黑名单
var blacklist = ['node_modules', 'favicon.ico'];

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

  if (name === null) {
    errors.push('name cannot be null');
    return done(warnings, errors);
  }
  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');
  }

  // 这块 有 issues 提出质疑 
  // 既然前后空格不给过,为啥中间带空格给过呢(空格被认为是 url 不安全的), for example: 'user name'
  // 显然作者没有同意这个提案...
  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
  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.
  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 安全的字符串 encode 以后是不会变的
  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);
      }
    }
    // 所有不符合上面那个这正则的以及在 user 和 pkg 上面玩骚操作的都被报错了。
    errors.push('name can only contain URL-friendly characters');
  }

  return done(warnings, errors);
});

// 希望有评论区的小伙伴能指定下我这个正则挂出去的意思是什么...
validate.scopedPackagePattern = scopedPackagePattern;

var done = function (warnings, errors) {
  var result = {
    // 一般用该属性来判断一个包名是否合法
    // 从这看来 warnings 是后加的
    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;
};

校验规则

  1. 包名必须是字符串(强制规则)
  2. 包名不能为空(强制规则)
  3. 包名不能以 .,_开头, 前后不能有空格, 不能过长(<=214 个字符), 必须为小写, 包名(不包括组织名)不支持特殊符号~'!()*
  4. 包名不能和内置的黑名单一致(目前黑名单只有 node_modules, favicon.ico)
  5. 包名不能和 node 内置的核心模块一致
  6. 包名应该只含有 URL-friendly 字符

拓展知识

1. 什么是 URL-friendly

真是一种抽象的概念。(可以寻思下 friendly 是对谁来说的, 对人,对爬虫?)

on-page-seo-basics-urls

2. 关于 scripts 里面 的 TAP_FLAGS 是什么?

{
  "cov:test": "TAP_FLAGS='--cov' npm run test:code",
  "test:code": "tap ${TAP_FLAGS:-'--'} test/*.js"
}

这边其实是 shell 的 语法

${TAP_FLAGS:-'--'} # 相当于 `${TAP_FLAGS || '--'}`

还是我格局小了。

--cov 干吗的?

node-tap/cli

--cov --coverage

Capture coverage information using 'nyc' This is enabled by default.

If a COVERALLS_REPO_TOKEN environment variable is set, then coverage is sent to the coveralls.io service.

3. npm publish 过程中什么时候用到这个包的呢

第二步就用上了

实际处理用的包

具体过程的文章先欠着吧...

最后

本人隶属于川哥源码阅读小分队第三分队,川哥 yyds!

如何参与,点击就看到了呀