npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mmntjs

v1.0.0

Published

Drop-in replacement for moment.js — a migration path to Temporal API

Readme

mmntjs

Most teams don't keep using moment.js because they love it. They keep using it because rewriting date logic across a large codebase is risky, expensive, and never becomes this quarter's priority.

mmntjs is designed for that reality.

Drop-in replacement for moment.js — migration path to Temporal API.

Website: mmntjs.veritycost.com

Quick Start

Option A: Zero-code alias (switch today)

Replace moment.js with no code changes:

npm install moment@npm:mmntjs
import moment from "moment";           // unchanged
moment("2024-01-01").add(1, "month");  // now runs on mmntjs

Option B: Automated codemod (migrate imports)

Swap imports from moment to mmntjs across your codebase:

npx mmntjs migrate --check ./src     # dry run first
npx mmntjs migrate --apply ./src     # apply codemod

Option C: Gradual migration (coexistence mode)

Migrate file by file — moment and mmntjs can coexist in the same project:

npm install mmntjs
// file-a.js — not yet migrated
import moment from "moment";       // still on moment.js

// file-b.js — migrated
import moment from "mmntjs";       // now on mmntjs

This works because the two runtimes don't conflict — you can move module by module at your own pace. No need for a big-bang switch.

Tip: Start with mmntjs audit ./src to identify which files use only compatible APIs. Those are your safest first candidates.

As a standalone library

npm install mmntjs
import moment from "mmntjs";
moment().format("YYYY-MM-DD");
moment.duration(2, "hours").humanize();

Entry Points

Import path contract

