Modernizr: Add exports statement as a build option for commonjs

Created on 31 Jan 2014  Â·  34Comments  Â·  Source: Modernizr/Modernizr

We use an implementation of CommonJS at work. It'd be great if the build page had an option to include an exports statement, so we'd be able to

var Modernizr = require("modernizr");

Most helpful comment

What's the hold up on this ticket? All we were asking for is 1 line of code...

I guess I'll use @pascalpp's solution (thanks!)

All 34 comments

Even if the default behavior was to attach to the window, it'd be great to have a jQuery-style noConflict method that could be used to restore the previous window.Modernizr value (I assume undefined) and return the Modernizr instance - then we could wrap that in our own preferred module flavor.

Ideal would be Ralph's suggestion however :-)

Hey @ralphholzmann, I'm not clear how the existing exports statement doesn't do this for you. Can you elaborate?

@patrickkettner I don't see any exports statement included in builds generated on http://modernizr.com/download/

No, but it is in the repo. Try npm install-ing this git repo.

On Fri, Jan 31, 2014 at 4:06 PM, Ralph Holzmann [email protected]
wrote:

@patrickkettner I don't see any exports statement included in builds generated on http://modernizr.com/download/

Reply to this email directly or view it on GitHub:
https://github.com/Modernizr/Modernizr/issues/1204#issuecomment-33842432

That's well and good, but npm isn't a part of our tool chain. Would be great if it could be a check box on the build page.

And commonjs is not a part of mine :)

That is why I think I'm not understanding your use case. What is it you want to do with the required code?

On Fri, Jan 31, 2014 at 4:09 PM, Ralph Holzmann [email protected]
wrote:

That's well and good, but npm isn't a part of our tool chain. Would be great if it could be a check box on the build page.

Reply to this email directly or view it on GitHub:
https://github.com/Modernizr/Modernizr/issues/1204#issuecomment-33842683

Like I said, I'd like to

var Modernizr = require("modernizr");

I'm just asking that an option for commonjs exports (or better yet, the UMD) be added to the custom build page.

I'm sorry, I'm not being clear. What code do you expect to require? The builder? The feature detects? Like I said, I don't use commonjs, so I'll need to you to walk me through the expectations. That being said, a pull request would illustrate it the best, most likely.

On Fri, Jan 31, 2014 at 4:13 PM, Ralph Holzmann [email protected]
wrote:

Like I said, I'd like to

var Modernizr = require("modernizr");

I'm just asking that an option for commonjs exports (or better yet, the UMD) be added to the custom build page.

Reply to this email directly or view it on GitHub:
https://github.com/Modernizr/Modernizr/issues/1204#issuecomment-33842993

I think I can answer this question. Because Modernizr is must optimal ran from the head, because you wan to prevent FOUC. And any kind of AMD loader is most optimal inside body tag because you don't want to block the browser from painting. We can't have an export statement in the Modernizr build because Modernizr will always execute before any AMD loader. Any kind of AMD loader requires the source of the AMD to be executed first.

But Modernizr is globalized so you don't need to define a module. But if you still want to show what kind of dependencies each module have just for the sake of dependencies. You can always add module defining code after the AMD loader have been executed:

In requirejs add this on main.js

define(function() {
    return Modernizr;
});

Would my suggestion (where it runs as a global initially, but has a noConflict method that returns Modernizr while simultaneously restoring window.Modernizr) work? That would seem to enable solving both cases.

@ljharb I don't see how it would solve @ralphholzmann issue?

I only see the usecase for having a noConflict method if the lib occupies an oftenly used namespace. In jQuery it is occupying the $ namespace that other libs also uses. Thereby having a noConflictmethod makes sense.

Modernizr is only occupying the Modernizr namespace which is not so attractive namespace. There could still be conflicts though. but they are minimal.

Just thinking out loud - it would enable us to have a custom module that just does:

module.exports = window.Modernizr.noConflict();

and then it would be removed from the global object - which would require our developers to require it in rather than relying on it being a global.

@ljharb makes sense :+1:

@ralphholzmann does @tinganho's suggestion solve your use case

Feels like this has gone a bit off topic.

My CommonJS knowledge isn’t too strong either, but I believe @ralphholzmann is simply asking for the Modernizr build output to optionally include this line:

exports.Modernizr = Modernizr;

The CommonJS module pattern is similar to AMD, although while objects created in an AMD module are exposed by returning them from the module function:

define(function () {
    var x = 1;
    return x;   // `x` is available to other modules
});

objects created in a CommonJS module are exposed by attaching them to a global exports object:

var x = 1;
exports.x = x;  // `x` is available to other modules

Confusingly, both use a require() method to import the exposed objects into another modules (because AMD was heavily influenced by CommonJS).

So noConflict etc aren’t really relevant – this isn’t about FOUC or anything like that – CommonJS users just want to use Modernizr like any other dependency.

