Struggling with implementing tree-shaking in date-fns 2

I am struggling to understand how the tree-shaking feature works in date-fns version 2...

To assist me, I have created a simple project using:

  • date-fns 2.1.0
  • webpack 4.39.3
  • typescript 3.6.2

The project consists of 2 files, one acting as the "library" and the other as the code to run, with the following structure...

import ParseDate from './parse-date'

const parse = new ParseDate();
const result = parse.getExpirationDate({ months: 3 });

console.log(result);

However, even though I only require 6 libraries for tree-shaking to work properly...

import { addYears, addMonths, addWeeks, addDays, addHours, addMinutes } from 'date-fns';

as mentioned in their documentation:

// Without tree-shaking:
import format from 'date-fns/format'
import parse from 'date-fns/parse'

// With tree-shaking:
import { format, parse } from 'date-fns'

Webpack is bundling the entire date-fns library, resulting in a file size of 726Kb !!

> npm run build

> <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8de9ecf9e8d2ebe3fed2f9e8fef9cdbca3bda3bd">[email protected]</a> build C:\Users\balexandre\date_fns_test
> tsc && webpack

Hash: 419a712549fc2309f21e
Version: webpack 4.39.3
Time: 820ms
Built at: 2019-09-09 17:27:36
    Asset     Size  Chunks             Chunk Names
bundle.js  726 KiB    main  [emitted]  main
Entrypoint main = bundle.js
chunk {main} bundle.js (main) 441 KiB [entry] [rendered]
    > ./src/index.js main
 [./src/index.js] 410 bytes {main} [depth 0] [built]
     single entry ./src/index.js  main
 [./src/parse-date.js] 1.27 KiB {main} [depth 1] [built]
     cjs require ./parse-date [./src/index.js] 6:35-58
     + 212 hidden modules

What could I be missing? It seems like a straightforward issue but I'm out of ideas :(

The project is available on GitHub for easy review (and git pull) here :)

Answer №1

If you want to optimize tree-shaking, you need to adjust TypeScript settings to compile to ES Modules instead of CommonJS and activate production mode in webpack:

  • In tsconfig.json, set module: 'es2015'
  • In webpack.config.js, set mode: 'production'

By following these steps, your final build size will be reduced significantly:

$ size-limit dist/bundle.js

  Package size: 724 B
  Includes all dependencies, minified and gzipped

Below is a recommended patch for your repository:

