picker icon indicating copy to clipboard operation
picker copied to clipboard

generatePicker imports all locales, with huge impact on bundle size

Open bc-jaka opened this issue 4 years ago • 15 comments

When swapping out moment.js with either day.js or date-fns, the generatePicker function imports all of the locales.

This results in a huge bundle and totally not needed in a lot of use cases. dateFns.ts#L26

Is there a way to get around this? It seems a little silly to have pre-loaded all locales when you only need one :)

image

bc-jaka avatar Mar 17 '21 12:03 bc-jaka

I'm having the same issue, is there any recommend solution to avoid increase bundle size ?

jquintozamora avatar Apr 28 '21 16:04 jquintozamora

I've just noticed this as well, sadly have no easy solution.

From the code it seems impossible to improve with just a configuration tweak. I guess the most straightforward "fix" would be to re-create the src/generate/dateFns.ts file in your project and tweak the imported locales. This will then also require changes in the places where the Locale object is used to make sure you have a fallback.

Would love to have an official solution though as https://ant.design/docs/react/replace-moment says You might want to replace Moment.js with another date library (now support dayjs and date-fns) to reduce bundle size. which might be a bit misleading if there is no way to also tweak locale imports.

tschanz-pcv avatar Jun 04 '21 09:06 tschanz-pcv

wow we just found out this issue, does anyone found a good solution yet? Or the only thing is to go with tschanz-pvc solution?

EDIT: My colleague cooked up this file which fixes the issue, however we hard coded enGB locale which is the one we need. Hope it helps https://gist.github.com/petrvecera/3daaab83c4a1efa0182ced135af717cf

petrvecera avatar Jul 31 '21 19:07 petrvecera

Same issue here. Screen Shot 2021-08-16 at 9 27 57 AM

tigressbailey avatar Aug 16 '21 01:08 tigressbailey

Webpack alias to the rescue!

Create ./date-fns-locale.js next to your Webpack config. Here you re-export all the locales you need:

export { default as enGB } from 'date-fns/locale/en-GB';
export { default as enUS } from 'date-fns/locale/en-US';

export { default as fr } from 'date-fns/locale/fr';
// ...etc

Here is the full list.

Add an alias to your webpack.config.js:

	resolve: {
		alias: {
			'date-fns/locale$': require.resolve('./date-fns-locales.js')
		}
	},

Mind the $ in the name! As explained in the Webpack alias documentation: this will only match imports ending in "date-fns/locale" and ensures that we can still import from "date-fns/locale/en-GB". Beware that other places you import x from "date-fns/locale" will also be aliased to date-fns-locales.js.

The resulting bundle: image


Edit: if you try to use a locale that is not exported from your date-fns-locales.js, you will get the error Cannot read property 'localize' of undefined.

To use en-GB you also need to include en-US as it extends from the US locale! (I fixed this in the code above)

ekeijl avatar Aug 26 '21 08:08 ekeijl

Thank you @ekeijl - I will test this out to see how it works!

bc-jaka avatar Aug 26 '21 09:08 bc-jaka

The webpack workaround sounds great...if I weren't using Create React App. I could probably hack something together using CRACO, but hopefully a proper solution can be found ASAP.

devuxer avatar May 14 '22 17:05 devuxer

Thanks for this workaround @ekeijl ! It worked in my case for the locales :)

It's still too bad that tree-shaking doesn't work out of the box, as this is one of the main points highlighted by date-fns ...

guillaumeprevost avatar May 31 '22 09:05 guillaumeprevost

For those using closed configurations like CRA and don't want to deal with CRACO or something like that, I was able to address this issue using patch-package. This is the diff it generated for me, and you can add other languages inside the new Locale object:

diff --git a/node_modules/rc-picker/lib/generate/dateFns.js b/node_modules/rc-picker/lib/generate/dateFns.js
index c8c7121..00de00d 100644
--- a/node_modules/rc-picker/lib/generate/dateFns.js
+++ b/node_modules/rc-picker/lib/generate/dateFns.js
@@ -1,7 +1,5 @@
 "use strict";
 
-var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
-
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -9,7 +7,7 @@ exports.default = void 0;
 
 var _dateFns = require("date-fns");
 
