如何编写一个健壮的 npm 包

这篇具有很好参考价值的文章主要介绍了如何编写一个健壮的 npm 包。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

无脑发布 npm

比如老王我,用npm init新建一个包,改把改把,然后来个npm publish,so easy ✌️!

Too young too naive, baby 👶!

请容我讲述一些发布过程中踩过的坑。

首先,算了也可以之后有空再说,我们需要通读npm的配置文档。

package.json doc

通用性👷

指定发布文件

利用package.jsonfiles字段精简发布体积。

{
  "files": ["dist", "lib", "module"]
}

若不指定files,每次发布会把所有不以.开头的文件都发布出去,导致发布体积过大(node_modules默认也不会被发布)。

README.md作为主文档,加不加都会发布,package.json也是。

指定源代码

{
  "source": "src/index.ts",
  "repository": {
    "type": "git",
    "url": "https://github.com/yourname/yourproject.git"
  }
}

通常来说我是不在npm发布中包括源代码的,因此都没有加过source字段,只是用repository来告知一下git仓库地址即可。

如果仓库是内部仓库或私人仓库并不对外,则source字段就有用了,将源代码发布后可让人帮忙debug找问题。

注意如果有source,则files也要加上souce对应的文件或文件夹。

发布sourcemap

一般来说我们发布的都是经过编译的代码,为了给使用者方便调试,只要不是源码,都要有对应的sourcemap文件,例如发布了一个dist/index.js则也需要一个dist/index.js.map文件与之配套。

指定安装源

如果你从来不用私有源,可跳过该项。

利用.npmrc指定安装源,用于当前项目与你的全局配置区分开。

否则当前项目很可能指定的内部npm源,导致外部用户无法利用lock文件安装。

例如

registry=https://registry.npmjs.org/

精确指定dependencies、devDependencies、peerDependencies

dependencies要尽量少,只有在运行时确实用到才放进去。

依赖的版本号要清晰指明,如"react": "16.x || 17.x"

否则,如果指定了"react": "17.0.0",则在使用了react 16的项目中,会引入两份react,造成一些莫名其妙的问题。

这种情况,react应放到peerDependencies中。

指定发布目标

如果你从来不在私有源发布,可跳过该项。

package.json中指定发布地址,在当前包与全局配置不一致时非常必要。

{
  "publishConfig": {
    "registry": "https://registry.npmjs.org"
  }
}

sideEffects

对应配置:

{ "sideEffects": false }

作用:在打包时进行treeshake可根据是否使用而优化相关的代码。

如果sideEffectstrue,则一旦引入,不管是否调用都不能被treeshake掉。

专用性🥷

类型配套

无论针对哪个环境,目前自带类型已经是既成事实的标配。

记得生成类型的.d.ts文件,并在package.json中指定。

{
  "types": "type/index.d.ts",
  "typings": "type/index.d.ts"
}

我一般会用一个专用的tsconfig.declaration.json来专门生成类型:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false,
    "emitDeclarationOnly": true,
    "declaration": true,
    "outDir": "types"
  }
}

作为后端库

package.json中指定main字段。

编译结果需要在nodejs环境中运行,输出commonjs格式模块。

为了兼容最新与将来,同时也要输出esmodule格式模块。

相关配置:

{
  "main": "lib/index.js",
  "module": "module/index.js",
  "jsnext:main": "module/index.js"
}

modulejsnext:main都是指esmodule格式,只是为了兼容某些特殊环境的别名。可能还有其他别名单我暂时就见过这俩。

其中module中的文件推荐使用特定的后缀名,例如.esm.js.mjs,但在一些工程相关工具中是否会有未知为题,不好说。

未来已来,现在大部分前端工程工具都会优先使用module指定的文件,单如果没有指定module,也会为了兼容去加载main

作为前端库

前端库其实要求比后端库更高,为啥?

因为现代前端开发环境要求支持所有后端环境,并延伸出前端环境的额外支持。也就是说后端库要求一般是前端库要求的子集。

需要扩展的是纯前端环境的运行格式,老格式amd已经被淘汰可以不用考虑,现在基本都被umd格式统一。

{
  "main": "lib/index.js",
  "module": "module/index.js",
  "unpkg": "dist/index.js",
  "umd:main": "dist/index.js",
  "jsdelivr": "dist/index.umd.production.min.js"
}

其中unpkgumd:mainjsdelivr都是为了更广泛兼容的指向浏览器环境运行的同一个目标别名。

通常来说commonjsesmoduleumd都不会将其依赖的其他包包括进去,只是在运行时才加载。

还有一种情况,可能只有我自己用到过,就是发布包中有些东西与外部环境有冲突,因此除了这些通用模式之外我又加了一个independent(取名叫standalong也比较合适)格式,将这个包的所有依赖都封装进去,可以不依赖外部环境独立使用。

例如mobx-value的独立运行文件。

