0
votes

I'm working on a library that is used in a number of web applications. The library itself does not use RequireJS - it's written in node, and then bundled with browserify - however, some of its dependents are using a RequireJS shim to include the library.

What we've found is that the RequireJS shim used by our dependents breaks with an "mismatched anonymous define()" error, if our library depends on a library that includes an anonymous define module, such as this one from the lodash source:

// Define as an anonymous module so, through path mapping, it can be
// referenced as the "underscore" module.
define(function() {
  return _;
});

I've found the RequireJS documentation on this problem; but all of its solutions seem to be with the assumption that the library importing the library with the anonymous module is using RequireJS, which isn't the case here.

How can I deal with this so that the downstream libraries don't need to do any special handling in order to require our library? Ideally, it'd be nice if we don't have to add custom logic for every library that we use with an anonymous module definition in it, too.

2

2 Answers

1
votes

You can try to bundle your application using the Browserify standalone option and derequire.

Maybe you have use derequire on lodash and then bundle your app using the standalone option.

The following is from the Browserify documentation:

When opts.standalone is a non-empty string, a standalone module is created with that name and a umd wrapper. You can use namespaces in the standalone global export using a . in the string name as a separator. For example: 'A.B.C'

Note that in standalone mode the require() calls from the original source will still be around, which may trip up AMD loaders scanning for require() calls. You can remove these calls with derequire:

$ npm install -g derequire $ browserify main.js --standalone Foo | derequire > bundle.js

opts.insertGlobalVars will be passed to insert-module-globals as the opts.vars parameter.

opts.externalRequireName defaults to 'require' in expose mode but you can use another name.

Note that if files do not contain javascript source code then you also need to specify a corresponding transform for them.

All other options are forwarded along to module-deps and browser-pack directly.

You can also find a good tutorial about the standalone option at http://www.forbeslindesay.co.uk/post/46324645400/standalone-browserify-builds

0
votes

So what I ended up doing was importing the requirejs optimizer into the browserify project, and running all of the files that included anonymous define modules through the requirejs optimizer, before bundling them, via a browerify transform function.

The code looked something like:

//Kept an explicit list of things to requirejs optimize, to avoid running every file through the optimizer
var modulesToRequireJSOptimize = ["lodash"];
var lib = browserify();
//...
lib.transform(requireJSOptimize, {global: true}); //global true to apply it to the modules

function requireJSOptimize(file) {
    var moduleName;
    //Determine if the file is one of the modules we need to optimize
    modulesToRequireJSOptimize.forEach(function(mod) {
        if(require.resolve(mod) === file) {
            moduleName = mod;
        }
    }
    if(!moduleName) {
         return through(); //do nothing
    }
    var end = function() {
        var pipe = this;
        var optimizerConfig = {
            name: moduleName
            optimize: "none" //don't minify
            paths: {}
            out: function(optimizedJS) {
                this.queue(optimizedJS);
                this.queue(null);
            }
         }
         //strip off .js
         optimizerConfig.paths[moduleName] = file.substr(0, file.length-3)
         requirejs.optimize(optimizerConfig, 
             function() {}, //success handled in optimizerConfig.out
             function(err) {} //handle err here
         );
    };
    return through(function () {}, end);
}

It's not pretty but it worked for our purposes. I'd recommend others try OweR ReLoaDeD's standalone+derequire first, but I thought I'd share my eventual solution for completeness sake.