-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: migrate from moment.js to date-fns for better performance and bundle size #5019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: migrate from moment.js to date-fns for better performance and bundle size #5019
Conversation
- Remove moment.js dependency (67.8KB) - Add date-fns with tree-shaking support (~3KB) - Create date adapter utility for smooth migration - Update all date components, - Update date helpers and utility functions - All existing tests pass - Bundle size reduction: ~65KB (26% improvement) Closes asyncapi#5014
📝 WalkthroughWalkthroughThe PR migrates the codebase from Moment.js to date-fns for improved performance and bundle size reduction. A new utility module introduces date helpers for formatting, comparison, and arithmetic operations. Moment imports are removed from components, pages, and scripts, replaced with corresponding date-fns-based helpers. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Deploy Preview for asyncapi-website ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
⚡️ Lighthouse report for the changes in this PR:
Lighthouse ran on https://deploy-preview-5019--asyncapi-website.netlify.app/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/navigation/EventFilter.tsx (1)
26-53: Remove unusedformatDateimport and movecurrentDateinside the effect to prevent unnecessary re-runs.
new Date()creates a new object each render. IncludingcurrentDatein the dependency array causes the effect to run on every render, which can create a render loop viasetData. Move it inside the effect and remove it from dependencies. Also remove the unusedformatDateimport.🐛 Proposed fix
-import { formatDate, isDateAfter, isDateBefore } from '@/utils/dateHelpers'; +import { isDateAfter, isDateBefore } from '@/utils/dateHelpers'; const currentDate = new Date(); const filterList: string[] = ['All', 'Upcoming', 'Recorded']; const [active, setActive] = useState<string>('All'); useEffect(() => { + const currentDate = new Date(); switch (active) { case ActiveState.All: setData(getEvents(data)); @@ - }, [active, data, setData, currentDate]); + }, [active, data, setData]);components/navigation/EventPostItem.tsx (1)
28-57: Guard against invalid dates — parseISO won’t throw, but format will.
parseDatereturns an Invalid Date for non‑ISO input; the try/catch won’t catch that andformatDatecan throw at render time. Add a validity check (orisValid) before settingactive=true.🐛 Proposed fix
- let postDate: Date = new Date(); - - try { - postDate = parseDate(post.date); - if (isDateBefore(postDate, currentDate)) { - active = false; - } - } catch { - active = false; - } + let postDate: Date = new Date(); + try { + postDate = parseDate(post.date); + if (Number.isNaN(postDate.getTime()) || isDateBefore(postDate, currentDate)) { + active = false; + } + } catch { + active = false; + }Also applies to: 84-84
🤖 Fix all issues with AI agents
In `@components/Calendar.tsx`:
- Around line 49-55: Prettier CI failed due to formatting in the Calendar
component around the JSX that renders event date and title; run Prettier (or
your project's formatter) on the Calendar.tsx block containing the JSX with
formatDate(event.date, 'd') and formatDateWithTimezone(event.date) so
spacing/indentation and line breaks match project rules (adjust the
span/div/h2/p indentation and wrap attributes consistently), then re-stage the
changes so CI passes.
In `@components/layout/BlogLayout.tsx`:
- Line 9: The formatDate helper currently uses parseISO and can produce an
Invalid Date for non-ISO inputs; update formatDate to catch parse errors and
fall back to native Date parsing, then verify validity with isValid before
formatting: in the formatDate function (and any duplicate at line ~74), try
parsing with parseISO for strings but catch exceptions and assign new Date(date)
as a fallback, then return format(parsedDate, formatStr) only if
isValid(parsedDate), otherwise return a safe placeholder like 'Invalid date' to
avoid broken rendered output and invalid dateTime attributes.
In `@components/navigation/BlogPostItem.tsx`:
- Around line 1-8: The import order in BlogPostItem.tsx fails CI; reorder
imports (or run the repo's import-sorting autofix) so external packages come
first (react, next, react-text-truncate), then type imports, then local project
imports—ensuring Link, React/forwardRef, TextTruncate are grouped before
BlogPostType, formatDate, and IBlogPost; run the project's autofix (e.g.,
yarn/npm script or eslint --fix) after reordering to satisfy the linter.
In `@components/navigation/EventFilter.tsx`:
- Line 4: Remove the unused import symbol formatDate from the import statement
in EventFilter.tsx; update the import to only include the used helpers
(isDateAfter, isDateBefore) so the line reads an import of those two functions,
and run lint to confirm the unused-import error is resolved (target the import
that currently reads import { formatDate, isDateAfter, isDateBefore } from
'@/utils/dateHelpers';).
In `@components/navigation/EventPostItem.tsx`:
- Line 51: Remove the trailing whitespace reported by Prettier in the
EventPostItem component file; open components/navigation/EventPostItem.tsx,
locate the EventPostItem component (around the area flagged) and delete any
extra spaces at the end of the line(s) (e.g., line ~51) so the file no longer
contains trailing spaces and passes formatting checks.
In `@components/newsroom/FeaturedBlogPost.tsx`:
- Around line 1-7: The import order in FeaturedBlogPost.tsx is failing CI; run
the project's import-sorting autofix (e.g., eslint --fix or the configured
import sorter) or manually reorder imports so external modules (react,
next/link, react-text-truncate) come first, then a blank line, then local
project imports (formatDate, BlogPostType, IBlogPost) and ensure
alphabetical/group ordering matches the repo lint rules; adjust the import
statements for Link, React, TextTruncate, formatDate, BlogPostType, and
IBlogPost accordingly and re-run lint.
In `@pages/community/index.tsx`:
- Around line 134-136: Prettier CI failed due to JSX formatting around the span
that renders {formatDateWithTimezone(event.date)}; re-run Prettier (or your
editor formatter) and update the JSX so it matches the project's Prettier
rules—ensure the span with className 'text-xs text-gray-500 md:text-sm' and the
{formatDateWithTimezone(event.date)} expression are formatted according to the
formatter (no extra spaces/line breaks), then save and commit the reformatted
pages/community/index.tsx changes.
In `@utils/dateHelpers.ts`:
- Around line 1-47: Validate parsed dates with date-fns isValid before using
format, isAfter, isBefore, or difference* to avoid RangeError on Invalid Date:
import isValid from 'date-fns' and in each function (formatDate, formatDateTime,
formatDateWithTimezone, isDateAfter, isDateBefore, getDaysDifference,
getHoursDifference, getMinutesDifference, parseDate) parse the input the same
way you already do, check if isValid(parsedDate) and if not return a safe
fallback (e.g., for format* functions return an empty string, for
isDateAfter/isDateBefore return false, for get*Difference return NaN or 0 per
app expectations, and for parseDate either throw a clear error or return new
Date(NaN) consistently); ensure formatDateWithTimezone also guards before
calling format/XXX.
| import type { IPosts } from '@/types/post'; | ||
|
|
||
| import BlogContext from '../../context/BlogContext'; | ||
| import { formatDate } from '../../utils/dateHelpers'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check frontmatter date fields in markdown files
fd -e md -e mdx | xargs rg -n '^date:\s*' -A 0 || trueRepository: asyncapi/website
Length of output: 12099
🏁 Script executed:
# Find and examine the formatDate implementation
fd -e ts -e tsx -e js -e jsx | xargs rg -l 'formatDate|parseISO' | head -20Repository: asyncapi/website
Length of output: 321
🏁 Script executed:
# Get context around line 74 in BlogLayout.tsx
fd -e tsx | xargs grep -l 'BlogLayout' | head -1 | xargs sed -n '70,80p'Repository: asyncapi/website
Length of output: 543
🏁 Script executed:
# Check dateHelpers implementation
fd -e ts -e tsx -e js -e jsx -path '*/utils/*' | xargs grep -l 'formatDate\|parseISO' || trueRepository: asyncapi/website
Length of output: 228
🏁 Script executed:
# Read dateHelpers.ts implementation
fd -name 'dateHelpers.ts' -o -name 'dateHelpers.js' | xargs catRepository: asyncapi/website
Length of output: 290
🏁 Script executed:
# Check how blog posts are loaded/parsed
fd -name '*blog*' -type f | xargs rg -l 'getAllPosts\|getBlogPosts\|frontmatter\|gray-matter' | head -5Repository: asyncapi/website
Length of output: 290
🏁 Script executed:
# Search for blog data loading in pages or API routes
fd -name 'index.tsx' -o -name 'index.ts' | xargs rg -l 'blog\|post' | head -5Repository: asyncapi/website
Length of output: 290
🏁 Script executed:
# Find and read dateHelpers.ts
find . -name 'dateHelpers.ts' -o -name 'dateHelpers.js' | head -1 | xargs catRepository: asyncapi/website
Length of output: 2086
🏁 Script executed:
# Search for blog data loading utilities
find . -type f -name '*.ts' -o -name '*.tsx' | xargs grep -l 'getAllPosts\|getBlogPosts' 2>/dev/null | head -5Repository: asyncapi/website
Length of output: 234
🏁 Script executed:
# Check BlogLayout context around line 74 and 9
find . -name 'BlogLayout.tsx' | xargs catRepository: asyncapi/website
Length of output: 3720
🏁 Script executed:
# Find where blog posts are loaded/parsed
find . -type f \( -name '*.ts' -o -name '*.tsx' \) | xargs grep -l 'post.date\|getPosts\|getAllPosts' 2>/dev/null | head -10Repository: asyncapi/website
Length of output: 410
🏁 Script executed:
# Check for blog data transformation/frontmatter parsing
find . -type f \( -name '*.ts' -o -name '*.tsx' \) | xargs grep -l 'frontmatter\|gray-matter\|parseISO' 2>/dev/null | head -10Repository: asyncapi/website
Length of output: 188
🏁 Script executed:
# Look for the types definition to understand what post.date is
find . -type f -name 'post.ts' -o -name 'post.d.ts' -o -name '*.ts' | xargs grep -l 'interface IPosts\|type IPosts' 2>/dev/null | head -5Repository: asyncapi/website
Length of output: 75
🏁 Script executed:
# Read the post types definition
cat ./types/post.tsRepository: asyncapi/website
Length of output: 621
🏁 Script executed:
# Read build-post-list.ts to see date processing
cat ./scripts/build-post-list.tsRepository: asyncapi/website
Length of output: 13127
🏁 Script executed:
# Check utils/api.ts for date handling
cat ./utils/api.tsRepository: asyncapi/website
Length of output: 1359
Add error handling for date parsing in formatDate.
parseISO from date-fns will fail on non-ISO dates and return Invalid Date, which breaks both the rendered date display and the dateTime attribute. While most blog dates are ISO-8601 compliant, malformed dates with missing leading zeros (e.g., 7:26:58 instead of 07:26:58) have been found in the frontmatter and will cause rendering issues. Add a fallback to gracefully handle parsing failures:
export function formatDate(date: string | Date, formatStr: string): string {
let parsedDate: Date;
try {
parsedDate = typeof date === 'string' ? parseISO(date) : date;
} catch (e) {
parsedDate = new Date(date); // fallback to native Date parsing
}
return isValid(parsedDate) ? format(parsedDate, formatStr) : 'Invalid date';
}Also applies to: 74-74
🤖 Prompt for AI Agents
In `@components/layout/BlogLayout.tsx` at line 9, The formatDate helper currently
uses parseISO and can produce an Invalid Date for non-ISO inputs; update
formatDate to catch parse errors and fall back to native Date parsing, then
verify validity with isValid before formatting: in the formatDate function (and
any duplicate at line ~74), try parsing with parseISO for strings but catch
exceptions and assign new Date(date) as a fallback, then return
format(parsedDate, formatStr) only if isValid(parsedDate), otherwise return a
safe placeholder like 'Invalid date' to avoid broken rendered output and invalid
dateTime attributes.
| import Link from 'next/link'; | ||
| import type { Ref } from 'react'; | ||
| import React, { forwardRef } from 'react'; | ||
| import TextTruncate from 'react-text-truncate'; | ||
|
|
||
| import { BlogPostType } from '@/types/components/navigation/BlogPostType'; | ||
| import { formatDate } from '@/utils/dateHelpers'; | ||
| import type { IBlogPost } from '@/types/post'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import order is failing CI.
The pipeline reports “Run autofix to sort these imports!”. Please run the repo’s import‑sorting autofix or reorder imports to satisfy lint.
🧰 Tools
🪛 GitHub Actions: PR testing - if Node project
[error] 1-1: Run autofix to sort these imports!
🤖 Prompt for AI Agents
In `@components/navigation/BlogPostItem.tsx` around lines 1 - 8, The import order
in BlogPostItem.tsx fails CI; reorder imports (or run the repo's import-sorting
autofix) so external packages come first (react, next, react-text-truncate),
then type imports, then local project imports—ensuring Link, React/forwardRef,
TextTruncate are grouped before BlogPostType, formatDate, and IBlogPost; run the
project's autofix (e.g., yarn/npm script or eslint --fix) after reordering to
satisfy the linter.
- Add isValid() validation to dateHelpers.ts - Fix import order in multiple components - Remove unused imports - Fix Prettier formatting - Revert unrelated config/tools.json changes - Address CodeRabbit review comments
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5019 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 22 22
Lines 796 796
Branches 146 146
=========================================
Hits 796 796 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This PR migrates the project from moment.js to date-fns, resulting in significant bundle size reduction and improved performance.
Changes:
Removed moment.js dependency (67.8KB minified)
Added date-fns with tree-shaking support (~3KB for used functions)
Created centralized date utility helper (dateHelpers.ts) for consistent date operations
Updated 11 components and utility files to use date-fns:
Calendar.tsx
BlogLayout.tsx
FeaturedBlogPost.tsx
BlogPostItem.tsx
EventFilter.tsx
EventPostItem.tsx
AnnouncementRemainingDays.tsx
index.tsx
staticHelpers.ts
compose.ts
All date formatting, parsing, and comparison logic preserved
No breaking changes to existing functionality
Benefits:
Bundle size reduction: ~65KB (significant improvement in page load performance)
Better tree-shaking support with date-fns
Modern, actively maintained library
Immutable date operations by default
Improved code maintainability with centralized date utilities
Testing:
All existing functionality verified
Date formatting outputs match previous behavior
Event filtering and sorting work correctly
Blog post dates display properly
Related issue(s)
Closes #5014
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.