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

Rollup + Babel + Lodash Bundle size #1403

Closed
willmcclellan opened this issue May 10, 2017 · 7 comments
Closed

Rollup + Babel + Lodash Bundle size #1403

willmcclellan opened this issue May 10, 2017 · 7 comments

Comments

@willmcclellan
Copy link

willmcclellan commented May 10, 2017

I've noticed some weird behaviour when using rollup with lodash + babel. I'm not getting the bundling output that is expected. I've created an example repo to illustrate the problem here: https://github.com/willmcclellan/babel-lodash-rollup-example

In that example I have 2 4 js files which import lodash's find method in two different ways:

  1. import find from 'lodash/fp/find'
  2. import find from 'lodash/fp/collection/find
  3. import { find } from 'lodash/fp (using babel-plugin-lodash)
  4. import find from 'lodash-es/find (Lodash ES)

With the following results

  1. creates a bundled file size of 179kb
  2. creates a bundled file size of 445bytes
  3. creates a bundled file size of 179kb
  4. creates a bundled file size of 76kb

That seems like strange behaviour, because by all accounts No. 1 should bundle just that file (and sub dependencies) and nothing else from the lodash package. But it's including what looks like everything from lodash, or at least a lot more than it should.

No. 2 works well, but that's not the recommended approach from what I understand (Jest also can't resolve using this style)... The babel-lodash-plugin suggests it would covert the imports to No 1. style, which in my example doesn't work.

I might be missing something here, but it does seem like a bug or at least the recommended approach isn't right. Let me know if I'm posting this in the wrong place as well.

@jdalton might also be able to shed some light.

@olsonpm
Copy link
Contributor

olsonpm commented May 12, 2017

The recommended way is to use babel-plugin-lodash

@marvinhagemeister
Copy link

The issue that you are hitting is that lodash-fp distributes a compiled umd bundle. Rollup can only tree-shake packages, that are exported via es6 export keyword.

From experience, lodash in general has always had a troubled past because of difficulties with there bundles. @olsonpm mentioned the right solution.

@willmcclellan
Copy link
Author

@olsonpm As mentioned in that link, all babel-plugin-lodash does is transform the import statements to the modular equivalent. So the result would be the same as example 1 above. I tried it anyway just to be sure and got the exact same bundle size (See Example 3 in the linked repo).

@marvinhagemeister I also added a test (Example 4 in linked repo) using lodash-es and that did reduce the file size, but only down to 79kb - so still very bloated.

@olsonpm
Copy link
Contributor

olsonpm commented May 14, 2017

Thanks for testing that explicitly. I'll take a further look and get back.

@olsonpm
Copy link
Contributor

olsonpm commented May 14, 2017

so I didn't look into lodash/fp/find since that likely is bloated with utilities related to the fp converter.

Strictly looking at lodash-es/find, the output is exactly what it should be. It seems bloated, but it's mostly a side-effect of lodash's overloaded function signatures (where any given utility takes many different types of parameters). This is actually one of the reasons I initially converted to ramda, before getting frustrated with that library too and ended up just writing my own simple functional utilities.

Below is me cascading through just a few of the lodash-es/find internal dependencies.

// lodash-es/find
import createFind from './_createFind.js';
import findIndex from './findIndex.js';

var find = createFind(findIndex);
export default find;


// _createFind.js
import baseIteratee from './_baseIteratee.js';
import isArrayLike from './isArrayLike.js';
import keys from './keys.js';

function createFind(findIndexFunc) {
  return function(collection, predicate, fromIndex) {
    var iterable = Object(collection);
    if (!isArrayLike(collection)) {
      var iteratee = baseIteratee(predicate, 3);
      collection = keys(collection);
      predicate = function(key) { return iteratee(iterable[key], key, iterable); };
    }
    var index = findIndexFunc(collection, predicate, fromIndex);
    return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
  };
}

export default createFind;


// _baseIteratee.js
import baseMatches from './_baseMatches.js';
import baseMatchesProperty from './_baseMatchesProperty.js';
import identity from './identity.js';
import isArray from './isArray.js';
import property from './property.js';

function baseIteratee(value) {
  // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
  // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
  if (typeof value == 'function') {
    return value;
  }
  if (value == null) {
    return identity;
  }
  if (typeof value == 'object') {
    return isArray(value)
      ? baseMatchesProperty(value[0], value[1])
      : baseMatches(value);
  }
  return property(value);
}

export default baseIteratee;


// _baseMatches.js
import baseIsMatch from './_baseIsMatch.js';
import getMatchData from './_getMatchData.js';
import matchesStrictComparable from './_matchesStrictComparable.js';

function baseMatches(source) {
  var matchData = getMatchData(source);
  if (matchData.length == 1 && matchData[0][2]) {
    return matchesStrictComparable(matchData[0][0], matchData[0][1]);
  }
  return function(object) {
    return object === source || baseIsMatch(object, source, matchData);
  };
}

export default baseMatches;

// etcetera

So as you can see, this is working as intended.

@willmcclellan
Copy link
Author

@olsonpm you're exactly right, it's behaving as expected and I got the same results with a webpack test. There is the https://github.com/lodash/lodash-webpack-plugin which reduces the file size even further, down to ~16kb before uglifying. Thanks for the input, I'll close this issue.

@olsonpm
Copy link
Contributor

olsonpm commented May 15, 2017

@willmcclellan keep in mind if you use lodash-webpack-plugin for the browser but use regular lodash on the server, it might be hard to mentally jump between the two. I don't know the context of your project and it's possible you have a much better memory than myself haha - but I even have trouble jumping between 'request-promise' on the server and 'axios' on the browser which have slightly different method signatures and options. For this reason I tend to opt with the lighter weight option for both environments. Just something to think about if you hadn't already.

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

No branches or pull requests

3 participants