個人で作った 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",