なっぱ箱

ゲームとか読書の感想とか時々技術系の話とか

TypeScript & ESLint/Prettier & webpack & Jestで最小構成のプロジェクトを作る

はじめに

Vue等であればvue-cliで新規プロジェクトを作ってしまえばいいのだが、 別にVue等を使わないCUIツールなり簡単なものをザクっと書きたいとき用に最小構成のものが欲しかったので作ってみた。

(あとはそもそもここ最近のモダンな構成をゼロから用意したことなかったので勉強がてら)

作ったもの

作ったものはこんな感じ。

github.com

├─dist
│   └─── bundle.js
│      
├─node_modules
├─src
│   ├─── app.spec.ts
│   └─── app.ts
│
├─ .gitignore
├─ .eslintrc.js
├─ .prettierrc
├─ jest.config.js
├─ package-lock.json
├─ package.json
├─ tsconfig.json
└─ webpack.config.js 

手順

プロジェクトの初期化

とりあえずプロジェクト用のディレクトリを切り、最低限のソースと.gitignoreを用意しておく。

$ mkdir minimum-ts-template && cd $_
$ npm init -y
$ mkdir src && touch src/app.ts .gitignore

src/app.ts

export const SayHello = (): string => {
  return 'Hello! TypeScript!'
}

console.log(SayHello())

.gitignore

node_modules
dist

TypeScript/Webpackの設定

必要なパッケージを追加し、それぞれの設定ファイルを作成。内容はお好きに変更してどうぞ。

$ npm i -D typescript ts-loader webpack webpack-cli
$ touch tsconfig.json webpack.config.js

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "es2015",
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] },
    "outDir": "./dist/",
    "removeComments": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

webpack.config.js

const path = require('path')

module.exports = {
  entry: './src/a.ts',
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
}

設定ファイルを追加したらpackage.jsonにbuildスクリプトを追加。

package.json

  "scripts": {
+   "build": "webpack --mode=development",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

これで下記のようにTypeScriptのビルドが出来るようになる。

$ npm run build

Hash: 12c5408bc864105b0202
Version: webpack 4.30.0
Time: 919ms
Built at: 2019-04-19 14:09:37
    Asset      Size  Chunks             Chunk Names
bundle.js  4.07 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./src/app.ts] 97 bytes {main} [built]
$ node dist/bundle.js
Hello! TypeScript!

ESlint/Prettierの設定

The future of TypeScript on ESLint

という事なのでTSLintではなくESLintにしましょう。

まずはパッケージの追加を行って各種設定ファイルを作成。細かいルールはお好きなように。

$ npm i -D eslint @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier
$ touch .prettierrc .eslintrc.js

.prettierrc

{
  "singleQuote": true,
  "semi": false,
  "trailingComma": "all"
}

.eslintrc.js

module.exports = {
  extends: ["eslint:recommended", "plugin:prettier/recommended"],
  plugins: ["@typescript-eslint", 'prettier'],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    sourceType: "module",
    project: "./tsconfig.json",
  },
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  rules: {
    "no-console": "warn",
    "@typescript-eslint/adjacent-overload-signatures": "warn",
    "@typescript-eslint/no-unnecessary-type-assertion": "error",
  }
}

設定ファイルを追加したらpackage.jsonにlintスクリプトを追加。

package.json

  "scripts": {
    "build": "webpack --mode=development",
+   "lint": "eslint --ext .js,.ts --ignore-path .gitignore .",
+   "lint:fix": "eslint --fix --ext .js,.ts --ignore-path .gitignore .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

これで下記のようにESLint/PrettierによるLintができるようになる。

$ npm run lint

D:\workspace\javascript\minimum-ts-template\src\app.ts
  2:10  error    Replace `"Hello!·TypeScript!";` with `'Hello!·TypeScript!'`  prettier/prettier
  3:2   error    Delete `;`                                                   prettier/prettier
  5:1   warning  Unexpected console statement                                 no-console
  5:24  error    Delete `;`                                                   prettier/prettier

✖ 4 problems (3 errors, 1 warning)
  3 errors and 0 warnings potentially fixable with the `--fix` option.
$ npm run lint:fix

D:\workspace\javascript\minimum-ts-template\src\app.ts
  5:1  warning  Unexpected console statement  no-console

✖ 1 problem (0 errors, 1 warning)

Jestの設定

最後にテストフレームワークのJestのパッケージを追加。やはり設定ファイルがあるのでそちらも追加。

$ npm i -D jest ts-jest @types/jest eslint-plugin-jest
$ touch jest.config.js

ESLintに設定を追加。

.eslintrc.

  module.exports = {
    extends: ["eslint:recommended", "plugin:prettier/recommended"],
-   plugins: ["@typescript-eslint", 'prettier'],
+   plugins: ["@typescript-eslint", 'prettier', "jest"],
    parser: "@typescript-eslint/parser",
    parserOptions: {
      sourceType: "module",
      project: "./tsconfig.json"
    },
    env: {
      browser: true,
      node: true,
      es6: true,
+     "jest/globals": true,
    },
    rules: {
      "no-console": "warn",
      "@typescript-eslint/adjacent-overload-signatures": "warn",
      "@typescript-eslint/no-unnecessary-type-assertion": "error",
    }
  }

設定ファイルを追加したらpackage.jsonのtestスクリプトを修正。

package.json

  "scripts": {
    "build": "webpack --mode=development",
    "lint": "eslint --ext .js,.ts --ignore-path .gitignore .",
    "lint:fix": "eslint --fix --ext .js,.ts --ignore-path .gitignore .",
-   "test": "echo \"Error: no test specified\" && exit 1"
+   "test": "jest"
  },

app.tsに対するテストを書いてみる。

touch src/app.spec.ts

src/app.spec.ts

import { SayHello } from './app'

describe('SayHello', () => {
  test('toBe "Hello! TypeScript!"', () => {
    expect(SayHello()).toBe('Hello! TypeScript!')
  })

  test('not toBe "Hello! JavaScript!"', () => {
    expect(SayHello()).not.toBe('Hello! JavaScript!')
  })
})

これで準備は整ったのでテストを実行。

$ npm run test

 PASS  src/app.spec.ts
  SayHello
    √ toBe "Hello! TypeScript!" (3ms)
    √ not toBe "Hello! JavaScript!"

  console.log src/app.ts:5
    Hello! TypeScript!

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.868s
Ran all test suites.

以上で最小構成のプロジェクトが完成!

全然関係ないけどはてなブログのコードブロックもQiitaみたいにファイル名つけられるようになったらいいなぁ・・・