個人で作った msgpack-rpc-lite という Node.js 向けパッケージを npm で公開しています。その CI 環境には GitHub と Travis CI と Codacy を使っています。
当初 JavaScript で書いていたものを TypeScript で書き直しました。その時悩んだことやハマったことで学んだコツを備忘録としてまとめました。
なお、TypeScript での書き直しで行った変更点は Comparing v0.0.6...v0.1.5 · naokikimura/msgpack-rpc-lite でご覧いただけます。
[Travis CI] skip_cleanup: true でデプロイ前のクリーンアップをスキップする
Travis CI のデプロイは、デフォルトではリリース前に install や script などのそれ以前のステップで追加や変更されたファイルをクリーンアップします。 tsc のコンパイル結果や npm install で取得した依存パッケージは消されてしまいます。それを回避するために deply の skip_cleanup オプションに true を設定します。
language: node_js
deploy:
provider: npm
+ skip_cleanup: true
email: "YOUR_EMAIL_ADDRESS"
api_key: "YOUR_AUTH_TOKEN"
on:
tags: true
repo: "YOUR_REPO"
ハマったこと
このような .travis.yml だったとき、デプロイが sh: tsc: command not found となって失敗しました。 install ステップでは失敗しなかったのに、なぜって感じでした。
language: node_js
node_js:
- "8"
deploy:
provider: npm
email: "YOUR_EMAIL_ADDRESS"
api_key: "YOUR_AUTH_TOKEN"
on:
tags: true
repo: "YOUR_REPO"
Releasing build artifacts - npm Releasing - Travis CI にちゃんと書いてありました。ドキュメントはよく読まないとダメですね。
参考
- Releasing build artifacts - npm Releasing - Travis CI
- Deploying your Code - Customizing the Build - Travis CI
[npm] .npmignore でパッケージに含めるファイルを制御する
.gitignore がある場合は .npmignore で別途パッケージに含めないファイルを指定しましょう。 .npmignore がないと .gitignore で無視しているファイルはパッケージに含まれません(逆に言うと、必要なファイルがパッケージに含まれないことがあります)。
ハマったこと
tsconfig.json で outDir コンパイラーオプションを設定してコンパイル結果の出力先を指定しました。 package.json の main フィールドもそれに合わせて変更しました。 また Git のトラッキング対象外するため .gitignore にも追記しました。
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
+ "outDir": "./dist",
{
"name": "msgpack-rpc-lite",
"version": "0.1.1",
- "main": "./index.js",
+ "main": "./dist/index.js",
coverage
.nyc_output
node_modules/
+ dist/
この状態で npm publish すると、npm はパッケージにコンパイル結果を含めてくれません。公開したパッケージを利用しようとしても Error: Cannot find module 'msgpack-rpc-lite' となってしまいます。
そこで .npmignore を .gitignore と同じ場所に作って、パッケージに含めたくないファイルを別途指定しました。なお、 node_modules は .npmignore には不要です。
別途指定といいましたが、実際は .gitignore をコピー & リネームして、パッケージに含めたいものを除外エントリーから取り除いただけです。
coverage
.nyc_output
参考
- Note on
.gitignore- npm Releasing - Travis CI - Keeping files out of your package | developers | npm Documentation
[npm] prepublish / prepublishOnly / prepare の違いを理解する
npm にパッケージを公開するためには、その準備として TypeScript コンパイラー tsc を実行してコンパイル結果を出力しておく必要があります。
npm install の後と npm publish の前の両方で発生する prepare イベントは、 tsc を実行するタイミングとして適しています。
しかし、 Node.js v6 以前 (厳密に言うと npm v4.2.0 より前) は、 prepare イベントは発生しません。そのため、必要であれば適宜自前で npm run prepare と実行する必要があります。 Travis CI であれば before_script と before_deploy のステップが prepare イベント発生に近いタイミングです。
language: node_js
node_js:
- lts/*
- 6
before_script:
- if [ $(echo "`npm -v 2>&1 | cut -d. -f-2` < 4.2" | bc) -eq 1 ]; then npm run prepare; fi
...
before_deploy:
- if [ $(echo "`npm -v 2>&1 | cut -d. -f-2` < 4.2" | bc) -eq 1 ]; then npm run prepare; fi
...
悩んだこと
TypeScript コンパイラー tsc を npm スクリプトで定義したいけど、どのスクリプトが適切なのかで悩みました。
Travis CI のデプロイライフサイクルは、ざっくりいうと npm i && npm t && npm publish だといえると思います。このライフサイクルで発火するイベントのスクリプトに tsc を実行するように定義したと考えました。
build-
prepublish/prepublishOnly/prepare
悩んだ末、今回は prepare に定義しました。
{
"name": "msgpack-rpc-lite",
"version": "0.1.3",
"main": "./dist/index.js",
"scripts": {
...
"prepare": "tsc"
},
...
}
build
よく見かけるのが、 build スクリプトで定義しているケース。
ただ、イベントで発火するスクリプトではない様子。 npm install や npm publish と連動して実行してほしいのに。 npm build としても定義したスクリプトは実行されず、 npm run build としないと実行されない。
...なんかしっくりこない。流儀に則っていない感がする。 Visual Studio Code を使っていて、手動ビルドやウォッチはビルドタスクで実行できるので、 build スクリプトとして定義しておく必要性がとくにない。ただ tsc を実行したいだけだし。
あと npm build は何に使うのかがよくわからない。
prepublish / prepublishOnly / prepare
npm publish に連動して実行される prepublish / prepublishOnly / prepare ですが、 npm のバージョンによって振る舞いがかなり異なります。詳しくは npm prepublish の現状と今後どう変わっていくか - Qiita を参照してください。
参考
- PREPUBLISH AND PREPARE | scripts | npm Documentation
- npm prepublish の現状と今後どう変わっていくか - Qiita
- The Build Lifecycle - Customizing the Build - Travis CI
[TypeScript] モジュールなら宣言ファイル (.d.ts) も公開する
パッケージをモジュールとして公開するなら宣言ファイル (.d.ts) も合わせて公開しましょう。
tsc の declaration コンパイルオプションに true を設定し、 package.json の types フィールドに出力される宣言ファイルを設定します。
ハマったこと
公開したパッケージを TypeScript でモジュールとして利用しようと import してコンパイルします。
import * as from 'msgpack-rpc-lite';
すると、次のようなエラーがでます。コンパイラーが「モジュール 'msgpack-rpc-lite' の宣言ファイルが見つかりませんでした。」と怒っています。
error TS7016: Could not find a declaration file for module 'msgpack-rpc-lite'. '~/Documents/workspace/jubaclient/node_modules/msgpack-rpc-lite/dist/index.js' implicitly has an 'any' type.
Try `npm install @types/msgpack-rpc-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'msgpack-rpc-lite';`
そこで、モジュールの tsconfig.json に "declaration": true を追加しました。
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
+ "declaration": true,
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
}
"include": [
"src/**/*"
]
}
加えて、 package.json に "types": "./dist/index.d.ts" を追加しました。
{
"name": "msgpack-rpc-lite",
"version": "0.1.4",
"main": "./dist/index.js",
+ "types": "./dist/index.d.ts",