Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/app/_shared/api/articles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import {Article} from '../../models/article';
import {Observable} from 'rxjs/Observable';

const items: Article[] = [
{
id: '从 Angluar-CLI 到 NPM',
title: '从 Angluar-CLI 到 NPM',
summary: '使用 Angular-CLI 开发的 module 要如何发布到 NPM 上呢?',
content: require('./timliu/ng-cli-to-npm/_index.md'),
first: true,
tags: ['Angular', 'npm'],
authors: ['timliu'],
},
{
id: 'Angular2中的StructuralDirective',
title: 'Angular 2 中的 Sturctural Directive',
Expand Down
201 changes: 201 additions & 0 deletions src/app/_shared/api/articles/timliu/ng-cli-to-npm/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# 从 Angluar-CLI 到 NPM

## 写作目的
使用 Angluar-CLI 写好了一个 module,需要重复使用,能不能发到 NPM 上去呢?
本文将用试错的方式来制作一个 `tl-ui` package(里面有一个 `tl-ui` module),然后发布到 NPM。(`tl-ui` 只是个随意起的名字。)

## 读者指引
- 本文所述方法是笔者通过试错摸索出来的,过程中大量参考了 [@ng-bootstrap/ng-bootstrap][]。
- 试错固然啰嗦,需要干货的同学请移步“总结”部分。
- 本文在测试安装本地 package 的过程中,没有使用 `npm link` -- 笔者试验多次均以 Error 告终。
- 阅读本文,需要读者简单了解一下知识点:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“以”下知识点:

- [`tsconfig.json` 的配置](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
- [Angular-CLI 指令](https://github.com/angular/angular-cli)
- [npm 指令](https://docs.npmjs.com/)

## 制作 tl-ui package (里面有个 tl-ui module)
敲指令:
```
npm i angular-cli -g
npm i typescript -g
ng new tl-ui // 这个是 package
cd tl-ui
ng g module tl-ui // 这个是 module
```
### 更改标签前缀设置
看一下 `src\app\tl-ui\tl-ui.component.ts`,selector 是 `app-tl-ui`,不想要那个 app 前缀?删掉不就行了?
selector 改成 `tl-ui`之后,tslint 又报错,“你得有 app 前缀啊”。可以这样:
- 修改 `angular-cli.json`:`"apps": [{ "prefix": "app"}]` => `"apps": [{ "prefix": ""}]`,这样 `ng g` 的时候就不会有自动前缀了。
- 修改 `tslint.json`:`directive/component-selector-prefix": [true, "app"]` => `directive/component-selector-prefix": [true, "app", "tl"]`,这样 tslint 就接受 tl 这个前缀了。

### serve 起来
然后,我们在 app.module 里使用 tl-component,看一下效果:

```ts
// `tl-ui\src\app\tl-ui\tl-ui.module.ts`
exports: [TlUiComponent]
// 'tl-ui\src\app\app.module.ts'
imports: [TlUiModule]
```
```html
<!-- `tl-ui\src\app\app.component.html` -->
<tl-ui></tl-ui>
```
`ng serve`,看到 'tl-ui works'。真不容易, module 算是制作完毕了。别忘了 `git commit`,出岔子的时候好 roll back。

## 安装本地的 tl-ui package
要使用某个 npm package,我们无非就是 `npm i xyzPackage` ,而后 `import { a, b, c } from xyzPackage` 。
能不能也这样来使用本地的 `tl-ui` package 呢?

### 转移 dependencies
让我们先切换到另一个 project 里:
```
> cd .. // 退出 tl-ui folder
> ng new another-project
> cd another-project
> npm i ../tl-ui (别冲动,先不要敲回车)
```
在安装一个 package 的时候,npm 会根据 package.json 来获取相关依赖(dependencies 项目下的内容)。
我们要在 `another-project` 下面使用 `tl-ui` module,真的需要安装 `tl-ui\package.json` 里面 denpendencies 吗?
先看一下 `tl-ui.module.ts` 和 `tl-ui.component.ts`,我们用到的只有 `@angular/core` 和 `@angular/common`。
但凡是个 angular 2 project,就会有这两个依赖,所以,我们可以先这样:将 `tl-ui\package.json` 里的 dependencies 下的内容,全部转移到 devDependencies 下面。
然后再在 `another-project` 目录下 `npm i ../tl-ui`。

现在可以 `npm i ../tl-ui`了。10秒钟左右就安装好了。我们可以通过查看 `another-project\node_modules\tl-ui` 来验证。
至于能不能用,通过 import 来试试看吧。

```ts
// `another-project\src\app\app.module.ts
import { TlUiModule } from 'tl-ui/src/app/tl-ui/tl-ui.module';
@NgModule({imports: [TlUiModule]})
```
还真能找到 `TlUiModule`。注意 `from 'tl-ui/src/app/tl-ui/tl-ui.module'`,这里最前面的 `tl-ui`不再是目录名,而是 package 的名字,对应 `tl-ui\package.json` 里的 name。

到底能不能用呢?(见证奇迹的时刻到了...)
```
> (at another-project) npm start (就是 ng serve)
```
然而,报错了:`Cannot find module 'tl-ui/src/app/tl-ui/tl-ui.module'`。
这是几个意思?搜索搜索,也没弄明白。这时我们可以去参考其他 angular 的第三方 package,比如 [@ng-bootstrap/ng-bootstrap][]。

### 创建根 index,并编译 ts 文件
如果我们安装了第三方库 @ng-bootstrap/ng-bootstrap,在 `another-project\node_modules\@ng-bootstrap\ng-bootstrap` 下,是怎样一番景象呢?
- 有 index.js,这样我们在 import 时,就不用写一长串的路径了;
- 每个 js 文件配备一个 d.ts 文件,这些是 js 文件对应的类型声明(declaration)文件。

为什么要 js + d.ts?这个我没深究,估计是 webpack 在使用 node_modules 时需要 js 文件,typescript 又需要对应的 d.ts 文件。
不管这些,我们来照葫芦画个瓢:
- 在 `tl-ui` package 根目录下,新建 `index.ts`,添加一行:
```ts
export { TlUiModule } from './src/app/tl-ui/tl-ui.module';
```
- 在 `tl-ui` package 根目录下,新建 `tsconfig.json`,添加几行(declaration 选项就是是否生成 d.ts 文件的开关):
```json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": false,
"sourceMap": false,
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": ["es6", "dom"]
},
"files": ["index.ts"]
}
```
- 在 `tl-ui` package 根目录下,运行 `tsc -p tsconfig.json`。现在能看到 index.js 以及 tl-ui.module.js 等等,还有对应的 d.ts 文件。

### 编译 ts 文件后重新安装
重新安装试试看。
```
> (at another-project) npm i ../tl-ui
```
报错:
```
... Error: EPERM: operation not permitted, rename '...another-project\node_modules\tl-ui' -> '...another-project\node_modules\.tl-ui.DELETE'
... Please try running this command again as root/Administrator.
```
好吧,用 Administrator 打开 cmd,重新安装,顺利通过。(我是不是暴露身份了 -- 发现一个用 windows 的小白)
其实,我们也可以先运行 `npm uni tl-ui`,然后再安装,也可以通过。

### 将 html template 写在 component.ts 里
serve 起来看一下。(见证奇迹的时刻又到了...)
```
> (at another-project) npm start
```
又报错:`Error: ... Can't read the url "tl-ui.component.html"`。没有 webpack,没有 loader,那就把 html 写到 ts 里好了:

```ts
// tl-ui\tl-ui\tl-ui.component.ts
@Component({
selector: 'tl-ui',
template: `<p>tl-ui works!</p>`,
styles: []
})
```
重新编译,`tsc -p tsconfig.json`;重新安装,`npm i ../tl-ui`(Administator 也会报错,重装两遍通过);(双到了见证奇迹的时刻)serve 起来,`npm start`。

奇 迹 终 于 出 现 了 -- `tl-ui works!`。

## 发布 tl-ui package 至 npm,并从 npm 安装
发布很简单,`npm publish` 就好,刚才我们每次更改 package 以后都要编译,可以把编译工作放到 npm script 里:

```json
// tl-ui\package.json
"scripts": { "prepublish": "tsc -p tsconfig.json" },
```
然后,`npm publish`。报错:`This package has been marked as private`。
改 `package.json`,`"private": false`;重新 publish。报错:`auth required for publishing`。额,还没注册用户...
根据提示运行 `npm adduser`,添加用户名、密码、email,登上了。重新 publish。
`+ tl-ui@0.0.0`,一个加号,几个意思?先 `npm show` 一下:
```
> npm show tl-ui time
{ modified: '2016-12-20T15:35:57.084Z',
created: '2016-12-20T15:35:57.084Z',
'0.0.0': '2016-12-20T15:35:57.084Z' }
```
好像有戏。我们回到 another-project,卸载之前安装的 tl-ui,然后,(见证奇迹的时刻叒到了):
```
> (at another-project) npm uni tl-ui
> npm i tl-ui -S
```
安 装 成 功!serve 起来,`npm start`,看到`tl-ui works!`。It works!

别着急庆祝,我们看看,如果在 another-project 里不删除 tl-ui,重新发布 tl-ui 以后,安装时是否报错。
首先,更改 `tl-ui` package,随便改,template 里加两个叹号吧;然后发布;
报错,版本号没改;来到 `package.json`,改 ` "version": "0.0.1",`;重新发布,通过;
然后新开一个 cmd 窗口,不用 Administrator 身份,来到 `another-project` 目录,不卸载 `tl-ui`,运行 `npm i tl-ui`,通过;
`npm start`,`tl-ui works!!!`。

## 给我们的 package 在 github 上安个家
这个就不说了。

## 这才只是个开始
拿 `tl-ui\package.json` 跟 [@ng-bootstrap/ng-bootstrap][] 的比较一下,就知道,这才只是个开始,做成一个 npm package ... 的路还很长。翻滚翻滚...

## 总结
要把 Angular-CLI 做好的 module 发到 NPM 上,需要(步骤很多,但都很简单):
- `ng new package awesome-package`
- `ng g module awesome-module`
- 为了标签前缀,稍微修改 `angular-cli.json` 和 `tslint.json`
- 完成 awesome-module 的开发(template 和 styles 直接写在 component 里)
- 根目录新建 `index.ts`,并 re-export `awesoe-module`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome

- 根目录新建 `tsconfig.json`,设置 declaration 为 true
- 在 `package.json` 里:
- 转移 dependencies 至 devDependencies (也许要保留一些在 dependencies,也许要添加 peerDependencies)
- 将 private 改为 false
- 添加 script: `"prepublish": "tsc -p tsconfig.json"`
- 设定版本号
- 在本地测试安装,先运行 `npm run prepublish`(就是 tsc),再移步其他 project ,以相对路径来安装
- 通过本地测试以后,运行 `npm adduser`,`npm publish`
- 别忘了 `git commit`、`git push`
- 现在可以 `npm i awesome-package`了,然后 `import { awsome-module } from 'awesome-package'`。
- 不是最后的最后,按照 npm docs 的说法,我们还需要:Brag about it. Send emails, write blogs, blab in IRC. Tell the world how easy it is to install your program!

## 参考
- [@ng-bootstrap/ng-bootstrap][]
- [NPM Developer Guide](https://docs.npmjs.com/misc/developers)

[@ng-bootstrap/ng-bootstrap]: https://github.com/ng-bootstrap/ng-bootstrap