From 0c4a4ab163f7a12ceecc48ee3aab05e79a74cf01 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Sat, 20 Jan 2024 01:15:39 -0500 Subject: [PATCH 1/2] refactor(deps): remove `moment` dep and usage `moment` has been [deprecated since Sep 2020](https://momentjs.com/docs/#/-project-status/) and recommends using native `Intl` or newer libraries that make use of native `Intl`, such as `luxon` and `dayjs` - `moment` is also a very large dependency and hence is ripe for pruning and replacement as well - replace all usage of `moment` with regular `Date` functions - `valueOf()` works the same way for `Date` and `moment` - `diff` doesn't exist, but it's a one-liner helper - `format` doesn't exist, but we can also use a simple helper for that - shave off X kb by replacing ~15 LoC Signed-off-by: Anton Gilgur --- .../workflow-node-info/workflow-node-info.tsx | 7 ++-- .../workflow-timeline/workflow-timeline.tsx | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/ui/src/workflows/components/workflow-node-info/workflow-node-info.tsx b/ui/src/workflows/components/workflow-node-info/workflow-node-info.tsx index daafdc31e2ed..7a03babd9aed 100644 --- a/ui/src/workflows/components/workflow-node-info/workflow-node-info.tsx +++ b/ui/src/workflows/components/workflow-node-info/workflow-node-info.tsx @@ -1,7 +1,6 @@ import {Tabs} from 'argo-ui/src/components/tabs/tabs'; import {Ticker} from 'argo-ui/src/components/ticker'; import {Tooltip} from 'argo-ui/src/components/tooltip/tooltip'; -import moment from 'moment'; import * as React from 'react'; import {useState} from 'react'; @@ -23,9 +22,9 @@ import {TIMESTAMP_KEYS} from '../../../shared/use-timestamp'; import './workflow-node-info.scss'; -function nodeDuration(node: models.NodeStatus, now: moment.Moment) { - const endTime = node.finishedAt ? moment(node.finishedAt) : now; - return endTime.diff(moment(node.startedAt)) / 1000; +function nodeDuration(node: models.NodeStatus, now: Date): number { + const endTime = node.finishedAt ? new Date(node.finishedAt) : now; + return (endTime.valueOf() - new Date(node.startedAt).valueOf()) / 1000; // ms to seconds } // Iterate over the node's subtree and find pod in error or fail diff --git a/ui/src/workflows/components/workflow-timeline/workflow-timeline.tsx b/ui/src/workflows/components/workflow-timeline/workflow-timeline.tsx index 98bb93629ec8..eaca3383ac16 100644 --- a/ui/src/workflows/components/workflow-timeline/workflow-timeline.tsx +++ b/ui/src/workflows/components/workflow-timeline/workflow-timeline.tsx @@ -1,5 +1,4 @@ import classNames from 'classnames'; -import moment from 'moment'; import React, {useEffect, useRef, useState} from 'react'; import {fromEvent, interval, Subscription} from 'rxjs'; @@ -19,9 +18,20 @@ interface WorkflowTimelineProps { nodeClicked?: (node: models.NodeStatus) => any; } +function dateDiff(dateLikeA: string | number, dateLikeB: string | number): number { + return new Date(dateLikeA).valueOf() - new Date(dateLikeB).valueOf(); +} + +function hhMMFormat(dateLike: string | number): string { + const date = new Date(dateLike); + // timeString format is '00:59:00 GMT-0500 (Eastern Standard Time)', hh:MM is '00:59' + const parts = date.toTimeString().split(':'); + return parts[0] + ':' + parts[1]; +} + export function WorkflowTimeline(props: WorkflowTimelineProps) { const [parentWidth, setParentWidth] = useState(0); - const [now, setNow] = useState(moment()); + const [now, setNow] = useState(new Date()); const containerRef = useRef(null); const resizeSubscription = useRef(null); @@ -41,7 +51,7 @@ export function WorkflowTimeline(props: WorkflowTimelineProps) { const isCompleted = props.workflow?.status && COMPLETED_PHASES.includes(props.workflow.status.phase); if (!refreshSubscription.current && !isCompleted) { refreshSubscription.current = interval(1000).subscribe(() => { - setNow(moment()); + setNow(new Date()); }); } else if (refreshSubscription.current && isCompleted) { refreshSubscription.current.unsubscribe(); @@ -62,15 +72,15 @@ export function WorkflowTimeline(props: WorkflowTimelineProps) { const nodes = Object.keys(props.workflow.status.nodes) .map(id => { const node = props.workflow.status.nodes[id]; - node.finishedAt = node.finishedAt || now.format(); - node.startedAt = node.startedAt || now.format(); + node.finishedAt = node.finishedAt || now.toISOString(); + node.startedAt = node.startedAt || now.toISOString(); return node; }) .filter(node => node.startedAt && node.type === 'Pod') .sort((first, second) => { - const diff = moment(first.startedAt).diff(second.startedAt); + const diff = dateDiff(first.startedAt, second.startedAt); if (diff <= 2) { - return moment(first.finishedAt).diff(second.finishedAt); + return dateDiff(first.finishedAt, second.finishedAt); } return diff; }); @@ -79,20 +89,20 @@ export function WorkflowTimeline(props: WorkflowTimelineProps) { return
; } - const timelineStart = moment(nodes[0].startedAt).valueOf(); - const timelineEnd = nodes.map(node => moment(node.finishedAt).valueOf()).reduce((first, second) => Math.max(first, second), moment(timelineStart).valueOf()); + const timelineStart = new Date(nodes[0].startedAt).valueOf(); + const timelineEnd = nodes.map(node => new Date(node.finishedAt).valueOf()).reduce((first, second) => Math.max(first, second), new Date(timelineStart).valueOf()); function timeToLeft(time: number) { return ((time - timelineStart) / (timelineEnd - timelineStart)) * Math.max(parentWidth, MIN_WIDTH) + NODE_NAME_WIDTH; } const groups = nodes.map(node => ({ - startedAt: moment(node.startedAt).valueOf(), - finishedAt: moment(node.finishedAt).valueOf(), + startedAt: new Date(node.startedAt).valueOf(), + finishedAt: new Date(node.finishedAt).valueOf(), nodes: [ Object.assign({}, node, { - left: timeToLeft(moment(node.startedAt).valueOf()), - width: timeToLeft(moment(node.finishedAt).valueOf()) - timeToLeft(moment(node.startedAt).valueOf()) + left: timeToLeft(new Date(node.startedAt).valueOf()), + width: timeToLeft(new Date(node.finishedAt).valueOf()) - timeToLeft(new Date(node.startedAt).valueOf()) }) ] })); @@ -100,9 +110,9 @@ export function WorkflowTimeline(props: WorkflowTimelineProps) { for (let i = groups.length - 1; i >= 1; i--) { const cur = groups[i]; const next = groups[i - 1]; - if (moment(cur.startedAt).diff(next.finishedAt, 'milliseconds') < 0 && moment(next.startedAt).diff(cur.startedAt, 'milliseconds') < ROUND_START_DIFF_MS) { + if (dateDiff(cur.startedAt, next.finishedAt) < 0 && dateDiff(next.startedAt, cur.startedAt) < ROUND_START_DIFF_MS) { next.nodes = next.nodes.concat(cur.nodes); - next.finishedAt = nodes.map(node => moment(node.finishedAt).valueOf()).reduce((first, second) => Math.max(first, second), next.finishedAt.valueOf()); + next.finishedAt = nodes.map(node => new Date(node.finishedAt).valueOf()).reduce((first, second) => Math.max(first, second), next.finishedAt.valueOf()); groups.splice(i, 1); } } @@ -113,7 +123,7 @@ export function WorkflowTimeline(props: WorkflowTimelineProps) {
{groups.map(group => [
- {moment(group.startedAt).format('hh:mm')} + {hhMMFormat(group.startedAt)}
, ...group.nodes.map(node => (
Date: Mon, 28 Oct 2024 15:20:52 -0400 Subject: [PATCH 2/2] update argo-ui to remove moment from it Signed-off-by: Anton Gilgur --- ui/package.json | 2 +- ui/yarn.lock | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ui/package.json b/ui/package.json index e10309100039..36025e020584 100644 --- a/ui/package.json +++ b/ui/package.json @@ -16,7 +16,7 @@ "node": ">=20" }, "dependencies": { - "argo-ui": "git+https://github.com/argoproj/argo-ui.git#6de6bfedc0ec0625122dd5162371fc1932f8d428", + "argo-ui": "git+https://github.com/argoproj/argo-ui.git#54f36c723d9a0e5d3386689298cbb9c27767fa00", "chart.js": "^2.9.4", "chartjs-plugin-annotation": "^0.5.7", "classnames": "^2.5.1", diff --git a/ui/yarn.lock b/ui/yarn.lock index 98dc72d52833..aa19be399549 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2336,9 +2336,9 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -"argo-ui@git+https://github.com/argoproj/argo-ui.git#6de6bfedc0ec0625122dd5162371fc1932f8d428": +"argo-ui@git+https://github.com/argoproj/argo-ui.git#54f36c723d9a0e5d3386689298cbb9c27767fa00": version "1.0.0" - resolved "git+https://github.com/argoproj/argo-ui.git#6de6bfedc0ec0625122dd5162371fc1932f8d428" + resolved "git+https://github.com/argoproj/argo-ui.git#54f36c723d9a0e5d3386689298cbb9c27767fa00" dependencies: "@fortawesome/fontawesome-free" "^6.2.1" "@tippy.js/react" "^3.1.1" @@ -2346,13 +2346,12 @@ arg@^4.1.0: core-js "^3.32.1" foundation-sites "^6.4.3" history "^4.10.1" - moment "^2.29.4" prop-types "^15.8.1" react-autocomplete "1.8.1" react-form "^2.16.0" react-helmet "^6.1.0" react-router-dom "^4.2.2" - react-toastify "9.0.8" + react-toastify "9.0.3" rxjs "^7.8.1" typescript "^4.9.5" uuid "^9.0.0" @@ -6584,7 +6583,7 @@ mkdirp@1.x: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -moment@^2.10.2, moment@^2.29.4, moment@^2.30.1: +moment@^2.10.2, moment@^2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== @@ -7395,10 +7394,10 @@ react-side-effect@^2.1.0: resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== -react-toastify@9.0.8: - version "9.0.8" - resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.0.8.tgz#3876c89fc6211a29027b3075010b5ec39ebe4f7e" - integrity sha512-EwM+teWt49HSHx+67qI08yLAW1zAsBxCXLCsUfxHYv1W7/R3ZLhrqKalh7j+kjgPna1h5LQMSMwns4tB4ww2yQ== +react-toastify@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.0.3.tgz#8e6d22651c85cb584c5ebd0b5e2c3bf0d7ec06ee" + integrity sha512-0QZJk0SqYBxouRBGCFU3ymvjlwimRRhVH7SzqGRiVrQ001KSoUNbGKx9Yq42aoPv18n45yJzEFG82zqv3HnASg== dependencies: clsx "^1.1.1"