diff --git a/tsconfig.json b/tsconfig.json
index 42d6d90..b64255d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,7 @@
 {
     "compilerOptions": {
       /* Basic Options */
-      "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
+      "target": "es2015",                       /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
       "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
        "allowJs": false,                        /* Allow javascript files to be compiled. */

diff --git a/webpack.config.js b/webpack.config.js
index 8ccbc94..1419137 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const path = require('path');

 const config = {
   entry: './src/index.js',
-  mode: 'development',
+  mode: 'production',
   stats: 'verbose',
   output: {
       path: path.resolve(__dirname, 'dist'),

Answer №2

If you're looking to optimize your tree-shaking process for date-fns functions, I have a solution that worked well for me. By setting up a Webpack alias (as referenced in the Github issue discussed here), you can streamline the import process.

I adapted this approach from a similar method used to improve tree-shaking for locales, mentioned in this discussion.

  1. To start, create a file like 'date-fns-functions.js' beside your webpack config. In this file, re-export only the necessary functions from date-fns that you use in your project:
export { default as parseISO } from 'date-fns/parseISO';
export { default as formatDistanceToNow } from 'date-fns/formatDistanceToNow';
export { default as parse } from 'date-fns/parse';
...
  1. Next, include an alias in your Webpack config:
    resolve: {
        alias: {
            'date-fns$': require.resolve('./date-fns-functions.js')
        }
    },

In my scenario with Nuxt.js bundling via Webpack, I added this configuration to nuxt.config.js instead:

extend (config, ctx) {
   config.resolve.alias['date-fns$'] = require.resolve('./date-fns-functions.js');
   ...
}

This setup ensures that each time you import something from date-fns, it will reference the alias and prevent the unnecessary inclusion of the entire library. As a result, your bundle size should decrease significantly.

Personally, this method effectively resolved my issue and optimized my bundle size.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Troubleshooting the transition from React version 16.0.0 to React version 18

I am encountering issues with my package.json file and Jenkins build, which fails due to the following problems. I'm currently running npm version 8.15.0 and Node version v16.17.1, while trying to upgrade from React 16 to React 18. package.json { & ...

Enhancing React Flow to provide updated selection and hover functionality

After diving into react flow, I found it to be quite user-friendly. However, I've hit a roadblock while attempting to update the styles of a selected node. My current workaround involves using useState to modify the style object for a specific Id. Is ...

The type definition file for '@wdio/globals/types' is nowhere to be found

I'm currently utilizing the webdriverio mocha framework with typescript. @wdio/cli": "^7.25.0" NodeJs v16.13.2 NPM V8.1.2 Encountering the following error in tsconfig.json JSON schema for the TypeScript compiler's configuration fi ...

Troubleshooting TypeScript: Issues with Object.assign and inheritance

After successfully using the code within an Angular project, I decided to switch to React only to find that the code is now producing unexpected results. class A { constructor(...parts: Partial<A>[]) { Object.assign(this, ...parts); } } cla ...

Sharing data between two unrelated components in Angular 4

I have an inquiry regarding passing data in Angular. Unlike the usual structure of <parent><child [data]=parent.data></child></parent>, my structure is different: <container> <navbar> <summary></summary& ...

Angular is unable to access functions or variables within a nested function

I am currently utilizing google.maps.geocoder to make location requests within my Angular app. When I provide a callback function with the results, it leads to unexpected code breaks when trying to call another function that displays map markers. It seem ...

Identifying unique properties with specific keys in a Typescript object

Can a specific type be used with one property when using the key in of type? Playground. type ManyProps = 'name' | 'age' | 'height' type MyObj = {[key in ManyProps]: number, name?: string} ...

Determine if an object contains a specific key in Typescript and access the value associated with that

Here is a snippet of my code: let show = { createTag: false, updateFeature: false, createFeatureGroup: false, deleteFeature: false, deleteCycle: false, }; I am retrieving a value from the querystring that I want to compare against the ...

Error encountered: TSX - The inline style attribute for "padding-top" is being rejected due to a mismatch in type assignment between '{ "padding-top": string; }' and 'Properties<string | number, string & {}>'

When I specify an inline style in TSX like <ModalHeading id="modal-1-heading" style={{"padding":"0px"}}>Mailing Address</ModalHeading> everything functions correctly. However, if I attempt to use the padding-top v ...

Mastering the Art of HTML Binding in Angular 8

I am facing a challenge in Angular 8 with displaying HTML content. The app I'm building in Angular 8 has a Flask backend that sends JSON data containing an HTML template to the frontend. My goal is to render this template in Angular. Here is the JSON ...

Best practices for transitioning a project from TypeScript 3 to TypeScript 4?

I am looking to upgrade my large monorepo, which was built using lerna, React, and TypeScript 3.7 around 2-3 years ago. My goal is to update it to TypeScript 4.8. Are there any tools available that can help me analyze and identify potential breaking chan ...

Inhibit unselected choices when a user exceeds choosing a certain number of options

I want users to be able to select a maximum of 3 options from a list. If a form has four options, once the user selects three, the fourth should become disabled. Here is the setup I have: import { Component, Input, ViewChild, OnInit, AfterViewI ...

Webpacker encounters an error while compiling React code: Configuration object is not valid

Currently, I am in the process of upgrading a functional Rails 5/React application that utilized webpack via npm to Rails 6. To do this, I started by creating a new Rails app with webpacker=rails hoping it would simplify the process for me. Despite transfe ...

Ways to efficiently update the API_BASE_URL in a TypeScript Angular client generated by NSwag

Is it possible to dynamically change the API_BASE_URL set in my TypeScript client generated by NSWAG? I want to be able to utilize the same client with different API_BASE_URLs in separate Angular modules. Is this achievable? Thank you for your assistance. ...

Troubleshooting Problem in Angular 6: Difficulty in presenting data using *ngFor directive (data remains invisible)

I came across a dataset that resembles the following: https://i.sstatic.net/S0YyO.png Within my app.component.html, I have written this code snippet: <ul> <li *ngFor="let data of myData">{{data.id}}</li> </ul> However, when I ...

What is the best way to create a memoized function in React?

I am currently developing an application using react and typescript, and I am facing a challenge in memoizing a function. const formatData = ( data: number[], gradientFill?: CanvasGradient ): Chart.ChartData => ({ labels: ["a", ...

Issue with promise chaining detected in Mocha testing framework

Encountering an error when attempting to enter text using sendkeys or click a button in a popup with TypeScript promise chaining in Mocha. composeNewMessage(mailCount: number, recepientsName: any, subject: string, messageContent: string, recipientLink?: ...

Converting Angular HTTP Response from Object of Objects to Array with Interface Typing

After receiving a response, the data is structured as follows: { "0": { "name": "Novartis AG", "symbol": "NVS", "has_intraday": false, "has_eod": true ...

Effortless Tree Grid Demonstration for Hilla

As someone who is just starting out with TypeScript and has minimal experience with Hilla, I kindly ask for a simple example of a Tree Grid. Please bear with me as I navigate through this learning process. I had hoped to create something as straightforwar ...

What are some ways to utilize TypeScript in incorporating extensions to `koa.Request` packages?

Struggling to utilize both koa-tree-router and koa-bodyparser simultaneously, encountering persistent TypeScript errors: export const userLoggingRouter = new KoaTreeRouter<any, DefaultContext>(); userLoggingRouter.post('/logs/action', (ctx ...