Skip to content

Commit 4bd2c3b

Browse files
authored
feat: lighter build by rewriting lodash imports (#1772)
Incorrect lodash imports are causing MFEs to import the entire lodash library. This change shaves off a few kB of the compressed build.
1 parent f531d54 commit 4bd2c3b

File tree

20 files changed

+126
-118
lines changed

20 files changed

+126
-118
lines changed

src/course-libraries/CourseLibraries.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
Cached, CheckCircle, Launch, Loop,
2121
} from '@openedx/paragon/icons';
2222

23-
import _ from 'lodash';
23+
import sumBy from 'lodash/sumBy';
2424
import { useSearchParams } from 'react-router-dom';
2525
import getPageHeadTitle from '../generic/utils';
2626
import { useModel } from '../generic/model-store';
@@ -109,7 +109,7 @@ export const CourseLibraries: React.FC<Props> = ({ courseId }) => {
109109
);
110110
const [showReviewAlert, setShowReviewAlert] = useState(false);
111111
const { data: libraries, isLoading } = useEntityLinksSummaryByDownstreamContext(courseId);
112-
const outOfSyncCount = useMemo(() => _.sumBy(libraries, (lib) => lib.readyToSyncCount), [libraries]);
112+
const outOfSyncCount = useMemo(() => sumBy(libraries, (lib) => lib.readyToSyncCount), [libraries]);
113113
const {
114114
isLoadingPage: isLoadingStudioHome,
115115
isFailedLoadingPage: isFailedLoadingStudioHome,

src/course-libraries/ReviewTabContent.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import {
1414
useToggle,
1515
} from '@openedx/paragon';
1616

17-
import _ from 'lodash';
17+
import {
18+
tail, keyBy, orderBy, merge, omitBy,
19+
} from 'lodash';
1820
import { useQueryClient } from '@tanstack/react-query';
1921
import { Loop, Warning } from '@openedx/paragon/icons';
2022
import messages from './messages';
@@ -49,7 +51,7 @@ interface BlockCardProps {
4951
const BlockCard: React.FC<BlockCardProps> = ({ info, actions }) => {
5052
const intl = useIntl();
5153
const componentIcon = getItemIcon(info.blockType);
52-
const breadcrumbs = _.tail(info.breadcrumbs) as Array<{ displayName: string, usageKey: string }>;
54+
const breadcrumbs = tail(info.breadcrumbs) as Array<{ displayName: string, usageKey: string }>;
5355

5456
const getBlockLink = useCallback(() => {
5557
let key = info.usageKey;
@@ -138,11 +140,11 @@ const ComponentReviewList = ({
138140
);
139141

140142
const outOfSyncComponentsByKey = useMemo(
141-
() => _.keyBy(outOfSyncComponents, 'downstreamUsageKey'),
143+
() => keyBy(outOfSyncComponents, 'downstreamUsageKey'),
142144
[outOfSyncComponents],
143145
);
144146
const downstreamInfoByKey = useMemo(
145-
() => _.keyBy(downstreamInfo, 'usageKey'),
147+
() => keyBy(downstreamInfo, 'usageKey'),
146148
[downstreamInfo],
147149
);
148150
const queryClient = useQueryClient();
@@ -241,9 +243,9 @@ const ComponentReviewList = ({
241243
if (isIndexDataLoading) {
242244
return [];
243245
}
244-
let merged = _.merge(downstreamInfoByKey, outOfSyncComponentsByKey);
245-
merged = _.omitBy(merged, (o) => !o.displayName);
246-
const ordered = _.orderBy(Object.values(merged), 'updated', 'desc');
246+
let merged = merge(downstreamInfoByKey, outOfSyncComponentsByKey);
247+
merged = omitBy(merged, (o) => !o.displayName);
248+
const ordered = orderBy(Object.values(merged), 'updated', 'desc');
247249
return ordered;
248250
}, [downstreamInfoByKey, outOfSyncComponentsByKey]);
249251

src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useState, useEffect } from 'react';
22

3-
import _ from 'lodash';
3+
import {
4+
includes, isEmpty, isFinite, isNaN, isNil,
5+
} from 'lodash';
46
// This 'module' self-import hack enables mocking during tests.
57
// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested
68
// should be re-thought and cleaned up to avoid this pattern.
@@ -65,7 +67,7 @@ export const hintsCardHooks = (hints, updateSettings) => {
6567

6668
const handleAdd = () => {
6769
let newId = 0;
68-
if (!_.isEmpty(hints)) {
70+
if (!isEmpty(hints)) {
6971
newId = Math.max(...hints.map(hint => hint.id)) + 1;
7072
}
7173
const hint = { id: newId, value: '' };
@@ -114,9 +116,9 @@ export const resetCardHooks = (updateSettings) => {
114116

115117
export const scoringCardHooks = (scoring, updateSettings, defaultValue) => {
116118
let loadedAttemptsNumber = scoring.attempts.number;
117-
if ((loadedAttemptsNumber === defaultValue || !_.isFinite(loadedAttemptsNumber)) && _.isFinite(defaultValue)) {
119+
if ((loadedAttemptsNumber === defaultValue || !isFinite(loadedAttemptsNumber)) && isFinite(defaultValue)) {
118120
loadedAttemptsNumber = `${defaultValue} (Default)`;
119-
} else if (loadedAttemptsNumber === defaultValue && _.isNil(defaultValue)) {
121+
} else if (loadedAttemptsNumber === defaultValue && isNil(defaultValue)) {
120122
loadedAttemptsNumber = '';
121123
}
122124
const [attemptDisplayValue, setAttemptDisplayValue] = module.state.attemptDisplayValue(loadedAttemptsNumber);
@@ -135,9 +137,9 @@ export const scoringCardHooks = (scoring, updateSettings, defaultValue) => {
135137
let unlimitedAttempts = false;
136138
let attemptNumber = parseInt(event.target.value, 10);
137139

138-
if (!_.isFinite(attemptNumber) || attemptNumber === defaultValue) {
140+
if (!isFinite(attemptNumber) || attemptNumber === defaultValue) {
139141
attemptNumber = null;
140-
if (_.isFinite(defaultValue)) {
142+
if (isFinite(defaultValue)) {
141143
setAttemptDisplayValue(`${defaultValue} (Default)`);
142144
} else {
143145
setAttemptDisplayValue('');
@@ -154,7 +156,7 @@ export const scoringCardHooks = (scoring, updateSettings, defaultValue) => {
154156
let newMaxAttempt = parseInt(event.target.value, 10);
155157
if (newMaxAttempt === defaultValue) {
156158
newMaxAttempt = `${defaultValue} (Default)`;
157-
} else if (_.isNaN(newMaxAttempt)) {
159+
} else if (isNaN(newMaxAttempt)) {
158160
newMaxAttempt = '';
159161
} else if (newMaxAttempt < 0) {
160162
newMaxAttempt = 0;
@@ -164,7 +166,7 @@ export const scoringCardHooks = (scoring, updateSettings, defaultValue) => {
164166

165167
const handleWeightChange = (event) => {
166168
let weight = parseFloat(event.target.value);
167-
if (_.isNaN(weight) || weight < 0) {
169+
if (isNaN(weight) || weight < 0) {
168170
weight = 0;
169171
}
170172
updateSettings({ scoring: { ...scoring, weight } });
@@ -187,18 +189,18 @@ export const useAnswerSettings = (showAnswer, updateSettings) => {
187189
];
188190

189191
useEffect(() => {
190-
setShowAttempts(_.includes(numberOfAttemptsChoice, showAnswer.on));
192+
setShowAttempts(includes(numberOfAttemptsChoice, showAnswer.on));
191193
}, [showAttempts]);
192194

193195
const handleShowAnswerChange = (event) => {
194196
const { value } = event.target;
195-
setShowAttempts(_.includes(numberOfAttemptsChoice, value));
197+
setShowAttempts(includes(numberOfAttemptsChoice, value));
196198
updateSettings({ showAnswer: { ...showAnswer, on: value } });
197199
};
198200

199201
const handleAttemptsChange = (event) => {
200202
let attempts = parseInt(event.target.value, 10);
201-
if (_.isNaN(attempts) || attempts < 0) {
203+
if (isNaN(attempts) || attempts < 0) {
202204
attempts = 0;
203205
}
204206
updateSettings({ showAnswer: { ...showAnswer, afterAttempts: attempts } });
@@ -214,7 +216,7 @@ export const useAnswerSettings = (showAnswer, updateSettings) => {
214216
export const timerCardHooks = (updateSettings) => ({
215217
handleChange: (event) => {
216218
let time = parseInt(event.target.value, 10);
217-
if (_.isNaN(time) || time < 0) {
219+
if (isNaN(time) || time < 0) {
218220
time = 0;
219221
}
220222
updateSettings({ timeBetween: time });

src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState, useEffect } from 'react';
2-
import _ from 'lodash';
2+
import isEmpty from 'lodash/isEmpty';
33
import messages from './messages';
44
// This 'module' self-import hack enables mocking during tests.
55
// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested
@@ -19,7 +19,7 @@ export const generalFeedbackHooks = (generalFeedback, updateSettings) => {
1919

2020
// eslint-disable-next-line react-hooks/rules-of-hooks
2121
useEffect(() => {
22-
if (_.isEmpty(generalFeedback)) {
22+
if (isEmpty(generalFeedback)) {
2323
setSummary({ message: messages.noGeneralFeedbackSummary, values: {}, intl: true });
2424
} else {
2525
setSummary({

src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import _ from 'lodash';
2+
import isNil from 'lodash/isNil';
33
import PropTypes from 'prop-types';
44
import { connect } from 'react-redux';
55
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -76,7 +76,7 @@ const ScoringCard = ({
7676
className="mt-3 decoration-control-label"
7777
checked={scoring.attempts.unlimited}
7878
onChange={handleUnlimitedChange}
79-
disabled={!_.isNil(defaultValue)}
79+
disabled={!isNil(defaultValue)}
8080
>
8181
<div className="x-small">
8282
<FormattedMessage {...messages.unlimitedAttemptsCheckboxLabel} />

0 commit comments

Comments
 (0)