かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Webpack Babel で TypeScript を ES5 にトランスパイルしたい

JS と CSS を HTML テンプレートで読み込んでという化石な環境で開発をしています。
前回は SCSS だけ webpack でビルドする環境を作りました。

今回は Vanilla JS でブラウザ対応を考えながら書くのは流石に DX が最悪なので最低限のトランスパイルできる環境を作成を作成しました。
環境設定は本当に分からんので間違いあればご指摘ください :pray:

環境
  • webpack 5.60.0
  • @babel/core 7.15.8
  • typescript 4.4.4

Webpack + babel で JS を ES5 にトランスパイルできる環境を作る

# webpack
$ npm i -D webpack webpack-cli
# babel
$ npm i -D @babel/core @babel/preset-env babel-loader babel-preset-minify
# polyfill
$ npm i core-js@3

Babel の設定

babel.config.js

module.exports = (api) => {
  const isProduction = api.env("production");

  return {
    presets: [
      [
        "@babel/preset-env",
        {
          useBuiltIns: "usage",
          corejs: 3,
        },
      ],
      [
        "minify",
        // production mode の時に console.log を削除する
        isProduction && {
          removeConsole: {
            exclude: ["error", "info"],
          },
        },
      ].filter(Boolean),
    ],
  };
};

Webpack の設定

webpack.config.js

module.exports = (env, argv) => {
  const mode = argv.mode || process.env.NODE_ENV || "development";
  
  if (!process.env.NODE_ENV) {
    process.env.NODE_ENV = mode
  }

  const enabledSourcemap = mode === "development";
  const useCache = mode === "development";

  return {
    mode,
    target: ["web", "es5"],
    cache: useCache,
    devtool: enabledSourcemap ? "inline-source-map" : false,
    entry: {
      "main": "src/index.js"
    },
    output: {
      path: `${OUTPUT_DIR}`,
      filename: "[name].js",
    },
    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /node_modules/,
          use: ["babel-loader"],
        },
      ],
    },
  };
};

build コマンド

package.json

{
  "scripts": {
    "build": "webpack --mode=production",
    "dev": "webpack --watch --progress"
  }
}

TypeScript を babel でトランスパイルする

ts-loader を使わなくても @babel/preset-typescript を使えば babel で直接 TypeScript をトランスパイルできる。(ビルド時の型チェックはしてなさそうですが)

$ npm i -D typescript @babel/preset-typescript

TypeScript 設定

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "allowJs": true,
    // default export を許容
    "allowSyntheticDefaultImports": true,
    // [...querySelectorAll()] の様な表記をエラーにしないう
    "downlevelIteration": true,
    // Enables experimental support for emitting type metadata for decorators which works with the module reflect-metadata.
    "emitDecoratorMetadata": true,
    // default import をエラーにしない (default import のヘルパーメソッドを生成する)
    "esModuleInterop": true,
    // デコレーターでエラーを表示しない
    "experimentalDecorators": true,
    // import 時にファイルの大文字小文字を区別する
    "forceConsistentCasingInFileNames": true,
    // 使用されてない変数をエラーにする
    "noUnusedLocals": true,
    // コンパイル結果を出力しない
    "noEmit": true,
    // 早期リターンを強要する (分岐条件内に return がないとエラー)
    "noImplicitReturns": true,
    // switch 内に break か return がないとエラー
    "noFallthroughCasesInSwitch": true,
    // JSON ファイルから型の抽出・生成ができるようにする
    "resolveJsonModule": true,
    // `*.d.ts` ファイツの方チェックを行わない (node_modules 内のライブラリの型定義ファイルの型チェックも行わない)
    "skipLibCheck": true
  },
  "include": ["src/*"],
  "exclude": ["node_modules"]
}

cf. tsconfig.jsonの全オプションを理解する(随時追加中) - Qiita

Babel, Webpack の設定を修正する

babel.config.js

  return {
    presets: [
      [
        "@babel/preset-env",
        {
          useBuiltIns: "usage",
          corejs: 3,
        },
      ],
+    "@babel/typescript",
      [
        "minify",

webpack.config.js

    entry: {
-     "main": "src/index.js"
+     "main": "src/index.ts"
    },
    // ...
    module: {
      rules: [
        {
-         test: /\.m?js$/,
+         test: /\.(mjs|js|ts)$/,
          exclude: /node_modules/,
          use: ["babel-loader"],
        },
      ],
    },
+   resolve: {
+     extensions: [".ts", ".js"],
+   }
  };

npm run build.ts ファイルもビルドできていれば OK

所管

昨今のフロントエンド開発だと React や Vue や Next や Nuxt でもビルド環境用意されているので、秘伝のタレを自分で作ることってなかなかなくなってる気がします。僕自身も webpack の設定から babel, tsconfig の設定を作るの久しぶりすぎて、その間に設定も色々変わってるし、環境づくりあまり面白くもないしなんもわからん…となりました。
もう react とか使える環境以外でフロントエンド書けない体になってしまった…


[参考]