chore: replace Lodash usage with native JS implementation (#31907)

Signed-off-by: hainenber <dotronghai96@gmail.com>
This commit is contained in:
Đỗ Trọng Hải 2025-01-22 01:33:31 +07:00 committed by GitHub
parent eec374426f
commit f8fe780f52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 45 additions and 54 deletions

View File

@ -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,

View File

@ -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,
);
}

View File

@ -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;
}

View File

@ -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(

View File

@ -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

View File

@ -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(

View File

@ -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<D extends DataRecord = DataRecord>(
/* 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"

View File

@ -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) {

View File

@ -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<Props> = ({
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),

View File

@ -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: [

View File

@ -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({

View File

@ -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;
}

View File

@ -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 = ({
</Option>,
];
}
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];

View File

@ -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

View File

@ -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,

View File

@ -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<any> = {
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<any> = {
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<any> = {
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<any> = {
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<any> = {
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<any> = {
name: fileName,
uid: 'xp',

View File

@ -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);