Tree shake Lodash with Webpack, Jest and Typescript

I had 3 goals:

  • production bundle with just those lodash functions, that are used ( not the whole library )
  • typescript valid code without errors ( correct lodash typings )
  • all tests passing
  • preact + preact-compat ( renderer )
  • redux + redux-observable + rx ( state management )
  • axios ( http req/res )
  • typescript 2.x ( for both type checking and transpiling to ES5)
  • webpack 2.x ( for bundling )
  • jest 20.x + ts-jest ( unit testing + snapshots )
  • lodash 4.x ( just few functions for some functionality )
  • and some other 3rd party libs…

Backstory

import { get } from 'lodash'import { usersService } from '../core'const main = () => {
userService
.getAll()
.then(data => get(data.response,'[3].address.zip'))
.then(userZip => dispatch(userZipReceived(userZip))
}
main()

Keep calm yo, there is a solution for sure! lodash-es ! oh is it?

bang! whole lodash in your production bundle

So can I tree shake lodash or what ???

Solution 1 ( non feasible for our use-case )

Sad Truth ( for lodash users ):

Solution 2 ( no babel )

Use subpath imports from lodash with Typescript

Typescript errors when using subpath with lodash
Tests are failing
Transpiled TS to ES5/CommonJS by ts-jest which is consumed by Jest -> Error lodash has no default exports rather than module.exports
lodash/get.js → common js, no es2015 default export
{
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"typeRoots": [
"node_modules/@types",
"manual_typings"
],
"paths": {
"lodash/*": [
"node_modules/@types/lodash-es/*"
],

},
}
  • “typeRoots” && “paths”:

Result:

TS without errors
tests are green
tree shaked lodash in production bundle

Solution 3 ( no babel )

Use subpath imports from lodash-es with Typescript

lodash-es provides correct subpath types
Failing test when using lodash-es
"transform": {"^.+\\.(j|t)sx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js",},"transformIgnorePatterns": [
"<rootDir>/node_modules/(?!lodash-es/.*)"
],
  • ^.+\\.(j|t)sx?$
{
"allowJs": true
}

Result:

tests are green
No Typescript errors
correctly tree shaked lodash-es in production bundle

lodash size ( non gzipped ) in our prod bundle ~= 30kb which is 100kb less than before! We are done here!

Takeaways:

  • We ended up using solution #3( although on bundle-analyzer it looks like that solution #2 has smaller footprint, but with solution 3 our final vendor bundle is 20kB smaller ). Also we prefer to use es2015 ready modules for further optimizations and another benefit is that we don’t have to hack types.
  • Jest is freakin’ awesome!, but! you have to always double config stuff (webpack and jest ) which feels MEH ( now you need to hire both webpack config senior developer and jest config senior dev :D )
  • Always remember that when you consume npm package which has “module” field -> webpack understands that, but if the package is missing proper UMD/commonjs you will have to transpile it as well within Jest
  • 3rd party types are not always excellent ( also DefinitelyTyped type versions doesn’t always match with used library version, so you’ve absolutely no guarantees, that you have actual and correct types for your particular version of 3rd party package => for that reason I always prefer 3rd party libs that are written in Typescript or ship type definitions directly. In edge cases you can modify/create custom types and use technique that was introduced in Solution 2)
  • Have to solve all this issues in 2017, feels like a huge tax payed for using only few lodash functions. I’ll probably use other, tree shake ready and more functional solution on next project ( yup I’m looking at you Ramda ! )
// install lodash (both commonjs nad es2015 modules) with types
yarn add lodash-es
// commonjs lodash will be used for tests
yarn add -D lodash @types/lodash-es
// jest.config.jsconst config = {
preset: 'ts-jest',
moduleNameMapper: {
// we'll use commonjs version of lodash for tests 👌
// because we don't need to use any kind of tree shaking right?!
'^lodash-es$': '<rootDir>/node_modules/lodash/index.js'
},
}

// tsconfig.json
{
compilerOptions: {
// other config settings
"esModuleInterop": true,
// no allowJs needed 💥🚨👌
}
}
import {get,pick} from 'lodash-es'

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Martin Hochel

Martin Hochel

Principal Engineer | Google Dev Expert/Microsoft MVP | @ngPartyCz founder | Speaker | Trainer. I 🛹, 🏄‍♂️, 🏂, wake, ⚾️& #oss #js #ts