自定义NPM包

环境初始化

  1. mkdir npm-log
  2. cd npm-log
  3. npm init -y

入口文件

  • 自定义依赖模块:
    • 模块是在 package.json 里通过 main 字段定义这个包对外暴露的入口;
      • 模块起源于node,语法默认支持commonjs规范
      • 模块若使用ES Module语法书写,通过 module 字段定义入口(需要打包工具配合使用)
  • 自定义命令行:
    • 如果是提供命令行工具,则需要通过 pkg#bin 字段来定义暴露的命令名称与实际执行的文件

这篇文章讲述自定义依赖模块的声明,后续会有专门篇幅进行自定义命令行的讲述

声明示例

  1. 创建lib/index.js
    const Noop = () => {}
    class Logmi {
       errorHandler = Noop
       successHandler = Noop
    
       static create (options) {
          return new Logmi(options)
       }
       constructor (options = {}) {
          console.log('---------create------', options)
       }
       log (msg, level) {
          console.log('log: start', msg, level)
    
       }
    }
    module.exports = Logmi
    
  2. 更新package.json
     "main": "lib/index.js"
    

开发环境

  • 自动日志
  • 版本更新
  1. husky
     npm install husky --save-dev
     npx husky install
    
    • 配置run-script:安装依赖后自动启动Git hooks
    "prepare": "husky install"
    
    • 追加测试钩子
      # Unix系统可用
      npx husky add .husky/pre-commit "npm run test"
      # Windows通过以下命令创建文件(引号在windows下不是正确的语法)
      npx husky add .husky/pre-commit
      # Windows去新建的文件中指定命令
      #!/bin/sh
      . "$(dirname "$0")/_/husky.sh"
    
      npm run test
    
  2. commitlint
    • commitlint提交信息校验工具
    • 需要和校验规范配合使用,官网默认规范@commitlint/config-conventional —— 可自定义。
    • commitlint绑定@commitlint/config-conventional
     npm i -D commitlint @commitlint/config-conventional
    # Unix
    echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
    # Windows
    echo module.exports = {extends: ['@commitlint/config-conventional']} > commitlint.config.js
    
    • 配置Git hook:在提交commit msg进行参数校验 —— 写在run-script中无效
       # Unix系统可用
       npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
       # Windows通过以下命令创建文件(引号在windows下不是正确的语法)
       npx husky add .husky/commit-msg
       # Windows去新建的文件中指定命令
       #!/bin/sh
       . "$(dirname "$0")/_/husky.sh"
    
       npx --no-install commitlint --edit $1
    
  3. standard-version
     npm i --save-dev standard-version
    
    • 配置run-script:发布前自动升级版本号 + 生成日志
    "prepublishOnly": "standard-version"
    

注意:这里不要使用prepublish钩子,该钩子在npm i时运行,而不是npm publish时运行。

调试

  1. 进入本地NPM
  • npm link创建软链接到全局node环境中
  1. 进入依赖包的项目A中
  • npm link <packageName>建立软链接依赖
  1. 在项目A需要调用的文件中
# 调用
 import Logmi from "log";
 const LogmiInstance = Logmi.create({
    url: 'http://localhost:3000'
 })
 LogmiInstance.log(`paste: ${JSON.stringify(paste)}`, 1)
  1. 启动项目A,即可调试

开发

NPM包是commonJS语法,使用require(),而非import...from...引入依赖。

  • 实例化参数

    • 工厂函数
    • 参数默认值
      • const Noop = () => {}:空语句
    • 传参校验
      • 参数数据实体类型
      • 校验警告
    • 参数合并
  • DTO

    • 校验DTO组成结构参数ParamChecker
    • 统一结构ContentWrapper
  • 配置信息统一分类处理

module.exports = {
  EXCEED_TRY_TIMES: 'Exceed try times',
}

打包发布

打包需要引入webpack,这里的package.json修改入口文件:

"main": "dist/logmi.js",
"module": "lib/index.js",

其中,main是暴露打包后的入口文件;
modulewebpack环境下暴露的入口文件;

package.json

