207
votes

Is there any way you can stop moment.js from loading all the locales (I just need English) when you're using webpack? I'm looking at the source and it seems that if hasModule is defined, which it is for webpack, then it always tries to require() every locale. I'm pretty sure this needs a pull request to fix. But is there any way we can fix this with the webpack config?

Here is my webpack config to load momentjs:

resolve: {
            alias: {
                moment: path.join(__dirname, "src/lib/bower/moment/moment.js")
            },
        },

Then anywhere I need it, I just do require('moment'). This works but it's adding about 250 kB of unneeded language files to my bundle. Also I'm using the bower version of momentjs and gulp.

Also if this can't be fixed by the webpack config here is a link to the function where it loads the locales. I tried adding && module.exports.loadLocales to the if statement but I guess webpack doesn't actually work in a way where that would work. It just requires no matter what. I think it uses a regex now so I don't really know how you would even go about fixing it.

6
Have you tried to use moment via nmp instead of bower?Andreas Köberle
I'm using bower for all my client libs, and npm for all my build tools. I want to keep it this way because of how my projects are laid out. Also if you look at the last reply of github.com/moment/moment/issues/1866 I solved my own problem but it requires a minor source edit. I still don't know how to fix this the right way as I dont know how you would distinguish between node and webpack.epelc

6 Answers

313
votes

The code require('./locale/' + name) can use every file in the locale dir. So webpack includes every file as module in your bundle. It cannot know which language you are using.

There are two plugins that are useful to give webpack more information about which module should be included in your bundle: ContextReplacementPlugin and IgnorePlugin.

require('./locale/' + name) is called a context (a require which contains an expression). webpack infers some information from this code fragment: A directory and a regular expression. Here: directory = ".../moment/locale" regular expression = /^.*$/. So by default every file in the locale directory is included.

The ContextReplacementPlugin allows to override the inferred information i.e. provide a new regular expression (to choose the languages you want to include).

Another approach is to ignore the require with the IgnorePlugin.

Here is an example:

var webpack = require("webpack");
module.exports = {
  // ...
  plugins: [
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /de|fr|hu/)
    // new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
  ]
};
8
votes

In our project, I include moment like this: import moment from 'moment/src/moment'; and that seems to do the trick. Our usage of moment is very simple though, so I'm not sure if there will be any inconsistencies with the SDK. I think this works because WebPack doesn't know how to find the locale files statically, so you get a warning (that's easy to hide by adding an empty folder at moment/src/lib/locale/locale) but no locale includes.

3
votes

UPDATE: 2021
There are many other libs that you may want to checkout:

ORIGINAL ANSWER:
Seems like the proper modular moment library will never come up However, I just ended up of using https://github.com/ksloan/moment-mini like import * as moment from 'moment-mini';

2
votes

Based on Adam McCrmick's answer, you were close, change your alias to:

resolve: {
    alias: {
        moment: 'moment/src/moment'
    },
},
2
votes

With webpack2 and recent versions of moment you can do:

import {fn as moment} from 'moment'

And then in webpack.config.js you do:

resolve: {
    packageMains: ['jsnext:main', 'main']
}
0
votes

Here's another solution using postinstall script in NPM installer.

You can put a line to your package.json file:

{
  "scripts": {
    ...
    "postinstall": "find node_modules/moment/locale -type f -not -name 'en-gb.js' -not -name 'pl.js' -printf '%p\\n' | xargs rm"
    ...
  }
}

In result unwanted locales will be removed immediately after npm install finish installing packages.

In my case only en-gb and pl locales will remain in bundle.

In case you already have postinstall script, you can add script to existing commands:

{
  "scripts": {
    ...
    "postinstall": "previous_command && find node_modules/moment/locale -type f -not -name 'en-gb.js' -not -name 'pl.js' -printf '%p\\n' | xargs rm"
    ...
  }
}