每天认识一个npm包,开源世界我闯荡!

1,069 阅读13分钟

这里主要收集一下平时在项目中或者看开源的时候,遇到的一些npm包,特此记录一下, 原本的分类有些不够准确,故就取消了,直接按照顺序来了, 在前端工程化中,这些包肯定有一款适合你.

ts 相关的

typescript 的tsc

typescript ts的编译 以及也可以进行类型文件的生成 简单demo, 首先你新建tsconfig.json

{
  "compilerOptions": {
    "outDir": "esm", // 输出目录
    "module": "es6", // 当前代码语法
    
    "jsx": "react-jsx",
    "allowUnreachableCode": false, // 报告执行不到的代码错误
    "allowUnusedLabels": false, // 不报告未使用的标签错误
    "alwaysStrict": false, // 以严格模式解析并为每个源文件生成 "use strict"语句
    "strictNullChecks": false, // 当尝试在可能为null或undefined的变量上访问属性时,报告错误 (一开始没开,现在开启导致很多错误,下次统一修复)
    "baseUrl": ".", // 工作根目录,会影响vscode import生成的路径
    "preserveConstEnums": false, // true 使用 const enum 产生内联成员,会导致没有引入的const enum也会生成代码
    "isolatedModules": false, // 将模块视为独立的,不会被其他模块所引用
    "experimentalDecorators": true, // 启用实验性的ES装饰器
    "moduleResolution": "Node", // 引入package.json不会报红
    "sourceMap": false, // 生产环境禁止开启
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "pretty": true,
    "noImplicitAny": false, // 是否默认禁用 any
    "removeComments": true, // 是否移除注释
    "forceConsistentCasingInFileNames": true, //禁止对同一个文件的不一致的引用。
    "paths": {
      "@heimdallr-sdk/*": ["packages/*/src"]
    },
    "types": [],
    "target": "ES5", // 编译成什么版本
    "declaration": true, // 是否自动创建类型声明文件
    // "declarationMap": true,
    // "declarationDir": "./dist/packages/", // 类型声明文件的输出目录
    "typeRoots": [
      // 指定某个文件夹的声明文件
      "node_modules/@types"
    ],
    "allowJs": false, // 不允许编译javascript文件。
    "lib": [
      // 编译过程中需要引入的库文件的列表
      "es6",
      "dom",
      "es2017"
    ]
  },
  "include": ["packages"],
  "exclude": ["node_modules"]
}

然后在package.json中新增命令


"scripts": {
    "build:esm": "rimraf esm && tsc -p tsconfig.json"
  },

Tsup

Tsup 可以快速打包 typescript 库,无需任何配置,并且基于esbuild进行打包,打包 ts 文件速度毫秒级,方便又高效

ts-morph

可以生成ts,类型声明, 自动生成文档等功能, 具体可以看官方文档 来完成类型的生成和导出 ts-morph.com/setup/

tsup 快速打包 ts 库

除了使用typescript的tsc。还可以试试这个更快的 tsup.egoist.dev/#typescript…

esno 用esbuild执行ts脚本

esno 是基于 esbuild 的 TS/ESNext node 运行时。该库会针对不同的模块化标准,采用不同的方案:

  • esno - Node in CJS mode - by esbuild-register
  • esmo - Node in ESM mode - by esbuild-node-loader

    esno index.ts
    esmo index.ts

    // 示例
    "script": {
      "test": "node test.js", // 运行js脚本
      "test": "esno test.ts", // 使用esbuild运行ts脚本
      
    }

pinyin-match 使用拼音/汉字快速检索目标

能够使用拼音快速检索目标。查询和匹配字符很有用

  1. 简体版27KB (gzip ≈ 19KB),繁体版86KB (gzip ≈ 60KB)
  2. 支持多音字、繁体字、拼音首字母匹配,具备分词功能
  3. 返回位置信息,可用于高亮匹配字符
  4. 在长多音字串下依然有高性能
let test = '123曾经沧海难为水除却巫山不是云'

PinyinMatch.match(test, '23曾'); // [1, 3]

PinyinMatch.match(test, 'cjc') // [3, 5]

PinyinMatch.match(test, 'cengjingcanghai') // [3, 6]

PinyinMatch.match(test, 'cengjingcangha') // [3, 6]

PinyinMatch.match(test, 'sdjkelwqf') // false

PinyinMatch.match(test, 'zengji ng cang') // [3, 5]

PinyinMatch.match(test, 'zengji ng cangsdjfkl') // false

magic-string 操作字符串和生成源映射的库