{
  "name": "log",
  "version": "1.0.0",
  "description": "",
  "main": "dist/logmi.js",
  "module": "lib/index.js",
  "scripts": {
    "prepare": "husky install",
    "build": "cross-env NODE_ENV=production webpack --config webpack.config.js --mode=production",
    "test": "echo \"npm run test\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@babel/preset-env": "^7.14.4",
    "@commitlint/cli": "^12.1.4",
    "@commitlint/config-conventional": "^12.1.4",
    "babel-loader": "^8.2.2",
    "cross-env": "^7.0.3",
    "husky": "^6.0.0",
    "standard-version": "^9.3.0",
    "terser-webpack-plugin": "^5.1.3",
    "webpack": "^5.38.1",
    "webpack-cli": "^4.7.0"
  },
  "dependencies": {
    "@babel/polyfill": "^7.12.1",
    "idb-managed": "^1.0.9"
  },
  "bundledDependencies": [
    "idb-managed"
  ]
}

pkg#main

作为第三方依赖包时,包的入口执行文件。

如果没有指定,默认为root目录下的index.js

pkg#bin

作为命令行工具时,包的入口执行文件

安装该包时,node会自动创建硬链接该包到全局执行环境。

  • String:单执行文件
  • Map:多执行文件

pkg#module

非官方配置rollupwebpack等打包工具提供的配置项。

指向的应该是一个基于ES6模块规范书写的模块。

pkg#private

设置"private": truenpm拒绝发布该包。

pkg#workspaces

结合monorepo的概念,创建工作区。

pkg#files

安装该包时,目录中包含在pkg.files中指定的文件结构。

默认包含:

package.json
README
CHANGES / CHANGELOG / HISTORY
LICENSE / LICENCE
NOTICE
The file in the "main" field

pkg#bundledDependencies

通过fptscp等工具传输该包时,需要将该包的依赖打包在一起。

  • bundledDependencies中指定依赖包的列表
    • 只需要指定包名,版本会在dependencies查找
  • 通过npm pack打包
  • 通过传输工具传输打好的*.tgz
  • 通过npm i *.tgz安装该包及其依赖

Note: 定义在bundledDependencies列表内的依赖,安装NPM包时,该依赖会嵌套在包文件下,而不会提升到node_modules目录的根下

pkg#peerDependencies

  • 表明该包对主包/主工具库的兼容性,而不是依赖性,这种关系称之为插件。
    • 主包一般会对插件暴漏的接口指定标准

peerDependencies指定的包@版本号表明,我们的包需要在指定包的环境下执行,需要一同安装。

打包

安装插件

npm i -D webpack-cli webpack cross-env terser-webpack-plugin
npm install --save-dev @babel/core babel-loader @babel/preset-env

npm install --save @babel/polyfill

配置babel.config.json

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.5"
      }
    ]
  ]
}

配置run-script

...
  "build": "cross-env NODE_ENV=production webpack --config webpack.config.js --mode=production",
...

webpack.config.js

const path = require('path')
const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin")

const resolve = dir => path.join(__dirname, '.', dir)

const isProd = process.env.NODE_ENV === 'production'

module.exports = {
  entry: {
    logmi: './lib/index.js'
  },
  output: {
    path: resolve('dist'), // 输出目录
    filename: '[name].js', // 输出文件
    libraryTarget: 'umd', // 采用通用模块定义
    library: 'logmi', // 库名称
    libraryExport: 'default', // 兼容 ES6(ES2015) 的模块系统、CommonJS 和 AMD 模块规范
    globalObject: 'this' // 兼容node和浏览器运行,避免window is not undefined情况
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  },
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  }
}

配置可见目录结构

...
  "files": [
    "dist/"
  ]
...

发布

若使用nrm维护多个npm源,需要切换到发布的目标源。或者通过pkg#publishConfig.npmrc指定目标源。

npm为例:

  1. 切换到npm
  nrm use npm
  1. NPM官网注册账号

  2. 命令行中登录用户

  npm login
  1. 在项目目录下发布包
  npm publish

补充知识

  • Peer Dependencies

    The peerDependencies configuration was originally designed to address the problem of NPM packages that were ‘plugins’ for other frameworks.

删除CHangeLog

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容