Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's the right way to use webpack-specific functionality in node.js? #183

Closed
fresheneesz opened this issue Feb 26, 2014 · 25 comments
Closed
Labels

Comments

@fresheneesz
Copy link
Contributor

webpack defines require.ensure and require.include, which aren't available in node.js proper. If I want to create modules that use these functions, but still run in node.js, how do I do that? They can certainly just be defined as no-ops in node.js.

@andreypopp
Copy link
Contributor

I also have a use case for this. I think it can be solved like this:

  • make webpack to process requireEnsure(id, cb) call like it processes require.ensure(id, cb)
  • create requireEnsure.js module with implementation for Node, which just does nothing but calls a callback
  • shim requireEnsure.js for browser (using webpack field in package.json) to an empty module

Use in your app like this:

var requireEnsure = require('requireEnsure');

requireEnsure('mod', function() {
   ...
});

@fresheneesz
Copy link
Contributor Author

I would improve on that by having the require be require('webpack/ensure') which would be a no-op function. Webpack could do one better than searching for functions called requireEnsure or ensure, but could instead replace requires of that exact file (it can find which file it is in the filesystem when compiling) and it replace it as a special module in a bundle.

It would be nice if there was a way to do it without having to require webpack/ensure or something like it every time, but that's probably the safest way. Any other way almost definitely has the possibility of conflicts or weird edge-cases.

sokra added a commit that referenced this issue Feb 27, 2014
@sokra
Copy link
Member

sokra commented Feb 27, 2014

Polyfill the functions on the top of a module using them:

if(typeof require.ensure !== "function") require.ensure = function(d, c) { c(require) };
if(typeof require.include !== "function") require.include = function() {};

I added a commit that would remove that polyfills in a bundled version.

@fresheneesz
Copy link
Contributor Author

Those would have to be added at the top of every module that wanted to use them? That's a lot of annoying boilerplate.. Not a very friendly way to add this functionality.

@andreypopp
Copy link
Contributor

Polyfill is a also a solution but I'd prefer having var requireEnsure = require('webpack/ensure') which is more clean, I think.

@sokra
Copy link
Member

sokra commented Feb 27, 2014

I don't think that this are many modules. You generally use chunking only in a same fraction of modules.

Using var requireEnsure = require('webpack/ensure') is also one line of boilerplate, but even if you don't want to run the module in node.js. require('webpack/ensure') cannot be used by other module systems, if they want to provide the same functionality.

I have a module system for node.js providing require.ensure, etc. but it's not up to date because I had not enough time to maintain it (require.ensure does work, but require.include doesn't). https://github.com/webpack/enhanced-require

@fresheneesz
Copy link
Contributor Author

The difference is that var ensure = require('webpack/ensure') is 4 times shorter (meaning more easily rememberable) and uses standard commonjs patterns of inclusion. That, in my mind, makes it far superior to the pollyfill. Of course the downside is that it doesn't follow the commonjs proposal someone mentioned. Then again, it is just a proposal.

This could actually extend to quite a number of modules. Many modules have rarely-used functionality, or have so many use cases that only a fraction of the module is ever actually used. In these cases, the module could manage loading (or preloading) code as necessary. Without an ability to do this, published modules can't be chunked apart, but must always be loaded whole.

You make a good point that other module systems can't use something webpack-specific, but once you go webpack, why would you go back? ; )

Interesting, so you're basically saying that enhanced-require does something similar to what we were proposing above? Meaning we could use it like this:

var erequire = oldrequire('enhanced-require')
erequire.ensure('module', function() {
   var module = require('module')
   // ...
})

?

@jhnns
Copy link
Member

jhnns commented Feb 27, 2014

Why do you bundle code for node.js? I'm just curious, because I can't imagine an use-case :)

@fresheneesz
Copy link
Contributor Author

You don't. The whole point would be to have modules that define their own split-points. For node.js they should be ignored, but for webpack, they should be used to do code splitting.

@fresheneesz
Copy link
Contributor Author

