stekhn
stekhn

Reputation: 2087

Move JSON files from webpack main bundle to own files

I'm building a small web application, where I am loading data from multiple JSON files.

import config from './config.json';
import data from './data.json';

console.log(config, data)

For my webpack build I want to exclude the JSON files from the bundle, since they are quite big (and could be loaded asynchronously). Currently I'm trying to use file-loader to achieve this.

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  devtool: '#cheap-source-map',
  resolve: {
    modules: ['node_modules']
  },
  entry: {
    main: path.resolve('./index.js')
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve('./dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      },
      {
        type: 'javascript/auto',
        test: /\.json$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'index.html',
      inject: 'body'
    })
  ]
};

With this configuration I get separate files, but they won't get imported. In this particular case the console.log() only returns the filename as strings data.json and config.json. It seems like the actual JSON files are not loaded.

What am I doing wrong? Is file-loader the way to go here?

Upvotes: 3

Views: 2263

Answers (1)

stekhn
stekhn

Reputation: 2087

Using file-loader will not do it. The solution is to use the SplitChunksPlugin, which is included in webpack since version 4. For older versions use the CommonsChunkPlugin.

Here is a working webpack.config.json file:

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  devtool: '#cheap-source-map',
  resolve: {
    modules: ['node_modules']
  },
  entry: {
    main: path.resolve('./index.js')
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve('./dist')
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 0,
      cacheGroups: {
        data: {
          test: /\.json$/,
          filename: '[name].js',
          name(module) {
            const filename = module.rawRequest.replace(/^.*[\\/]/, '');
            return filename.substring(0, filename.lastIndexOf('.'));
          },
        }
      }
    }
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'index.html',
      inject: 'body'
    })
  ]
};

The files get loaded in the app (see requests in DevTools) and console.log(config, data) outputs an array/object.

However, this solution will output the JSON files as JavaScript. This works fine for me, but might be a problem if you rely on the files being JSON. Example for a simple config.json:

(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[function(n){n.exports={name:"test"}}]]);

If you are bothered by the source maps, you can specify an exclude rule with the SourceMapDevToolPlugin.

Upvotes: 1

Related Questions