If we add CommonJS compatibility, we should probably add AMD compatibility too – although that reopens the question of whether we should be exposing our AMD structure per-detect, or just making the whole script require()able.

But my vote is a +1 for adding this option.

I don't really have a dog in this fight, but I don't see why we would wait on amd to add commonjs, if it really is a single line.

+1 for UMD because it allows for greater consumption by build tools. I'd separate the logic like:

(function(root, name, make) {
  if (typeof module != 'undefined' && module.exports) module.exports = make();
  else root[name] = make();
}(this, 'Modernizr', function() {
  // Make and return Modernizr in here
}));

@stucox If we are going to define Modernizr as an module to an AMD than the AMD source code need to be loaded before Modernizr. That means AMD source code needed to be put inside the head, which will block the browser from painting.

It is the AMD source code that makes that you can use module.exports etc. (Export a module)

Modernizr doesn’t _always_ have to be in the head – some discussion around that is on #878… it’s just the easiest configuration, which is why we recommend it.

If someone decides the pros of making it async outweigh the cons, then being AMD and/or CommonJS compatible (or UMD) saves them having to provide any kind of shim config.

Any updates on this one? I would like to do a PR if needed to get things started...
Also, the reference to this is expected to be window, but it isn't when included with require. Should be fixed with this as well.

@SlexAxton usually has an opinion on these things.

Fancy raising a PR then @jtangelder? @ryanve’s UMD suggestion sounds good to me.

You can use Modernizr with Browserify and CommonJS by Browsernizr

I've seen some pretty nice affordances for CommonJS and AMD in libraries like URI.js. At the top of this module, https://github.com/medialize/URI.js/blob/master/src/URI.js , you'll see a first attempt to export the object in the CommonJS style, then AMD, then finally as a global on the window object if the CommonJS or AMD container was not detected.

A few patterns for implementing this adaptive export can be seen here: https://gist.github.com/CrocoDillon/9990078

@woldie +1, this would be an excellent upgrade. Instead of automatically adding Modernizr to the global window, it would be much better to try to detect the context that the developer is using.

At this point in the world of JS, I don't think anything should be trying to detect the context. I think it would be best if Modernizr strictly was a node-style commonJS module, and those who wish to use it in other contexts can trivially run it through browserify, webpack, or similar. UMDs go out of date, or out of vogue, and add a lot of boilerplate that most people don't need.

@ljharb This would be just as good, imo. I just don't like Modernizr forcing itself into the global context.

I think I definitely agree that UMD has not stood the test of time. And I agree that in general commonjs is the default authorship format these days.

But for Modernizr, it can't run outside of a browser. And it can't be loaded nicely with another copy of modernizr (it relies on global browser state, and modifies class names on the html element in most people's builds). It's explicitly a global/singleton dependency.

I'm not against this idea, but I think it's much more than a s/window/module.exports/g.

I've found in my apps, I generally include a tiny, built modernizr inline in the head, let it leak the global and mess with the <html> element, and then when my app spins up, I shim a module over the global:

// AMD
define('modernizr', [], function() {
  return window.Modernizr;
});

// CommonJs
module.exports = window.Modernizr;

This provides good performance, by allowing you to load Modernizr in the head of your document, without a request, or the need for a module system, and without thrashing the page render by adding classes asynchronously. And it still allows you to have an explicit dependency tree.

By packaging Modernizr with your app, you almost necessarily cause repaints and reflows, since you're likely loading that app asynchronously (even if you don't use class names, many of the tests have to inject elements into the dom).

Yay! Let's do it!!
On Sep 21, 2015 4:50 PM, "Joshua Wise" [email protected] wrote:

@woldie https://github.com/woldie +1, this would be an excellent
upgrade. Instead of automatically adding Modernizr to the global window,
it would be much better to try to detect the context that the developer is
using.

—
Reply to this email directly or view it on GitHub
https://github.com/Modernizr/Modernizr/issues/1204#issuecomment-142139621
.

It seems the JS community as a whole is going towards npm builds for their web projects. Why not make the base code with module.exports instead so that users can just import the tests that they want in their code?

+1

this is kind of a hack, but I'm using an npm script in our package.json which handles this for us:

    "build:modernizr": "modernizr -c ./config/modernizr.config.json -d ./src/vendor/modernizr.js; echo '\n\nmodule.exports = window.Modernizr\n' >> ./src/vendor/modernizr.js",

that script builds modernizr for us, and then appends module.exports onto the end of the file. still relies on the fact that the closure is leaking window.Modernizr, but at least we can use var Modernizr = require('vendor/modernizr') to get a reference to it.

What's the hold up on this ticket? All we were asking for is 1 line of code...

I guess I'll use @pascalpp's solution (thanks!)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BasJan81 picture BasJan81  Â·  9Comments

lovetrivedi picture lovetrivedi  Â·  4Comments

aaarichter picture aaarichter  Â·  6Comments

evanrmurphy picture evanrmurphy  Â·  7Comments

BenRichter picture BenRichter  Â·  9Comments