如何发布一个 TypeScript 编写的包到 NPM 官方仓库

该文章根据 CC-BY-4.0 协议发表,转载请遵循该协议。
本文地址:https://fenying.net/post/2018/01/16/how-to-publish-typescript-package-to-npmjs/

Overview

本文介绍如何发布一个包到 npmjs.org,包括创建、发布、撤回、更新、使用分支等等,以及其中 包含的一些小技巧。

0. 创建

创建一个包很简单,就是手动或者用 npm init 命令直接生成一个 package.json 文件,一个 包就算是创建出来了。剩下的就是给它添加内容而已了。

如果使用 npm init 命令配合 -y 或者 --yes 参数,可以不使用命令行交互模式,而 快速地创建一个 package.json 文件。

1npm init -y

比如我这里创建出来的是:

name 字段默认是当前所在的目录名称。

 1{
 2  "name": "test",
 3  "version": "0.1.0",
 4  "description": "",
 5  "main": "index.js",
 6  "scripts": {
 7    "test": "echo \"Error: no test specified\" && exit 1"
 8  },
 9  "keywords": [],
10  "author": "Angus.Fenying <[email protected]> (https://fenying.net/)",
11  "license": "Apache-2.0"
12}

什么,你说你创建出来是下面这样的?有些字段的初始值和我的不一样?

 1{
 2  "name": "test",
 3  "version": "1.0.0",
 4  "description": "",
 5  "main": "index.js",
 6  "scripts": {
 7    "test": "echo \"Error: no test specified\" && exit 1"
 8  },
 9  "keywords": [],
10  "author": "",
11  "license": "ISC"
12}

别急,那是因为你没有设置 NPM 的一些默认配置。通过下面三条命令,可以设置创建新的 NPM 包时使用的默认属性。

1npm config set init-author-name "Angus.Fenying"             # 你的名称
2npm config set init-author-email "[email protected]" # 你的邮箱
3npm config set init-author-url "https://fenying.net"        # 你的个人网页
4npm config set init-license "Apache-2.0"                    # 开源授权协议名
5npm config set init-version "0.1.0"                         # 版本号

1. 准备

现在你可以为 package.json 添加内容了,比如修改如下:

 1{
 2  "name": "npm-tutorial-test-1",
 3  "version": "0.1.0",
 4  "description": "This is a test package for my NPM tutorial.",
 5  "main": "index.js",
 6  "scripts": {
 7    "test": "echo hello"
 8  },
 9  "keywords": [
10      "test"
11  ],
12  "author": "NPM Learner <[email protected]> (https://sample.com/)",
13  "license": "MIT"
14}

然后添加一个 README.md 文件,内容如下:

1# NPM Tutorial Test 1
2
3This is a test package for my NPM tutorial.
4
5## License
6
7This package is published under [MIT license](./LICENSE).

添加你的 LICENSE 文件(开源协议),以及一个 index.js 文件。

现在,一个最基本的包就是准备好了,等待发布了。

2. 发布

既然包已经准备好了,那么就可以发布了。

2.0. 修正下载源

如果你曾经为了加速 NPM 下载速度而修改了它的下载源,那么请改回去,不然是无法发布的。

检查方式:

1npm config get registry

NPM 的官方源是 https://registry.npmjs.org/,如果你看到控制台打印是这个,那么就没 问题了。而如果不是,那么请使用下面的命令修改回去:

1npm config set registry https://registry.npmjs.org/

2.1. 创建并登录 npmjs.org 账户

如果你没有 npmjs.org 账户,那么先要创建一个,方法很简单:

1npm adduser

根据提示创建即可。

记得验证邮箱,否则将无法发布包。

如果已经有 npmjs.org 账户了,请直接登录:

1npm login

3. 真正的开始

上面的教程只是一个最简单的 DEMO,远远不能满足我们的发布需要,下面以一个需要编译的 TypeScript 包为例,看看如何发布和管理。

3.1. 合理的 package.json