Btw, I tried out enhanced-require, and it works pretty well - tho I did run into one bug (which wouldn't really affect code I would care about anyway, I don't think).

@jhnns
Copy link
Member

jhnns commented Mar 1, 2014

So you'd like to pre-bundle your (npm-)modules so they can be used on the client and the server?

@andreypopp
Copy link
Contributor

@jhnns I'd like to pre-bundle only for use on the client but I still want to be able to execute the same code (which use require.ensure) in Node.

My use case is I'm using React UI library and it allows to render UI components on server as well as on the client.

@jhnns
Copy link
Member

jhnns commented Mar 2, 2014

Ah ok. So is your module part of your application or of a npm-module?

@fresheneesz
Copy link
Contributor Author

I'd like to be able to use this for npm-modules. No pre-bundling, but rather it would have the split point ready for when someone does want to bundle it.

@jhnns
Copy link
Member

jhnns commented Mar 3, 2014

Mhmm. I always tried to leave bundle-specific stuff to the application because npm-modules should not depend on a specific bundler. They should be plain node-style modules.

Bundling is still a new topic and there are barely any standards. The only thing that might work across bundlers is the browser-field (#151).

@fresheneesz
Copy link
Contributor Author

The ideal solution should cause non-supporting bundlers/module-loaders to treat it exactly like node does - a no op.

@sokra sokra added the question label Mar 10, 2014
@mbfisher
Copy link

mbfisher commented Jan 5, 2015

What's the suggested way to do this, 9 months on?

M

@jhnns
Copy link
Member

jhnns commented Jan 7, 2015

I guess it's still shimming like sokra explained it

@sokra
Copy link
Member

sokra commented Jan 8, 2015

yep

@ryanflorence
Copy link

/me goes ahead and adds the shim to 16 files

@KpjComp
Copy link

KpjComp commented Jan 6, 2016

+1, for a better solution than shimming every file.
or using 'enhanced-require'

I'm currently building an App stack that can be used in Browser & Node.

As an example: A lot of Javascript engines now implement Promises, but what I'm doing is poly-filling with Bluebird for ones that don't, and keeping the native version for speed. Bluebird compressed is still 75k, so bundling here makes sense.

Because polyfilling node's require is not ideal, polyfilling another global say like requireEnsure would be a lot easer and only needs to be done once at app startup. Of course this would require webpack to see requireEnsure as an alias for require.ensure ...

@snadn
Copy link

snadn commented Jan 6, 2016

I use this

let proto = Object.getPrototypeOf(require);
!proto.hasOwnProperty("ensure") && Object.defineProperties(proto, {
    "ensure": {
        value: function ensure(modules, callback) {
            callback(this);
        },
        writable: false
    },
    "include": {
        value: function include() {},
        writable: false
    }
});

in global of node

@fresheneesz
Copy link
Contributor Author

@KpjComp FYI Bluebird is surprisingly/embarrassingly faster than native last I checked.

@KpjComp
Copy link

KpjComp commented Jan 6, 2016

@snadn: Nice one,.. Only problem is webpack is giving a critical dependencies warning for this bit -> var proto = Object.getPrototypeOf(require);
Although everything seems to work fine.

@fresheneesz: I did a couple of tests,
Although not a complete test, I did a simple ->

var k = 0;var proms = [];console.time('t1');for (var l = 0; l < 10000; l ++) proms.push(new Promise(function (resolve, reject) { k ++; resolve(); }));Promise.all(proms).then(function () { console.timeEnd('t1'); })

My results,..
Chrome Browser:
Bluebird: 377.339ms, Native: 255.331ms
Node:
Bluebird: 79.960ms, Native: 38.801ms

Ah!!, forget that. What I didn't do in Bluebird was ->
Promise.config({longStackTraces: false});

Node Bluebird then gives 9.998ms.. and then Chrome bluebird -> 71.670ms

So indeed Native promises are embarrassingly slower,.. What gives, doesn't make any sense..

@SpaceK33z
Copy link
Member

Closing since there are plenty of clear answers here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants