環境
- node 9.4.0
- TypeScript 2.9.2
- npm 6.1.0
- webpack 4.16.3
- Visual Studio Code 1.25.1
予めtj/n: Node version management等を使って、Node.jsをインストールしておきましょう。
実践
適当な名前でプロジェクトディレクトリを作っておきます。ココではts-sample
とします。
$ mkdir ts-sample
$ cd ts-sample/
$ git init
$ curl https://raw.githubusercontent.com/github/gitignore/master/Node.gitignore > .gitignore
webpackでTypeScriptのBuild環境構築
まずpackage.json
を作成します。
$ npm init
適当に答えていくと、こんな感じになります。
{
"name": "ts-sample",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT"
}
TypeScriptと、ビルドするために必要なパッケージを、devDependencies
でインストールします。
今回はwebpackを使うため、ts-loader
もインストールします。
$ npm install --save-dev webpack webpack-cli typescript ts-loader
TypeScriptの設定であるtsconfig.json
を作成します。
手動で作成しても良いですが、$ tsc --init
で自動生成することもできます。
$ ./node_modules/typescript/bin/tsc --init
webpackの設定であるwebpack.config.js
を作成します。
module.exports = {
entry: './src/index.ts',
target: 'node',
module: {
rules: [
{
test: /\.ts$/,
use: ['ts-loader'],
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
}
};
npmパッケージとして公開しない場合の設定
もしnpmパッケージとして公開する予定がない場合は、package.jsonに"private": true
を付けておきましょう。誤って$ npm publish
で公開してしまう事を回避できます。
また、このときdescription
など「公開しないならば不要な設定項目」は一緒に消してしまっても構いません。(private
でない場合、package.jsonが正しく設定されていないと各種警告が表示されます)
{
- "name": "ts-sample",
- "version": "1.0.0",
- "description": "",
+ "private": true,
"main": "index.js",
参考: package.json | npm Documentation
ソースコードの記述
TypeScriptのコーディングを始める前に、Node.js自体の型定義ファイルをインストールしておきます。
$ npm install --save-dev @types/node
実際のソースコードを書いていきます。
$ mkdir src
$ code src/index.ts
今回は例として、コマンドライン引数から数字を受け取って、Fizz Buzzで返すだけのCLIプログラムを書いてみます。
const num : number = +process.argv[2];
console.log(fizzbuzz(num));
function fizzbuzz(num : number) : string {
if (num % 15 == 0) {
return "FizzBuzz";
} else if (num % 3 == 0) {
return "Fizz";
} else if (num % 5 == 0) {
return "Buzz";
}
return num.toString();
}
ビルドして実行
webpackでビルドするnpm scriptを記述します。
"scripts": {
+ "build": "webpack",
ビルドします。
$ npm run build
実行してみます
$ node dist/main.js 1
1
$ node dist/main.js 2
2
$ node dist/main.js 3
Fizz
うまく動きました。
npm経由でコマンドとして実行
作成したプログラムを、CLIアプリケーションとして実行できるように設定していきます。
まずpackage.jsonにbin
の設定を追加します。
+ "bin": {
+ "ts-sample": "./bin/ts-sample.js"
+ },
参考: package.json | npm Documentation
設定したbin/ts-sample.js
ファイルからプログラムを実行できるようにします。
#!/usr/bin/env node
require('../dist/main.js');
$ npm link
コマンドを使うと、このプロジェクトに対してシンボリックリンクを貼り、ローカルでnpm moduleをglobal installしたかのように使えるようになります。
$ npm link
こうしてpackage.json
内でbin
に指定したキーで、コマンドが実行になります。
$ ts-sample 15
FizzBuzz
開発/本番の各ビルドステージの設定
現在出力されたjs:dist/main.js
は、プロダクションビルドの設定になっています。
中身はminifyされており単体では読むことができません。
!function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){"use strict";var n=+process.argv[2];console.log(function(e){if(e%15==0)return"FizzBuzz";if(e%3==0)return"Fizz";if(e%5==0)return"Buzz";return e.toString()}(n))}]);
また、開発時はビルド用になんどもコマンドを打つのも面倒です。
- 開発時にはファイルの変更を監視したい
- 開発時にはソースマップを有効にしたい
- 本番ビルドではソースマップを無効にしたい
- 後に
npm publish
する直前に自動で本番ビルドしたい
これらの問題を解決するため、webpackの設定を開発用と本番用で分離します。
webpack.dev.js
を作成し、webpack.config.js
を読み込んで設定をmergeします。
まずmergeするためのライブラリを読み込みます。
$ npm install webpack-merge --save-dev
webpack.dev.js
を作成し、下記の記述をします。
const merge = require('webpack-merge'); // v5以上は { merge } = require('webpack-merge') とする
const common = require('./webpack.config.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
});
package.json
に、それぞれのビルド用のコマンドを追記します
"scripts": {
+ "prepare": "webpack --config webpack.config.js",
+ "watch": "webpack --config webpack.dev.js --watch"
},
こうして、開発時はnpm run watch
することでファイル変更時に自動でビルドされるようになりました。
$ npm run watch
このときはdist/main.js
の中身も読みやすいものになっています。
var num = +process.argv[2];
console.log(fizzbuzz(num));
function fizzbuzz(num) {
if (num % 15 == 0) {
return "FizzBuzz";
}
else if (num % 3 == 0) {
return "Fizz";
}
else if (num % 5 == 0) {
return "Buzz";
}
return num.toString();
}
またprepare
は、$ npm publish
する前などに自動的に実行されます。なので、公開時には自動的に本番の設定でビルドされたものがpublishされます。もちろん、$ npm run prepare
で手動実行することもできます。
参考: scripts | npm Documentation
VSCodeでWatchする
ここではVisual Studio Codeのnpm連携機能を使ってみます。
コマンドパレットから>Tasks: Configure Task
→npm: watch
すると、下記JSONが生成されます。
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": []``
}
]
}
コマンドパレットからtask npm: watch
すると、ターミナルウィンドウがひとつ自動的に立ち上がり$ npm watch
を実行します。
また、VSCodeの設定でnpm.enableScriptExplore
を有効にすると、package.json
内のnpm scriptsを読み取ってサイドバーに表示されます。
ここでwatchをクリックすることでも$ npm watch
を実行できます。
npmignoreの設定
ここでは、npmパッケージとして公開したい場合を考えます。
- gitリポジトリには、
dist/
以下のビルド済JavaScriptファイルは無視して、ビルド前のTypeScriptソースコードのみを登録したい - npmパッケージとしては、
src/
以下のTypeScriptソースコードは無視して、ビルド済のJavaScriptファイルのみを登録したい
これらを実現するため、.gitignore
に下記追記します
dist/
そして.npmignore
を作成し、下記追記します
src/
すると、gitリポジトリにはTypeScriptコードのみが含まれ、npmパッケージにはビルド済JavaScriptファイルのみが含まれるようになります。
参考: developers | npm Documentation
利用
GitHubに上がっていれば、npm install
コマンドでインストールすることが可能です。
$ npm install --global github_user_name/project_name
そしてnpmパッケージとして$ npm publish
済であれば、下記コマンドでnpmからインストールできます。
$ npm install --global project_name
その他の詳細は、公式ドキュメントが詳しいです。
https://docs.npmjs.com/cli/install
参考
- React & Webpack · TypeScript
- How to create and publish an npm module in TypeScript
- Node.jsでCLIアプリ · JavaScriptの入門書 #jsprimer
また、こうしてできたCLIツールが下記になります。
s2terminal/i_read_u: Extracting commands from README markdown file