如何发布一个属于自己的 npm 包?

对于前端菜鸟的我而言,npm的用途就是管理项目以及安装各种工具包。但进入职场之后,接到一个需求,要求我把项目中某个功能抽离出来,单独发布一个 npm 包。脑子第一想法,我这菜鸟也配发布 npm 包。但是就算再菜,需求来了,也要硬着头皮上啊。本文就记录个人是如何去发布了一个属于自己的 npm 包的。

node.js 中对于的定义如下:

包是由 package.json 文件描述的文件夹树。 包由包含 package.json 文件的文件夹和所有子文件夹组成,直到包含另一个 package.json 文件的下一个文件夹或名为 node_modules 的文件夹。

一 、创建npm包

npm 包,通俗点说就是一个 node.js 项目,只不过这个项目进行了一些特殊配置和处理,并且注册到了 npm 仓库中,可共享给所有开发者。

所以创建一个包,其实就是创建一个项目,本文就以dali-publish-test为例:

  • 首先创建一个名为 dali-publish-test 的文件夹
  • 然后命令行切换到当前目录,然后执行npm init,快速初始化。(该命令其实就是在当前目录下创建一个package.json文件,其中包含项目的一些基本信息,用于管理项目)

这样我们就初始化好一个 npm 包了:

本文采用的是最基础的方式初始化一个 npm 包,当然社区中已经有很多成熟的构建工具。

紧接着,我们为该包创建一个 git 仓库,用于进行包的版本控制。如下图所示,在github(或:gitlab、gitee等)中创建一个新的仓库:

然后依次执行以下命令:

ruby 复制代码
git init 
git add . 
git commit -m "first commit" 
git branch -M main 
git remote add origin <git@github.com>:dali-yy/dali-publish-test.git 
git push -u origin main

这样就可以将本地创建的包和原创仓库建立联系了。

创建了git仓库之后,那么就可以在 package.json 文件中更新 repository字段信息如下:

json 复制代码
"repository": {
    "type": "git",
    "url": "创建的仓库地址"
}

最后,我们在根目录下新建一个index.js文件,并输入以下内容:

JavaScript 复制代码
function daliPublishTest() {
    console.log('dali-test-publish');
}

module.exports = daliPublishTest;

这样一个简单的 npm 包就创建好了。

注意:这里介绍的只是创建包的一种简单方式,主要是用作示例。创建一个包并不是一定要按上述流程,还有其他更好的方式。

二、发布npm包

上述创建好一个简单的 npm 包之后,接下来进入主题,如何将该包发布到 npm 仓库呢。

2.1 创建 npm 账户

在发布之前,我们首先要创建一个 npm 账户,链接:www.npmjs.com/signup

2.2 添加注册表用户

创建好 npm 账户之后,我们可以使用 npm adduser 命令在指定的注册表中创建新用户,并将凭据保存到 .npmrc 文件中。(如果未指定注册表,则将使用默认注册表)

markdown 复制代码
// 按 npm 官方提示,`npm adduser` 后面会被拆分为 `npm login` 和 `npm register` 两个命令
npm adduser

如下图所示,我们执行 npm add-user 命令之后,需要输入 UsernamePasswordEmail 等信息,然后输入邮箱收到的验证码,验证成功后即可添加。

需要注意的是,为了提高安装速度,大部分人的镜像源可能已经切换到其他的镜像源。这可能会因为没有权限而导致您执行npm addUSer失败。所以,如果执行失败,可尝试执行如下命令,切换到官方镜像源:

arduino 复制代码
npm config set registry https://registry.npmjs.org/

2.3 发布包

上述准备工作都完成以后,我们就可以执行如下命令发布npm包了。

复制代码
npm publish

然后我们去官网搜索 dali-publish-test,如下图所示:

这样一个简单的 npm 包就发布好了。

2.4 使用包

npm 包发布好之后,我们就可以执行如下命令安装该包:

css 复制代码
npm i dali-publish-test

然后在代码中按如下方式使用:

JavaScript 复制代码
const daliPublishTest = require('dali-publish-test');

daliPublishTest();

2.5 更新包

后期想要对包进行更新迭代,我们可以使用 npm version 来更新包的版本号。

arduino 复制代码
// patch:补丁号,修复bug,小变动,如 v1.0.0->v1.0.1 
npm version patch 

// minor:次版本号,增加新功能,如 v1.0.0->v1.1.0 
npm version minor 

// major:主版本号,不兼容的修改,如 v1.0.0->v2.0.0 
npm version major

