源码阅读——validate-npm-package-name

2023-05-16

文章目录

  • 前言
  • 一、源码阅读工具
  • 二、阅读源码
    • 1. 目录结构
    • 2.package.json
    • 3.index.js
  • 三、使用该包
    • 1. vue-cli中使用
    • 2. create-react-app 中使用
  • 总结


前言

validate-npm-package-name 是一个用于检测npm包是否符合标准的包,被很多脚手架创建工具使用:vue-clicreate-react-app
源码地址:https://github.com/npm/validate-npm-package-name


一、源码阅读工具

  1. 本地编辑器从github上下载源码
  2. 在线网页版编辑器:github.dev(在网页端使用 VS Code 编辑 GitHub 项目——github.dev)
    这里介绍下,github.dev是github官方提供的在线vscode编辑器,将github访问URL中的com直接替换成dev,即可在网页版vscode中打开仓库代码阅读编辑。
    在这里插入图片描述

二、阅读源码

1. 目录结构

2.package.json

  • 入口文件:index.js
  • builtins 依赖包会列出node所有内置模块
{
  "name": "validate-npm-package-name",
  "version": "3.0.0",
  "description": "Give me a string and I'll tell you if it's a valid npm package name",
  "main": "index.js",   // 入口文件
  "directories": {
    "test": "test"
  },
  "dependencies": {    // 生产依赖包
    "builtins": "^1.0.3"  
  },
  "devDependencies": { // 开发依赖包
    "standard": "^8.6.0",
    "tap": "^10.0.0"
  },
  ...
}

打印 builtins 模块内容:(全是node内置模块)

[
  'assert',         'buffer',   'child_process',
  'cluster',        'console',  'constants',
  'crypto',         'dgram',    'dns',
  'domain',         'events',   'fs',
  'http',           'https',    'module',
  'net',            'os',       'path',
  'process',        'punycode', 'querystring',
  'readline',       'repl',     'stream',
  'string_decoder', 'timers',   'tls',
  'tty',            'url',      'util',
  'v8',             'vm',       'zlib'
]

3.index.js

源码如下:

'use strict'

var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
var builtins = require('builtins')  // node中所有的内置模块列表,参考上面
var blacklist = [   // 黑名单列表,数组中的不允许设置为包名,会抛出error
  'node_modules',
  'favicon.ico'
]
// 全局导出validate方法
var validate = module.exports = function (name) {

  // 警告:用于表示过去package name允许、如今不允许的兼容error
  var warnings = []
  // 存储不符号合格的包名的规则
  var errors = []
  
  // 校验包名为null,结束并返回valid error结果
  if (name === null) {
    errors.push('name cannot be null')
    return done(warnings, errors)
  }
  // 包名为undefined,结束并返回valid error结果
  if (name === undefined) {
    errors.push('name cannot be undefined')
    return done(warnings, errors)
  }
  // 包名为非字符串,结束并返回valid error结果
  if (typeof name !== 'string') {
    errors.push('name must be a string')
    return done(warnings, errors)
  }
  // 包名字符串长度为0,抛出error
  if (!name.length) {
    errors.push('name length must be greater than zero')
  }
  // 包名以.开头,抛出error
  if (name.match(/^\./)) {
    errors.push('name cannot start with a period')
  }
  // 包名为_开头,抛出error
  if (name.match(/^_/)) {
    errors.push('name cannot start with an underscore')
  }
  // 包名字符串前后有空格,抛出error
  if (name.trim() !== name) {
    errors.push('name cannot contain leading or trailing spaces')
  }

  // 包名是否包含在上面定义的blacklist黑名单列表中,若是则不被允许,抛出error
  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
  // 包名是否和node内置模块名一致,若小写一致,则抛出warnings
  builtins.forEach(function (builtin) {
    if (name.toLowerCase() === builtin) {
      warnings.push(builtin + ' is a core module name')
    }
  })

  // 包名长度超过214,抛出warnings
  if (name.length > 214) {
    warnings.push('name can no longer contain more than 214 characters')
  }

  // 包名中是否有大写,若有大写字母抛出warnings(eg:mIxeD CaSe nAMEs)
  if (name.toLowerCase() !== name) {
    warnings.push('name can no longer contain capital letters')
  }
  // 包名是否特殊字符:~'!()*,若包含抛出warning
  // '@babel/core'.split('/').slice(-1)[0] --> 'core'
  if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) {
    warnings.push('name can no longer contain special characters ("~\'!()*")')
  }
  
  // 经encodeURIComponent编码后不相等, 包含non-url-safe字符
  // 关于encodeURIComponent不转义哪些字符: A-Z a-z 0-9 - _ . ! ~ * ' ( )
  if (encodeURIComponent(name) !== name) {
    // 如上所示:var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
    // 这可能是一个局部包,例如@user/package
    var nameMatch = name.match(scopedPackagePattern)
    if (nameMatch) {
      var user = nameMatch[1]
      var pkg = nameMatch[2]
      // user 和 package部分的包名 encodeURIComponent 结果都相同,都能被转义,则正常返回valid结果
      if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
        return done(warnings, errors)
      }
    }
    // 若不符合scopedPackagePattern规则,则抛出error
    errors.push('name can only contain URL-friendly characters')
  }
  // 最终结束,返回valid结果
  return done(warnings, errors)
}

