# Shakapacker (v7)
_Official, actively maintained successor to [rails/webpacker](https://github.com/rails/webpacker).ShakaCode stands behind the long-term maintenance and development of this project for the Rails community._
* β οΈ See the [6-stable](https://github.com/shakacode/shakapacker/tree/6-stable) branch for Shakapacker v6.x code and documentation. :warning:
* See [V7 Upgrade](./docs/v7_upgrade.md) for upgrading from the v6 release.
* See [V6 Upgrade](./docs/v6_upgrade.md) for upgrading from v5 or prior v6 releases.
[![Ruby specs](https://github.com/shakacode/shakapacker/workflows/Ruby%20specs/badge.svg)](https://github.com/shakacode/shakapacker/actions)
[![Jest specs](https://github.com/shakacode/shakapacker/workflows/Jest%20specs/badge.svg)](https://github.com/shakacode/shakapacker/actions)
[![Rubocop](https://github.com/shakacode/shakapacker/workflows/Rubocop/badge.svg)](https://github.com/shakacode/shakapacker/actions)
[![JS lint](https://github.com/shakacode/shakapacker/workflows/JS%20lint/badge.svg)](https://github.com/shakacode/shakapacker/actions)
[![node.js](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen.svg)](https://www.npmjs.com/package/shakapacker)
[![Gem](https://img.shields.io/gem/v/shakapacker.svg)](https://rubygems.org/gems/shakapacker)
[![npm version](https://badge.fury.io/js/shakapacker.svg)](https://badge.fury.io/js/shakapacker)
Shakapacker makes it easy to use the JavaScript pre-processor and bundler [Webpack v5+](https://webpack.js.org/)
to manage frontend JavaScript in Rails. It can coexist with the asset pipeline,
leaving Webpack responsible solely for frontend JavaScript, or can be used exclusively, making it also responsible for images, fonts, and CSS.
Check out 6.1.1+ for [SWC](https://swc.rs/) and [esbuild-loader](https://github.com/privatenumber/esbuild-loader) support! They are faster than Babel!
See a comparison of [Shakapacker with jsbundling-rails](https://github.com/rails/jsbundling-rails/blob/main/docs/comparison_with_webpacker.md). For an in-depth discussion of choosing between `shakapacker` and `jsbundling-rails`, see the discussion [Webpacker alternatives - which path should we go to? #8783](https://github.com/decidim/decidim/discussions/8783) and the resulting PR [Switch away from Webpacker to Shakapacker #10389](https://github.com/decidim/decidim/pull/10389).
For discussions, see our [Slack Channel](https://reactrails.slack.com/join/shared_invite/enQtNjY3NTczMjczNzYxLTlmYjdiZmY3MTVlMzU2YWE0OWM0MzNiZDI0MzdkZGFiZTFkYTFkOGVjODBmOWEyYWQ3MzA2NGE1YWJjNmVlMGE).
---
### ShakaCode Support
[ShakaCode](https://www.shakacode.com) offers support for upgrading from Webpacker and using Shakapacker. If interested, contact Justin Gordon, [justin@shakacode.com](mailto:justin@shakacode.com). We're also [hiring](https://jobs.lever.co/shakacode/3bdbfdb3-4495-4611-a279-01dddb351abe)!
Here's a testimonial of how ShakaCode can help, from [Florian GΓΆΓler](https://github.com/FGoessler) of [Blinkist](https://www.blinkist.com/), January 2, 2023:
> Hey Justin π
>
> I just wanted to let you know that we today shipped the webpacker to shakapacker upgrades and it all seems to be running smoothly! Thanks again for all your support and your teams work! π
>
> On top of your work, it was now also very easy for me to upgrade Tailwind and include our external node_module based web component library which we were using for our other (more modern) apps already. That work is going to be shipped later this week though as we are polishing the last bits of it. π
>
> Have a great 2023 and maybe we get to work together again later in the year! π
Read the [full review here](https://clutch.co/profile/shakacode#reviews?sort_by=date_DESC#review-2118154). Here's [another review of a Shakapacker migration that led to more work](https://clutch.co/profile/shakacode#reviews?sort_by=date_DESC#review-2096078).
---
- [Prerequisites](#prerequisites)
- [Features](#features)
- [Optional support](#optional-support)
- [Installation](#installation)
- [Rails v6+](#rails-v6)
- [Using alternative package managers](#using-alternative-package-managers)
- [Note for Yarn v2 usage](#note-for-yarn-v2-usage)
- [Concepts](#concepts)
- [Usage](#usage)
- [Configuration and Code](#configuration-and-code)
- [View Helpers](#view-helpers)
- [View Helpers `javascript_pack_tag` and `stylesheet_pack_tag`](#view-helpers-javascript_pack_tag-and-stylesheet_pack_tag)
- [View Helpers `append_javascript_pack_tag`, `prepend_javascript_pack_tag` and `append_stylesheet_pack_tag`](#view-helper-append_javascript_pack_tag-prepend_javascript_pack_tag-and-append_stylesheet_pack_tag)
- [View Helper: `asset_pack_path`](#view-helper-asset_pack_path)
- [View Helper: `image_pack_tag`](#view-helper-image_pack_tag)
- [View Helper: `favicon_pack_tag`](#view-helper-favicon_pack_tag)
- [View Helper: `preload_pack_asset`](#view-helper-preload_pack_asset)
- [Images in Stylesheets](#images-in-stylesheets)
- [Server-Side Rendering (SSR)](#server-side-rendering-ssr)
- [Development](#development)
- [Automatic Webpack Code Building](#automatic-webpack-code-building)
- [Compiler strategies](#compiler-strategies)
- [Common Development Commands](#common-development-commands)
- [Webpack Configuration](#webpack-configuration)
- [Babel configuration](#babel-configuration)
- [SWC configuration](#swc-configuration)
- [esbuild loader configuration](#esbuild-loader-configuration)
- [Integrations](#integrations)
- [React](#react)
- [Typescript](#typescript)
- [CSS](#css)
- [Postcss](#postcss)
- [Sass](#sass)
- [Less](#less)
- [Stylus](#stylus)
- [CoffeeScript](#coffeescript)
- [Other frameworks](#other-frameworks)
- [Custom Rails environments](#custom-rails-environments)
- [Upgrading](#upgrading)
- [Paths](#paths)
- [Additional paths](#additional-paths)
- [Deployment](#deployment)
- [Example Apps](#example-apps)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [License](#license)
- [Supporters](#supporters)
## Prerequisites
- Ruby 2.6+
- Rails 5.2+
- Node.js 12.13.0+ || 14+
- Yarn
## Features
- Rails view helpers that fully support Webpack output, including HMR and code splitting.
- Convenient but not required webpack configuration. The only requirement is that your webpack configuration creates a manifest.
- HMR with the `shakapacker-dev-server`, such as for hot-reloading React!
- Automatic code splitting using multiple entry points to optimize JavaScript downloads.
- [Webpack v5+](https://webpack.js.org/)
- ES6 with [babel](https://babeljs.io/), [SWC](https://swc.rs/), or [Esbuild](https://github.com/privatenumber/esbuild-loader)
- Asset compression, source-maps, and minification
- CDN support
- Extensible and configurable. For example, all major dependencies are specified as peers, so you can upgrade easily.
### Optional support
_Requires extra packages to be installed._
- React
- TypeScript
- Stylesheets - Sass, Less, Stylus and Css, PostCSS
- CoffeeScript
## Installation
### Rails v6+
With Rails v6+, skip JavaScript for a new app and follow below Manual Installation Steps to manually add the `shakapacker` gem to your Gemfile.
```bash
rails new myapp --skip-javascript
```
_Note, Rails 6 installs the older v5 version of webpacker unless you specify `--skip-javascript`._
Add `shakapacker` gem to your `Gemfile`:
```bash
bundle add shakapacker --strict
```
Then run the following to install Shakapacker:
```bash
./bin/bundle install
./bin/rails shakapacker:install
```
Before initiating the installation process, ensure you have committed all the changes. While installing Shakapacker, there might be some conflict between the existing file content and what Shakapacker tries to copy. You can either approve all the prompts for overriding these files or use the `FORCE=true` environment variable before the installation command to force the override without any prompt.
When `package.json` and/or `yarn.lock` changes, such as when pulling down changes to your local environment in team settings, be sure to keep your NPM packages up-to-date:
```bash
yarn
```
Note, in v6+, most JS packages are peer dependencies. Thus, the installer will add the packages:
```bash
yarn add @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/runtime babel-loader \
compression-webpack-plugin terser-webpack-plugin \
webpack webpack-assets-manifest webpack-cli webpack-merge webpack-sources webpack-dev-server
```
Previously, these "webpack" and "babel" packages were direct dependencies for `shakapacker`. By
making these peer dependencies, you have control over the versions used in your webpack and babel configs.
### Using alternative package managers
There is experimental support for using package managers besides Yarn classic for managing JavaScript dependencies using the [`package_json`](https://github.com/G-Rath/package_json) gem.
This can be enabled by setting the environment variable `SHAKAPACKER_USE_PACKAGE_JSON_GEM` to `true`; Shakapacker will then use the `package_json` gem which in turn will look for the [`packageManager`](https://nodejs.org/api/packages.html#packagemanager) property in the `package.json` or otherwise the `PACKAGE_JSON_FALLBACK_MANAGER` environment variable to determine which manager to use, defaulting to `npm` if neither are found.
See [here](https://github.com/G-Rath/package_json#specifying-a-package-manager) for a list of the supported package managers and more information; note that `package_json` does not handle ensuring the manager is installed.
> **Note**
>
> The rest of the documentation assumes that `package_json` is not being used, and so always references `yarn` - you should instead use the package manager of your choice for these commands.
### Note for Yarn v2 usage
If you are using Yarn v2 (berry), please note that PnP modules are not supported unless you're using `SHAKAPACKER_USE_PACKAGE_JSON_GEM`.
To use Shakapacker with Yarn v2, make sure you set `nodeLinker: node-modules` in your `.yarnrc.yml` file as per the [Yarn docs](https://yarnpkg.com/getting-started/migration#step-by-step) to opt out of Plug'n'Play behavior.
## Concepts
At its core, Shakapacker's essential function is to:
1. Provide configuration by a single file used by both Rails view helpers and JavaScript webpack compilation code.
2. Provide Rails view helpers, utilizing this configuration file so that a webpage can load JavaScript, CSS, and other static assets compiled by webpack, supporting bundle splitting, fingerprinting, and HMR.
3. Provide a community-supported, default webpack compilation that generates the necessary bundles and manifest, using the same configuration file. This compilation can be extended for any needs.
## Usage
### Configuration and Code
You will need your file system to correspond to the setup of your `config/shakapacker.yml` file.
Suppose you have the following configuration:
`shakapacker.yml`
```yml
default: &default
source_path: app/javascript
source_entry_path: packs
public_root_path: public
public_output_path: packs
nested_entries: false
# And more
```
And that maps to a directory structure like this:
```
app/javascript:
βββ packs: # sets up webpack entries
β βββ application.js # references ../src/my_component.js
β βββ application.css
βββ src: # any directory name is fine. Referenced files need to be under source_path
β βββ my_component.js
βββ stylesheets:
β βββ my_styles.css
βββ images:
βββ logo.svg
public/packs # webpack output
```
Webpack intelligently includes only necessary files. In this example, the file `packs/application.js` would reference `../src/my_component.js`
`nested_entries` allows you to have webpack entry points nested in subdirectories. This defaults to true as of shakapacker v7. With `nested_entries: false`, you can have your entire `source_path` used for your source (using the `source_entry_path: /`) and you place files at the top level that you want as entry points. `nested_entries: true` allows you to have entries that are in subdirectories. This is useful if you have entries that are generated, so you can have a `generated` subdirectory and easily separate generated files from the rest of your codebase.
To enable/disable the usage of contentHash in any node environment (specified using the `NODE_ENV` environment variable), add/modify `useContentHash` with a boolean value in `config/shakapacker.yml`. This feature is disabled for all environments except production by default. You may not disable the content hash for a `NODE_ENV` of production as that would break the browser caching of assets. Notice that despite the possibility of enabling this option for the development environment, [it is not recommended](https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling).
#### Setting custom config path
You can use the environment variable `SHAKAPACKER_CONFIG` to enforce a particular path to the config file rather than the default `config/shakapacker.yml`.
### View Helpers
The Shakapacker view helpers generate the script and link tags to get the webpack output onto your views.
Be sure to consult the API documentation in the source code of [helper.rb](./lib/shakapacker/helper.rb).
**Note:** For your styles or static assets files to be available in your view, you would need to link them in your "pack" or entry file. Otherwise, Webpack won't know to package up those files.
#### View Helpers `javascript_pack_tag` and `stylesheet_pack_tag`
These view helpers take your `shakapacker.yml` configuration file and the resulting webpack compilation `manifest.json` and generate the HTML to load the assets.
You can then link the JavaScript pack in Rails views using the `javascript_pack_tag` helper. If you have styles imported in your pack file, you can link them by using `stylesheet_pack_tag`:
```erb
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>
```
The `javascript_pack_tag` and `stylesheet_pack_tag` helpers will include all the transpiled
packs with the chunks in your view, which creates HTML tags for all the chunks.
You can provide multiple packs and other attributes. Note, `defer` defaults to showing.
```erb
<%= javascript_pack_tag 'calendar', 'map', 'data-turbo-track': 'reload' %>
```
The resulting HTML would look like this:
```
```
In this output, both the calendar and map codes might refer to other common libraries. Those get placed in something like the vendor bundle. The view helper removes any duplication.
Note, the default of "defer" for the `javascript_pack_tag`. You can override that to `false`. If you expose jquery globally with `expose-loader,` by using `import $ from "expose-loader?exposes=$,jQuery!jquery"` in your `app/javascript/application.js`, pass the option `defer: false` to your `javascript_pack_tag`.
**Important:** Pass all your pack names as multiple arguments, not multiple calls, when using `javascript_pack_tag` and the `stylesheet_pack_tag`. Otherwise, you will get duplicated chunks on the page.
```erb
<%# DO %>
<%= javascript_pack_tag 'calendar', 'map' %>
<%# DON'T %>
<%= javascript_pack_tag 'calendar' %>
<%= javascript_pack_tag 'map' %>
```
While this also generally applies to `stylesheet_pack_tag`,
you may use multiple calls to stylesheet_pack_tag if,
say,
you require multiple `