magic-string 是一个用于操作字符串和生成源映射的小而快的库;

其实它最主要的功能就是对一些源代码和庞大的 AST 字符串做轻量级字符串的替换;

vite 工具源码和 @vue/compiler-sfc 中大量使用;

使用:

typescript
复制代码
import MagicString from 'magic-string';
const s = new MagicString('problems = 99');

// 替换 problems -> answer
s.overwrite(0, 8, 'answer')
s.toString() // 'answer = 99'

// 生成 sourcemap
var map = s.generateMap({
  source: 'source.js',
  file: 'converted.js.map',
  includeContent: true
})

gh-pages 将指定文件放在指定分支的包

是一个帮助我们将前端打包文件存放在指定分支 gh-pagesnpm 包,简单来说, 就是可以将我们的指定文件存放到指定仓库的指定分支上面, 后期结合githubActions可以做自动化部署等操作

npm install gh-pages --save-dev

// 使用
var ghpages = require('gh-pages');
// 文件中使用             
ghpages.publish(dir, options, callback);
dir: 要部署的文件夹
options: 配置项, 见下面示例
callback: 回调函数
// 示例, 参数解释
ghpages.publish('dist', {
  src: '**/*', // 对dist里面的文件进行过滤,符合条件的才会被发布, 类似其他工具的includes
  branch: 'master', // 指定分支
  repo: 'https://example.com/other/repo.git', // 指定仓库地址
  dest: 'master/project', // 目标分支下的目标文件夹, 默认是放在分支下的根目录
  dotfiles: true, // 是否包含类似于.env这样的文件, 默认不包含 false
  add: false, // 默认情况下,在从配置中添加文件之前,将删除目标分支中的现有文件。如果您希望任务添加新文件,但保留现有文件不变,请在选项中进行设置
  remote: 'origin', // 默认值为origin,但可以将其配置为推送到任何远程
  tag: '', // 如果设置了,将会为指定分支创建一个tag
  message: 'Auto-generated commit', // 所提交的提交消息
  user: {
    name: 'Joe Code',
    email: 'coder@example.com'
  }, // 默认不用配置, 你本地的git环境配置了相关的用户信息
  remove: "*.json", // 配置提交的时候要删除的文件, 如果配置了add,则此配置项忽略
  silent: true, //如果你得repo需要动态拼接,则需要把这个配置项打开
  beforeAdd: () => {}, // 发布之前执行的自定义回调, 是个函数
  
});
// 在命令行中使用, 下面的意思是 将dist文件 部署到 https://gitee.com/***的master分支上
"scripts": {
  "deploy": "gh-pages -d dist -b master -r https://gitee.com/***"
}

ali-oss配置阿里云oss

配置阿里云oss的sdk工具包

Friendly-errors-webpack-plugin美化控制台输出的

webpack plugin

美化控制台输出的,比webpack自带的stats配置更高度定制化

const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");

module.exports = {
  plugins: [
    new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: ['You application is running here http://localhost:3000'],
          notes: ['Some additional notes to be displayed upon successful compilation']
        },
        // 是否每次都清除控制台输出
        clearConsole: true,
      })
  ]
}

webpack-bundle-analyzer分析打包后的资源体积

webpack plugin

分析打包后的资源体积,为优化提供可视化分析数据

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

mini-css-extract-plugin css提取到单独文件

webpack plugin

  • 将样式代码提取到单独的文件中, 它还提供了相应的loader,基础配置如下

    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    module.exports = {
      plugins: [new MiniCssExtractPlugin({
        filename: 'css/[name].[contenthash:7].css'
      })],
      module: {
        rules: [
          {
            test: /.(less|css)$/,
            // 注意 该loader和style-loader不兼容,需要注意下,下面是通过isProd判断是否是生产环境
            use: [isProd ? MiniCssExtractPlugin.loader : 'style-loader', "css-loader", "less-loader"],
          },
        ],
      },
    };

optimize-css-assets-webpack-plugincss压缩

webpack plugin

  • 压缩css

    const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    module.exports = {
      optimization: {
        minimizer: [
          new OptimizeCSSAssetsPlugin({})
        ]
      }
    }

uglifyjs-webpack-pluginjs压缩

  • 压缩js代码
  • 也可开启缓存和多进程

    const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
    module.exports = {
      optimization: {
        minimizer: [
          new UglifyJsPlugin({
            cache: true,
            parallel: true,
            sourceMap: false
          })
        ]
      }
    }

babel-loader开启缓存