validate.scopedPackagePattern = scopedPackagePattern
// 输出valid结果:reuslt
var done = function (warnings, errors) {
 // 对象格式
  var result = {
    // 一般用该属性来判断一个包名是否合法: 有errors或者warnings抛出
    validForNewPackages: errors.length === 0 && warnings.length === 0,
    // 兼容最开始node package name带来的遗留问题,那个时候有些包名不规范:有error抛出
    validForOldPackages: errors.length === 0,
    warnings: warnings,
    errors: errors
  }
  // 若warning,errors数组length=0,则从reuslt对象移除对应属性
  if (!result.warnings.length) delete result.warnings
  if (!result.errors.length) delete result.errors
  return result
}

三、使用该包

1. vue-cli中使用

vue create xxx 新建项目时,就用到了该包
源码地址:https://github1s.com/vuejs/vue-cli/blob/HEAD/packages/@vue/cli/lib/create.js#L8-L30

// @vue/cli/lib/create.js
const validateProjectName = require('validate-npm-package-name')

async function create (projectName, options) {

  const cwd = options.cwd || process.cwd()
  const inCurrent = projectName === '.'
  const name = inCurrent ? path.relative('../', cwd) : projectName

  const result = validateProjectName(name)  // 这里校验
  // 如上源码所示
  //var result = {
  //  validForNewPackages: errors.length === 0 && warnings.length === 0,
  //  validForOldPackages: errors.length === 0,
  //  warnings: warnings,
  //  errors: errors
  //}
  if (!result.validForNewPackages) { // 说明有抛出error或者warning
    console.error(chalk.red(`Invalid project name: "${name}"`))
    result.errors && result.errors.forEach(err => { // 打印所有的error
      console.error(chalk.red.dim('Error: ' + err))
    })
    result.warnings && result.warnings.forEach(warn => { // 打印所有的warning
      console.error(chalk.red.dim('Warning: ' + warn))
    })
    exit(1)
  }
  ...
}

2. create-react-app 中使用

源码地址:https://github1s.com/facebook/create-react-app/blob/04482a6c2c6639c19deb330c48e4fa5573a1654e/packages/create-react-app/createReactApp.js

const validateProjectName = require('validate-npm-package-name');

// 创建脚手架方法
function createApp(name, verbose, version, template, useYarn, usePnp) {
  ...
  const root = path.resolve(name);
  // path.basename() 方法返回 path 的最后一部分
  const appName = path.basename(root);

  checkAppName(appName);
  ....
}

// 检测包名方法
function checkAppName(appName) {
  const validationResult = validateProjectName(appName);  // 这里
  // 如上源码所示
  //var result = {
  //  validForNewPackages: errors.length === 0 && warnings.length === 0,
  //  validForOldPackages: errors.length === 0,
  //  warnings: warnings,
  //  errors: errors
  //}
  if (!validationResult.validForNewPackages) {  // 说明有error或者warning抛出
    console.error(   // 打印错误
      ....
    );
    [
      ...(validationResult.errors || []),
      ...(validationResult.warnings || []),
    ].forEach(error => {
      console.error(chalk.red(`  * ${error}`));  // 打印error和wanring
    });
    console.error(chalk.red('\nPlease choose a different project name.'));
    // 退出进程
    process.exit(1);
  }
  ...
}

