Skip to content

Make optional jQuery require bundler-friendly (#4184)#4304

Open
BigBalli wants to merge 1 commit intojashkenas:masterfrom
BigBalli:fix-4184-jquery-bundling
Open

Make optional jQuery require bundler-friendly (#4184)#4304
BigBalli wants to merge 1 commit intojashkenas:masterfrom
BigBalli:fix-4184-jquery-bundling

Conversation

@BigBalli
Copy link
Copy Markdown

@BigBalli BigBalli commented Apr 6, 2026

Fixes #4184.

Problem

The CommonJS branch of the UMD wrapper does:

var _ = require('underscore'), $;
try { $ = require('jquery'); } catch (e) {}

The try/catch is correct at runtime in Node.js, but Webpack, Rollup,
esbuild, packd and other bundlers statically analyze the literal
require('jquery') call before the try/catch ever runs. Users who
don't actually want jQuery still see their bundles fail with
Cannot find module 'jquery'.

Fix

Indirect the optional require through a variable so the bundler's
static analysis does not follow it:

var nodeRequire = typeof require === 'function' && require;
try { if (nodeRequire) $ = nodeRequire('jquery'); } catch (e) {}

This is the same trick used by ws and other libraries with optional
native deps. Runtime behavior is unchanged: the require still happens
in Node, the try/catch still swallows ENOENT, and Backbone.$ is
still populated when jQuery is installed.

Also declare jquery as an optional peer dependency in package.json
so npm/yarn/pnpm document the relationship cleanly without forcing the
install:

"peerDependencies": { "jquery": ">=1.11.0" },
"peerDependenciesMeta": { "jquery": { "optional": true } }

Test

  • npm run lint passes.
  • Smoke-tested require('./backbone.js') in a fresh Node process with
    no jQuery installed: loads cleanly, Backbone.VERSION === '1.6.1',
    Backbone.$ === undefined as expected.

The literal `require('jquery')` form (even inside try/catch) is
statically analyzed by Webpack, Rollup, esbuild and packd, so users
who do not actually want jQuery still see their bundles fail with
"Cannot find module 'jquery'".

Indirect the optional require through a variable that bundlers do
not follow:

  var nodeRequire = typeof require === 'function' && require;
  try { if (nodeRequire) $ = nodeRequire('jquery'); } catch (e) {}

Behavior is unchanged: jQuery remains optional, the try/catch still
swallows the runtime ENOENT in Node, and `Backbone.$` is still set
when jQuery is installed.

Also declare `jquery` as an optional peerDependency in package.json
so npm/yarn/pnpm document the relationship cleanly without forcing
the install.
@jgonggrijp
Copy link
Copy Markdown
Collaborator

Thank you for the awesome work in this pull request and the previous four!

I made a superficial glance over all five PRs and they look very good to me. I will be reviewing them in more detail (and almost certainly also merging them) over the next two weeks or so. Do you expect any merge conflicts between them, and if so, which order would you recommend?

Copy link
Copy Markdown
Collaborator

@jgonggrijp jgonggrijp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with the modification in the UMD wrapper. 👍

Regarding the modification in the package.json, I have a question. I realize I suggested adding a peerDependecy, but now I wonder how much this might annoy users who are stuck with older versions of npm. I found these two threads about the introduction of peerDependenciesMeta:

npm/cli#1247
npm/cli#224

peerDependenciesMeta appears to have been introduced in npm v6 or v7. As of v7, npm apparently went "back" to installing peerDependencies by default. Which implies that some older versions of npm, which did not recognize peerDependenciesMeta yet, would also install peerDependencies by default. Do you know which versions of npm are affected by this and how problematic the resulting behavior could be?

I can look into this as well, but I thought I'd ask in case you already know the answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot find module 'jquery'

2 participants