Migrate to modern ESM dependencies#3311
Conversation
| const updatedFetchOptions = cloneDeep(fetchOptions); | ||
| updatedFetchOptions.headers.set('Authorization', `Bearer ${newTokens.accessToken}`); |
There was a problem hiding this comment.
Bug: The cloneDeep function from es-toolkit doesn't support Headers objects, causing the token refresh flow to fail and log the user out when it tries to call .set().
Severity: HIGH
Suggested Fix
Manually handle the cloning of the Headers object instead of relying on cloneDeep. Create a new Headers instance from the old one before cloning the rest of the fetchOptions object. For example: const newHeaders = new Headers(fetchOptions.headers); const updatedFetchOptions = { ...cloneDeep(fetchOptions), headers: newHeaders };
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: src/commons/utils/RequestHelper.tsx#L94-L95
Potential issue: The migration from lodash to `es-toolkit`'s `cloneDeep` function
introduces a runtime error during the authentication token refresh process. When a token
expires and needs refreshing, the `cloneDeep` function is called on fetch options
containing a `Headers` Web API object. The `es-toolkit` implementation does not properly
clone this object, instead converting it to a plain JavaScript object. The subsequent
attempt to call `updatedFetchOptions.headers.set(...)` fails with a `TypeError`, as the
plain object lacks the `.set` method. This error is caught and incorrectly handled as a
refresh failure, causing the user to be logged out.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Pull request overview
This PR migrates the frontend away from legacy CommonJS utilities (lodash, classnames) to modern ESM-friendly, smaller alternatives (es-toolkit, clsx), updating imports across the UI and saga/utils layers and adjusting dependency/resolution wiring.
Changes:
- Replace
classnamesusages withclsxthroughout the React UI. - Replace
lodashutilities withes-toolkit/es-toolkit/compatequivalents across pages, commons utilities, features, and sagas. - Update
package.jsonandyarn.lock(including aresolutionsalias forclassnames→clsx).
Reviewed changes
Copilot reviewed 84 out of 85 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Updates lockfile for new deps (es-toolkit, clsx) and removals (lodash, classnames, @types/lodash). |
| package.json | Swaps deps to clsx + es-toolkit, removes lodash/classnames, adds resolutions alias. |
| src/pages/stories/Story.tsx | Replace classnames with clsx. |
| src/pages/sourcecast/Sourcecast.tsx | Replace classnames with clsx. |
| src/pages/sicp/subcomponents/SicpToc.tsx | Replace lodash/cloneDeep with es-toolkit/cloneDeep. |
| src/pages/sicp/Sicp.tsx | Replace classnames with clsx. |
| src/pages/playground/Playground.tsx | Replace classnames with clsx; replace lodash/isEqual with es-toolkit/isEqual. |
| src/pages/notFound/NotFound.tsx | Replace classnames with clsx. |
| src/pages/missionControl/MissionControl.tsx | Replace classnames with clsx. |
| src/pages/login/NusLogin.tsx | Replace classnames with clsx. |
| src/pages/login/LoginVscodeCallback.tsx | Replace classnames with clsx. |
| src/pages/login/LoginPage.tsx | Replace classnames with clsx. |
| src/pages/login/LoginCallback.tsx | Replace classnames with clsx. |
| src/pages/githubCallback/GitHubCallback.tsx | Replace classnames with clsx. |
| src/pages/createStore.ts | Replace lodash/throttle with es-toolkit/throttle. |
| src/pages/academy/sourcereel/Sourcereel.tsx | Replace classnames with clsx. |
| src/pages/academy/groundControl/subcomponents/GroundControlDropzone.tsx | Replace classnames with clsx. |
| src/pages/academy/grading/subcomponents/gradingSubmissionsTableUtils.tsx | Replace classnames with clsx in ag-grid class props. |
| src/pages/academy/grading/subcomponents/GradingWorkspace.tsx | Replace classnames with clsx. |
| src/pages/academy/grading/subcomponents/GradingSubmissionsTable.tsx | Replace classnames with clsx; replace lodash/debounce with es-toolkit/debounce. |
| src/pages/academy/grading/subcomponents/GradingColumnCustomHeaders.tsx | Replace classnames with clsx. |
| src/pages/academy/grading/subcomponents/GradingBadges.tsx | Replace classnames with clsx. |
| src/pages/academy/gameSimulator/subcomponents/assetViewer/AssetViewerUtils.tsx | Replace lodash/set with es-toolkit/compat/set. |
| src/pages/academy/gameSimulator/subcomponents/assetViewer/AssetViewer.tsx | Replace lodash/cloneDeep with es-toolkit/cloneDeep. |
| src/pages/academy/dashboard/Dashboard.tsx | Replace lodash/startCase with es-toolkit/startCase. |
| src/pages/academy/adminPanel/subcomponents/assessmentConfigPanel/AssessmentConfigPanel.tsx | Replace lodash utils with es-toolkit equivalents. |
| src/pages/academy/adminPanel/subcomponents/AddUserPanel.tsx | Replace lodash/uniqBy with es-toolkit/uniqBy. |
| src/pages/academy/adminPanel/subcomponents/AddStoriesUserPanel.tsx | Replace lodash/uniqBy with es-toolkit/uniqBy. |
| src/pages/academy/academyRoutes.ts | Replace lodash/memoize with es-toolkit/memoize. |
| src/pages/academy/Academy.tsx | Replace classnames with clsx. |
| src/features/remoteExecution/RemoteExecutionDeviceDialog.tsx | Replace classnames with clsx. |
| src/features/remoteExecution/PeripheralContainer.tsx | Replace classnames with clsx. |
| src/features/gameSimulator/GameSimulatorService.ts | Replace lodash/sortBy with es-toolkit/compat/sortBy. |
| src/features/game/utils/StyleUtils.ts | Replace lodash helpers with es-toolkit / es-toolkit/compat. |
| src/features/game/save/GameSaveRequests.ts | Replace lodash/isEmpty with es-toolkit/compat/isEmpty. |
| src/features/game/chapter/GameChapterHelpers.ts | Replace lodash/sortBy with es-toolkit/compat/sortBy. |
| src/features/cseMachine/CseMachineUtils.ts | Replace lodash helpers with es-toolkit / es-toolkit/compat. |
| src/commons/workspace/tests/WorkspaceReducer.test.ts | Replace lodash/cloneDeep with es-toolkit/cloneDeep. |
| src/commons/utils/RequestHelper.tsx | Replace lodash/cloneDeep with es-toolkit/cloneDeep. |
| src/commons/utils/MemoizeHelper.ts | Replace lodash/isEqual with es-toolkit/isEqual. |
| src/commons/utils/JsSlangHelper.ts | Replace lodash helpers with es-toolkit / es-toolkit/compat. |
| src/commons/sourceRecorder/SourceRecorderTable.tsx | Replace lodash/sortBy with es-toolkit/compat/sortBy. |
| src/commons/sourceRecorder/SourceRecorderEditor.tsx | Replace lodash/isEqual with es-toolkit/isEqual. |
| src/commons/sideContent/content/remoteExecution/SideContentRemoteExecution.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/githubAssessments/SideContentEditableTestcaseCard.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentToneMatrix.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentTestcaseCard.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentSubstVisualizer.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentSessionManagement.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentResultCard.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentLeaderboardCard.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentDataVisualizer.tsx | Replace classnames with clsx. |
| src/commons/sideContent/content/SideContentCseMachine.tsx | Replace classnames with clsx; replace lodash/debounce with es-toolkit/debounce. |
| src/commons/sideContent/content/SideContentContestVoting.tsx | Replace classnames with clsx. |
| src/commons/sideContent/SideContentHelper.ts | Replace lodash runtime provider with es-toolkit/compat provider mapping. |
| src/commons/sideBar/SideBar.tsx | Replace classnames with clsx. |
| src/commons/sagas/WorkspaceSaga/helpers/runTestCase.ts | Replace lodash/random with es-toolkit/random. |
| src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts | Replace lodash/pick with es-toolkit/pick. |
| src/commons/sagas/RemoteExecutionSaga.ts | Replace lodash/pickBy with es-toolkit/compat/pickBy. |
| src/commons/sagas/BackendSaga.ts | Replace lodash key transforms with es-toolkit / es-toolkit/compat. |
| src/commons/repl/ReplInput.tsx | Replace classnames with clsx. |
| src/commons/repl/Repl.tsx | Replace classnames with clsx. |
| src/commons/navigationBar/NavigationBar.tsx | Replace classnames with clsx. |
| src/commons/mocks/StoreMocks.ts | Replace lodash merge helpers with es-toolkit/compat equivalents. |
| src/commons/mobileWorkspace/mobileSideContent/MobileSideContent.tsx | Replace classnames with clsx. |
| src/commons/grading/GradingText.tsx | Replace classnames with clsx. |
| src/commons/gitHubOverlay/RepositoryDialog.tsx | Replace classnames with clsx. |
| src/commons/gitHubOverlay/FileExplorerDialog.tsx | Replace classnames with clsx. |
| src/commons/fileSystemView/FileSystemViewContextMenu.tsx | Replace classnames with clsx. |
| src/commons/editor/tabs/EditorTab.tsx | Replace classnames with clsx. |
| src/commons/editor/EditorContainer.tsx | Replace lodash/pick with es-toolkit/pick usage. |
| src/commons/editingWorkspace/EditingWorkspace.tsx | Replace classnames with clsx. |
| src/commons/dialogs/ConfirmDialog.tsx | Replace classnames with clsx. |
| src/commons/controlBar/ControlBar.tsx | Replace classnames with clsx. |
| src/commons/assessmentWorkspace/AssessmentWorkspace.tsx | Replace classnames with clsx; replace lodash/isEqual with es-toolkit/isEqual. |
| src/commons/assessment/AssessmentOverviewCard.tsx | Replace classnames with clsx. |
| src/commons/assessment/AssessmentNotFound.tsx | Replace classnames with clsx. |
| src/commons/assessment/Assessment.tsx | Replace lodash/sortBy with es-toolkit/sortBy. |
| src/commons/application/ApplicationWrapper.tsx | Replace classnames with clsx. |
| src/commons/achievement/utils/AchievementInferencer.ts | Replace lodash helpers with es-toolkit equivalents. |
| src/commons/achievement/control/goalEditor/EditableGoal.tsx | Replace lodash/cloneDeep with es-toolkit/cloneDeep. |
| src/commons/achievement/control/achievementEditor/achievementSettings/EditablePrerequisiteUuids.tsx | Replace lodash/without with es-toolkit/without. |
| src/commons/achievement/control/achievementEditor/achievementSettings/EditableGoalUuids.tsx | Replace lodash/without with es-toolkit/without. |
| src/commons/achievement/control/achievementEditor/EditableCard.tsx | Replace lodash/cloneDeep with es-toolkit/cloneDeep. |
| src/commons/Markdown.tsx | Replace classnames with clsx. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } from '@blueprintjs/core'; | ||
| import { IconNames } from '@blueprintjs/icons'; | ||
| import { sortBy } from 'lodash'; | ||
| import { sortBy } from 'es-toolkit'; |
| import clsx from 'clsx'; | ||
| import { debounce } from 'es-toolkit'; | ||
| import { t } from 'i18next'; |
Pull Request Test Coverage Report for Build 23157220461Details
💛 - Coveralls |
Description
es-toolkitis a mostly drop-in, faster, smaller replacement oflodash.clsxis smaller, faster, and supports more features compared toclassnamesType of change
How to test
Checklist