After upgrading webpack 4.46.0 to webpack 5.88.2 & shakapacker 7.0.3 (rails 6.1.7.6) got the Uncaught ReferenceError: $ is not defined
error. Here the current setup:
package.json
{
"name": "app",
"private": true,
"dependencies": {
"@babel/runtime": "^7.22.15",
"@fortawesome/fontawesome-free": "^6.4.2",
"@hotwired/turbo-rails": "^7.3.0",
"@popperjs/core": "^2.11.8",
"@rails/ujs": "^7.0.8",
"babel-loader": "^9.1.3",
"bootstrap": "^5.3.1",
"core-js": "^3.32.2",
"corejs-typeahead": "^1.3.3",
"css-loader": "^6.8.1",
"exports-loader": "^4.0.0",
"expose-loader": "^4.1.0",
"file-loader": "^6.2.0",
"flag-icons": "^6.11.0",
"handlebars": "^4.7.8",
"inflection": "^2.0.1",
"jbuilder": "^0.0.5",
"jquery": "^3.7.1",
"jquery-ui": "^1.13.2",
"jquery-ui-dist": "^1.13.2",
"jquery-ujs": "^1.2.3",
"jstree": "^3.3.15",
"mini-css-extract-plugin": "^2.7.6",
"moment": "^2.29.4",
"popper.js": "^1.16.1",
"postcss": "^8.4.29",
"rails-erb-loader": "^5.5.2",
"sass-loader": "^13.3.2",
"shakapacker": "^7.0.3",
"sidekiq": "^1.1.1",
"style-loader": "^3.3.3",
"webpack": "^5.88.2",
"webpack-assets-manifest": "^5.1.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0",
"webpack-sources": "^3.2.3",
"yarn": "^1.22.19"
},
"babel": {
"presets": [
"./node_modules/shakapacker/package/babel/preset.js"
]
},
"devDependencies": {
"@babel/core": "^7.22.19",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-destructuring": "^7.22.15",
"@babel/plugin-transform-regenerator": "^7.22.10",
"@babel/plugin-transform-runtime": "^7.22.15",
"@babel/preset-env": "^7.22.15",
"babel-plugin-macros": "^3.1.0",
"clean-webpack-plugin": "^4.0.0",
"compression-webpack-plugin": "^10.0.0",
"moment-timezone": "^0.5.43",
"regenerator-runtime": "^0.14.0",
"sass": "^1.67.0",
"stylus": "^0.60.0",
"terser-webpack-plugin": "^5.3.9",
"url-loader": "^4.1.1"
}
}
config/webpack/webpack.config.js
const { generateWebpackConfig } = require('shakapacker')
const webpackConfig = generateWebpackConfig()
const customConfig = require('./custom')
module.exports = generateWebpackConfig()
config/webpack/custom.js
var path = require('path');
var webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = (process.env.NODE_ENV !== "staging") && (process.env.NODE_ENV !== "production");
module.exports = {
mode: 'development',
entry: '../../app/javascript/packs/application.js',
output: {
path: path.resolve(__dirname, '../public'),
publicPath: 'public/',
filename: '[name].js', // could be omitted, that's the default
assetModuleFilename: 'public/images/[name].[ext]'
},
resolve: {
modules: ['node_modules'],
extensions: ['.css', '.sass', '.scss', '.js', '.json'],
alias: {
jquery: 'jquery',
'jquery-ui': 'jquery-ui/jquery-ui.js',
typeahead: 'core-typeahead',
}
},
module: {
rules: [
{
test: /.html.erb$/,
loader: 'rails-erb-loader'
},
{
test: require.resolve('jquery'),
loader: 'expose-loader',
options: {
exposes: ['$', 'jQuery'],
},
},
{
test: /.s[ac]ss$/i,
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
"style-loader",
],
},
{
test: /.(jpe?g|png|gif|svg|gif|png|jpg|eot|ttf|otf|woff|woff2)$/i,
loader: 'file-loader'
},
]
},
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
optimization: {
splitChunks: {
chunks: 'all'
}
},
progress: true,
stats: {
errorDetails: true, //this does show errors
colors: true,
modules: true,
reasons: true
}
}
config/webpack/environment.js
const webpack = require('webpack');
const { generateWebpackConfig } = require('shakapacker')
const erb = require('./loaders/erb');
const customConfig = {
resolve: {
fallback: {
dgram: false,
fs: false,
net: false,
tls: false,
child_process: false
}
}
};
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
jquery: 'jquery',
Popper: ['popper.js', 'default'],
Rails: ['@rails/ujs'],
moment: 'moment'
})
);
environment.loaders.prepend('erb', erb);
environment.config.merge(customConfig);
module.exports = environment;
config/webpack/development.js
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
app/assets/javascript/packs/application.js
...
import $ from 'jquery';
window.jQuery = $;
window.$ = $;
import 'jquery-ui-dist/jquery-ui';
...
app/views/layouts/application.html.erb
...
<%= stylesheet_pack_tag 'application', :media => "all", :data => {:turbo => {:track => 'reload'}} %>
<%= javascript_pack_tag 'application', :data => {:turbo => {:track => 'reload'}, :defer => false} %>
...
No compilation errors occur, any clue why isn’t jquery loading?
2
Answers
I figured out that the error wasn't related to webpacker/shakapacker configuration, but to a
js.erb
file which was not loaded properly due to the missing 'defer' attribute.app/views/layouts/application.html.erb
The Webpacker helper
javascript_pack_tag
will generate the HTML code withdefer
set totrue
as default (which is new in webpacker 5).Therefore the
js.erb
file won't wait for the script and it will load before jquery. Addingdefer = true
tojavascript_include_tag
fixed the issuePlease check out Shakapacker README file and v6 upgrade docs in which two approaches are proposed. I quote them here:
Using
expose-loader
Use
resolve.alias
in webpack config:Use ProvidePlugin in webpack config