先来看一个 package.json 的内容:

 1{
 2  "name": "full-sample",
 3  "version": "0.1.0",
 4  "description": "A sample to learn NPM.",
 5  "main": "./dist/index.js",
 6  "scripts": {
 7    "prepare": "npm run rebuild",
 8    "build": "tsc -p .",
 9    "rebuild": "npm run clean && npm run lint && npm run build",
10    "test": "echo See directory sources/tests",
11    "clean": "rm -rf dist",
12    "lint": "tslint --project tslint.json"
13  },
14  "keywords": [
15    "npm",
16    "sample"
17  ],
18  "author": "NPM Learner <[email protected]> (https://sample.com/)",
19  "license": "Apache-2.0",
20  "repository": {
21    "type": "git",
22    "url": "git+https://github.com/learn-npm/full-sample.git"
23  },
24  "bugs": {
25    "url": "https://github.com/learn-npm/full-sample/issues"
26  },
27  "homepage": "https://github.com/learn-npm/full-sample#readme",
28  "types": "./dist/index.d.ts",
29  "typings": "./dist/index.d.ts",
30  "dependencies": {
31    "sequelize": "^4.24.0"
32  },
33  "devDependencies": {
34    "@types/node": "^8.0.51",
35    "@types/sequelize": "^4.0.79",
36    "typescript": "^2.6.1"
37  },
38  "engines": {
39    "node": ">=8.0.0"
40  }
41}

假定项目的 TypeScript 源代码都在 sources 目录,编译结果都在 dist 目录。

上面与 DEMO 有什么区别呢?下面逐个字段解释:

  • homepage

    指定项目的主页地址,如果没有一般可以使用项目的 GitHub 地址。

  • bugs.url

    指定项目的 Bug 反馈地址,一般可以用项目的 GitHub Issue 地址。

  • repository.urlrepository.type

    指定项目的源码仓库地址,可以指定是 git/cvs/svn。

  • main

    指定 Node.js 中 require(“moduel-name”) 导入的默认文件。

  • keywords

    指定项目的关键词,合理设置有利于让他人发现你的项目。

  • engines

    设置项目对引擎的版本要求,比如 node、electron、vscode 等。

  • typestypings

    设置项目内置的 TypeScript 模块声明文件入口文件。

3.2. scripts 字段

scripts 字段作为单独一节解释,因为它是用于构建控制和发布控制的工具。

  • scripts.build

    这个允许使用 npm run build 命令直接编译 TS 代码。

  • scripts.lint

    这个允许使用 npm run lint 命令调用 TSLinter 对代码进行格式检查。

  • scripts.clean

    这个允许使用 npm run clean 命令清理编译结果。

  • scripts.rebuild

    这个允许使用 npm run rebuild 命令清理编译结果然后重新生成。

  • scripts.prepare

    这个不是给我们用的,而是 NPM 提供的钩子,这个命令会在执行 npm publish 的时候 被调用。因此可以用这个钩子进行发布前构建。

3.3. Git 设置

Git 应当使用 .gitignore 文件忽略那些编译结果,以及 NPM 依赖的包文件:

1/node_modules/
2/dist/
3*.log

3.4. NPM 包文件设置

NPM 打包发布的时候,会默认把当前目录下所有文件打包。但是 Git 仓库中,有些东西是不需要 发布到 NPM 的,因此我们需要使用一个文件 .npmignore 来忽略这些文件,常用配置如下:

1/.git/
2/.vscode/
3/docs/
4/node_modules/
5.gitignore
6.npmignore
7tslint.json
8tsconfig.json
9*.log

这些文件都不是发布需要的内容,因此可以忽略。

3.5. 配置 tsconfig.json

前面说了,假定 TypeScript 的代码在 sources 目录下,编译的输出目录则为 dist。那么需要 在 tsconfig.json 里面通过 rootDiroutDir 选项指定。

其次,为了让其它 TypeScript 程序能正常使用你的包,你还应该设置 declaration 字段为 true,使之自动生成 *.d.ts 文件。此处我们假定模块的入口是 index.js ,因此你必须 实现一个 index.ts 文件,作为模块的入口。

另外,如果要实现 TypeScript 源码调试,则需要开启 sourceMap 选项,以生成源码映射 文件。

3.6. 使用 tslint.json

现在,就可以愉快地编写代码了吗?不不不,为了规范代码,我们还需要配置下 TSLinter。通过 下面的命令初始化一个 tslint.json 文件。

1npm install tslint -g
2tslint --init

TS Linter 的规则非常多,而且默认规则限制非常严格,可以适当根据提示解除一些限制。比如 作者就采用了如下配置,仅供参考:

 1{
 2    "defaultSeverity": "error",
 3    "extends": [
 4        "tslint:recommended"
 5    ],
 6    "jsRules": {
 7    },
 8    "rules": {
 9        "interface-name": false,
10        "trailing-comma": false,
11        "max-classes-per-file": false,
12        "ordered-imports": false,
13        "variable-name": false,
14        "prefer-const": false,
15        "member-ordering": false,
16        "no-bitwise": false,
17        "forin": false,
18        "object-literal-sort-keys": false,
19        "one-line": [false],
20        "object-literal-key-quotes": [false],
21        "no-string-literal": false,
22        "no-angle-bracket-type-assertion": false,
23        "only-arrow-functions": false,
24        "no-namespace": false,
25        "no-internal-module": false,
26        "unified-signatures": false,
27        "ban-types": false,
28        "no-conditional-assignment": false,
29        "radix": false
30    },
31    "rulesDirectory": []
32}

