webpackwebpack-file-loader

Webpack FileLoader insert referenced File to HTML


I am building my react application using the following Webpack configuration and I need to add a reference to external.config.js file in my index.html to include it with versioning. Here, external.config.js file is processed using file-loader to maintain as a plain js file.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const devConfig = require('./config/dev.config.json');
const certConfig = require('./config/cert.config.json');
const stagingConfig = require('./config/staging.config.json');
const productionConfig = require('./config/production.config.json');
const packageJson = require('./package.json');

const version = packageJson.version;

module.exports = (env) => {
  let configPath;
  switch (env) {
    case 'dev':
      configPath = devConfig;
      break;
    case 'cert':
      configPath = certConfig;
      break;
    case 'staging':
      configPath = stagingConfig;
      break;
    case 'production':
      configPath = productionConfig;
      break;
    default:
    // error logs
  }

  return {
    mode: 'development',
    entry: {
      bundle: path.resolve(__dirname, 'src', 'index.js')
    },
    output: {
      path: __dirname + '/dist',
      filename: `[name].v${version}.js`
    },
    module: {
      rules: [
        {
          test: /\.(woff|woff2|ttf|eot)$/,
          use: 'file-loader?name=fonts/[name].[ext]!static',
        },
        {
          test: /external.config.js$/,
          loader: 'file-loader',
          options: {
            name: `[name].v${version}.[ext]`,
          },
        },
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              '@babel/preset-react',
            ],
            plugins: ['@babel/plugin-syntax-dynamic-import', '@babel/plugin-proposal-class-properties'],
          },
        },
        {
          test: /\.(scss|css)$/,
          use: [
            'style-loader',
            'css-loader',
            'sass-loader',
          ],
        },
        {
          test: /\.html$/,
          use: [
            { loader: 'html-loader' },
          ],
        },
      ],
    },
    externals: {
      Config: JSON.stringify(configPath),
    },
    resolve: {
      extensions: ['.js', '.jsx', '.css', '.less', '.json'],
      modules: ['node_modules', 'path/to/your/static_resource'],
    },
    plugins: [new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'src', 'index.html') })],
  };
};

I tried it as a Webpack entry but I was unable to stop it into transpiling.


Solution

  • I was able to achieve this using HtmlWebpackPlugin which seems pretty supportive in managing the html output. First, I had to remove the html-loader in webpack.config which I wasn't using for a purpose and then the added the following configuration to HtmlWebpackPlugin.

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const devConfig = require('./config/dev.config.json');
    const certConfig = require('./config/cert.config.json');
    const stagingConfig = require('./config/staging.config.json');
    const productionConfig = require('./config/production.config.json');
    const packageJson = require('./package.json');
    
    const version = packageJson.version;
    const externalConfigPath = `/external.config.v${version}.js?`;
    
    module.exports = (env) => {
      let configPath;
      switch (env) {
        case 'dev':
          configPath = devConfig;
          break;
        case 'cert':
          configPath = certConfig;
          break;
        case 'staging':
          configPath = stagingConfig;
          break;
        case 'production':
          configPath = productionConfig;
          break;
        default:
        // error logs
      }
    
      return {
        mode: 'development',
        entry: {
          bundle: path.resolve(__dirname, 'src', 'index.js'),
        },
        output: {
          path: __dirname + '/dist',
          filename: `[name].v${version}.js`
        },
        module: {
          rules: [
            {
              test: /\.(woff|woff2|ttf|eot)$/,
              use: 'file-loader?name=fonts/[name].[ext]!static',
            },
            {
              test: /external.config.js$/,
              loader: 'file-loader',
              options: {
                name: `[name].v${version}.js`,
              },
            },
            {
              test: /\.(js|jsx)$/,
              exclude: /node_modules/,
              loader: 'babel-loader',
              options: {
                presets: [
                  '@babel/preset-env',
                  '@babel/preset-react',
                ],
                plugins: ['@babel/plugin-syntax-dynamic-import', '@babel/plugin-proposal-class-properties'],
              },
            },
            {
              test: /\.(scss|css)$/,
              use: [
                'style-loader',
                'css-loader',
                'sass-loader',
              ],
            },
          ],
        },
        externals: {
          Config: JSON.stringify(configPath),
        },
        resolve: {
          extensions: ['.js', '.jsx', '.css', '.less', '.json'],
          modules: ['node_modules', 'path/to/your/static_resource'],
        },
        plugins: [new HtmlWebpackPlugin({ title: 'Custom template', configURL: externalConfigPath, template: path.resolve(__dirname, 'src', 'index.html') })],
      };
    };

    Then added the following reference into my script tag src attribute in the index.html file.

    <script type="text/javascript" src=<%= htmlWebpackPlugin.options.configURL %>></script>

    References : https://github.com/jantimon/html-webpack-plugin#options