Webpack でバンドルしたライブラリを他の JavaScript から参照したい

大体の JavaScript は Webpack でバンドルするのですが、一部対象外の JavaScript を用意したときに Webpack でバンドルしたライブラリを外部から参照できなかったのでメモ。

構成

ディレクトリ構造

今回は以下のような環境を想定。

PROJECT_ROOT/
    ├ dist/
    │   ├ js/
    │   │   └ external.js  // Webpack でバンドルしない JavaScript
    │   │
    │   └ index.html
    │
    ├ src/
    │   └ js/
    │       └ app.js       // Webpack でバンドルする JavaScript (ここでは例として jQuery を import)
    │
    ├ package.json
    └ webpack.config.js    // src/js/app.js を dist/js/app.js に出力

src/js/app.js

import $ from 'jquery';

$(() => {
    $('#verse1').text('Watching unseen untouched');
});

jQuery を import して、内部で使用している簡単な jQuery を作成。

dist/js/external.js

$(() => {
    $('#verse2').text('Empty exposed');
});

バンドルしないスクリプトも似たような構成。ただし、 <script>タグ で jQuery を読み込むことを前提とした作りになっています。

dist/index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Empty Exposed</title>
    <meta name="description" content="expose-loader のテスト">
</head>
<body>
    <p id="verse1"></p>
    <p id="verse2"></p>
    <script src="./js/app.js" defer></script>
    <script src="./js/external.js" defer></script>
</body>
</html>

単純に dist/js/app.jsdist/js/external.js を読み込んで実行する前提の HTML です。

webpack.config.js

const webpackTerser = require('terser-webpack-plugin');
const path          = require('path');
const glob          = require('glob');

const entry = () => {
    const entries = glob
        .sync(
            '**/*.js',
            {
                cwd: './src/js'
            }
        )
        .map(function (key) {
            return [key, path.resolve('./src/js', key)];
        });
    return Object.fromEntries(entries)
};
const configs = {
    mode: 'development',
    entry: entry(),
    output: {
        filename: '[name]',
        path: path.join(path.join(__dirname, 'dist'), 'js')
    },
    optimization: {
        minimizer: [
            new webpackTerser({
                extractComments: 'some',
                terserOptions: {
                    compress: {
                        drop_console: false,
                    },
                },
            }),
        ],
    },
    devtool: 'inline-source-map'
};

module.exports = configs;

webpack.config.js はこのような感じで。

検証

この状態で Webpack でバンドルすると、 dist/js/external.js からは dist/js/app.js にバンドルされた jQuery が参照できません。

そのため、以下のようなエラーが表示されてしまいます。

外部JSからはバンドルされたライブラリが参照できないため、 Uncaught ReferenceError: $ is not defined エラーが発生
外部JSからはバンドルされたライブラリが参照できないため、 Uncaught ReferenceError: $ is not defined エラーが発生

Uncaught ReferenceError: $ is not defined

対処

そこで、 expose-loader を加えた ( yarn add -D expose-loader ) 上で webpack.config.js を以下のように書き換えます。

coconst webpackTerser = require('terser-webpack-plugin');
const path          = require('path');
const glob          = require('glob');

const entry = () => {
    const entries = glob
        .sync(
            '**/*.js',
            {
                cwd: './src/js'
            }
        )
        .map(function (key) {
            return [key, path.resolve('./src/js', key)];
        });
    return Object.fromEntries(entries)
};
const configs = {
    mode: 'development',
    entry: entry(),
    output: {
        filename: '[name]',
        path: path.join(path.join(__dirname, 'dist'), 'js')
    },
    module: { // ここから
        rules: [
            {
                test: require.resolve("jquery"),
                loader: "expose-loader",
                options: {
                    exposes: ["$", "jQuery"],
                }
            }
        ]
    }, // ここを追記
    optimization: {
        minimizer: [
            new webpackTerser({
                extractComments: 'some',
                terserOptions: {
                    compress: {
                        drop_console: false,
                    },
                },
            }),
        ],
    },
    devtool: 'inline-source-map'
};

module.exports = configs;

これでもう一度ビルド。

外部JSからもバンドルされたライブラリを参照できるようになり、エラーがなくなって画面へのテキスト出力もされるようになった
外部JSからもバンドルされたライブラリを参照できるようになり、エラーがなくなって画面へのテキスト出力もされるようになった

今度はエラーがなく、出力処理が正常にできたことが確認できました。OKです。

参考

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。