Rollupでexpress serverをビルドする

目次

こんにちは、@Himenonです。Rollup で express サーバーをビルドする方法を紹介します。ブラウザ以外で動く JavaScript をバンドルする動機としては、Node.js のサーバーをコンテナ化する際に Image サイズが大きくならないようにする方法として利用することができると考えられます。Rollup のビルド設定を紹介した後、webpack との比較を紹介していきます。

Rollup とは

Rollup は JavaScript のためのモジュールバンドラーです。最近だと Web フロントエンドの開発で利用される vite は内部でモジュール解決などの Rollup の一部の機能を利用しています。Rollup の別の使い方としては、CommonJS や AMD 形式のモジュール形式に対応していたり、ESM にも利用され、これらをバンドルする事が可能です。

この機能をうまく使えば、サーバーサイドの言語もうまく一つのファイルにまとめることが可能で、node_modules なしでスタンドアローンで起動するファイルが作成できます。

express を Rollup でビルドする設定

rollup.config.mjs ファイルを作成し、次のように記述します。

import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";
import resolve from "@rollup/plugin-node-resolve";
import { swc } from "rollup-plugin-swc3";

/**
 * @type {import("rollup").RollupOptions}
 */
export default {
  input: "src/app.ts",
  output: {
    format: "cjs",
    file: "dist/rollup-server.cjs",
    generatedCode: {
      constBindings: true,
    },
    sourcemap: false,
  },
  plugins: [
    resolve({
      extensions: [".ts", ".mjs", ".js", ".json"],
      moduleDirectories: ["src", "node_modules"],
      preferBuiltins: true,
    }),
    commonjs(),
    json(),
    swc({
      sourceMaps: true, // output.sourcemapのフラグで制御できるようにする
      jsc: {
        target: "es2020",
        parser: {
          syntax: "typescript",
          tsx: false,
          dynamicImport: false,
        },
      },
    }),
  ],
};
  • @rollup/plugin-node-resolve(^15.0.2)NodeJS のモジュール解決のアルゴリズムを利用して、node_modules を利用可能にする。
  • @rollup/plugin-commonjs(^24.1.0)CommonJS を ES6 に変換し、Rollup でバンドルできるようする具体的には require を展開してくれる
  • @rollup/plugin-json(^6.0.0)json を ES6 に変換
  • rollup-plugin-swc3(^0.8.1)SWC を Rollup で利用できるようにする

express を webpack でビルドする設定

Rollup と同じように、webpack も express をビルドできます。詳細は省きますが、webpack の設定では、swc-loader や regenerator-runtime といったライブラリが必要となります。

import * as path from "path";

/**
 * @type {import("webpack").Configuration}
 */
export default {
  mode: "production",
  target: "node16.13",
  entry: {
    "webpack-server": ["regenerator-runtime", "./src/app.ts"],
  },
  output: {
    path: path.resolve("dist"),
    filename: "[name].cjs",
    clean: false,
  },
  devtool: "source-map",
  optimization: {
    minimize: false,
  },
  module: {
    rules: [
      {
        test: /\.(js|mjs|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "swc-loader",
        },
      },
    ],
  },
  resolve: {
    extensions: [".mjs", ".js", ".ts", ".json"],
    modules: ["node_modules"],
  },
  externalsPresets: { node: true },
};

各種バージョン

  • webpack(^5.80.0)
  • webpack-cli(^5.0.2)

Rollup と webpack で機能やバンドル結果を比較する

前述の設定で各挙動を比較すると次のようになります。

Rollupwebpackwatch コマンド rollup -c rollup.config.mjs –watchwebpack –watchbuild コマンド rollup -c rollup.config.mjswebpackbuild 時間(3 回平均)959ms380msWatch Server の起動時間(5 回平均)1180 ms390 msHot Reload の時間(5 回平均)385 ms143 msserver.cjs のファイルサイズ(minify なし)960 KB940 KBserver.js.map のファイルサイズ 1040 KB685 KB

バンドル速度について

webpack のほうが Rollup より 2.4〜3 倍近く速く処理されています。

Tree-Shaking について

依存するライブラリが express だけなので、Tree-Shaking の挙動の違いによるファイルサイズの差は大きく見られませんでした。

まとめ

Rollup で NodeJS のアプリケーションを 1 ファイルにバンドルする設定を紹介し、webpack と比較しました。今回の条件ではパフォーマンスとして webpack に軍配が上がりましたが、キャッシュの設定を見直したり、依存関係やビルド戦略次第では差が縮まるのではないでしょうか。

サンプルコード