webpack loader

    {
            test: /.(jsx?|babel|es6)$/,
            include: process.cwd(),
            exclude: "node_modules",
            use: {
              loader: 'babel-loader',
              options: {
                compact: true,
                // 这个配置
                cacheDirectory: true
              }
            }
    },

terser-webpack-plugin

  • 使用多进程并发运行以提高构建速度
  • 删除注释

    const TerserPlugin = require('terser-webpack-plugin');
    module.exports = {
      optimization: {
        minimizer: [
          new TerserPlugin({
            terserOptions: {
              // 这里
             parallel: true,
             // 删除注释
             format: {
                comments: false,
              },
            },
            extractComments: false,
          })
        ]
      }
    }

hard-source-webpack-plugin

可开启缓存, 增加二次打包或者热更新速度

    const HardSourcePlugin = require('hard-source-webpack-plugin');
    module.exports = {
      plugins: [new HardSourcePlugin()]
    }

style-loader的替换方案

webpack loader

  • 在webpack5中,资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

    // 该例子就是控制资源在一定大小内使用内联的形式
    module: {
      rules: [
        {
            test: /.(png|svg|jpg|jpeg|gif)$/i,
            type: "asset/resource",
            parser: {
              //转base64的条件
              dataUrlCondition: {
                maxSize: 10 * 1024, // 10kb
              },
            },
            generator: {
              filename: "static/[name].[contenthash:7][ext]",
            },
          },
      ]
    }

thread-loader

webpack loader

  • 多进程程处理loader
  • 使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。
  • 请仅在耗时的操作中使用此 loader!

    module.exports = {
      module: {
        rules: [
          {
            test: /.js$/,
            include: path.resolve('src'),
            use: [
              "thread-loader",
              // 耗时的 loader (例如 babel-loader)
            ],
          },
        ],
      },
    };

happypack

webpack plugin

  • 启动多进程来优化构建速度,但是作者不维护了,替换方案请使用thread-loader

@babel/polyfill 按需引入

详情见 目录babel相关@babel/polyfill 按需引入

babel相关的

babel-plugin-add-module-exports

如果希望对它的转码符合 commonJS,就需要安装 babel-plugin-add-module-exports插件,并在.babelrc 文件内声明该插件。

在 babel5 时代, export default {} 除了会被转译成 exports.default = {},还会加一句 module.exports = exports.default,这是为了兼顾commonJS规范。但在 babel6 时代,后面一句不再添加,这是为了区分commonJS和ES6的模块定义。

k

babel-plugin-module-resolver

是一个Babel模块解析插件, 在.babelrc中可以配置模块的导入搜索路径

简单的来说就跟webpack的resolve.alias配置很相似.举例:

    // .bablerc文件内
    {
      "plugins": [
        ["module-resolver", {
          "root": ["./"],
          "alias": {
            "P":"./app/p"
          }
        }]
      ]
    }

如上配置后,在项目里面如果有如下路径都会被解析成配置项里面的alias对应的路径

    //import Mp from '../../p/MyPropTypes';

    import Mp from 'P/MyPropTypes'

    import MyUtilFn from 'utils/MyUtilFn';

    //import MyUtilFn from '../../../../utils/MyUtilFn';

babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props

这几个是配置项目支持jsx语法的,主要是针对vue项目的,react的话需要安装@babel/preset-react,直接安装使用就行,不需要额外配置

@babel/polyfill 按需引入

项目中如果想使用新语法,并使浏览器兼容的话,就需要安装@babel/polyfill, 正常是在入口文件中直接import'@babel/polyfill'引入,但打包你会发现有个core-js体积很大,是因为直接医用,会把@babel/polyfill全量引入并打包了,所以优化点就是要按需引入

    // .babelrc 文件中

    {
      "presets": [
        [
          "@babel/preset-env",
          // 通过babel的预设插件来进行配置,                core对应的版本
          { "modules": false, "useBuiltIns": "usage", "corejs": 3 }
        ]
      ]
    }

配置后无需引入 polyfill,babel 会自动按需加载需要的功能,下图为当前配置对应的相应包的版本号

