Which plugin should keep only needed moment.js locales: IgnorePlugin or ContextReplacementPlugin?
0 reputation · 30 May 2021, 04:52 UTC
0 reputation · 30 May 2021, 04:52 UTC
I'm bundling an application that uses moment.js for date formatting across a small set of regions. Because moment loads locales through a dynamic require, webpack turns it into a context module and the production bundle ends up containing every locale file in the package, even though the app only formats dates in two or three of them.
Webpack itself has no date or time zone handling, so the only lever I have is how the build treats moment's locale context. The documentation describes two techniques: IgnorePlugin, which can block the locale requires entirely (in webpack 5 via an options object with resourceRegExp and contextRegExp) so each needed locale must be imported explicitly in application code, and ContextReplacementPlugin, which narrows the context to a whitelist regex so matching locales stay in the bundle automatically.
Both approaches are documented and both reduce bundle size, but they put ongoing locale maintenance in different places — explicit imports in source code versus a regex in webpack config — and I can't find guidance on when one is preferred over the other.
Which of the two plugins is the better fit for a small, stable locale set, and does that answer change if locales are added frequently? Is there a documented way to verify at build time that no unintended locale slipped back into the bundle?
35275 reputation · 30 May 2021, 06:03 UTC
For a small, stable locale set, ContextReplacementPlugin is the better fit. It narrows moment's locale context module with a whitelist regex in your webpack config, so the matching locales stay in the bundle automatically and your application code needs no changes. IgnorePlugin is the better choice when locales are added or removed frequently, because it forces every locale to be an explicit, visible import in source code — making additions opt-in rather than dependent on someone remembering to update a regex.
Both plugins attack the same problem: moment loads locales via a dynamic require, which webpack converts into a context module containing every locale file. The difference is where the maintenance burden lands.
webpack.config.js controls everything. With two or three locales that never change, you write the regex once and never touch it again. The risk: if a new locale is needed later and the regex isn't updated, that locale is silently missing from the bundle — a runtime formatting failure, not a build error.resourceRegExp and contextRegExp) blocks moment's locale requires entirely. Each locale you need must be imported explicitly, e.g. import 'moment/locale/fr';. Bundle size is minimal by construction, and every locale addition shows up in code review as a deliberate change. The cost is touching source files and risking a runtime error if someone formats a date in a locale nobody imported.So the decision rule is: stable set → config-side filtering (ContextReplacementPlugin); churning set → code-side explicit imports (IgnorePlugin), where each addition is auditable.
// webpack.config.js (webpack 5)
const webpack = require('webpack');
// Option A: keep only en, fr, de in the locale context
new webpack.ContextReplacementPlugin(
/moment[/\\]locale$/,
/en|fr|de/
);
// Option B: drop all locales, import them explicitly in app code
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
});Test the exact regexes against your webpack and moment versions — path separators and context paths differ between Windows and POSIX builds, and moment vs moment-timezone have different locale paths.
There is no built-in webpack switch that fails the build on unexpected locales, but two practical checks work well:
webpack-bundle-analyzer (or grep the emitted bundle for moment/locale/) in CI and assert only the expected locale modules appear. A simple grep-based CI step is the closest thing to a build-time guard.moment.locales() and compare the result against your expected list. This catches both over-inclusion (regex too broad) and under-inclusion (missing explicit import).Note that both plugins are webpack-specific; if you migrate to Vite or Rollup you'll need their equivalents. Also, if you're early enough in the project, consider whether a modern alternative (date-fns, Day.js, Luxon, or the native Intl API) with tree-shakeable locale imports removes the problem entirely — moment itself is in maintenance mode.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.