| Import | gzip (bundled) | gzip (dist) | Description | |--------|--------------:|------------:|-------------| | mmntjs | 45.1 KB | 63.4 KB | Full compatibility (default) — core + display + utc + locale registry + format-parse | | mmntjs/lite | 14.8 KB | 20.5 KB | ISO-centric, size-first — core-lite + strict parsing, no locale registry, no display extras | | mmntjs/full | 45.1 KB | 63.4 KB | Same as default — explicit alias | | mmntjs/fns | 0.5-1.3 KB* | 6.8 KB | Tree-shakeable Date helpers — single format is 507 B gzip, parseISO+format+addDays is 1.3 KB gzip | | mmntjs/temporal | 46.7 KB | 102.6 KB | Temporal bridge — toTemporal(m) / fromTemporal(t) | | mmntjs/plugin/* | — | +separate | Optional plugins (utc, format-parse) — self-contained, add features to lite | | mmntjs/locale/* | — | +1-5 KB | Individual locales (136 total) — tree-shakeable, each <2 KB gzip | | mmntjs-timezone | 81.1 KB | 39.0 KB | Separate package — full IANA timezone data + installTimezone(moment) |

bundled: measured from source with Bun.build({minify:true, target:"browser"}) — represents what consumer bundlers produce. dist: raw tsup output with splitting:false — self-contained files, some code duplication across entries is expected. * mmntjs/fns is fully tree-shakeable, so its bundled size depends on what you import.

// Use lite + plugins for smaller bundles
import moment from "mmntjs/lite";
import "mmntjs/plugin/format-parse";
import "mmntjs/locale-auto/ja";
import "mmntjs-timezone";

If you want the smallest locale import with explicit registration instead of side effects:

import moment from "mmntjs";
import { jaLocale } from "mmntjs/locale/ja";

moment.locale("ja", jaLocale);

What each entry includes

| Feature | lite | default | full | temporal | |---------|:------:|:---------:|:------:|:----------:| | moment() / format / parse (ISO) | ✅ | ✅ | ✅ | — | | Strict ISO 8601 parsing | ✅ | ✅ | ✅ | — | | .add() / .subtract() / .startOf() / .endOf() | ✅ | ✅ | ✅ | — | | .diff() / .from() / .to() | ✅ | ✅ | ✅ | — | | UTC mode (moment.utc() / .utc() / .local()) | ✅ | ✅ | ✅ | — | | Locale registry (moment.locale() / defineLocale()) | — | ✅ | ✅ | — | | Custom format parsing (moment("…", "YYYY-MM-DD")) | via plugin | ✅ | ✅ | — | | .format("LLL") locale-aware | via plugin | ✅ | ✅ | — | | moment.duration() | via plugin | ✅ | ✅ | — | | moment.min() / moment.max() / moment.parseZone() | — | ✅ | ✅ | — | | toTemporal(m) / fromTemporal(t) | — | — | — | ✅ | | CLI (mmntjs migrate / mmntjs audit) | — | — | — | — |

lite includes .format("YYYY-MM-DD") (basic format tokens) and ISO parsing. Add format-parse plugin for custom format strings and locale-aware long date formats.

Modularity guarantees

  • Timezone is fully opt-in: mmntjs-timezone is a separate package. Core bundles (lite, default, full) contain zero timezone resolution code — no Intl.DateTimeFormat references.
  • Temporal is opt-in: mmntjs/temporal is the only entry that exports toTemporal/fromTemporal. Neither lite nor default pulls @js-temporal/polyfill.
  • Locales are tree-shakeable: Each locale is a standalone module. Importing mmntjs/locale/ja gives you pure locale data; importing mmntjs/locale-auto/ja auto-registers that locale for migration convenience.
  • CLI is separate: The mmntjs CLI binary uses dist/bin/cli.js; none of the library entry points contain CLI code.
  • Side effects: Core entry initialization (moment() setup) always runs on import. plugin/* and locale-auto/* submodules are marked as side-effectful for bundler safety. locale/* export pure data and are fully tree-shakeable.

Platform Support

| Runtime | Support | |---------|---------| | Node.js | 16+ (CJS require("mmntjs"), ESM) | | Browser | IIFE via CDN (<script src="…/mmntjs.min.js">) | | Bun | Native ESM, first-class support | | Deno | Compatible via npm specifiers |

<script src="https://cdn.jsdelivr.net/npm/mmntjs/dist/mmntjs.min.js"></script>
<script>
  mmntjs().format("LLLL");
</script>
// Node.js CJS
const moment = require("mmntjs");

Three Pillars

1. Near-100% Drop-in Compatibility

moment.js's own test suite: 630/630 pass (52 QUnit files via compat layer).
Oracle comparison: 112 properties, 45k+ assertions against upstream moment.js.
Mutation: 48/48 killed in the current curated mutation suite.
Fuzzing: 11 coverage-guided harnesses + grammar-based ISO 8601 generator.

The only known incompatibilities are malformed/edge-case strings discovered through fuzzing (e.g. sign-prefixed strings without delimiters). These are under active repair — see REMAINING.md for the shortlist.

Compatibility story: Fuzzer found moment("0000 03") — both engines must produce 2000-03-01 (year 2000, March 1). Our ISO table parser was matching "0000 03" as year-0000 month-03, a wrong answer. Rather than adding more regex special cases, we introduced a _claimed sentinel: when the table parser finds a low-confidence match, it returns _claimed: true to delegate to JavaScript's native new Date(str) — exactly what moment.js does as its last resort. This single mechanism closed 7+ fuzz-discovered gaps without adding parser complexity.

136 locales, timezone, duration, calendar, custom format parse — all existing moment.js API surface covered.

Runs on Node 16+, browsers (IIFE/CDN), Bun, and Deno. CJS and ESM both supported.

TypeScript types included — import moment from "moment" resolves to mmntjs's types automatically. No @types/moment needed.

2. Modular And Measurable

| Entry | gzip (bundled) | Notes | |-------|---------------:|-------| | mmntjs (full) | 45.1 KB | Full compatibility entry for migration | | mmntjs/lite | 14.8 KB | Recommended default when you do not need the full surface | | mmntjs/fns | 507 B - 1.3 KB | Single helpers stay tiny; size scales with imports | | mmntjs-timezone | 81.1 KB | Separate package; named-zone data is opt-in |

lite drops locale registry, Temporal bridge, custom format parse, and marginal APIs — add them back via plugins only when needed.

3. Faster Than moment.js On The Main Compatibility Paths

In the current public moment comparison table, mmntjs wins every tracked row. Against date-fns, the picture is mixed: the object-oriented mmntjs API wins read-heavy rows, while date-fns often wins fresh-object mutation rows. The standalone mmntjs/fns entry closes much of that gap and wins 19 of 26 direct Date-helper comparisons.

| Operation | mmntjs | date-fns | vs moment.js | |-----------|--------:|---------:|-------------:| | format YYYY-MM-DD | 57 ns | 1.41 us (24.7x) | 395 ns (8.2x) | | parse ISO string | 275 ns | 1.02 us (3.7x) | 3.97 us (17.9x) | | diff in days | 22 ns | 850 ns (39.2x) | 421 ns (13.0x) | | add 1 day | 296 ns | 99 ns (3.0x slower) | 2.34 us (9.0x) | | startOf day | 253 ns | 91 ns (2.8x slower) | 2.22 us (10.8x) | | moment() / new Date() | 147 ns | 35 ns (4.2x slower) | 260 ns (1.6x) | | mmntjs/fns format YYYY-MM-DD | 507 B gzip | — | single-import bundle |

The losses against date-fns on fresh-object rows are structural, not implementation gaps. Every moment() call allocates a wrapper object around a Date — this is the cost of preserving moment-compatible mutability, method chaining, .fn/.prototype extensibility, and locale context. date-fns operates on bare Date instances and skips that overhead entirely. The [fresh] benchmark marker amplifies the difference because it creates and discards a wrapper per iteration; real applications amortize it by reusing Moment objects. If you want date-fns-style standalone helpers without the wrapper, mmntjs/fns is the closer comparison point — it wins 19 of 26 direct Date-helper rows.

Representative Bun microbenchmarks on Apple Silicon. ns-scale results use median-of-repeated warmed runs after warmup — see BENCHMARKS.md for methodology, noise markers, and caveats.

For month/quarter/year comparisons, note that date-fns uses calendar-difference helpers while mmntjs matches moment.js's truncated fractional diff semantics. Those rows are still useful as implementation-cost comparisons, but they are not result-equivalent APIs.

Techniques: decomposed field cache, lazy init, Shape stability, charCodeAt parsing, branch reduction, pre-computed tables. See Performance Analysis, Techniques, Benchmarks.

Beyond a Runtime Replacement

mmntjs is not just a drop-in replacement. It also serves as a migration-analysis toolchain for legacy Moment codebases targeting Temporal:

Legacy moment.js codebase
  │
  ├── mmntjs audit ./src        ← Observability: inventory all moment API usage
  ├── mmntjs stats ./src        ← Quantify: usage patterns, migration surface
  │
  ├── mmntjs migrate --check    ← Dry-run compatibility assessment
  ├── mmntjs migrate --apply    ← Automated codemod (moment → mmntjs)
  │
  └── mmntjs report ./src       ← Temporal migration guidance per module

Each phase is independent. Start with audit to understand your legacy surface, then migrate to mmntjs at your own pace, and finally generate a Temporal migration report when ready.

Development

bun install
bun run build          # CJS + ESM + IIFE + DTS
bun run test:hard      # Full test suite
bun run lint           # oxlint

License

MIT