mobx-value independent

注意浏览器环境输出的都是优化后的.production.min格式,也必须同时输出.development后缀的开发模式,为了方便使用者调试方便。

因为最大的使用者,往往就是我们自己,不要连自己都糊弄了事~

作为命令行工具

多配置兼容

命令行工具一般需要很多参数,例如tsc,当参数过多时没人愿意每次都输入长长的参数,因此需要配置文件的支持。

那么选哪种配置格式呢?

此时cosmiconfig隆重登场!以一句名言形容,小孩子才做选择,成年人全都要!

兼容各种配置,各种位置,详情参见其api

还有一点,如果需要读取一些周边的json配置,不要用原生的JSON.parse,很多json是带注释的或者编写不规范,用json5读取兼容好。

还有一个精简版:lilconfig,功能差不多,我下次打算试试。

配置文件类型校验

刚入门typescript时,我尝试用typescript作为配置文件,然后在运行时利用类型机制达到校验配置的目的。

但这样会丢失很多灵活性,限制死了配置文件的来源与格式,并由于库的typescript环境与应用所在的typescript环境不一致,也导致了很多工程问题(对我说的就是ts-gear)。

后来发现通过注释文档的方式,js文件中也同样可以校验类型,而且js文件对运行时更友好。

例如webpack.config.js这样配置

/**
 * @type {import('webpack').Configuration}
 * */
const config = {...}
export default config

配置文件运行时校验

我们的程序要读配置,但配置是使用者提供的,谁知道用户会写些什么,即使有上面那步提到的类型校验把关,也会有很多边界问题类型根本管不了。

因此,运行时配置数据校验就是必备环节。

不光是校验不通过时终止运行,还必须给出一个合理且精准的错误提示。

推荐一个协议、两个校验工具与一个漂亮的格式化提示工具。

协议是json schema,校验工具为joiajv,提示输出工具为chalk

指定可运行文件

package.json中指定bin

{
  "bin": "bin/run.js"
}

对于大部分js脚本,都要在运行文件头部指定运行环境。

#! /usr/bin/env node

然后别忘了在发布前添加可执行属性,务必整合在自动化发布脚本中。

chmod +x bin/run.js

可调用api

例如babel,我们不光能使用@babel/cli在命令行使用,也可以在自己的程序里import babel from 'babel'来调用其api

一个命令行工具通常也是一个第三方库,方便集成到调用者自身的脚本与环境中。

其他特定环境

例如针对react-native,这个我就见过,没实际用过。

{
  "react-native": "dist/index.esm.js"
}

最后不论什么格式,都记得输出配套sourcemap.map文件。

健壮性🏋

指定运行环境:engine与os

尤其对于命令行工具,这俩点很重要,不然很容易就换个人换个电脑就莫名报错。

{
  "engine": "node>=14",
  "os": ["linux", "darwin"]
}

有否配套测试用例

  • 有可运行的配套测试用例。
  • README.md上有可见的测试覆盖率统计,让人可以放心使用。

测试用例放在哪?

最初我习惯按照jest推荐的模式,将所有测试用例放在__tests__文件夹内。

最近两年看了好多别的语言的单测用例,我现在更倾向于将测试文件与源文件放在一起。因为测试用例,就是源代码的一部分!

比如以下这种目录结构

src/setter.ts
src/setter.test.ts

测试运行时机

npm prepublishOnly的钩子一定要加上运行测试用例。

有余力的情况,可以再配置个额外的流水线,github上有好多免费的配套流水线,自己折腾折腾。

代码校验配套

项目必须有一个较好的文档规则校验流程,大多数情况我使用eslint,然后配上airbnbprettier的校验规则。

校验有两个重要作用,一个是真的能解决很多隐性bug,另一个是代码漂亮,之后看你项目源码的人也会觉得舒服,关键是面试时也能拿的出手。

如果有面试者给我看自己的开源作品,如果代码风格都不行,立即就判定不行,也不用再看什么逻辑能力了,招进来也是挖坑。

好的代码风格必须依赖校验工具,最好把校验流程也集成到发布的钩子上。

推广性🤹

文档

使用.markdownlint配置规范自己的markdown文档,否则很容易写飞了。

要不人家一看文档,项目质量很容易就露馅了不是🤭

配套展示用例

  • 一个方法是在项目中自带一个可运行的样例,让人clone之后运行指定命令即可查看样例。
  • 更好一些,部署一个可以在线查看的例子,并在主文档上附上直达链接。
  • 更进一步,项目增大之后,需要说明的地方越来越多,一个README已经太长。使用docusaurus等类似的工具部署一个独立的文档站点。

有否自动化版本管理

Why?因为版本号与兼容性是强相关的,具体参考semver规范。

  • 使用husky/yorkie等规范提交日志。
  • 使用standard-version等自动生成CHANGELOG并根据规则自动提升版本号。

