自分を攻略していく記録

自分がやりたいことを達成するには何をすればいいのか、その攻略していく過程をつらつらと

Node.js+ExpressでAPIサーバを立てる(ES6, Webpack, Flow, Jest, ESLint)

まずJavaScriptの歴史をざっと見てみる

1995年にJavaが登場したが、その勢いに便乗して、ブレンダン・アイクが開発したLiveScriptがJavaScriptへと改名したことにより、 JavaScript爆誕

その翌年、マイクロソフトJavaScriptのライセンスがなかったために独自のJScriptを開発。ところが、JavaScriptを開発していたNetscape Navigator社は、JavaScriptJScriptの互換性があまりにひどかったことから1997年にECMA InternationalにJavaScriptJScriptの統合を依頼。この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 と呼ばれるものを使って対応することになる。

以下を参照

qiita.com

Node.jsでAPIサーバを実装していく

では、APIサーバをNode.js(Express)で構築して行くことにする。フロントエンドをVue.jsReact.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でお馴染みの flowESLintJestにも対応させるべく、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

f:id:ngo275:20181107180200p:plain

これで生成された .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

ソースコードはこちら。

github.com