美文网首页
自定义NPM包

自定义NPM包

作者: 说叁两事 | 来源:发表于2021-06-07 14:39 被阅读0次

    环境初始化

    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

    相关文章

      网友评论

          本文标题:自定义NPM包

          本文链接:https://www.haomeiwen.com/subject/wbcqeltx.html