总结

validate-npm-package-name 源码

  • 共100行左右
  • 阅读比较简单,就是一些判断。涉及较多的正则判断
  • 通过const validateProjectName = require('validate-npm-package-name')使用
  • 使用场景:用脚手架例如vue-cli创建项目时,能够校验创建的包名是否符合规范
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

源码阅读——validate-npm-package-name 的相关文章

  • nodejs“npm 错误!代码 SELF_SIGNED_CERT_IN_CHAIN”

    我是 Nodejs 和 npm 的新手 我正在尝试安装 log4js 这是安装命令 npm install log4js 我从 Windows 命令行运行此命令 在标记旋转一段时间后 出现以下错误 npm ERR Windows NT 6
  • 如何/在哪里发布 Python 包

    如果一个人创建了一个有用的 Python 包 那么如何 在哪里发布 宣传它以供其他人使用 我已经把它放到了 github 上 但几周后谷歌也没有找到它 包装整洁完整 我制作它供我个人使用 不与其他人分享将是一种耻辱 这是 PyPI 指南 h
  • Material UI Icons npm 安装问题:无法解析依赖树

    问题已解决 问题末尾的解决方案 我试图安装材质 UI 图标 https mui com components material icons 在我使用 npm 的 Netflix Clone React js 项目中 但出现以下错误 PS D
  • 使用 cabal new-install 重新安装相同版本的软件包

    我正在开发 Haskell 包 我还没有上传到Hackage 版本号是0 1 0 0 我正在使用新风格的 Cabal 命令 为了在我处理包的同时测试它 使库可用于测试项目 我运行cabal new install lib构建包后 然而 我注
  • 自动 npm install --legacy-peer-deps 用于单个依赖项

    假设我有一个package json像这样 name my app version 0 1 0 dependencies aws sdk client s3 3 21 0 testing library react 11 2 5 axios
  • 尝试从 npm 安装 Angular 2 Material - 源文本中无法识别的标记

    我从 PowerShell 尝试以下命令 以管理员身份 npm install angular2 material 并得到错误 Unrecognized token in source text At line 1 char 13 npm
  • 为什么 NPM 7.17 不安装我的对等依赖项

    我有一个main依赖于包的项目package1在本地文件存储中 package1同行取决于快递 name package1 version 1 0 0 description main index js scripts test echo
  • 选择性罐包装

    我有一个小program jar 它使用了巨大的library jar 的一小部分 有没有一种工具可以将多个 jar 重新打包成一个 以便它可以独立运行并且尽可能小 Update 大小事项 有proguard http proguard s
  • npm install 冻结并显示 IdealTree:chatting: sill IdealTree buildDeps

    当我安装任何东西时 npm 冻结了 即使删除 package lock json 也没有任何变化 这里有一个类似的问题 https stackoverflow com questions 50522376 npm install hangs
  • npm install [package] 删除软件包

    npm install package 命令正在删除其他软件包 我必须运行 npm install 命令来重新安装它们 这是一项繁琐的任务 所有包都记录在 package json 和 package lock json 文件中 如果我 n
  • Python包不安装子模块

    我在 dev 分支中创建了一个具有以下结构的包 在验证包安装正确之前不会合并到 main mypackage init py setup py requirements txt module py subpackage one init p
  • 排除 npm 包中的测试代码?

    The 开发依赖 https docs npmjs com files package json devdependenciesnpm 的 package json 文档的部分说要在那里列出您的测试依赖项 以便您的包的用户不必拉取额外的依赖
  • npm ENOSELF 错误 - 在其 gh-pages 分支中安装包

    我想使用 Lib 本身为 GitHub 上的 JS Lib 编写一些文档 我创建了一个名为的空分支gh pages并开始构建文档页面 现在我需要使用我正在记录的库编写一些 Javascript 因为谁不会使用自己的库 我尝试通过安装它npm
  • 使用 npm API 检索带有版本的私有 npm 包列表,可能吗?

    我在 npmjs org 上有一个私有 npm 注册表 其中包含几个私有 npm 包 我们正在转向内部私人 npm 注册表 verdaccio 长话短说 使用我们的 AWS 基础设施 verdaccio 服务器可能会因多种原因而被重建 主要
  • R CMD 检查警告:在文档对象中使用的函数/方法...但不在代码中

    我正在写一个包 但一个持久的R CMD check警告阻止我完成包裹并将其发布到 CRAN 我用roxygen2对于内联文档 尽管这可能不是错误的根本原因 如果您知道如何删除此警告 我很可能可以找到一种方法来使用roxygen2 如何删除警
  • npm install 始终使用神器注册表并忽略 .npmrc 文件

    我的 npmrc 文件以前包含registry https company jfrog io如此一来npm install使用了该注册表 现在我想在我的个人项目中使用公共注册表 但即使我删除了 npmrc 文件 甚至在包含公共注册表的项目根
  • 发布到 npm 时出现问题

    所以我尝试使用 React 构建一个开源项目并将其推送到 npm 问题是 我的组件虽然在测试环境中运行良好 安装到其他组件 但当我将其发布到 npm 并下载包并尝试访问它时 它给了我一个错误 这是代码的一个小示例 import React
  • npm install 的 --save 选项是什么?

    我看到一些教程 其中命令是 npm install save 什么是 save选项是什么意思 更新 npm 5 As of npm 5 0 0 http blog npmjs org post 161081169345 v500 安装的模块
  • Laravel Homestead 中 npm 安装错误有解决方案吗?

    Windows 10 家园 虚拟盒6 0 8 流浪者2 2 5 节点 v12 5 0 npm v6 10 1 我想做的就是在新安装的 Laravel 应用程序中执行 npm install 命令 但我不断收到错误 经过两天的谷歌搜索并尝试了
  • NPM 无法在 Windows 上安装“truffle”

    我正在尝试使用 npm 安装 truffle 但我不熟悉 NodeJS 并且不明白为什么 npm 不会安装它 我尝试npm install g truffle在具有管理员权限的 Powershell 中 经过几行输出后 我收到以下错误消息块

