Read more

Webpack: How to avoid multiple versions of jQuery

Emanuel
April 29, 2019Software engineer at makandra GmbH

To avoid multiple versions of a package, you can manually maintain a resolutions section in your package.json. We recommend you to do this for packages like jQuery. Otherwise the jQuery library attached to window might not include the functions of your packages that depend on jQuery.

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

Note: This is only an issue in case you want to use a package functionality from window e.g. $(...).datepicker() from your dev console or any other javascript within the application.

Background

By default yarn will create a folder node_modules that includes all packages and their dependencies. In the example below we see two packages of jQuery. Such duplicates will occur each time no common version could be found or for other reason (like in the example where a common version would have been possible).

yarn.lock

jquery@2.x:
  version "2.2.4"
  resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02"
  integrity sha1-LInWiJterFIqfuoywUUhVZxsvwI=

"jquery@>=1.7.1 <4.0.0":
  version "3.4.0"
  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.0.tgz#8de513fa0fa4b2c7d2e48a530e26f0596936efdf"
  integrity sha512-ggRCXln9zEqv6OqAGXFEcshF5dSBvCkzj6Gm2gzuR5fWawaX8t7cxKVkkygKODrDAzKdoYw3l/e3pm3vlT4IbQ==

package.json

{
  "dependencies": {
    "@rails/webpacker": "4.x",
    "bootstrap-datepicker": "1.8.0", # the package says it needs "jquery": ">=1.7.1 <4.0.0" as a dependency
    "jquery": "2.2.4",
}
node_modules/jquery
node_modules/bootstrap-datepicker/node_modules/jquery

Actual issue

Within the Rails Webpacker config we now provide a jQuery object to boostrap-datepicker:

config/webpack/environment.js

environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'window.jQuery': 'jquery',
  })
)

And a jQuery object to window, to have access to jQuery from our dev console or any other javascript within the application.

app/webpack/packs/main.js

window.$ = jQuery
window.jQuery = jQuery

Now this will not allow us to use any functionality from boostrap-datepicker on the window object.

$('<div></div>').datepicker() // Uncaught TypeError: $(...).datepicker is not a function

Solution

You can maintain a resolutions section within your package.json. This allows you to force the same version of a package for all or an individual package. Another approach (not yet tested) is to set a resolve alias for jquery.

Force all dependencies to use jQuery 2.2.4

{
  "dependencies": {
    "@rails/webpacker": "4.x",
    "bootstrap-datepicker": "1.8.0",
    "jquery": "2.2.4",
  },
  "resolutions": {
    "jquery": "2.2.4"
  }
}

Force bootstrap-datepicker to use jQuery 2.2.4

{
  "dependencies": {
    "@rails/webpacker": "4.x",
    "bootstrap-datepicker": "1.8.0",
    "jquery": "2.2.4",
  },
  "resolutions": {
    "bootstrap-datepicker/jquery": "2.2.4"
  }
}

Carvet: Using resolutions will ignore any compatibilities. This might break the packages functionality.

Note: You can use yarn install --flat to find all duplicated packages in your project and add a resolution entry for each. You should throw away the changes in your package.json afterwards, as this approach will not work with real world projects.

References:

Emanuel
April 29, 2019Software engineer at makandra GmbH
Posted by Emanuel to makandra dev (2019-04-29 17:31)