まずJavaScriptの歴史をざっと見てみる
1995年にJavaが登場したが、その勢いに便乗して、ブレンダン・アイクが開発したLiveScriptがJavaScriptへと改名したことにより、 JavaScript爆誕。
その翌年、マイクロソフトがJavaScriptのライセンスがなかったために独自のJScriptを開発。ところが、JavaScriptを開発していたNetscape Navigator社は、JavaScriptとJScriptの互換性があまりにひどかったことから1997年にECMA InternationalにJavaScriptとJScriptの統合を依頼。このJavaScriptのメイン機能を乗せた言語をECMAScript(ES)と言う。統合を依頼とはいったものの、この統合がなかなかうまく進まず、2000年代前後は、JavaScriptが重い・セキュリティホールになりがち、などと言った理由から人気が落ち、Flashの方が人気を博した。
しかし、2005年にGoogle mapがAjaxを駆使して非同期通信をWebブラウザからできるようにしたことで、JavaScriptが再度盛り上がることとなった。2008年にはV8というJavaScriptエンジンが登場したりと、さらに熱が高まっていった。
JavaScriptの最大の欠点は モジュール機能の欠如している ことだった。それを解決するべく生まれたのが CommonJS である。これはあくまで規格であって、2009年に登場したNode.jsはCommonJSの1つである。Node.jsの登場によって、JavaScriptはブラウザ以外からも利用できることが証明された。そして、Node.jsのようにCommonJSで作られたツール群をブラウザでも利用できるようにしたい、というニーズが生まれ、その解決方法がbrowserifyやWebpackである。gulpやgruntはタスクランナー(JavaScriptのコードを書いてから、実際にブラウザで使えるまでの諸々の作業を自動化してくれるツール)だがWebpackはその機能も含んでいる。
一方で、ECMAScriptの策定も進んでおり、2016年に策定されたECMAScriptはES7と呼ばれ、2015年に作成されたものはES6と呼ばれる。ES6やES7のように新しい構文が登場する一方で、ついていけないブラウザには babel と呼ばれるものを使って対応することになる。
以下を参照
Node.jsでAPIサーバを実装していく
では、APIサーバをNode.js(Express)で構築して行くことにする。フロントエンドをVue.jsやReact.jsで実装するとなると、同じリポジトリでAPIサーバ・フロントエンドの実装を行うことが想定されるが、その場合は、Node.jsもWebpackでES6の記法に従うことになるだろう(const hoge = require('hoge')
ではなく、 import hoge from 'hoge'
という記法になるイメージ)。
まずExpressをインストールして簡単なサーバを構築する
$ yarn add express
次にルートディレクトリに、 app.js
を作成して以下のように編集する。
// app.js const express = require('express'); const app = express(); app.get('/', function(req, res){ res.send({ message: 'hello world' }); }); app.listen(3000);
ディレクトリ構成は以下のようになる。
. ├── app.js ├── package.json └── yarn.lock
node app.js
とすると http://localhost:3000/ が起動するはず。
{ "message": "hello world" }
が返ってくれば成功。
Node.jsをWebpack+ES6に対応させる
今の状態だとモジュールのインポートがReact.jsなどと異なった形である( import hoge from 'hoge'
ではなく、 const hoge = require('hoge')
)。React.jsでお馴染みの flow や ESLint、Jestにも対応させるべく、WebpackでES6対応する。
Webpackのセットアップ
$ yarn add -D babel-core babel-polyfill babel-preset-env babel-loader@7 webpack webpack-cli webpack-merge flow-babel-webpack-plugin webpack-node-externals $ yarn add -D babel-regenerator-runtime babel-preset-es2015 babel-preset-stage-2
Webpackの設定ファイルを書く。
configというディレクトリを作成し、 webpack.config.base.js
という名前で以下のように実装する。
const path = require('path'); const nodeExternals = require('webpack-node-externals'); const FlowBabelWebpackPlugin = require('flow-babel-webpack-plugin'); module.exports = { target: 'node', externals: [nodeExternals()], mode: 'development', entry: ['babel-regenerator-runtime', './app.js'], output: { path: path.resolve(__dirname, '../dist'), filename: 'server.js', }, resolve: { modules: ['node_modules'], }, module: { rules: [ { test: /\.js$/, use: [ { loader: 'babel-loader', options: { plugins: ['transform-flow-strip-types'], presets: ['flow', 'stage-2'], }, }, ], // node_modules は除外する exclude: /node_modules/, }, ], }, plugins: [new FlowBabelWebpackPlugin()], node: { net: 'empty', tls: 'empty', dns: 'empty', fs: 'empty', __dirname: true, }, watchOptions: { ignored: ['node_modules'], }, };
続いて、同じディレクトリに、 webpack.config.dev.js
と言う名前で以下のように編集する。
const merge = require('webpack-merge'); const baseConfig = require('./webpack.config.base'); const config = merge(baseConfig, { mode: 'development', }); module.exports = config;
.babelrc
というファイルを作成し、中身を以下のように編集しておく。
{ "presets": ["es2015", "flow"] }
flowのセットアップ
続いて、flowのセットアップをしていく。
$ yarn add -D babel-cli babel-preset-flow flow-bin $ yarn flow init
すると、 .flowconfig
が生成されるので、以下のように編集しておく。
[ignore] .*/node_modules/.* .*/dist/.* .*/test/.* [include] [libs] flow-typed [lints] deprecated-call-syntax=off [options] esproposal.decorators=ignore module.name_mapper='^src\/\(.*\)$' -> '<PROJECT_ROOT>/src/\1' module.system=haste all=false
ESLintのセットアップ
コードの静的解析ツールESLintを入れる。
$ yarn add -D eslint $ ./node_modules/.bin/eslint --init
これで生成された .eslintrc.json
を以下のように編集する。
{ "extends": "airbnb-base", "parserOptions": { "ecmaVersion": 7, "sourceType": "module" }, "parser": "babel-eslint", "env": { "browser": true, "es6": true }, "plugins": ["flowtype"], "rules": { "flowtype/no-types-missing-file-annotation": 1, "max-len": [2, 100, 4, {"ignoreUrls": true}] } }
上の eslint
の初期化時には関連するライブラリをインストールしていないので、以下でインストールする。
$ yarn add -D babel-eslint eslint-plugin-flowtype eslint-config-airbnb-base eslint-plugin-import
.eslintignore
というファイルを生成して、ESLintの対象から外すものを明示しておく。
server.js flow-typed __tests__ __mocks__ node_modules .idea .vscode
これで yarn eslint .
と実行すると、ESLintの結果が出力される。ここで出たエラーは、 yarn eslint . --fix
とすると自動で修正される。
Jestのセットアップ
$ yarn add -D jest $ yarn add -D babel-jest babel-core
Node.jsをWebpackで起動する
この時点で、 Node.jsをWebpackで起動するのに必要なものは揃った。
package.json
は以下のようにする。
{ "main": "./dist/server.js", "scripts": { "start": "node ./dist/server.js", "flow": "flow", "eslint": "eslint .", "test": "jest", "webpack-dev": "webpack --config config/webpack.config.dev.js" }, "dependencies": { "express": "^4.16.4" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-eslint": "^10.0.1", "babel-jest": "^23.6.0", "babel-loader": "7", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", "babel-preset-es2015": "^6.24.1", "babel-preset-flow": "^6.23.0", "babel-preset-stage-2": "^6.24.1", "babel-regenerator-runtime": "^6.5.0", "eslint": "^5.8.0", "eslint-config-airbnb-base": "^13.1.0", "eslint-plugin-flowtype": "^3.2.0", "eslint-plugin-import": "^2.14.0", "flow-babel-webpack-plugin": "^1.1.1", "flow-bin": "^0.85.0", "jest": "^23.6.0", "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-merge": "^4.1.4", "webpack-node-externals": "^1.7.2" } }
最初に作成した app.js
の中身をES6に対応させるために1行目を以下のように書き換える。
import express from 'express';
package.json
にあるように起動コマンドを実行してみる。
$ yarn webpack-dev $ yarn start
すると、 localhost:3000
が起動できるようになっているのが確認できる。
編集したファイルを自動で反映するようにする
$ yarn add -D nodemon concurrently
必要なライブラリのインストールが完了したら次は、 package.json
に以下の行を追加する。
{ ... "script": { ... "dev": "concurrently \"NODE_ENV=development webpack -w --config config/webpack.config.dev.js\" \"nodemon --watch ./dist/server.js\"" }, ... }
以下でローカルホストが起動したら成功。
$ yarn dev
ソースコードはこちら。