-var Locale = _interopRequireWildcard(require("date-fns/locale"));
+var Locale = { enUS: require("date-fns/locale/en-US") };
 
 var dealLocal = function dealLocal(str) {
   return str.replace(/_/g, '');

matheusgrieger avatar Jun 08 '22 17:06 matheusgrieger

@matheusgrieger ,

That's amazing!

But it made me realize that even though I'm using Antd/rc-picker, the locales seem to be getting loaded by date-fns itself.

Any thoughts on what could be causing this? I'm hoping I can use a similar hack to fix that, but I'm not sure where to look.

EDIT No, I'm wrong. I forgot to do a build before analyzing. Applying your patch reduces my bundle size by almost 1 MB! Thanks for this solution!

devuxer avatar Jun 09 '22 03:06 devuxer

@matheusgrieger,

Follow-up: this seems to work in development, but in production, I'm getting all kinds of errors from dateFns.js for pages that have a Calendar or DatePicker on them, so I'm having to roll back the patch for now.

Still glad to know about patch-package, though!

devuxer avatar Jun 12 '22 08:06 devuxer

@devuxer that's weird, I'm using this patch in production and it seems to work just fine. Are you sure you added all locales your app needs?

matheusgrieger avatar Jun 13 '22 17:06 matheusgrieger

@matheusgrieger, My app only uses en-US as far as I know. Perhaps there's something about the server (Azure App Service, in my case) that's causing the issue (e.g., maybe the server locale is different than my development machine's locale, though it's a U.S. server, so it shouldn't be).

Edit:

More details:

If I attempt to open a page with an Antd Calendar component on it, I get this error in the console:

TypeError: Cannot read properties of undefined (reading 'weekStartsOn')
    at Object.getWeekFirstDay (dateFns.js:93:28)
    at dateUtil.js:96:44
    at me (DateBody.js:22:18)
    at ui (react-dom.production.min.js:157:137)
    at Yu (react-dom.production.min.js:267:460)
    at Tc (react-dom.production.min.js:250:347)
    at Oc (react-dom.production.min.js:250:278)
    at Sc (react-dom.production.min.js:250:138)
    at bc (react-dom.production.min.js:243:163)
    at react-dom.production.min.js:123:115

If I attempt to open a page with an Antd DatePicker components, I get this error in the console:

RangeError: locale must contain localize property
    at R (index.js:363:11)
    at Object.format (dateFns.js:130:27)
    at J (dateUtil.js:126:79)
    at useValueTexts.js:19:23
    at o (useMemo.js:6:30)
    at Ae (useValueTexts.js:8:10)
    at _e (RangePicker.js:409:24)
    at ui (react-dom.production.min.js:157:137)
    at Yu (react-dom.production.min.js:267:460)
    at Tc (react-dom.production.min.js:250:347)

devuxer avatar Jun 13 '22 21:06 devuxer

Coming up on three years of this bug being open. I just ran into it myself!

For anyone using Vite/Rollup instead of Webpack, the same workaround posted above works in your vite.config.js file, but for whatever reason I couldn't get the $ to work in the regex, despite it being supported according to Rollup documentation.

I have a central file where I manage all language imports anyway, so I just used the regular string to resolve, since I won't be importing anything directly from date-fns/locale/en-US for example.

// vite.config.js
    resolve: {
      alias: [
        { find: "date-fns/locale", replacement: "date-fns-locale-config.js" },
      ],
    },
    
// date-fns-locale.config.js
export { default as enCA } from "date-fns/esm/locale/en-CA";
export { default as enUS } from "date-fns/esm/locale/en-US";
export { default as es } from "date-fns/esm/locale/es";
export { default as ptBR } from "date-fns/esm/locale/pt-BR";

Then just import from date-fns/locale as needed.

import { enCA } from "date-fns/locale";

mendahu avatar Feb 12 '24 18:02 mendahu

With Vite:

// vite.config.mts
resolve: {
  alias: {
    // fix DatePicker's date-fns bundle size
    'date-fns/locale': fileURLToPath(new URL('date-fns-locale', import.meta.url)),
  }
}
// date-fns-locale.mjs
export { enGB } from './node_modules/date-fns/locale.mjs';

xsjcTony avatar Mar 08 '24 06:03 xsjcTony