如果配置后报错core-js/***什么无法解析之类的,就在node_modules文件中找到@babel/polyfill中的packages.json,查看里面的core-js的版本,然后在项目中安装一下,并把.babelrc中的corejs配置改为对应的版本

@babel/plugin-transform-runtime

优化项 能减少babel内公共方法代码的重复的使用带来的问题

    {
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "regenerator": true
          }
        ]
      ]
    }

dotenv 将 .env 文件 中的环境变量加载到 process.env 中

dotenv 是一个零依赖模块,可将 .env 文件 中的环境变量加载到 process.env 中,或者解析env文件的变量

如何使用:

创建 .env 文件

    S3_BUCKET="YOURS3BUCKET"
    SECRET_KEY="YOURSECRETKEYGOESHERE"

使用

    import * as dotenv from 'dotenv'
    dotenv.config()
    console.log(process.env) 

    // 解析
    const env = dotenv.parse(
            fs.readFileSync(path.resolve(process.cwd(), "env.product"))
          );

npm-run-all 并行 串行 执行多个npm脚本

这个工具是为了解决官方的 npm run 命令无法同时运行多个脚本的问题,它可以把诸如 npm run clean && npm run build:css && npm run build:js && npm run build:html 的一长串的命令通过 glob 语法简化成 npm-run-all clean build:* 这样精致小巧的模样。再者大家也知道 shell 的 & 语法实际上是不支持 cmd 的,为了跨平台也最好使用这样的第三方库来提供支持。preact 中就是用来对数量繁多的子模块进行并行构建和顺序测试。

    --parallel: 并行运行多个命令,例如:npm-run-all --parallel lint build
    --serial: 多个命令按排列顺序执行,例如:npm-run-all --serial clean lint build:**
    --continue-on-error: 是否忽略错误,添加此参数 npm-run-all 会自动退出出错的命令,继续运行正常的
    --race: 添加此参数之后,只要有一个命令运行出错,那么 npm-run-all 就会结束掉全部的命令

    run-s:为 npm-run-all --serial的缩写;

    run-p:为 npm-run-all --parallel的缩写

uppercamelcase 转换成大驼峰命名规范

  • 将短划线/点/下划线/空格分隔的字符串转换为大写字母,也就是转换成大驼峰命名规范

image.png

picocolors 在终端修改输出字符样式的 npm 包 作用同chalk

picocolors 是一个可以在终端修改输出字符样式的 npm 包,说直白点就是给字符添加颜色;

    import pc from "picocolors"

    console.log(
      pc.green(`How are ${pc.italic(`you`)} doing?`)
    )

优点

  • 无依赖包;
  • 比 chalk 体积小 14 倍,速度快 2 倍;
  • 支持 CJS 和 ESM 项目

注意

  1. 不过 chalk 为了优化,在最近的最新版本 v5 中已剔除依赖包
  2. 因为 picocolors 包比较小,所以功能边界没有 chalk 的全面

chalk 控制log在控制台输出的颜色

  • 控制log在控制台输出的颜色
  • "chalk": "2.4.2" 新版的是采用的ESM模式,如果在node中使用,就安装开头的这个版本

image.png

json-templater 模板语法

使用特定的模板语法, 相当于es6模板语法的高级版,当变量过多的时候使用这个,更方便

    var render = require('json-templater/string');
    render('{{xfoo}} {{say.what}}', { xfoo: 'yep', say: { what: 'yep' } });

update-notifier检查库的更新

    // 引用 update-notifier 库,用于检查更新
    const updateNotifier = require('update-notifier')
    // 引用 chalk 库,用于控制台字符样式
    const chalk = require('chalk')
    // 引入 package.json 文件,用于 update-notifier 库读取相关信息
    const pkg = require('../package.json')

    // updateNotifier 是 update-notifier 的方法,其他方法可到 npmjs 查看
    const notifier = updateNotifier({
      // 从 package.json 获取 name 和 version 进行查询
      pkg,
        // 设定检查更新周期,默认为 1000 * 60 * 60 * 24(1 天)
        // 这里设定为 1000 毫秒(1秒)
      updateCheckInterval: 1000,
    })

    function updateChk() {
      // 当检测到版本时,notifier.update 会返回 Object
        // 此时可以用 notifier.update.latest 获取最新版本号
      if (notifier.update) {
        console.log(`New version available: ${chalk.cyan(notifier.update.latest)}, it's recommended that you update before using.`)
        notifier.notify()
      } else {
        console.log('No new version is available.')
      }
    }

    // 将上面的 updateChk() 方法导出
    module.exports = updateChk

rimraf 删除文件和文件夹的

以包的形式包装rm -rf命令,用来删除文件和文件夹的,不管文件夹是否为空,都可删除.

此处就可以在windows上删除node_modules文件特别有用和高效,只需要配置相关命令即可

    npm install rimraf --save-dev

    const rimraf = require('rimraf');
    rimraf('./test.txt', function (err) { // 删除当前目录下的 test.txt
      console.log(err);
    });


    or
    "scripts": {
        ...
        "build": "rimraf dist"
      },

commander 注册命令,解析命令

命令行工具,有了它我们就可以读取命令行命令,知道用户想要做什么了,总之就是注册命令,解析命令

具体参考github.com/tj/commande…

    const { Command } = require('commander');
    const program = new Command();

    program
      .name('string-util')
      .description('CLI to some JavaScript string utilities')
      .version('0.8.0');

    program.command('split')
      .description('Split a string into substrings and display as an array')
      .argument('<string>', 'string to split')
      .option('--first', 'display just the first substring')
      .option('-s, --separator <char>', 'separator character', ',')
      .action((str, options) => {
        const limit = options.first ? 1 : undefined;
        console.log(str.split(options.separator, limit));
      });

    program.parse();

inquirer交互式命令行工具

交互式命令行工具,给用户提供一个漂亮的界面和提出问题流的方式,例如使用vuecli创建项目时,会有提问和选项与用户进行交互

    #!/usr/bin/env node

    // 交互式命令行
    const inquirer = require("inquirer");

    // 自定义交互式命令行的问题和简单校验

    let question = [
      {
        name: "name",
        type: "input",
        message: "请输入模板名称",
        validate(val) {
          if (val === "") {
            return "请输入模板名称";
          } else if (tplObj[val]) {
            return "模板已存在!";
          } else {
            return true;
          }
        },
      },
      {
        name: "type",
        type: "rawlist",
        message: "请选择模板仓库类型",
        choices: [
          {
            name: "github:",
          },
          {
            name: "gitlab:",
          },
          {
            name: "Bitbucket:",
          },
        ],
      },
      {
        name: "url",
        type: "input",
        message: "请输入模板地址",
        validate(val) {
          if (val === "") return "请输入模板地址";
          return true;
        },
      },
    ];

    inquirer.prompt(question).then((options) => {
      // options是用户输入的参数  是一个对象
      let { name, url, type } = options;
    });

download-git-repo 下载远程仓库

下载远程模板工具,负责下载远程仓库的模板项目

ora 显示加载中的效果

用于显示加载中的效果,类似于前端页面的 loading 效果,像下载模板这种耗时的操作,有了 loading 效果可以提示用户正在进行中,请耐心等待

log-symbols

日志彩色符号,用来显示√ 或 × 等的图标

minimist 命令行参数解析工具,

命令行参数解析工具,可以更直观的让我们使用 命令行参数

    node index.js --beep=boop -t -z 12 -n5 foo bar 

Node.js 程序启动后可以直接从process.argv中读取到参数列表:

    console.log(process.argv);
    // ['/bin/node', '/tmp/index.js', '--beep=boop', '-t', '-z', '12', '-n5', 'foo', 'bar']

从上述代码中可以看到,process.argv 变量是一个数组,数组前两项分别是 node 程序位置和js脚本位置,数组中随后的元素都是我们启动Node.js后的参数,这些参数以空格分隔成数组。

虽然从 process.argv 中可以得到启动参数列表,但是我们仍需要对参数进行进一步解析处理才行

    const argv = require('minimist')(process.argv.slice(2));
    console.dir(argv);
    // { _: [ 'foo', 'bar' ], beep: 'boop', t: true, z: 12, n: 5 }

经过 minimist 解析的process.argv是一个对象,例如,我们可以直接从访问 argv.beep 得到 --beep=boop 参数的值。

globby glob 快速批量导入、读取文件的库

node环境中搜索文件使用的库 globby 是 glob的增强版 后面可以直接使用globby

image.png

image.png

fast-glob 快速批量导入、读取文件的库

该包提供了遍历文件系统的方法github.com/mrmlnc/fast…

    import glob from "fast-glob";

    const files = await glob("*.ts", {
        cwd: process.cwd(), // 需要匹配的工作目录, 默认是process.cwd()
        absolute: true, // 返回绝对路径
        onlyFiles: true, // 只返回文件
      });
      // 返回值 [ 'D:/zpp/组件库/multiple-technologies/packages/components-v3/index.ts' ]

Rollup中用的包

@rollup/plugin-node-resolve

@rollup/plugin-commonjs

解析commonjs ts vue文件的包

     import { nodeResolve } from "@rollup/plugin-node-resolve";
    import commonjs from "@rollup/plugin-commonjs";
    import vue from "rollup-plugin-vue";
     const config = {
          input,
          plugins: [nodeResolve(), typescript(), vue(), commonjs()],
          external: (id) => /^vue/.test(id) || /^@soul-cli/.test(id), // 排除掉vue和@soul-cli的依赖
        };