How do I use Browserify with external dependencies?
Asked Answered
B

3

50

I am trying to slowly introduce Browserify into my site, but I don't want to rewrite all the js and I don't want duplicate instances of jquery and other libraries bundled with my Browserify build.

If I build my module listing jquery as an external dependency, how do I then point it at my global jquery instance? Also the goal is to eliminate the mylibs global (example below), so I don't want to use it in my module.

This is what I'm trying to do (psudo-code). This would be in my site's repo - not the module's. The module would be installed with Bower:

var mylibs.jQuery = $.noConflict(); // global used by lots of existing code
module.exports = {
    jquery: mylibs.jQuery // can be imported by my module as require('jquery')
};

Something like that is what I'm trying to achieve. Is this possible?

Brut answered 17/4, 2014 at 5:33 Comment(0)
A
73

You can achieve that by using browserify-shim. Assuming that you've got a module named mymodule.js that depends on jQuery in the global scope with the following contents:

var $ = require('jQuery');

console.log(typeof $);
  1. Install browserify-shim:

    npm install browserify-shim --save-dev
    
  2. In package.json file, tell browserify to use browserify-shim as a transform:

    {
        "browserify": {
            "transform": [ "browserify-shim" ]
        }
    }
    
  3. In package.json file, tell browserify-shim to map jQuery to the jQuery in the global scope:

    {
        "browserify-shim": {
            "jQuery": "global:jQuery"
        }
    }
    
  4. Run browserify

    browserify mymodule.js > bundle.js
    

If you examine bundle.js you will notice that require('jQuery') is replaced with (window.jQuery).

Archpriest answered 17/4, 2014 at 9:12 Comment(4)
AFAIK jQuery has CommonJS support in the latest versions.Toinette
@Toinette that's not useful if you need jquery both as module (because of your own code) and global (because of third party plugins outside npm). The shim solves that issue.Lunch
This (browserify-shim) does not work with transitive requires as it requires (!) that the [offending] modules also include the appropriate package.json configuration - hard to do when said modules are external.Inshore
How do we do this if used in multiple files?Sello
I
10

Browserify-shim is not transitive across node modules: it can be used to correctly shim top-level (in your own package.json) modules, but it cannot shim modules in other npm packages (with their own package.json files).

This is awkward when dealing with a node module that depends on the jQuery module (eg. a plugin that has a peer dependency), but the jQuery library should still be external.

My solution - similar in concept to the pseudo-code - was to create a custom 'preload shim', with the help of browserify itself.

  1. Exclude the jquery module from the generation of bundle.js, but otherwise build the bundle normally.

    Install the appropriate node/npm modules to meet the build requirements. The to-be-excluded "external" modules will not be included in the bundle but are required to fulfill the compilation dependency resolution.

     browserify -x jquery .. > dist/bundle.js
    
  2. Create a file called jquery.js and include this content:

     module.exports = window.jQuery; // or whatever
    
  3. Generate a shim.js including just the previous file.

     browserify -r jquery.js > dist/shim.js
    

    Then edit the file to use jQuery as the module name.

  4. In the browser, load jquery (the external dependency), shim.js, and then bundle.js.

    When the bundle file tries to load the jquery module - which it does not define - it will fallback to the module (previously) defined in the shim file and run the custom code. In this case that's piping through a previously defined global.

    Or: what browserify-shim "global:" tries to do, only actually .. globally.


Using the browserify module directly - instead of grunt, which I am re-growing to loathe - may have resulted in a 'more refined' solution.

Inshore answered 22/3, 2016 at 5:31 Comment(3)
Hey @user2864740, I was wondering if you came up with any more elegant solutions to this issue? I am trying to set out to do what you did "shim modules in other npm packages" and this feels like the closest thing I've found to explaining a solution for it.Odell
@Odell I have not yet found a better approach.Inshore
Note that Browserify allows the module/target name to be given along with the -r option, so manual editing of the shim file can be avoided: browserify -r ./jquery.js:jQuery > dist/shim.js. Multiple -r options can also be given to bundle more than one module into the same file, if necessary.Boylan
P
-1

This can be done with a 1-liner:

echo "module.exports=window.jQuery" > node_modules/jquery.js

Add 1 line to your build script for each external dependency. No need to pass any special options to Browserify.

Pettifog answered 5/11, 2018 at 19:22 Comment(4)
Fragile hack. Works until somehow clones the repo.Tussle
Could you say any more about this? I clone my repos to my production servers to deploy. Never, ever had a problem approaching it this way -- I am sure you are referencing a valid issue, and I'm hoping you might flesh it out a bit.Pettifog
I'm assuming this is based on the fact that node_modules wouldn't normally be in your source control. This can be resolved in git by setting up your ignore like this: node_modules/* newline !node_modules/jquery.js I think hes right though is a bit of a nasty hack but hey, I think all solutions to this problem are kinda hacks. This is actually a very simple one that doesn't depend on any other node modules like browserify-shim.Supersaturate
@Lightbulb - node_modules doesn't need to be under source control. The build script goes roughly: echo "module.ex..." > node_modules/... && browserify .... I.e., node_modules is prepared as part of the build; there's no danger of it not being ready after someone clones the repo. If they want to build, they have to npm install first anyway. Help me see what I'm missing.Pettifog

© 2022 - 2024 — McMap. All rights reserved.