随机推荐

  • 基于OpenLTE的4G移动通信网络实验指导书

    基于本人本科毕业设计的成果 xff0c 设计了一套基于开源SDR项目 OpenLTE的实验指导书 xff0c 可以指引读者通过平台源码 平台提供的实验和结合实验对3GPP规范的解读分析来更直观 更多元立体的学习无线通信技术 xff0c 而不
  • 一行代码实现数组中数据频次值

    问题 xff1a 一行代码实现统计数组中每个name出现的次数 数组示例如下 xff1a 期望结果 xff1a 39 哈哈 39 2 39 哈哈1 39 1 39 哈哈2 39 2 span class token keyword var
  • mac bash_profile报错导致所有命令失效解决办法

    项目场景 xff1a 搭建flutter环境时 xff0c 在mac下需要配置环境变量 问题描述 xff1a 配置环境变量 xff0c 需要修改 bash profile文件 xff0c 修改文件保存退出后 发现文件有报错 xff0c 导致
  • 我理解的“大前端”

    前言 随着业务场景越来越复杂 xff0c 前端技术也越来越丰富 xff0c 这几年也迎来爆发期 xff0c 大前端 的概念逐渐涌现 本图根据本人理解整理 xff0c 如有不足 xff0c 欢迎指正 xff0c 感谢 一 终端 PC端 PC端
  • 前端获取用户地理定位的几种方式(Geolocation API,微信,腾讯地图)

    文章目录 前言一 Geolocation API二 微信 SDK1 引入jssdk2 获取签名 xff0c 注入配置3 调用JS SDK api 获取位置 三 第三方服务 xff08 腾讯地图服务 xff09 1 引入js文件2 获取定位
  • H5 软键盘自动搜索功能

    业务场景 xff1a 通常APP中的顶部搜索栏 xff0c 都配一个搜索按钮 同时输入文字软键盘弹起 xff0c 回车键自动变成搜索键 xff0c 点击软键盘中的搜索能进行搜索功能 xff0c 如下图taobao所示 xff1a 思考 软键
  • 基于vue-cli3构建自己的UI库

    文章目录 前言一 创建项目二 编写组件1 button组件2 引入字体图标icon文件3 引入Button组件看效果 三 修改目录结构1 packages文件夹2 打包修改2 demo展示 四 将UI库部署到npm上五 项目使用自己的UI库
  • vue3源码分析(三)—— 响应式系统(reactivity)

    系列文章目录 目录分析初始化流程响应式系统shared工具函数 文章目录 系列文章目录前言一 定义响应式数据1 reactive target 2 createReactiveObject2 1 入参2 2 响应式创建过程2 3 proxy
  • vue3源码分析(四)—— shared工具函数

    系列文章目录 目录分析初始化流程响应式系统shared工具函数 文章目录 系列文章目录前言1 数组中移除某元素2 字符串转数字3 转为字符串4 判定值是否发生改变5 判定数据类型5 1 数组5 2 Map5 3 Set5 4 Date5 5
  • 如何将两个rosbag包合并或者提取rosbag包中某些话题到一个rosbag里

    代码叫做merge bag py 运行的时候 python merge bag py v 1028msf bag msf bag vinReNoOutlier bag 就把msf bag和vinReNoOutlier bag完全合并在一起了
  • 解决 vscode中js变量 文件不能自动跳转问题~

    项目场景 xff1a 在项目开发中 xff0c 为了便于理解js代码逻辑和调试 xff0c 通常会使用快捷键自动定位到变量原始定义的文件位置 mac中快捷键 xff1a command 43 鼠标点击 但在vue项目开发中 xff0c 发现
  • vue3源码分析(二)—— 初始化流程

    系列文章目录 目录分析初始化流程响应式系统shared工具函数 文章目录 系列文章目录前言一 createApp在项目中的使用二 createApp源码追溯1 创建app实例1 1 ensureRenderer1 2 ensureRende
  • JS基础 ——解释执行

    文章目录 前言一 词法分析二 预编译创建全局作用域GO对象创建局部作用域AO对象 三 代码执行总结 前言 大家都知道 xff0c JS是一种不需要编译的解释型语言 但其实在浏览器执行JS代码前 xff0c 也有一个词法分析和预编译过程 xf
  • vue 项目中引入字体文件的正确方式~

    文章目录 前言一 开发中需要什么样的字体1 字体图标2 特殊字体 二 项目中引入字体文件1 字体文件2 css文件3 项目使用该字体 总结 前言 在UI设计稿中 xff0c 可能会有一些特殊字体 xff0c 或者是一些字体图标 对于特殊字体
  • vue3 使用 swiper轮播库

    文章目录 前言一 Swiper引入方式1 HTML标签引入方式2 CommonJs引入方式3 ES引入方式 xff08 采用 xff09 二 使用Swiper总结 前言 轮播图在前端开发中 xff0c 是常见需求 而Swiper库是最受前端
  • vue-cli2 老项目webpack3升级webpack5过程总结

    文章目录 背景一 webpack5环境要求二 升级步骤1 脚手架webpack cli2 升级webpack包3 更新webpack相关插件3 1 不推荐或被移除的插件3 2 升级babel到7版本3 3 更新插件 4 修改配置4 1 新增
  • 前端下载文件

    文章目录 前言二进制流前端核心实现下载功能有 xff1a 一 a标签 43 download属性二 window open url 34 blank 34 三 form表单四 接口请求 43 blob 43 a标签 43 download属
  • 前端JS 云打印 LODOP实践

    文章目录 前言一 Lodop是什么 xff1f 二 如何使用Lodop1 下载打印插件2 配置打印机3 html中植入打印控件4 调用Lodop对应的JS相关方法接口实现打印功能 三 Lodop主要方法接口三 注意点总结 前言 一般B S系
  • axios源码——工具函数utils.js

    文章目录 前言一 工具函数所在目录二 判定数据类型的函数1 isArray 判定数组 2 isString 判定字符串 3 isNumber 判定数值 4 isObject 判定对象 5 isPlainObject 判定纯对象 6 isUn
  • 源码阅读——validate-npm-package-name

    文章目录 前言一 源码阅读工具二 阅读源码1 目录结构2 package json3 index js 三 使用该包1 vue cli中使用2 create react app 中使用 总结 前言 validate npm package