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

WebPack emulates require('underscore') incorrectly #2852

Closed
dmaicher opened this issue May 25, 2020 · 11 comments
Closed

WebPack emulates require('underscore') incorrectly #2852

dmaicher opened this issue May 25, 2020 · 11 comments
Labels

Comments

@dmaicher
Copy link

dmaicher commented May 25, 2020

I updated from 1.9.2 to 1.10.2 today and for me mixins are not working anymore when using require('underscore');

This fails with TypeError: _.camelcase is not a function:

const _ = require('underscore');
const underscoreString = require('underscore.string');

_.mixin(underscoreString.exports());

console.log(_.camelcase('foo-bar'));

but this works:

import _ from 'underscore';
const underscoreString = require('underscore.string');

_.mixin(underscoreString.exports());

console.log(_.camelcase('foo-bar'));
@jgonggrijp
Copy link
Collaborator

Sorry to hear that this is biting you, @dmaicher. However I cannot reproduce this with Underscore 1.10.2 and underscore.string 3.3.5. Is there anything special about your setup?

@dmaicher
Copy link
Author

Thanks for your fast reply 😊

I'm using webpack@4.43.0 and the issue happens with code bundled for the browser. When I run the code with node on the CLI then it appears to be fine 😕

@dmaicher
Copy link
Author

Here is a minimal reproducer: https://github.com/dmaicher/underscore_issue_2852

@jgonggrijp
Copy link
Collaborator

Here is your problem:

https://github.com/dmaicher/underscore_issue_2852/blob/348eef226ec7392b2ced2b92ddf2fcba2517ab5c/public/build/app.js#L13

That import path, underscore/modules/index-all.js, must WebPack have obtained from the module field in our package.json:

underscore/package.json

Lines 17 to 18 in 5d8ab5e

"main": "underscore.js",
"module": "modules/index-all.js",

whereas the main field is what it should have used, given that you are using CommonJS imports. module is for ES imports in tools that support them, main is for everybody else. This explains why your code works with import but not with require().

Apparently, WebPack nowadays assumes ES modules by default but has a fallback for CommonJS imports. I guess you'll have to configure it somehow to not assume ES modules or to ignore the module field. Or maybe you want to switch to ES modules.

I'm closing this now because this is a WebPack issue rather than an Underscore issue. Feel free to continue commenting, though.

@dmaicher
Copy link
Author

@jgonggrijp thanks for checking 👍

I was able to fix it by adding an alias for resolving in the webpack config:
'underscore': 'underscore/underscore.js',

But this seems a bit odd to me. Many other packages I use specify "main" and "module" inside their package.json and for them it works fine.

Webpack takes the first field it finds in the order ['browser', 'module', 'main']
See https://webpack.js.org/configuration/resolve/#resolvemainfields

I'm curious why it only fails for underscore but seems to be working for other packages 😊

@jgonggrijp
Copy link
Collaborator

If you name those other packages, I'm happy to take a look. Anyway, module and main are not meant to be interchangeable, so WebPack's treating them as such is unwarranted:

https://github.com/rollup/rollup/wiki/pkg.module

The documentation you linked to hints at a perhaps slightly less ugly solution, which is to set resolve.mainFields to ['main']. Again, I believe WebPack shouldn't be considering module in the first place, since you are using CommonJS imports.

@dmaicher
Copy link
Author

dmaicher commented May 26, 2020

@dmaicher
Copy link
Author

Related webpack issue: webpack/webpack#5756

@jgonggrijp
Copy link
Collaborator

Yes, that is your issue. Although there also seems to be something off with the way WebPack is emulating CommonJS when resolving to the module. It appears that it is doing something like this:

// converting modules/index-all.js to ES3
var underscoreESModule = {
    'default': _,
    map: _.map,
    filter: _.filter,
    // ...
};
// in your code that imports underscore
var _ = underscoreESModule;

The latter line would make sense if you were doing this:

import * as _ from 'underscore';

but what you're actually doing is equivalent to this:

import _ from 'underscore';

and the right way to translate that to ES3 in WebPack would be as follows:

var _ = underscoreESModule['default'];

This is also exactly the problem that other people are running into in webpack/webpack#5756.

Other build tools such as Browserify and Rollup handle this more intelligently. Just saying...

@jgonggrijp jgonggrijp changed the title mixins not working with 1.10.2 and require('underscore') WebPack emulates require('underscore') incorrectly Jul 24, 2020
@codeninja
Copy link

Here is your problem:

https://github.com/dmaicher/underscore_issue_2852/blob/348eef226ec7392b2ced2b92ddf2fcba2517ab5c/public/build/app.js#L13

That import path, underscore/modules/index-all.js, must WebPack have obtained from the module field in our package.json:

underscore/package.json

Lines 17 to 18 in 5d8ab5e

"main": "underscore.js",
"module": "modules/index-all.js",

whereas the main field is what it should have used, given that you are using CommonJS imports. module is for ES imports in tools that support them, main is for everybody else. This explains why your code works with import but not with require().

Apparently, WebPack nowadays assumes ES modules by default but has a fallback for CommonJS imports. I guess you'll have to configure it somehow to not assume ES modules or to ignore the module field. Or maybe you want to switch to ES modules.

I'm closing this now because this is a WebPack issue rather than an Underscore issue. Feel free to continue commenting, though.

This reply just solved an issue I've been battling for 12 freaking days in our webpack build.

THANK YOU!

@codeninja
Copy link

@jgonggrijp thanks for checking 👍

I was able to fix it by adding an alias for resolving in the webpack config: 'underscore': 'underscore/underscore.js',

But this seems a bit odd to me. Many other packages I use specify "main" and "module" inside their package.json and for them it works fine.

Webpack takes the first field it finds in the order ['browser', 'module', 'main'] See https://webpack.js.org/configuration/resolve/#resolvemainfields

I'm curious why it only fails for underscore but seems to be working for other packages 😊

This reply just solved an issue I've been battling for 12 freaking days in our webpack build.

THANK YOU!

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

3 participants