Skip to content

Webpack 5でのNode.jsコアモジュールのポリフィル

問題の概要

Webpack 5では、Node.jsのコアモジュール(fspathcryptoなど)の自動ポリフィルが廃止されました。これにより、ブラウザ環境でNode.jsモジュールを使用するライブラリをバンドルする際に、次のようなエラーが発生します:

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

この変更はパフォーマンスとバンドルサイズの最適化を目的としていますが、既存のプロジェクトや特定のライブラリを使用している場合に問題が発生することがあります。

解決アプローチ

問題に対処するには、主に以下の3つの方法があります:

  1. ポリフィルの追加: 必要なNode.jsモジュール用のブラウザ互換ポリフィルをインストールして設定する
  2. 空モジュールの設定: 実際には必要ないモジュールに対して空のモジュールを使用する
  3. 依存関係の見直し: そもそもNode.jsコアモジュールに依存するライブラリの使用を避ける

解決方法

方法1: 個別ポリフィルの設定(推奨)

Webpack設定ファイルで必要なモジュールごとにポリフィルを設定します:

javascript
// webpack.config.js
module.exports = {
  // 他の設定...
  resolve: {
    fallback: {
      "fs": false,
      "tls": false,
      "net": false,
      "path": false,
      "zlib": false,
      "http": require.resolve("stream-http"),
      "https": require.resolve("https-browserify"),
      "stream": require.resolve("stream-browserify"),
      "crypto": require.resolve("crypto-browserify"),
      "buffer": require.resolve("buffer")
    }
  },
  plugins: [
    new webpack.ProvidePlugin({
      process: 'process/browser',
      Buffer: ['buffer', 'Buffer']
    })
  ]
};

必要なポリフィルパッケージをインストール:

bash
npm install stream-http https-browserify stream-browserify crypto-browserify buffer process

方法2: 包括的なポリフィルプラグインの使用

簡易的な解決策として、node-polyfill-webpack-pluginを使用する方法もあります:

javascript
// webpack.config.js
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");

module.exports = {
  // 他の設定...
  plugins: [
    new NodePolyfillPlugin()
  ]
};
bash
npm install node-polyfill-webpack-plugin

WARNING

この方法は簡単ですが、すべてのNode.jsコアモジュールを含めるため、バンドルサイズが大きくなる可能性があります。必要なモジュールのみを個別にポリフィルする方法を推奨します。

方法3: 空モジュールによる無効化

実際には必要ないモジュールに対しては、空のモジュールを設定できます:

javascript
resolve: {
  fallback: {
    "fs": false,
    "path": false,
    "crypto": false
  }
}

または、package.jsonでブラウザフィールドを使用する方法もあります:

json
{
  "browser": {
    "fs": false,
    "path": false,
    "os": false
  }
}

フレームワーク別対応

Create React Appの場合

Create React AppではWebpack設定を直接変更できないため、以下の方法を使用します:

CRACOを使用する方法

  1. CRACOと必要なポリフィルをインストール:
bash
npm install @craco/craco crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer process
  1. craco.config.jsを作成:
javascript
const webpack = require('webpack');

module.exports = {
  webpack: {
    configure: (config) => {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        "crypto": require.resolve("crypto-browserify"),
        "stream": require.resolve("stream-browserify"),
        "assert": require.resolve("assert"),
        "http": require.resolve("stream-http"),
        "https": require.resolve("https-browserify"),
        "os": require.resolve("os-browserify/browser"),
        "url": require.resolve("url"),
        "buffer": require.resolve("buffer")
      };
      config.plugins = (config.plugins || []).concat([
        new webpack.ProvidePlugin({
          process: 'process/browser',
          Buffer: ['buffer', 'Buffer']
        })
      ]);
      return config;
    }
  }
};
  1. package.jsonのスクリプトを変更:
json
{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  }
}

react-app-rewiredを使用する方法

  1. 必要なパッケージをインストール:
bash
npm install react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer process
  1. config-overrides.jsを作成:
javascript
const webpack = require('webpack');

module.exports = function override(config) {
  const fallback = config.resolve.fallback || {};
  Object.assign(fallback, {
    "crypto": require.resolve("crypto-browserify"),
    "stream": require.resolve("stream-browserify"),
    "assert": require.resolve("assert"),
    "http": require.resolve("stream-http"),
    "https": require.resolve("https-browserify"),
    "os": require.resolve("os-browserify/browser"),
    "url": require.resolve("url"),
    "buffer": require.resolve("buffer")
  });
  config.resolve.fallback = fallback;
  config.plugins = (config.plugins || []).concat([
    new webpack.ProvidePlugin({
      process: 'process/browser',
      Buffer: ['buffer', 'Buffer']
    })
  ]);
  return config;
};
  1. package.jsonのスクリプトを変更:
json
{
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  }
}

Vue.jsの場合

Vue CLIを使用している場合、vue.config.jsで設定します:

javascript
const { defineConfig } = require('@vue/cli-service')
const webpack = require('webpack');

module.exports = defineConfig({
  configureWebpack: {
    plugins: [
      new webpack.ProvidePlugin({
        Buffer: ['buffer', 'Buffer'],
      }),
      new webpack.ProvidePlugin({
        process: 'process/browser',
      })
    ],
    resolve: {
      fallback: {
        "os": require.resolve("os-browserify/browser"),
        "url": require.resolve("url/"),
        "crypto": require.resolve("crypto-browserify"),
        "https": require.resolve("https-browserify"),
        "http": require.resolve("stream-http"),
        "assert": require.resolve("assert/"),
        "stream": require.resolve("stream-browserify"),
        "buffer": require.resolve("buffer")
      }
    }
  }
});

ベストプラクティス

  1. 必要なポリフィルのみをインストール: バンドルサイズを最小化するため、実際に必要なポリフィルのみを追加してください。

  2. 依存関係の確認: まずは本当にNode.jsコアモジュールのポリフィルが必要か確認しましょう。場合によっては、ブラウザ互換の代替ライブラリを使用できる可能性があります。

  3. 段階的な移行: 大規模なプロジェクトでは、一度にすべてを移行するのではなく、必要なモジュールから順番に対応しましょう。

  4. パフォーマンスモニタリング: ポリフィル追加前後でバンドルサイズとパフォーマンスを比較し、最適化の効果を測定しましょう。

トラブルシューティング

  • 特定のモジュールが不足している場合: エラーメッセージに基づいて、該当するポリフィルパッケージを追加してください。
  • ポリフィル後にビルドが失敗する場合: ポリフィルパッケージのバージョン互換性を確認してください。
  • ブラウザで実行時エラーが発生する場合: ポリフィルが正しく適用されているか、ブラウザの開発者ツールで確認してください。

まとめ

Webpack 5でのNode.jsコアモジュールの自動ポリフィル廃止は、パフォーマンス向上のための重要な変更です。この変更に対応するには、プロジェクトの要件に応じて適切なポリフィル戦略を選択し、必要なモジュールのみを追加することが重要です。フレームワークごとの設定方法を理解し、バンドルサイズと機能のバランスを取ることが、現代的なフロントエンド開発では不可欠です。