例如我对包的内容进行简单的修改后,然后执行npm version patch命令,包的版本号就更新到了 1.0.1(执行npm version命令之前要确保git已经提交)

然后再执行 npm publish 即可更新包的版本了。

2.6 取消发布

我们可以执行如下命令从这将从注册表中删除软件包版本,删除其条目并删除 tarball。

css 复制代码
npm unpublish --force

注意:

  1. 官方提示,如果您的目的是鼓励用户升级,或者您不想再维护软件包,请考虑使用 deprecate 命令。所以,尽量不要去执行npm unpublish
  2. 这个命令是删除软件包版本,而不是移除该包。如上图所示,只是移除了 dali-publish-test@1.0.1版本而已。去npm官网仍然可以搜索到 dali-publish-test@1.0.0 版本。

三、npm cli包

npm 不仅支持发布像上述这样包去共享代码,也支持发布一个 cli 工具。

例如我们安装 webpack 的同时,也会伴随安装 webpack-cli工具。安装好后,我们就可以在命令行执行 npx webpack 命令去对项目进行打包。(关于为什么安装了 webpack-cli 之后,尽可以在命令行执行 npx webpack呢?具体原因可见此推文。)

那么,如果我们想要发布一个这样的 cli 工具,需要进行哪些特殊处理呢。我们以上述发布的包为例,继续进行如下修改:

  1. 首先我们创建 bin 目录,并在该目录下新建一个 cli.js 文件,文件内容如下:
JavaScript 复制代码
#!/usr/bin/env node
const daliPublishTest = require('../index');

function main() {
  daliPublishTest();
}

注意 :文件开头的#!/usr/bin/env node一定不能省略 ,这是声明执行当前脚本的环境是node环境。

  1. package.json 文件中添加 bin 字段,它是命令名到本地文件名的映射:
json 复制代码
{
    "bin": {
        "dali-publish-test": "./bin/cli.js"
    }
}

其中:

  • dali-publish-test:命令名
  • ./bin/cli.js:实际执行的脚本文件

至此,我们就可以执行 npm publish 进行发布了。

发布完成后,我们再去安装该包,在命令行执行 npx dali-publish-test 就可以看到命令行输出 "dali-publish-test"。

此外,市面上还提供了很多好用的 npm 包来协助我们发布一个 cli 工具:

四、 同时支持 CommonJS 和 ES module

上述我们发布的包,未进行任何额外配置,会默认被视为 CommonJS 模块。但目前社区发布的很多包都是同时支持ES模块CommonJS的,所以这里我们也来探索一下如何发布一个能够同时支持这两个模块的包。

探索之前,首先介绍 package.json文件中的几个相关字段:

4.1 包入口点

package.json 文件中,mainexports 两个字段都可以用于定义包的入口点,也均适用于 ES Module 和 CommonJS。

  • main:仅定义包的主要入口点。(所有 node.js 版本都支持,且 node.js 10 以下版本该字段必须)
  • exports"main"的现代替代方案,允许定义多个入口点、环境之间的条件入口解析支持,并防止除"exports" 中定义的入口点之外的任何其他入口点。此封装允许模块作者清楚地为他们的包定义公共接口。
json 复制代码
{
  "name": "my-package",
  "exports": {
    /* 相当于 main 字段的语法糖*/
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}

4.2 type 字段

type 字段定义了 Node.js 应该如何解释 .js 文件。

  • 如果 package.json 文件没有 "type" 字段,则 .js 文件将被视为 CommonJS
  • 如果 type 字段的值为 module,则 .js 文件将被视为 ES模块

4.3 条件导出

条件导出提供了一种根据特定条件映射到不同路径的方法。CommonJS 和 ES 模块导入都支持它们。

比如,包想要为 require()import 提供不同的 ES 模块导出可以这样写:

json 复制代码
// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}

所以如果我们想发布一个同时支持 ES Module 和 CommonJS 的包,首先我们需要生成两套规范的代码,并在package.json 文件中通过 exports 字段进行配置即可。

关于代码的生成或打包,大家可以考虑使用 Rollup

5. 发布一个 TypeScript 包

TypeScript是JavaScript的的超集,其与JavaScript的主要区别在于类型声明。

如果您的包是使用 TypeScript 编写的,只需在发布前使用 tsc将ts代码编译成js代码,并生成类型声明即可。

本文到这里就结束啦!

相关推荐
飘尘10 分钟前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆20 分钟前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师1 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆1 小时前
VSCode自动格式化三要素
前端
爱勇宝2 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen3 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518135 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode5 小时前
Redis 在生产项目的使用
前端·后端
LiaCode5 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战5 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github