From f8fe780f52604dd83aa6cdf2e6c9cec01dc09852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Wed, 22 Jan 2025 01:33:31 +0700 Subject: [PATCH] chore: replace Lodash usage with native JS implementation (#31907) Signed-off-by: hainenber --- .../src/operators/utils/timeOffset.ts | 5 +++-- .../src/components/SafeMarkdown.tsx | 4 ++-- .../src/query/normalizeOrderBy.ts | 4 ++-- .../legacy-preset-chart-deckgl/src/utils.ts | 3 +-- .../plugin-chart-echarts/src/utils/forecast.ts | 5 ++--- .../src/utils/treeBuilder.ts | 4 ++-- .../plugin-chart-table/src/TableChart.tsx | 6 ++++-- .../components/SaveDatasetModal/index.tsx | 4 ++-- .../src/SqlLab/components/SqlEditor/index.tsx | 9 +++++---- .../Chart/DrillBy/DrillByModal.test.tsx | 4 ++-- .../components/DatasourcePanel/index.tsx | 3 +-- .../components/controls/BoundsControl.tsx | 4 ++-- .../controls/ColorSchemeControl/index.tsx | 8 ++++---- .../controls/DatasourceControl/index.jsx | 3 +-- .../components/CustomFrame.tsx | 3 +-- .../UploadDataModel/UploadDataModal.test.tsx | 13 ++++++------- superset-frontend/src/reduxUtils.ts | 17 +++++------------ 17 files changed, 45 insertions(+), 54 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/timeOffset.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/timeOffset.ts index 8a7d9a964..852b4eb1a 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/timeOffset.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/timeOffset.ts @@ -18,7 +18,6 @@ * under the License. */ import { JsonObject } from '@superset-ui/core'; -import { isString } from 'lodash'; export const getTimeOffset = ( series: JsonObject, @@ -36,7 +35,9 @@ export const hasTimeOffset = ( series: JsonObject, timeCompare: string[], ): boolean => - isString(series.name) ? !!getTimeOffset(series, timeCompare) : false; + typeof series.name === 'string' + ? !!getTimeOffset(series, timeCompare) + : false; export const getOriginalSeries = ( seriesName: string, diff --git a/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx b/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx index 02db7dadb..d35c2160e 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx @@ -19,7 +19,7 @@ import { useEffect, useMemo, useState } from 'react'; import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; import remarkGfm from 'remark-gfm'; -import { mergeWith, isArray } from 'lodash'; +import { mergeWith } from 'lodash'; import { FeatureFlag, isFeatureEnabled } from '../utils'; interface SafeMarkdownProps { @@ -33,7 +33,7 @@ export function getOverrideHtmlSchema( htmlSchemaOverrides: SafeMarkdownProps['htmlSchemaOverrides'], ) { return mergeWith(originalSchema, htmlSchemaOverrides, (objValue, srcValue) => - isArray(objValue) ? objValue.concat(srcValue) : undefined, + Array.isArray(objValue) ? objValue.concat(srcValue) : undefined, ); } diff --git a/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts b/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts index b07338781..840cf4c1a 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { isEmpty, isBoolean } from 'lodash'; +import { isEmpty } from 'lodash'; import { QueryObject } from './types'; @@ -30,7 +30,7 @@ export default function normalizeOrderBy( Array.isArray(orderbyClause) && orderbyClause.length === 2 && !isEmpty(orderbyClause[0]) && - isBoolean(orderbyClause[1]) + typeof orderbyClause[1] === 'boolean' ) { return queryObject; } diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts index aac2a739e..55d05fe5b 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts @@ -25,7 +25,6 @@ import { QueryFormData, SequentialScheme, } from '@superset-ui/core'; -import { isNumber } from 'lodash'; import { hexToRGB } from './utils/colors'; const DEFAULT_NUM_BUCKETS = 10; @@ -140,7 +139,7 @@ export function getBreakPointColorScaler( } else { // interpolate colors linearly const linearScaleDomain = extent(features, accessor); - if (!linearScaleDomain.some(isNumber)) { + if (!linearScaleDomain.some(i => typeof i === 'number')) { scaler = colorScheme.createLinearScale(); } else { scaler = colorScheme.createLinearScale( diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts index c7244baf4..1f9239011 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { isNumber } from 'lodash'; import { DataRecord, DTTM_ALIAS, ValueFormatter } from '@superset-ui/core'; import type { OptionName } from 'echarts/types/src/util/types'; import type { TooltipMarker } from 'echarts/types/src/util/format'; @@ -64,7 +63,7 @@ export const extractForecastValuesFromTooltipParams = ( const { marker, seriesId, value } = param; const context = extractForecastSeriesContext(seriesId); const numericValue = isHorizontal ? value[0] : value[1]; - if (isNumber(numericValue)) { + if (typeof numericValue === 'number') { if (!(context.name in values)) values[context.name] = { marker: marker || '', @@ -97,7 +96,7 @@ export const formatForecastTooltipSeries = ({ formatter: ValueFormatter; }): string[] => { const name = `${marker}${sanitizeHtml(seriesName)}`; - let value = isNumber(observation) ? formatter(observation) : ''; + let value = typeof observation === 'number' ? formatter(observation) : ''; if (forecastTrend || forecastLower || forecastUpper) { // forecast values take the form of "20, y = 30 (10, 40)" // where the first part is the observation, the second part is the forecast trend diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts index cda78da93..919d6e6db 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/treeBuilder.ts @@ -17,7 +17,7 @@ * under the License. */ import { DataRecord, DataRecordValue } from '@superset-ui/core'; -import { groupBy as _groupBy, isNumber, transform } from 'lodash'; +import { groupBy as _groupBy, transform } from 'lodash'; export type TreeNode = { name: DataRecordValue; @@ -28,7 +28,7 @@ export type TreeNode = { }; function getMetricValue(datum: DataRecord, metric: string) { - return isNumber(datum[metric]) ? (datum[metric] as number) : 0; + return typeof datum[metric] === 'number' ? (datum[metric] as number) : 0; } export function treeBuilder( diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx index b9cc335a3..77905ea56 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx @@ -61,7 +61,7 @@ import { PlusCircleOutlined, TableOutlined, } from '@ant-design/icons'; -import { isEmpty, isNumber } from 'lodash'; +import { isEmpty } from 'lodash'; import { ColorSchemeEnum, DataColumnMeta, @@ -899,7 +899,9 @@ export default function TableChart( /* The following classes are added to support custom CSS styling */ className={cx( 'cell-bar', - isNumber(value) && value < 0 ? 'negative' : 'positive', + typeof value === 'number' && value < 0 + ? 'negative' + : 'positive', )} css={cellBarStyles} role="presentation" diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 98dcd3486..5df4d0c30 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -50,7 +50,7 @@ import { mountExploreUrl } from 'src/explore/exploreUtils'; import { postFormData } from 'src/explore/exploreUtils/formData'; import { URL_PARAMS } from 'src/constants'; import { SelectValue } from 'antd/lib/select'; -import { isEmpty, isString } from 'lodash'; +import { isEmpty } from 'lodash'; interface QueryDatabase { id?: number; @@ -280,7 +280,7 @@ export const SaveDatasetModal = ({ // Remove the special filters entry from the templateParams // before saving the dataset. let templateParams; - if (isString(datasource?.templateParams)) { + if (typeof datasource?.templateParams === 'string') { const p = JSON.parse(datasource.templateParams); /* eslint-disable-next-line no-underscore-dangle */ if (p._filters) { diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx index cab82cd02..4d1aded5b 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx @@ -50,7 +50,7 @@ import type { CursorPosition, } from 'src/SqlLab/types'; import type { DatabaseObject } from 'src/features/databases/types'; -import { debounce, throttle, isBoolean, isEmpty } from 'lodash'; +import { debounce, throttle, isEmpty } from 'lodash'; import Modal from 'src/components/Modal'; import Mousetrap from 'mousetrap'; import Button from 'src/components/Button'; @@ -281,9 +281,10 @@ const SqlEditor: FC = ({ if (unsavedQueryEditor?.id === queryEditor.id) { dbId = unsavedQueryEditor.dbId || dbId; latestQueryId = unsavedQueryEditor.latestQueryId || latestQueryId; - hideLeftBar = isBoolean(unsavedQueryEditor.hideLeftBar) - ? unsavedQueryEditor.hideLeftBar - : hideLeftBar; + hideLeftBar = + typeof unsavedQueryEditor.hideLeftBar === 'boolean' + ? unsavedQueryEditor.hideLeftBar + : hideLeftBar; } return { hasSqlStatement: Boolean(queryEditor.sql?.trim().length > 0), diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillByModal.test.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillByModal.test.tsx index 2299f091b..a57dd675e 100644 --- a/superset-frontend/src/components/Chart/DrillBy/DrillByModal.test.tsx +++ b/superset-frontend/src/components/Chart/DrillBy/DrillByModal.test.tsx @@ -19,7 +19,7 @@ import { useState } from 'react'; import fetchMock from 'fetch-mock'; -import { omit, isUndefined, omitBy } from 'lodash'; +import { omit, omitBy } from 'lodash'; import userEvent from '@testing-library/user-event'; import { waitFor, within } from '@testing-library/react'; import { render, screen } from 'spec/helpers/testing-library'; @@ -166,7 +166,7 @@ test('should generate Explore url', async () => { form_data: { ...omitBy( omit(formData, ['slice_id', 'slice_name', 'dashboards']), - isUndefined, + i => i === undefined, ), groupby: ['name'], adhoc_filters: [ diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index d15df0a2f..2d2c0783d 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -30,7 +30,6 @@ import { ControlConfig } from '@superset-ui/chart-controls'; import AutoSizer from 'react-virtualized-auto-sizer'; import { FixedSizeList as List } from 'react-window'; -import { isArray } from 'lodash'; import { matchSorter, rankings } from 'match-sorter'; import Alert from 'src/components/Alert'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; @@ -142,7 +141,7 @@ export default function DataSourcePanel({ const allowedColumns = useMemo(() => { const validators = Object.values(dropzones); - if (!isArray(_columns)) return []; + if (!Array.isArray(_columns)) return []; return _columns.filter(column => validators.some(validator => validator({ diff --git a/superset-frontend/src/explore/components/controls/BoundsControl.tsx b/superset-frontend/src/explore/components/controls/BoundsControl.tsx index 10b355d17..87914d04b 100644 --- a/superset-frontend/src/explore/components/controls/BoundsControl.tsx +++ b/superset-frontend/src/explore/components/controls/BoundsControl.tsx @@ -19,7 +19,7 @@ import { useEffect, useRef, useState } from 'react'; import { InputNumber } from 'src/components/Input'; import { t, styled } from '@superset-ui/core'; -import { debounce, parseInt } from 'lodash'; +import { debounce } from 'lodash'; import ControlHeader from 'src/explore/components/ControlHeader'; type ValueType = (number | null)[]; @@ -47,7 +47,7 @@ const parseNumber = (value: undefined | number | string | null) => { if ( value === null || value === undefined || - (typeof value === 'string' && Number.isNaN(parseInt(value))) + (typeof value === 'string' && Number.isNaN(Number.parseInt(value, 10))) ) { return null; } diff --git a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx index fd9dc85f0..b479c4f3d 100644 --- a/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx +++ b/superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx @@ -30,7 +30,7 @@ import { CategoricalColorNamespace, } from '@superset-ui/core'; import AntdSelect from 'antd/lib/select'; -import { isFunction, sortBy } from 'lodash'; +import { sortBy } from 'lodash'; import ControlHeader from 'src/explore/components/ControlHeader'; import { Tooltip } from 'src/components/Tooltip'; import Icons from 'src/components/Icons'; @@ -163,7 +163,7 @@ const ColorSchemeControl = ({ } let result = value || defaultScheme; if (result === 'SUPERSET_DEFAULT') { - const schemesObject = isFunction(schemes) ? schemes() : schemes; + const schemesObject = typeof schemes === 'function' ? schemes() : schemes; result = schemesObject?.SUPERSET_DEFAULT?.id; } return result; @@ -179,8 +179,8 @@ const ColorSchemeControl = ({ , ]; } - const schemesObject = isFunction(schemes) ? schemes() : schemes; - const controlChoices = isFunction(choices) ? choices() : choices; + const schemesObject = typeof schemes === 'function' ? schemes() : schemes; + const controlChoices = typeof choices === 'function' ? choices() : choices; const allColorOptions: string[] = []; const filteredColorOptions = controlChoices.filter(o => { const option = o[0]; diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx index d4ed74a01..cc8474b6d 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx @@ -51,7 +51,6 @@ import ViewQueryModalFooter from 'src/explore/components/controls/ViewQueryModal import ViewQuery from 'src/explore/components/controls/ViewQuery'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { safeStringify } from 'src/utils/safeStringify'; -import { isString } from 'lodash'; import { Link } from 'react-router-dom'; const propTypes = { @@ -383,7 +382,7 @@ class DatasourceControl extends PureComponent { let extra; if (datasource?.extra) { - if (isString(datasource.extra)) { + if (typeof datasource.extra === 'string') { try { extra = JSON.parse(datasource.extra); } catch {} // eslint-disable-line no-empty diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx b/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx index ec558c090..378956c64 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/components/CustomFrame.tsx @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { isInteger } from 'lodash'; import { t, customTimeRangeDecode } from '@superset-ui/core'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; import { Col, Row } from 'src/components'; @@ -76,7 +75,7 @@ export function CustomFrame(props: FrameComponentProps) { value: string | number, ) { // only positive values in grainValue controls - if (isInteger(value) && value > 0) { + if (typeof value === 'number' && Number.isInteger(value) && value > 0) { props.onChange( customTimeRangeEncode({ ...customRange, diff --git a/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx b/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx index 54b817654..7347e13bd 100644 --- a/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx +++ b/superset-frontend/src/features/databases/UploadDataModel/UploadDataModal.test.tsx @@ -24,7 +24,6 @@ import { render, screen } from 'spec/helpers/testing-library'; import userEvent from '@testing-library/user-event'; import { waitFor } from '@testing-library/react'; import { UploadFile } from 'antd/lib/upload/interface'; -import { forEach } from 'lodash'; fetchMock.post('glob:*api/v1/database/1/csv_upload/', {}); fetchMock.post('glob:*api/v1/database/1/excel_upload/', {}); @@ -782,7 +781,7 @@ test('Columnar, form post', async () => { test('CSV, validate file extension returns false', () => { const invalidFileNames = ['out', 'out.exe', 'out.csv.exe', '.csv', 'out.xls']; - forEach(invalidFileNames, fileName => { + invalidFileNames.forEach(fileName => { const file: UploadFile = { name: fileName, uid: 'xp', @@ -795,7 +794,7 @@ test('CSV, validate file extension returns false', () => { test('Excel, validate file extension returns false', () => { const invalidFileNames = ['out', 'out.exe', 'out.xls.exe', '.csv', 'out.csv']; - forEach(invalidFileNames, fileName => { + invalidFileNames.forEach(fileName => { const file: UploadFile = { name: fileName, uid: 'xp', @@ -814,7 +813,7 @@ test('Columnar, validate file extension returns false', () => { '.parquet', 'out.excel', ]; - forEach(invalidFileNames, fileName => { + invalidFileNames.forEach(fileName => { const file: UploadFile = { name: fileName, uid: 'xp', @@ -827,7 +826,7 @@ test('Columnar, validate file extension returns false', () => { test('CSV, validate file extension returns true', () => { const invalidFileNames = ['out.csv', 'out.tsv', 'out.exe.csv', 'out a.csv']; - forEach(invalidFileNames, fileName => { + invalidFileNames.forEach(fileName => { const file: UploadFile = { name: fileName, uid: 'xp', @@ -840,7 +839,7 @@ test('CSV, validate file extension returns true', () => { test('Excel, validate file extension returns true', () => { const invalidFileNames = ['out.xls', 'out.xlsx', 'out.exe.xls', 'out a.xls']; - forEach(invalidFileNames, fileName => { + invalidFileNames.forEach(fileName => { const file: UploadFile = { name: fileName, uid: 'xp', @@ -858,7 +857,7 @@ test('Columnar, validate file extension returns true', () => { 'out.exe.zip', 'out a.parquet', ]; - forEach(invalidFileNames, fileName => { + invalidFileNames.forEach(fileName => { const file: UploadFile = { name: fileName, uid: 'xp', diff --git a/superset-frontend/src/reduxUtils.ts b/superset-frontend/src/reduxUtils.ts index 20153cb0d..ea87285cb 100644 --- a/superset-frontend/src/reduxUtils.ts +++ b/superset-frontend/src/reduxUtils.ts @@ -19,14 +19,7 @@ import { nanoid } from 'nanoid'; import { compose } from 'redux'; import persistState, { StorageAdapter } from 'redux-localstorage'; -import { - isEqual, - omitBy, - omit, - isUndefined, - isNull, - isEqualWith, -} from 'lodash'; +import { isEqual, omitBy, omit, isEqualWith } from 'lodash'; import { ensureIsArray } from '@superset-ui/core'; export function addToObject( @@ -195,12 +188,12 @@ export function areObjectsEqual( let comp1 = obj1; let comp2 = obj2; if (opts.ignoreUndefined) { - comp1 = omitBy(comp1, isUndefined); - comp2 = omitBy(comp2, isUndefined); + comp1 = omitBy(comp1, i => i === undefined); + comp2 = omitBy(comp2, i => i === undefined); } if (opts.ignoreNull) { - comp1 = omitBy(comp1, isNull); - comp2 = omitBy(comp2, isNull); + comp1 = omitBy(comp1, i => i === null); + comp2 = omitBy(comp2, i => i === null); } if (opts.ignoreFields?.length) { const ignoreFields = ensureIsArray(opts.ignoreFields);