DEV Community

Miki Stanger
Miki Stanger

Posted on • Edited on

Having Moment.js Replacements Is Not Enough

Having Moment.js Replacements Is Not Enough

A few months ago, moment.js announced the project is now in maintenance mode. They discourage you from using it in new projects, offer alternatives (including their own Luxon) and tell of a bright future with Temporal (a stage 2 proposal at the time of writing).
This is good; Moment.js has served us well, but it currently a huge library that doesn’t support tree-shaking, and isn't as performant as other libraries.
It is, however, still a dependency of many, many libraries, and it’ll take a long time for this to change (if at all - not all libraries have active development going on).
As long as you’re installing one of these packages, you’re going to have a
non-tree-shakable, 232kb (68kb if your dependency developer knew to ignore locales) hunk of code, in addition to whatever time-and-date library you’d like to be using.
In my case, I usually end up using moment.js in my larger projects, as I see no reason to include two libraries to work with dates, especially when one is this huge.

What can we do?

We CAN wait a few years, but most companies won’t appreciate this kind of timeline. Another solution is to provide moment API wrappers for the new generation of time and date libraries. I’ll explain:

Let’s say I’m publishing my own cool new time and date library. Let’s call it Chair.js, as coming with good names for libraries is hard (I spent more time finding a name for Inventar than coding its first release), and I looked at a chair just now.

Chair is light, fast and simple to work with. It sits well in its role (eh?). Its API is modern and immutable, which means that it’s different than moment.js.
I also know that developers wouldn’t use Chair if they have moment.js installed as a dependency of a dependency already, which they probably do. So I’ll make sure that these dependencies of dependencies can easily use Chair instead, by providing an additional package - chair-moment-wrapper.

chair-moment-wrapper:

  • Has a similar interface to that of moment.js, or at least its most used features. This interface calls Chair behind the scenes.
  • Has Chair as a peer dependency, so no additional copy of it is installed.
  • Is optimally tree-shakable.
  • Isn’t necessarily optimized for performance. It can get away with being at moment.js’ ballpark.

As a module, it should add itself as moment, or the user should install it with an alias (npm install —save moment@npm:chair-moment-wrapper or yarn add moment@npm:chair-moment-wrapper).
It should also replace the moment global object, if it’s added.

With this kind of wrappers for major time and date libraries, most developers could pick one knowing they wholly and completely replace moment.js - even for their dependencies. They could reap most of Chair’s size and performance benefits and, most importantly, could more quickly get to the day where they won’t have to use it.

So, developers of Luxon, date-fns, js-joda, Day.js - What do you think?

Top comments (8)

Collapse
 
etienneburdet profile image
Etienne Burdet

It is an interesting idea, but I don't think this works—or at least really saves time—because tree-shaking is not the main thing at stake.

When switching to a lib like date-fns (fp flavor !) or Luxon, you're changing the way you manipulate dates too much: from a mutable moment.js instance to a set of pure functions and thus no mutable instance anymore. This way more important than tree-shaking.

Swapping moment.add(1, 'week') foraddWeek(1)(moment) just won't work and moment.addWeek(1) just doesn't solve anything. You would still have that mutable moment object that goes completly crazy in declarative templates with reactive bindings/stores.

There is no way around it, you will have to re-engineer everything at some point. In my opinion, the sooner the better and a wrapper would just add some spaghetti code.

Collapse
 
mimafogeus2 profile image
Miki Stanger

I completely agree, which is why I'm suggesting an additional, independent layer.
On your own code - you should totally work with date-fns, or luxon (or Chair :P)

BUT your dependencies are using, and many will keep using, moment. This means that moment is installed in addition to your choice, and you end up with your choice AND moment in your code.

The skeleton I'm offering is to be used by your dependencies.
For dependencies that use the moment global object, or use it as a peer dependency, the fix will be simple.
For other, you might have to configure Webpack somehow and/or rebuild them from the source, which might be a bit of a headache. BUT, it's the kind of a headache that could remove a lot of code from your project, and remove a dependency in an old project that recommends its users to use something else.

Collapse
 
etienneburdet profile image
Etienne Burdet

Ho my bad, I didn't realize you were talking about dependencies using moment.js—I though you would use the wrapper for not changing legacy code in your own project.

Yeah, rebuilding dependicies is totally not an option… Do you have any major package relying on moment in mind btw? (I don't use any…)

Thread Thread
 
mimafogeus2 profile image
Miki Stanger • Edited

I hope to get into bundle optimization at work soon, where I'm almost sure I'll find moment lurking. I expect it to be in any project that has a date selector, time-related visualizations etc.
I know this has been the case in my previous workplaces where I took care to check that.

About rebuilding dependencies - that might actually be possible. Since npm usually installs the whole package - unbuilt code etc, included - it might be possible to create a script that takes selected packages, creates a link so moment will use the skeleton instead and rebuilds.
It could be a bit of a headache and take some time, but this script could only be ran every time you update one of those packages. Maybe one of the npm pre/post scripts could take care of that automatically.

Collapse
 
cjthompson profile image
Chris Thompson

If the goal is to reduce bundle size, then you would need to have a Webpack plugin that would replace the "moment.js" dependency with your compatible library. Otherwise, even if you write on that uses the same API, "moment.js" will still be included in the bundle.

Collapse
 
agroupp profile image
Arthur Groupp

I recently started to work on this npmjs.com/package/@numty/datetime project, maybe it will help to solve some of the current problems. It's written on TS and 100% tree shakable, so it's good for using in Angular or React. Anyway, I'll be happy if it help anyone.

Collapse
 
jenueldev profile image
Jenuel Oras Ganawed

Day.js is enough for me. The moment.js is really awesome but still waiting for the update to make it optimized. Day.js is my best alternative for moment.js they almost like the same.

Collapse
 
mimafogeus2 profile image
Miki Stanger

moment.js will probably not get such an optimization. They're officially in maintenance mode, and recommend using alternatives in new projects (momentjs.com/docs/#/-project-status/)

The problem is that the dependencies you use probably still use moment, so you get it installed in addition to day.js - and that's a lot "in addition of" :)

Day.js is the best candidate for my offer here, as it is the most similar to moment already :) Creating an API that exposes similar day.js methods but provides a moment-like mutable object would allow these dependencies to use day.js as well, so moment will not be bundled in your project at all.