如果想在 Visual Studio Code 里面实现 TS Linter 智能提示,则还需要安装 VSCode 的 TSLint 扩展。

现在,可以尽情地编写代码了。

4. 维护

4.1. 版本号维护

维护一个包,肯定是要进行包的版本升级的。如何进行呢?手动修改 package.json 的 version 字段是一个办法,但是显得有点 low。可以使用下面的命令:

1npm version v0.1.0      # 版本号变成 0.1.0,即显式设置版本号。
2npm version patch       # 版本号从 0.1.0 变成 0.1.1,即修订版本号加一。
3npm version minor       # 版本号从 0.1.1 变成 0.2.0,即子版本号加一。
4npm version major       # 版本号从 0.2.0 变成 1.0.0,即主版本号加一。

但是,除此之外,还有四条命令,用于创建“预发布版本”,也就是非稳定版本。

 1npm version v1.2.3
 2
 3# 版本号从 1.2.3 变成 1.2.4-0,就是 1.2.4 版本的第一个预发布版本。
 4npm version prepatch
 5
 6# 版本号从 1.2.4-0 变成 1.3.0-0,就是 1.3.0 版本的第一个预发布版本。
 7npm version preminor
 8
 9# 版本号从 1.2.3 变成 2.0.0-0,就是 2.0.0 版本的第一个预发布版本。
10npm version premajor
11
12# 版本号从 2.0.0-0 变成 2.0.0-1,就是使预发布版本号加一。
13npm version prerelease

注意: version 命令默认会给你的 git 仓库自动 commit 一把,并打一个 tag。如果不想它动你的 git 仓库,你应该使用 --no-git-tag-version 参数,例如:

1npm --no-git-tag-version version patch

如果你想一劳永逸,那么可以使用如下 NPM 设置彻底禁止它:

1npm config set git-tag-version false  # 不要自动打 tag
2npm config set commit-hooks false     # 不要自动 commit

4.2. 使用标签

以 TypeScript 为例,通过 npm info typescript 可以看到 dist-tags 字段有着五个 值,分别是 latest, beta, rc, next, insiders,这些都是 dist-tag,可以 称之为标签——你可以把它理解为 git 里面的分支。

有什么用呢?其实,我们平时用 npm install xxxxxx 的时候,是使用了一个潜在的选项 tag = latest,可以通过 npm config list -l | grep tag 看到。

因此实际上是执行了 npm install xxxxxx@latest。也就是安装了 latest 这个标签 对应的最新版本。

不同的标签可以有不同的版本,这就方便我们发表非稳定版本到 npm 上,与稳定版本分开。

默认是发布到 latest 标签下的。

例如 npm publish --tag dev 就可以发布一个版本到 dev 标签下。

4.3. 使用前缀

如果你使用过 AngularJS 或者 TypeScript,那么肯定知道有一些包的名字是这样的:

  • @types/node
  • @types/jquery
  • @angular/core

这里面的 @types/@angular/ 叫做包前缀(scope)。

作者起初以为使用包前缀也是收费的,后来仔细阅读了文档才发现公开的包可以免费使用 包前缀。

那我们怎么使用呢?很简单,首先在 package.json 里面把 name 字段加上一个前缀。

前缀必须是你 NPM 账户的用户名,比如你注册了一个用户名为 abc 的账户,则你只能使用 @abc/ 为你的包前缀。

举个例子,将你的包名设置为 @abc/test

如果你要初始化一个带包前缀的包,则可以使用下面的命令。

1npm init --scope=abc # 当然你还可以加上个 `-y` 快速创建。

或者你想每次都使用 @abc/ 包前缀?加个设置即可:

1# 这样每次初始化新的 package.json,都将自动应用 @abc/ 包前缀。
2npm config set scope abc

现在,可以发布你的包到 npmjs.org 了。哦不,别忘了一点:

官方文档表示:所有带前缀的包,在发布的时候,默认都是发布为私有包。

这意味着你不能就这么发布,因为你(可能)不是付费用户,不能发布私有的包。那怎么办呢?别 担心,npm publish 命令还有一个参数 --access ,通过这个参数可以指定发布的是公共包 还是私有包。因此,只要用下面的命令就可以发布一个公共的,带包前缀的包了:

1npm publish --access=public

(完)

comments powered by Disqus