fix: use lodash-es in the client (#41931)

This commit is contained in:
Oliver Eyton-Williams
2021-04-28 23:11:20 +02:00
committed by GitHub
parent db369fbed1
commit 9be2fb9a10
37 changed files with 95 additions and 43 deletions

View File

@ -43,10 +43,6 @@
"react-bootstrap": { "react-bootstrap": {
"transform": "react-bootstrap/lib/${member}", "transform": "react-bootstrap/lib/${member}",
"preventFullImport": true "preventFullImport": true
},
"lodash": {
"transform": "lodash/${member}",
"preventFullImport": true
} }
} }
], ],

View File

@ -2,6 +2,8 @@ const env = require('../config/env');
const webpack = require('webpack'); const webpack = require('webpack');
const { createFilePath } = require('gatsby-source-filesystem'); const { createFilePath } = require('gatsby-source-filesystem');
// TODO: ideally we'd remove lodash and just use lodash-es, but we can't require
// es modules here.
const uniq = require('lodash/uniq'); const uniq = require('lodash/uniq');
const { blockNameify } = require('../utils/block-nameify'); const { blockNameify } = require('../utils/block-nameify');
@ -231,10 +233,6 @@ exports.onCreateBabelConfig = ({ actions }) => {
'@freecodecamp/react-bootstrap': { '@freecodecamp/react-bootstrap': {
transform: '@freecodecamp/react-bootstrap/lib/${member}', transform: '@freecodecamp/react-bootstrap/lib/${member}',
preventFullImport: true preventFullImport: true
},
lodash: {
transform: 'lodash/${member}',
preventFullImport: true
} }
} }
}); });

View File

@ -6,7 +6,8 @@ module.exports = {
'^(?!.*\\.module\\.css$).*\\.css$': '<rootDir>/src/__mocks__/styleMock.js', '^(?!.*\\.module\\.css$).*\\.css$': '<rootDir>/src/__mocks__/styleMock.js',
// CSS Modules - match files that end with 'module.css' // CSS Modules - match files that end with 'module.css'
'\\.module\\.css$': 'identity-obj-proxy', '\\.module\\.css$': 'identity-obj-proxy',
'react-i18next': '<rootDir>/src/__mocks__/react-i18nextMock.js' 'react-i18next': '<rootDir>/src/__mocks__/react-i18nextMock.js',
'^lodash-es$': 'lodash'
}, },
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/.cache/'], testPathIgnorePatterns: ['/node_modules/', '<rootDir>/.cache/'],
globals: { globals: {

View File

@ -8,10 +8,6 @@ const babelOptions = {
'@freecodecamp/react-bootstrap': { '@freecodecamp/react-bootstrap': {
transform: '@freecodecamp/react-bootstrap/lib/${member}', transform: '@freecodecamp/react-bootstrap/lib/${member}',
preventFullImport: true preventFullImport: true
},
lodash: {
transform: 'lodash/${member}',
preventFullImport: true
} }
} }
] ]

View File

