Skip to content

Instantly share code, notes, and snippets.

@tachyons
Last active January 21, 2022 08:12
Show Gist options
  • Save tachyons/03520c06b0f7a5a149d498bbc1f46200 to your computer and use it in GitHub Desktop.
Save tachyons/03520c06b0f7a5a149d498bbc1f46200 to your computer and use it in GitHub Desktop.
Js bundling webpacker 5

Switch from Webpacker 5 to jsbundling-rails with webpack 5

This guide provides step-by-step instructions to migration from Webpacker 5 to jsbundling-rails with webpack 5. For upgrading to Webpacker 6 instead, follow this guide or for comparison between Webpacker and jsbundling-rails, see this.

1. Setup jsbundling-rails

First install jsbundling-rails:

# Add to your Gemfile
+ gem 'jsbundling-rails'

# From the CLI, rebuild the bundle
./bin/bundle install

# From the CLI, create a baseline configuration
./bin/rails javascript:install:webpack

The installation script will:

  • Add builds to the manifest
  • Ignore builds from git
  • Setup foreman for running multiple processes
  • Create ./webpack.config.js
  • Add the build script to package.json

Optional: Move your webpack configuration

If you would like to minimize the diff between Webpacker and jsbundling-rails:

  1. Move ./webpack.config.js into ./config/webpack/
  2. Change the config path in package.json
- "build": "webpack --config ./webpack.config.js"
+ "build": "webpack --config ./config/webpack/webpack.config.js"
  1. Change the output path in webpack.config.js
- path: path.resolve(__dirname, "app/assets/builds"),
+ path: path.resolve(__dirname, '..', '..', 'app/assets/builds')

2. Remove Webpacker

  1. Delete the following files
  • ./bin/webpack
  • ./bin/webpack-dev-server
  • ./config/initializers/webpacker.rb
  • ./config/webpacker.yml
  • ./config/webpack/development.js
  • ./config/webpack/environment.js
  • ./config/webpack/production.js
  • ./config/webpack/test.js
  1. Remove Webpacker gem
# Remove from your Gemfile
- gem 'webpacker'
  1. Run ./bin/bundle install

3. Install dependencies

Webpacker includes many dependencies by default while jsbundling-rails leaves it to you. If you're only handling JavaScript with no modifications you'll only need to install webpack-cli. Treat the rest of this section ala-carte.

# From the CLI, add webpack-cli
yarn add webpack-cli

# Then remove Webpacker packages
yarn remove @rails/webpacker webpack-dev-server

Optional: Babel

Babel is used to transpile source code to earlier versions of JavaScript.

  1. Install packages
# From the CLI, add babel presets
yarn add @babel/core @babel/preset-env
  1. Configure Babel
// In package.json, add
+ "babel": {
+   "presets": ["@babel/env"]
+ }
  1. Use the loader in webpack
// in webpack.config.js, add
module.exports = {
  module: {
    rules: [
+       {
+         test: /\.(js)$/,
+         exclude: /node_modules/,
+         use: ['babel-loader'],
+       },
    ],
  },
};

Example: Babel + React + TypeScript

You can use Babel to transpile front-end frameworks and TypeScript. This example setup uses React with TypeScript.

  1. Install packages
# From the CLI, add babel presets
yarn add @babel/core @babel/preset-env @babel/react @babel/preset-typescript
  1. Configure Babel
// In package.json, add
+ "babel": {
+   "presets": [
+     "@babel/env",
+     "@babel/react",
+     "@babel/preset-typescript"
+   ]
+ }
  1. Configure webpack
// in webpack.config.js, add
module.exports = {
  module: {
    rules: [
+       {
+         test: /\.(js|jsx|ts|tsx|)$/,
+         exclude: /node_modules/,
+         use: ['babel-loader'],
+       },
    ],
  },
};

Optional: CSS + SASS

With the right loaders, webpack can handle CSS files. This setup only uses jsbundling-rails, excluding cssbundling-rails from handling files.

  1. Install packages
# From the CLI, add loaders, plugins, and node sass
yarn add css-loader sass sass-loader mini-css-extract-plugin webpack-fix-style-only-entries
  1. Configure webpack
// In webpack.config.js

// Extracts CSS into .css file
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// Removes exported JavaScript files from CSS-only entries
// in this example, entry.custom will create a corresponding empty custom.js file
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');

module.exports = {
  entry: {
    // add your css or sass entries
    application: [
      './app/assets/javascripts/application.js',
      './app/assets/stylesheets/application.scss',
    ],
    custom: './app/assets/stylesheets/custom.scss',
  },
  module: {
    rules: [
      // Add CSS/SASS/SCSS rule with loaders
      {
        test: /\.s[ac]ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ],
  },
  resolve: {
    // Add additional file types
    extensions: ['.js', '.jsx', '.scss', '.css'],
  },
  plugins: [
    // Include plugins
    new RemoveEmptyScriptsPlugin(),
    new MiniCssExtractPlugin(),
  ],
};

Optional: Fonts, Images, SVG

Webpack can handle other files. This setup may vary on your use, but will look something like this:

  1. Configure webpack
// in webpack.config.js, add
module.exports = {
  module: {
    rules: [
+       {
+         test: /\.(jpe?g|svg|png|gif|ico|eot|ttf|woff2?)(\?v=\d+\.\d+\.\d+)?$/i,
+         type: 'asset/resource',
+       },
    ],
  },
};

4. Test your build

Confirm you have a working webpack configuration. You can rebuild the bundle with:

yarn build --progress --color

If you have multiple entries, it's recommended to confirm one at a time, and finally the entire bundle.

5. Replace Webpacker pack tags

Find + replace uses of Webpacker's asset tags.

# Webpacker tag       # Sprockets tag
javascript_pack_tag = javascript_include_tag
stylesheet_pack_tag = stylesheet_link_tag

Once the tags are replaced your app should be working same as before!

Optional: Add support for development environment

jsbundling-rails ships with only production mode. You can dramatically speed up build times during development by switching to mode: 'development'.

// Make the following changes in webpack.config.js
+ const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production';

module.exports = {
-  mode: "production",
+  mode,
-  devtool: "source-map",+  optimization: {
+    moduleIds: 'hashed',
+  }
}

Optional: Use esbuild-loader to speedup builds

Webpack builds with can be slower, one way to improve the speed is offload some load to esbuild using esbuild-loader

 {
      test: /\.(js)$/,
      exclude: /node_modules/,
+   use: ['esbuild-loader'],
-   use: ['esbuild-loader'],

  },
optimization: {
        moduleIds: 'deterministic',
        minimize: true,
        minimizer: [
            new ESBuildMinifyPlugin({
                target: 'es2015',
                css: true,
            })],
    },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment