@wakamshaさんの AltJS 課題を webpack, TypeScript, React でやってみた + α

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、今月入社したアップル信者ではない齋藤です。 先日 Naoki YAMADA (@wakamsha) さんが書かれたスライドに課題があり 弊社に2つブログが上がったわけですが、僕も書いてみます。

以下が弊社にすでに上がっているブログです。 * @wakamshaさんの AltJS, AltCSS 課題を webpack, SCSS, TypeScript, React でやってみた * @wakamshaさんのAltJS,AltCSS課題をGulp, SCSS, TypeScript でやってみた

課題

スライドに書かれている課題は以下です。

  • JavaScript, CSS いずれも何かしらのプリプロセッサを使用すること(※HTMLは任意)
  • ローカルサーバーの起動
  • ファイルを変更したら自動的にビルド処理が実行
  • コンテンツは「Hello, world」でOK
  • 自信がある人は 何かしらのフレームワークも導入してみよう

回答

回答としてはこちらです。

  • タスクランナーとしてnpm scripts with npm-run-all
  • バンドルはWebpack
  • AltJSはTypeScript
  • AltCSSは次回の課題
  • 今回はwebpack-dev-serverで開発
  • テストはjest, watchテストはjestの場合、インタラクティブモードになるのでchokidarでファイル監視
  • Lintはeslint + eslint-typescript-parser まわして+ prettierでフォーマット
  • 正直lintとtestはCIとかで回して開発中はWebpackだけでもいい気がした

検証環境

  • macOS Sierra 10.12.5
  • Node.js v8.0.0
  • npm v5.0.1

構築

リポジトリを初期化します。 -yオプションでインタラクションなしで吐いてくれます。

$ npm init -y
{
  "name": "sandbox",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "MIT"
}

ライセンスのデフォルトはISCですが設定で変えられます`。

で、色々インストールした結果のpackage.jsonがこちら

{
  "name": "sandbox",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "npm-run-all -p watch:*",
    "build": "npm-run-all lint test build:js",
    "build:js": "webpack",
    "watch:js": "webpack-dev-server --open",
    "watch:lint": "chokidar 'src/**/*.ts*' -c 'npm run lint'",
    "lint": "eslint --fix src/**/*.ts*",
    "test": "jest",
    "watch:test": "chokidar 'src/**/*.ts*' -c 'npm run test'"
  },
  "jest": {
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "transform": {
      "\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
    },
    "testMatch": [
      "src/__test__/**/*.ts?(x)"
    ],
    "collectCoverage": true,
    "collectCoverageFrom": [
      "src/**/*.ts?(x)"
    ]
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@types/jest": "^19.2.4",
    "@types/react": "^15.0.27",
    "@types/react-dom": "^15.5.0",
    "chokidar-cli": "^1.2.0",
    "cross-env": "^5.0.1",
    "eslint": "^3.19.0",
    "eslint-plugin-jest": "^20.0.3",
    "eslint-plugin-prettier": "^2.1.1",
    "eslint-plugin-react": "^7.0.1",
    "jest": "^20.0.4",
    "npm-run-all": "^4.0.2",
    "prettier": "^1.4.4",
    "prettier-eslint": "^6.3.0",
    "ts-jest": "^20.0.5",
    "ts-loader": "^2.1.0",
    "typescript": "^2.3.4",
    "typescript-eslint-parser": "^3.0.0",
    "webpack": "^2.6.1",
    "webpack-dev-server": "^2.4.5"
  },
  "dependencies": {
    "react": "^15.5.4",
    "react-dom": "^15.5.4"
  }
}

TypeScript

TypeScriptの設定はこちら

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es5",
    "lib": [
      "dom",
      "es2016",
      "es2017"
    ],
    "jsx": "react",
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "sourceMap": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "listFiles": false,
    "stripInternal": true,
    "skipDefaultLibCheck": true,
    "pretty": false
  },
  "exclude": [
    "node_modules"
  ]
}

ESLintの設定

ESLintにはディレクトリ構造に従って設定を切り替えてくれる機能があるのですが 今回は基本になる設定とテストで利用する設定を用意します。

まずは基本的に利用する設定がこちら。

{
  "extends": "eslint:recommended",
  "parser": "typescript-eslint-parser",
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
      "classes": true,
      "jsx": true
    }
  },
  "plugins": [
    "prettier",
    "react"
  ],
  "ecmaFeatures": {
    "modules": true,
    "classes": true
  },
  "rules": {
    "prettier/prettier": [
      "error",
      {
        "semi": false,
        "trailingComma": "es5"
      }
    ],
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
    "quotes": [
      "error",
      "double",
      {
        "avoidEscape": true,
        "allowTemplateLiterals": true
      }
    ],
    "semi": [
      "error",
      "never"
    ],
    "no-var": "error",
    "prefer-const": "error",
    "prefer-spread": "error",
    "prefer-template": "error",
    "no-console": "error",
    "no-undefined": "error",
    "dot-notation": "error",
    "no-extra-bind": "error",
    "no-multi-spaces": "error",
    "no-useless-return": "error",
    "wrap-iife": "error",
    "no-lonely-if": "error"
  }
}

で、テストの設定はこちら。jest向けにプラグインを追加しています。

{
  "plugins": [
    "jest"
  ],
  "env": {
    "jest/globals": true
  }
}

Webpack

あっさり目が好みなので Webpackの設定はこんな感じ

"use strict"
const path = require("path")
module.exports = {
  devServer: {
    contentBase: "./",
    port: 3000
  },
  devtool: "source-map",
  entry: "./src/index.tsx",
  output: {
    path: path.resolve("./target/"),
    filename: "bundle.js",
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader",
        options: {},
      },
    ],
  },
}

React

React完全マスターした

import * as React from "react"
import * as ReactDOM from "react-dom"

document.addEventListener("DOMContentLoaded", () => {
  ReactDOM.render(<div>Hello World test</div>, document.getElementById("app"))
})

ビルド周り

  • 開発中はnpm start
  • npm run buildでバンドルしたファイル生成

今回はタスクを基本2つで用意しましたが 開発や検証向けに設定を切り替えながらバンドルすることもあるかと考え cross-envを入れています。

また、開発中はnpm startでファイルを監視しながらlintやtestに加えて webpack-dev-serverによるブラウザリロードが走ります。

まとめ

がっつりnpm wayに乗せた形にしてみました。

今回はTypeScriptとESLintを使って静的にファイルを検証する環境を構築しました。 (ガッチガチやぞ!!) また、今回CSSは自分に知見がなかったので、次回の課題にしておきます。

初めての記事で緊張しましたが いかがだったでしょうか?

それではまた、次回の記事で。 コメントお待ちしております。