@ -5236,6 +5236,15 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ=="
}, },
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bl": { "bl": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -9765,6 +9774,12 @@
"typedarray-to-buffer": "^3.1.5" "typedarray-to-buffer": "^3.1.5"
} }
}, },
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"optional": true
},
"filesize": { "filesize": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
@ -17136,6 +17151,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}, },
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"lodash.camelcase": { "lodash.camelcase": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -18133,6 +18153,12 @@
"resolved": "https://registry.npmjs.org/name-all-modules-plugin/-/name-all-modules-plugin-1.0.1.tgz", "resolved": "https://registry.npmjs.org/name-all-modules-plugin/-/name-all-modules-plugin-1.0.1.tgz",
"integrity": "sha1-Cr+2rYNXGLn7Te8GdOBmV6lUN1w=" "integrity": "sha1-Cr+2rYNXGLn7Te8GdOBmV6lUN1w="
}, },
"nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"optional": true
},
"nanoid": { "nanoid": {
"version": "3.1.22", "version": "3.1.22",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
@ -25479,7 +25505,11 @@
"version": "1.2.13", "version": "1.2.13",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true "optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
}, },
"globby": { "globby": {
"version": "6.1.0", "version": "6.1.0",

View File

@ -81,6 +81,7 @@
"i18next": "19.9.2", "i18next": "19.9.2",
"jquery": "3.6.0", "jquery": "3.6.0",
"lodash": "4.17.21", "lodash": "4.17.21",
"lodash-es": "4.17.21",
"monaco-editor": "0.22.3", "monaco-editor": "0.22.3",
"nanoid": "3.1.22", "nanoid": "3.1.22",
"path-browserify": "1.0.1", "path-browserify": "1.0.1",

View File

@ -32,7 +32,7 @@ import envData from '../../../config/env.json';
import RedirectHome from '../components/RedirectHome'; import RedirectHome from '../components/RedirectHome';
import { Loader, Spacer } from '../components/helpers'; import { Loader, Spacer } from '../components/helpers';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import { User } from '../redux/propTypes'; import { User } from '../redux/propTypes';
const { clientLocale } = envData; const { clientLocale } = envData;

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import Loader from '../components/helpers/Loader'; import Loader from '../components/helpers/Loader';
import { import {

View File

@ -6,7 +6,7 @@ import { maybeUrlRE } from '../utils';
import { Spacer, Link } from '../components/helpers'; import { Spacer, Link } from '../components/helpers';
import { projectMap, legacyProjectMap } from '../resources/certAndProjectMap'; import { projectMap, legacyProjectMap } from '../resources/certAndProjectMap';
import ProjectModal from '../components/SolutionViewer/ProjectModal'; import ProjectModal from '../components/SolutionViewer/ProjectModal';
import { find, first } from 'lodash'; import { find, first } from 'lodash-es';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
const propTypes = { const propTypes = {

View File

@ -1,6 +1,6 @@
import chai from 'chai'; import chai from 'chai';
import '@babel/polyfill'; import '@babel/polyfill';
import __toString from 'lodash/toString'; import { toString as __toString } from 'lodash-es';
import { format as __format } from '../../utils/format'; import { format as __format } from '../../utils/format';
import curriculumHelpers from '../../utils/curriculum-helpers'; import curriculumHelpers from '../../utils/curriculum-helpers';

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { kebabCase } from 'lodash'; import { kebabCase } from 'lodash-es';
import normalizeUrl from 'normalize-url'; import normalizeUrl from 'normalize-url';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {

View File

@ -1,6 +1,6 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { curry } from 'lodash'; import { curry } from 'lodash-es';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Row, Col } from '@freecodecamp/react-bootstrap'; import { Row, Col } from '@freecodecamp/react-bootstrap';

View File

@ -1,6 +1,6 @@
import React, { Component, useMemo } from 'react'; import React, { Component, useMemo } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { reverse, sortBy } from 'lodash'; import { reverse, sortBy } from 'lodash-es';
import { import {
Button, Button,
Modal, Modal,

View File

@ -5,7 +5,7 @@ import { bindActionCreators } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { SearchBox } from 'react-instantsearch-dom'; import { SearchBox } from 'react-instantsearch-dom';
import { HotKeys, ObserveKeys } from 'react-hotkeys'; import { HotKeys, ObserveKeys } from 'react-hotkeys';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash-es';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { searchPageUrl } from '../../../utils/algolia-locale-setup'; import { searchPageUrl } from '../../../utils/algolia-locale-setup';

View File

@ -1,7 +1,7 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connectStateResults, connectHits } from 'react-instantsearch-dom'; import { connectStateResults, connectHits } from 'react-instantsearch-dom';
import isEmpty from 'lodash/isEmpty'; import { isEmpty } from 'lodash-es';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { searchPageUrl } from '../../../utils/algolia-locale-setup'; import { searchPageUrl } from '../../../utils/algolia-locale-setup';

View File

@ -5,7 +5,7 @@ import {
connectStateResults, connectStateResults,
connectAutoComplete connectAutoComplete
} from 'react-instantsearch-dom'; } from 'react-instantsearch-dom';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import EmptySearch from './EmptySearch'; import EmptySearch from './EmptySearch';
import NoResults from './NoResults'; import NoResults from './NoResults';

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { find, first, values, isString } from 'lodash'; import { find, first, values, isString } from 'lodash-es';
import { import {
Table, Table,
Button, Button,

View File

@ -8,7 +8,7 @@ import {
FormControl, FormControl,
HelpBlock HelpBlock
} from '@freecodecamp/react-bootstrap'; } from '@freecodecamp/react-bootstrap';
import { findIndex, find, isEqual } from 'lodash'; import { findIndex, find, isEqual } from 'lodash-es';
import isURL from 'validator/lib/isURL'; import isURL from 'validator/lib/isURL';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';

View File

@ -16,7 +16,7 @@ import {
} from '@freecodecamp/react-bootstrap'; } from '@freecodecamp/react-bootstrap';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import isEmail from 'validator/lib/isEmail'; import isEmail from 'validator/lib/isEmail';
import { isString } from 'lodash'; import { isString } from 'lodash-es';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { Spacer } from '../components/helpers'; import { Spacer } from '../components/helpers';

View File

@ -1,6 +1,6 @@
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import { takeEvery, put } from 'redux-saga/effects'; import { takeEvery, put } from 'redux-saga/effects';
import { isError } from 'lodash'; import { isError } from 'lodash-es';
import { isHandledError, unwrapHandledError } from '../utils/handled-error'; import { isHandledError, unwrapHandledError } from '../utils/handled-error';
import { reportClientSideError } from '../utils/report-error'; import { reportClientSideError } from '../utils/report-error';

View File

@ -1,5 +1,5 @@
import { createAction, handleActions } from 'redux-actions'; import { createAction, handleActions } from 'redux-actions';
import { uniqBy } from 'lodash'; import { uniqBy } from 'lodash-es';
import store from 'store'; import store from 'store';
import { createTypes, createAsyncTypes } from '../utils/createTypes'; import { createTypes, createAsyncTypes } from '../utils/createTypes';

View File

@ -1,7 +1,7 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex'; import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { first } from 'lodash'; import { first } from 'lodash-es';
import EditorTabs from './EditorTabs'; import EditorTabs from './EditorTabs';
import ActionRow from './ActionRow'; import ActionRow from './ActionRow';
import envData from '../../../../../config/env.json'; import envData from '../../../../../config/env.json';

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import noop from 'lodash/noop'; import { noop } from 'lodash-es';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Button, Modal } from '@freecodecamp/react-bootstrap'; import { Button, Modal } from '@freecodecamp/react-bootstrap';

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import sanitizeHtml from 'sanitize-html'; import sanitizeHtml from 'sanitize-html';
import './output.css'; import './output.css';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
const propTypes = { const propTypes = {
defaultOutput: PropTypes.string, defaultOutput: PropTypes.string,

View File

@ -6,7 +6,7 @@ import {
partial, partial,
stubTrue, stubTrue,
template as _template template as _template
} from 'lodash'; } from 'lodash-es';
import { import {
compileHeadTail, compileHeadTail,

View File

@ -1,4 +1,4 @@
import { cond, identity, stubTrue, conforms } from 'lodash'; import { cond, identity, stubTrue, conforms } from 'lodash-es';
const HTML$JSReg = /html|js/; const HTML$JSReg = /html|js/;

View File

@ -8,7 +8,7 @@ import {
overSome, overSome,
partial, partial,
stubTrue stubTrue
} from 'lodash'; } from 'lodash-es';
import protect from '@freecodecamp/loop-protect'; import protect from '@freecodecamp/loop-protect';

View File

@ -11,7 +11,7 @@ import {
cancel cancel
} from 'redux-saga/effects'; } from 'redux-saga/effects';
import { channel } from 'redux-saga'; import { channel } from 'redux-saga';
import escape from 'lodash/escape'; import { escape } from 'lodash-es';
import i18next from 'i18next'; import i18next from 'i18next';
import { import {

View File

@ -14,7 +14,7 @@ import { createCurrentChallengeSaga } from './current-challenge-saga';
import { challengeTypes } from '../../../../utils/challengeTypes'; import { challengeTypes } from '../../../../utils/challengeTypes';
import { getTargetEditor } from '../utils/getTargetEditor'; import { getTargetEditor } from '../utils/getTargetEditor';
import { completedChallengesSelector } from '../../../redux'; import { completedChallengesSelector } from '../../../redux';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
export const ns = 'challenge'; export const ns = 'challenge';
export const backendNS = 'backendChallenge'; export const backendNS = 'backendChallenge';

View File

@ -1,4 +1,4 @@
import { toString, flow } from 'lodash'; import { toString, flow } from 'lodash-es';
import { format } from '../../../utils/format'; import { format } from '../../../utils/format';
// we use two different frames to make them all essentially pure functions // we use two different frames to make them all essentially pure functions

View File

@ -1,5 +1,5 @@
import { toSortedArray } from '../../../../../utils/sort-files'; import { toSortedArray } from '../../../../../utils/sort-files';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
export function getTargetEditor(challengeFiles) { export function getTargetEditor(challengeFiles) {
if (isEmpty(challengeFiles)) return null; if (isEmpty(challengeFiles)) return null;

View File

@ -2,7 +2,7 @@ import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import { uniq } from 'lodash'; import { uniq } from 'lodash-es';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';

View File

@ -1,4 +1,4 @@
import { has } from 'lodash'; import { has } from 'lodash-es';
import standardErrorMessage from './standardErrorMessage'; import standardErrorMessage from './standardErrorMessage';
import reportedErrorMessage from './reportedErrorMessage'; import reportedErrorMessage from './reportedErrorMessage';

View File

@ -1,5 +1,5 @@
/* global expect */ /* global expect */
import { isObject } from 'lodash'; import { isObject } from 'lodash-es';
import sinon from 'sinon'; import sinon from 'sinon';
import { import {
isHandledError, isHandledError,

View File

@ -5905,6 +5905,27 @@
} }
} }
}, },
"mock-require": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
"integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
"dev": true,
"requires": {
"get-caller-file": "^1.0.2",
"normalize-path": "^2.1.1"
},
"dependencies": {
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
}
}
},
"morgan": { "morgan": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",

View File

@ -55,6 +55,7 @@
"live-server": "1.2.1", "live-server": "1.2.1",
"lodash": "4.17.21", "lodash": "4.17.21",
"mocha": "8.3.2", "mocha": "8.3.2",
"mock-require": "3.0.3",
"puppeteer": "8.0.0", "puppeteer": "8.0.0",
"readdirp": "3.6.0", "readdirp": "3.6.0",
"rehype": "11.0.0", "rehype": "11.0.0",

View File

@ -17,6 +17,13 @@ require('@babel/register')({
only: [clientPath] only: [clientPath]
}); });
const mockRequire = require('mock-require');
const lodash = require('lodash');
// lodash-es can't easily be used in node environments, so we just mock it out
// for the original lodash in testing.
mockRequire('lodash-es', lodash);
const createPseudoWorker = require('./utils/pseudo-worker'); const createPseudoWorker = require('./utils/pseudo-worker');
const { const {
default: createWorker default: createWorker
@ -24,7 +31,8 @@ const {
const { assert, AssertionError } = require('chai'); const { assert, AssertionError } = require('chai');
const Mocha = require('mocha'); const Mocha = require('mocha');
const { flatten, isEmpty, cloneDeep, isEqual } = require('lodash');
const { flatten, isEmpty, cloneDeep, isEqual } = lodash;
const { getLines } = require('../../utils/get-lines'); const { getLines } = require('../../utils/get-lines');
const jsdom = require('jsdom'); const jsdom = require('jsdom');