はじめに
なんだかんだWebのフロントで一番きついのは、環境構築だと思います。
フロントを始めた時に、こんなドキュメントがあればハマらなかっただろうな…と思うことを備忘します。
ただ、こんな記事を書いておいてなんですが、よくわからなければ公式ドキュメントとエラーメッセージをよく読むのが解決への近道です。
前提条件
タイトルにも書いてある通りNode.jsとnpmのインストールは一通り終わってる前提です。
そして、型がないと不安でプログラムが書けない人向けです。
目指す姿
- Linterできちんとビルド前に静的チェックされる
- TypeScriptで書いたコードが無事index.htmlとかとともにディレクトリに出力される
- ちゃんとデバッグできるようにする
まず.gitignore
何はともあれ、.gitignoreを最初にコミットします。
github/gitignoreからNode.gitignoreとIDEのxxx.gitignore(筆者はWebstorm使ってるのでJetBrains.gitignore)、あと必要に応じてmacOS.gitignoreなどをマージして作ってます。
package.jsonを作る
package.jsonとはnpmの設定ファイルです。
ですので、これを置いておけば、同じ環境のnpmならnpmコマンドは同じように動きます。
作り方は簡単でリポジトリルートで
npm init
するだけです。そうすると
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
が表示されて、幾つかの質問をされるので、それに適当に応えるだけです。
殆どの項目はnpmモジュールとして切り出す際に使われるであろう項目だと思うので、そこまで真剣になる必要はないと思っています。
一応、聞かれる項目は以下の通り。
- package name
- デフォルトでディレクトリの名前が入ってるので、そのままで
- version
- デフォルトで1.0.0が入ってるので、特段こだわりなければそのままで
- description
- このモジュールの説明を書いておきましょう
- entry point
- このモジュールがrequireされたときのエントリーポイントです、npmモジュールとして切り出したりするなら別ですがウェブサイトをそのままnpmパッケージにすることはないと思うので、デフォルトのままでOKです
- test command
- ここではテスト実行時に走らせるコマンドを登録するのですが、テストについては別記事で解説する予定なので、一旦空のままにしておきます
- git repository
- デフォルトで正しいアドレスが入ってれば、そのままでOK
- keywords
- キーワードなるものの設定、スペース区切りで複数設定可能
- author
- 作者情報、これもgitのユーザー名がデフォルトで入ると思うので、こだわりなければそのままで
- license
- ライセンス情報、利用可能な文字列は[SPDX](https://spdx.org/licenses/)を参照
これらを指定するのが面倒なら
package.jsonでprivate
というパラメータを使用することができます。これをtrue
にすることで、上記の情報を設定しなくてもエラーが出なくなります。その場合は、以下のようなpackage.json
を自分で配置すればOKです。
{
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
必要なモジュールをインストールする
それでは必要なモジュールをインストールします。
React
↓のコマンドでインストールできます。
npm install --save react react-dom
npm install --save-dev @types/react @types/react-dom
@types/xxxというやつは型定義ファイルというもので、JavaScriptで書かれたモジュールのTypeScriptの型を定義したものです。基本的にはTypeScriptでnpmライブラリを使うならば、@types/xxxを一緒にインストールしておけばよろしいのですが、パッケージそのものに型定義がついているものもあるので注意が必要です。(大抵は公式にインストールの仕方書いてあるし、間違えて@types/xxxインストールしたらnpmがwarnしてくれます。)
詳しくはこちらを参照。
React以外
こっちが鬼門で、筆者は結局
- TypeScriptで書く(じぶん)
- TSLint通す(tslint)
- JavaScriptに変換する(typescript)
- いっこのjsファイルにする(webpack)
という流れに落ち着いてます。create-react-appとかparcelとかgulpとか色々ありまくりなのですが、これが、それなりの自由度で、それなりに息が長そうな組み合わせだと勝手に思ってます。
それぞれについてちょっとした説明と、インストールの仕方を以下に書いておきます。
webpackのインストールの仕方
webpackはたくさんのjsファイルを1個にしてくれるすごいやつです。あと、いろんなloaderを注入できるのでこいつでビルドチェインを作ります。
webpackのインストールは↓
npm install --save-dev webpack webpack-cli
TypeScriptのインストールの仕方
こいつのおかげで、型が付きます!ありがたいです!ts-loaderをwebpackに注入して使うとビルドの中で変換をやってくれます。
インストールは↓
npm install --save-dev typescript ts-loader
Tslintのインストールの仕方
TypescriptのLinterとwebpackにいれるtslint-loaderをインストールしておきます。
今回は設定はairbnbのものを使うので、それもまとめてインストールします。
インストールは↓
npm install --save-dev tslint tslint-loader tslint-config-airbnb
html-webpack-pluginをインストールしておく
index.htmlをビルドの時にターゲットディレクトリに配置してくれるすごいやつです。これがあれば細かいことは気にせず、ターゲットディレクトリをまるっとあげるだけでOKになります。
インストールは↓
npm install --save-dev html-webpack-plugin
webpack-serveもインストールしておく
開発用の簡易Webサーバー。これがあるとブラウザで見れるので、いい感じ。
インストールは↓
npm install --save-dev webpack-serve
Babelとかいうやついらんの?
当初これがよくわかりませんでした。babel-preset-envがどうのこうのとか色々書いてあって、何がどうなっとるんだと。
結論から言うといりません。BabelはナウでヤングなJavaScriptをどこでも動くようにするものです。TypeScriptがもし仮にナウでヤングなJavaScriptしか吐けないのであれば、組み合わせて使う必要があると思いますが、TypeScriptはビルドターゲットをES2015にできますので、これだけで充分です。
webpackの設定
webpackは設定そのものがJavaScriptになっていて自由度が非常に高いですが、最低限のことを書くだけならとても簡単だし、モジュールが注入しやすいです。
webpackは実行時に設定ファイルを指定できますので、いくつか置いておいて叩きワケできるようにしておくとよいです。
本番用
本番用のビルド設定は、デバッグのための設定なしでみたいな感じで設定します。
ソースとコードコメントというかたちで何をやっているか書いておきます。
const path = require('path'); // 絶対パスに変換するために
const htmlWebpackPlugin = require('html-webpack-plugin'); // index.htmlをビルドチェインの中で作っちゃう
module.exports = {
mode: 'production', // 使える文字列が決まってる、本番用なのでproduction。
entry: './src/index.tsx', // エントリポイントの指定、src下に書いていくので src/index.tsxにしとく
module: {
rules: [
{ // Linterを走らせる
enforce: 'pre', // ビルド前処理だよってこと
loader: 'tslint-loader', // tslint-loaderを使う
test: /\.tsx?$/, // tslint-loaderに渡すファイルの正規表現。xxx.tsとxxx.tsxの正規表現。
exclude: [ // 渡さないファイル
/node_modules/
],
options: {
emitErrors: true // これ設定しとくとTSLintが出してくれたwarningをエラーとして扱ってくれる、要するに-Wall
}
},
{
loader: 'ts-loader', // ts-loaderを使う、こいつがトランスパイルしてくれる
test: /\.tsx?$/,
exclude: [
/node_modules/
],
options: {
configFile: 'tsconfig.prod.json' // TypeScriptのコンパイル設定ファイル
}
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ] // importの時に、これらの拡張子は解決してもらえる、要するにHoge.tsxをimport Hoge from './Hoge'みたいに書ける
},
output: {
filename: 'static/js/bundle.js', // 仕上がりファイルの置き場
path: path.resolve(__dirname, 'dist') // 出力ディレクトリの指定の絶対パス
},
plugins: [
new htmlWebpackPlugin({
template: "index.html" // 同じ階層にあるindex.htmlを元に、デプロイ用のindex.htmlを作って出力ディレクトリに配置してくれる
})
]
};
開発用
開発用のビルド設定は、デバッグのための設定をいれてみたいな感じで設定します。
本番用とのdiffというかたちで乗っけておきます。
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
- mode: 'production',
+ mode: 'development',
entry: './src/index.tsx',
+ devtool: 'inline-source-map', // デバッグできるように
module: {
rules: [
{
enforce: 'pre',
loader: 'tslint-loader',
test: /\.tsx?$/,
exclude: [
/node_modules/
],
options: {
emitErrors: true
}
},
{
loader: 'ts-loader',
test: /\.tsx?$/,
exclude: [
/node_modules/
],
options: {
- configFile: 'tsconfig.prod.json'
+ configFile: 'tsconfig.dev.json'
}
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: 'static/js/bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new htmlWebpackPlugin({
template: "index.html"
})
]
};
ブラウザ確認用
ブラウザ確認用のビルド設定は、開発用のビルド設定にwebpack-dev-serverの設定を加えます。
こちらは開発用とのdiffというかたちで乗っけておきます。
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.tsx',
devtool: 'inline-source-map',
module: {
rules: [
{
enforce: 'pre',
loader: 'tslint-loader',
test: /\.tsx?$/,
exclude: [
/node_modules/
],
options: {
emitErrors: true
}
},
{
loader: 'ts-loader',
test: /\.tsx?$/,
exclude: [
/node_modules/
],
options: {
configFile: 'tsconfig.dev.json'
}
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: 'static/js/bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new htmlWebpackPlugin({
template: "index.html"
})
],
+ serve: {
+ content: path.resolve(__dirname, 'dist'),
+ port: 3000,
+ }
};
TypeScriptとTSLintの設定
webpack内で設定した、tsconfigとLinterの設定をここでつくります。
tsconfig.xxx.json
デバッグ用の設定を入れる入れないを切り分けるので、2つ作ってます。
違いは"sourceMap": true
の部分だけですので、開発用は本番用を拡張しています。
{
"compilerOptions": {
"module": "es6",
"target": "es5",
"jsx": "react",
"lib": ["es2018", "dom"],
"moduleResolution": "node",
"removeComments": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
{
"extends": "./tsconfig.prod.json",
"compilerOptions": {
"sourceMap": true
}
}
- sourceMap
- デバック用の設定
- module
- 変換のためのモジュール指定、変換先がES2015のときはES6かES2015を指定しとく
- target
- できあがりはだいたいの環境で動くようにES2015にしとく
- jsx
- React用にreactを指定
- lib
- 標準ライブラリの読み込み、es201xとdomを指定しておく
- moduleResolution
- モジュールの解決方式を指定、Node.js方式にするためnodeを指定
- removeComments
- ビルド成果物からコメントを取り除く
- strict
- 怪しげな書き方をできないように
- noUnusedLocals
- 未使用のローカル変数禁止
- noUnusedParameters
- 未使用のパラメータ禁止
- noImplicitReturns
- 怪しげなReturn禁止
- noFallthroughCasesInSwitch
- switchのときのdefaultし忘れ防止
tslint.json
ベースはairbnbのものを使ってインデントサイズだけ4にしてます。
tslint.jsonというファイル名で他と同様ルートにおいておけばいい。
{
"extends": "tslint-config-airbnb",
"rules": {
"ter-indent": [true, 4]
}
}
package.jsonに設定を追加
最後に、npm run xxx
でビルドとかできるように設定を追加しておきます。
package.jsonのscriptsというエレメント内にすでにtestがあるはずなので、その下に追加しましょう。
testの行は、今回使いませんので削っておきましょう。
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "start": "webpack-serve --config webpack.config.preview.js",
+ "build": "webpack --config webpack.config.dev.js",
+ "build:prod": "webpack --config webpack.config.prod.js"
},
これで、npm run start
でブラウザ確認用、npm run build
で開発用、npm run build:prod
で本番用ビルドが走ります。
最後に
これで環境は完成です。こんなディレクトリ構成になっているはずです。
./
├ node_modules // 外部パッケージのインストール先、バージョン管理対象外
├ .gitignore
├ package.json
├ package-lock.json // npmが勝手に作る管理用ファイル、バージョン管理対象
├ README.md
├ tsconfig.dev.json
├ tsconfig.prod.json
├ tslint.json
├ webpack.config.dev.json
└ webpack.config.prod.json
ここに実際にindex.htmlとTypeScriptソースを追加してみます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>react sample</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
import * as React from 'react';
import * as ReactDOM from 'react-dom';
ReactDOM.render(
<div>Hello world</div>,
document.getElementById('root'));
npm run build
もしくは、npm run build:prod
するとdistディレクトリに全てが出力されます。
./
├ dist
│ ├ index.html
│ └ static
│ └ js
│ └ bundle.js
│
├ node_modules
├ src
│ └ index.tsx
├ .gitignore
├ index.html
├ package.json
├ package-lock.json
├ README.md
├ tsconfig.dev.json
├ tsconfig.prod.json
├ tslint.json
├ webpack.config.dev.json
└ webpack.config.prod.json
またnpm run start
すれば、http://localhost:3000 でHello worldが表示されます。(事前ビルド不要)
できあがったソース群はこちら
https://github.com/IgnorantCoder/webpack-typescript-react-sample
おまけ (Debug on WebStorm)
WebStormによるデバッグがやや癖があったのでおまけに記載。
- Run > Edit Configurations
- +ボタンからJavaScript Debugを追加、URLにhttp://localhost:3000 を追加
- npm run startしてから、この追加した構成をDebug buildするとアタッチされる