最后留个作业

  • 你有什么npm发布时的关键经验这里没提到的,帮我补充下🤝
  • 当我们再一次运行npm publish,脑编译一下,想想这期间都发生了些什么,还少些什么?

作者:京东零售 王凡

内容来源:京东云开发者社区文章来源地址https://www.toymoban.com/news/detail-458366.html

到了这里,关于如何编写一个健壮的 npm 包的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何自己创建一个工具项目并上传到npm上使用

    选择框架:   选择语言: 在项目中使用命令安装项目依赖:   运行项目:  运行成功界面: 注意: version:当前发布的版本号,项目每次改动后需要修改版本后再发布,  默认要0.0.1开始才能上传,相同版本是传不上去的,一定要记得每次更改了代码都改下版本号。 name: 包

    2024年02月12日
    浏览(41)
  • 如何使用Python编写一个代理池测试工具

    部分数据来源: ChatGPT 背景:         在进行网络爬虫时,我们经常需要使用代理池来防止IP被网站禁封。代理池是一个保存多个代理IP地址的数据库,我们可以从数据库中随机选择一个代理地址来进行网络请求,避免过多请求来自同一个IP地址。 为了提高爬虫程序的稳定

    2024年02月08日
    浏览(41)
  • vue3中如何编写一个组件---demo

    当使用 Vue 3 来开发组件时,可以使用 Composition API 来定义组件逻辑和实现组件传值。下面是一个详细的 Vue 3 组件示例和讲解: 假设我们要开发一个简单的待办事项列表组件,包括添加待办事项、显示待办事项列表以及删除待办事项的功能。 在 TodoList.vue 文件中: 在上面的示

    2024年02月11日
    浏览(37)
  • 七分钟教会你如何编写一个合格的测试用例

    目录 1、测试用例的基本要素 2、根据测试用例去测试带来的好处 3、测试用例的设计方法 3.1、等价类 3.2、边界值 3.3、错误猜测法 3.4、场景法 3.5、因果图法  3.6、正交排列 4、怎样判断一个测试用例是好的测试用例         测试用例是为了实施测试而向被测试的系统提供

    2024年02月03日
    浏览(53)
  • Verilog Tutorial(6)如何编写一个基础的Testbench

    在自己准备写verilog教程之前,参考了许多资料----FPGA Tutorial网站的这套verilog教程即是其一。这套教程写得不错,只是没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。 这是网站原文:https://fpgatutorial.com/verilog/ 这是系列导航:Verilog教程系列文章导航 本文将

    2023年04月10日
    浏览(36)
  • 如何使用vite框架封装一个js库,并发布npm包

    目录 前言介绍 一、创建一个vite项目 1、使用创建命令:  2、选择others 3、 模板选择library 4、选择开发语言 ​编辑 二、安装依赖 三、目录介绍 1、vite.config.ts打包文件 2、package.json命令配置文件 三、发布npm 1、注册npm账号 2、设置npm源 3、登录到npm 4、推送到npm仓库 5、查看

    2024年01月25日
    浏览(45)
  • 一篇文章教会你如何编写一个简单的Shell脚本

    Shell脚本概念 Shell 脚本是一种用于自动化执行一系列命令和操作的脚本文件。它使用的是 Shell 解释器(如 Bash、Korn Shell、Zsh 等)来解释和执行其中的命令。Shell 脚本通常用于编写简单的任务和工作流程,可以帮助我们进行系统管理、批量处理、自动化部署等任务。 以.sh后缀

    2024年02月10日
    浏览(50)
  • 如何使用PHPUnit编写一个PHP单元测试-简单的代码示例

    在软件开发过程中,单元测试是一种重要的测试方法,可以确保代码的质量和可靠性。在PHP开发中,也可以通过编写单元测试来验证代码的正确性。下面将介绍一些编写PHP单元测试的基本步骤和常用工具。 首先,你需要一个PHP单元测试框架,比如PHPUnit。PHPUnit是PHP社区最流行

    2024年02月04日
    浏览(56)
  • 如何编写一个好的测试用例?才能防止背黑锅

    在这之前,思考一个问题,下面这个简单的QQ登录页面,一共又多少条测试用例? 答案是100条以上。 那么,什么是测试用例? 测试用例是用于检查特定应用程序/软件是否正常工作所需的软件测试的定义格式。测试用例由需要检查以测试应用程序或软件的一组特定条件组成,

    2024年01月20日
    浏览(48)
  • 如何编写一个可变参数函数?如何让所有单片机的所有串口实现printf函数?

    (1)由于真的复习不下去,就想着写一篇博客拉回自己的心思。于是想到了长期有疑惑,但是一直没有进行深入了解的C语言可变参数函数。 (2)本人查阅了一些网上的资料,以及自己的理解写出来了这一片博客。首先再次感谢肯哥的答疑。 (3)借鉴文章: C51单片机中如何

    2024年02月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包