This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch chore/enable-additional-oxlint-rules
in repository https://gitbox.apache.org/repos/asf/superset.git

commit c9929fcc130199ec2fd43942ef3b86546d830cf4
Author: Evan Rusackas <[email protected]>
AuthorDate: Fri Feb 20 17:01:38 2026 -0800

    chore(frontend): enable additional oxlint rules for better code hygiene
    
    Enable 17 new oxlint rules that enforce best practices:
    
    **Zero-violation rules (enabled as errors):**
    - eslint/no-useless-constructor - Remove empty constructors
    - eslint/no-else-return - Unnecessary else after return
    - eslint/no-array-constructor - Use [] instead of new Array()
    - eslint/no-new-wrappers - Don't use new String/Number/Boolean()
    - eslint/no-regex-spaces - Use {n} instead of multiple spaces
    - eslint/no-object-constructor - Use {} instead of new Object()
    - unicorn/no-length-as-slice-end - Don't pass .length to .slice() end
    - unicorn/no-useless-spread - Remove unnecessary spreads
    - unicorn/no-thenable - Don't export .then on non-Promises
    - unicorn/escape-case - Consistent escape character casing
    
    **Small fix rules (auto-fixed violations):**
    - unicorn/prefer-array-flat-map - Use .flatMap() instead of .map().flat()
    - unicorn/prefer-array-some - Use .some() instead of .findIndex() !== -1
    - unicorn/throw-new-error - Use throw new Error() not throw Error()
    - unicorn/prefer-negative-index - Use .at(-1) for negative indices
    - unicorn/prefer-math-trunc - Use Math.trunc() instead of | 0
    
    Also adds the oxc plugin to enable additional rule coverage (193 -> 205 
rules).
    
    Co-Authored-By: Claude Opus 4.5 <[email protected]>
---
 superset-frontend/oxlint.json                      |  25 ++-
 .../src/shared-controls/mixins.tsx                 |   4 +-
 .../src/shared-controls/sharedControls.tsx         |  14 +-
 .../src/components/MetadataBar/ContentConfig.tsx   |   2 +-
 .../src/components/MetadataBar/MetadataBar.tsx     |   4 +-
 .../src/TTestTable.tsx                             |   2 +-
 .../src/controlPanel.tsx                           |  48 +++---
 .../src/transformProps.ts                          | 172 ++++++++++-----------
 .../src/components/Handlebars/HandlebarsViewer.tsx |   2 +-
 .../plugin-chart-table/src/DataTable/DataTable.tsx |   1 +
 .../plugin-chart-table/src/controlPanel.tsx        |  48 +++---
 .../plugin-chart-table/src/transformProps.ts       | 164 ++++++++++----------
 superset-frontend/scripts/check-custom-rules.js    |   6 +-
 .../src/SqlLab/reducers/getInitialState.ts         |   2 +-
 .../src/components/DatabaseSelector/index.tsx      |   4 +-
 .../src/components/GridTable/Header.tsx            |   5 +-
 superset-frontend/src/components/ListView/utils.ts |   5 +-
 .../src/components/MessageToasts/reducers.ts       |   2 +-
 .../RefreshFrequency/RefreshFrequencySelect.tsx    |   4 +-
 .../components/nativeFilters/FilterBar/state.ts    |   2 +-
 .../FiltersConfigForm/FiltersConfigForm.tsx        |   2 +-
 .../src/dashboard/util/getOverwriteItems.ts        |   2 +-
 .../src/dashboard/util/isValidChild.test.ts        |   2 +-
 .../AnnotationLayerControl/AnnotationLayer.tsx     |   2 +-
 .../controls/SelectAsyncControl/index.tsx          |   2 +-
 .../explore/components/controls/SelectControl.tsx  |   2 +-
 superset-frontend/src/views/CRUD/utils.tsx         |  25 ++-
 27 files changed, 281 insertions(+), 272 deletions(-)

diff --git a/superset-frontend/oxlint.json b/superset-frontend/oxlint.json
index 942965a865..6fa1bbf125 100644
--- a/superset-frontend/oxlint.json
+++ b/superset-frontend/oxlint.json
@@ -1,6 +1,14 @@
 {
   "$schema": "./node_modules/oxlint/configuration_schema.json",
-  "plugins": ["import", "react", "jest", "jsx-a11y", "typescript", "unicorn"],
+  "plugins": [
+    "import",
+    "react",
+    "jest",
+    "jsx-a11y",
+    "typescript",
+    "unicorn",
+    "oxc"
+  ],
   "env": {
     "browser": true,
     "node": true,
@@ -73,6 +81,12 @@
       "as-needed",
       { "requireReturnForObjectLiteral": false }
     ],
+    "no-useless-constructor": "error",
+    "no-else-return": "error",
+    "no-array-constructor": "error",
+    "no-new-wrappers": "error",
+    "no-regex-spaces": "error",
+    "no-object-constructor": "error",
 
     // === Import plugin rules ===
     "import/no-unresolved": "error",
@@ -256,6 +270,15 @@
     "unicorn/no-new-array": "error",
     "unicorn/no-invalid-remove-event-listener": "error",
     "unicorn/no-useless-length-check": "error",
+    "unicorn/no-length-as-slice-end": "error",
+    "unicorn/no-useless-spread": "error",
+    "unicorn/no-thenable": "error",
+    "unicorn/escape-case": "error",
+    "unicorn/prefer-array-flat-map": "error",
+    "unicorn/prefer-array-some": "error",
+    "unicorn/throw-new-error": "error",
+    "unicorn/prefer-negative-index": "error",
+    "unicorn/prefer-math-trunc": "error",
     "unicorn/filename-case": "off",
     "unicorn/prevent-abbreviations": "off",
     "unicorn/no-null": "off",
diff --git 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/mixins.tsx
 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/mixins.tsx
index 19310b83c8..6587723cc7 100644
--- 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/mixins.tsx
+++ 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/mixins.tsx
@@ -80,9 +80,9 @@ export const datePickerInAdhocFilterMixin: Pick<
     // 2) there was a time filter in adhoc filters
     if (
       state?.controls?.time_range?.value ||
-      ensureIsArray(control.value).findIndex(
+      ensureIsArray(control.value).some(
         (flt: any) => flt?.operator === 'TEMPORAL_RANGE',
-      ) > -1
+      )
     ) {
       return undefined;
     }
diff --git 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
index 0b9953b95a..bf087565ab 100644
--- 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
+++ 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
@@ -443,14 +443,12 @@ const order_by_cols: SharedControlConfig<'SelectControl'> 
= {
   default: [],
   shouldMapStateToProps: () => true,
   mapStateToProps: ({ datasource }) => ({
-    choices: (datasource?.columns || [])
-      .map(col =>
-        [true, false].map(asc => [
-          JSON.stringify([col.column_name, asc]),
-          `${getColumnLabel(col.column_name)} [${asc ? 'asc' : 'desc'}]`,
-        ]),
-      )
-      .flat(),
+    choices: (datasource?.columns || []).flatMap(col =>
+      [true, false].map(asc => [
+        JSON.stringify([col.column_name, asc]),
+        `${getColumnLabel(col.column_name)} [${asc ? 'asc' : 'desc'}]`,
+      ]),
+    ),
   }),
   resetOnHide: false,
 };
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/ContentConfig.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/ContentConfig.tsx
index a4a9f14f40..1291538200 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/ContentConfig.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/ContentConfig.tsx
@@ -132,7 +132,7 @@ const config = (contentType: ContentType) => {
       };
 
     default:
-      throw Error(`Invalid type provided: ${type}`);
+      throw new Error(`Invalid type provided: ${type}`);
   }
 };
 
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/MetadataBar.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/MetadataBar.tsx
index 3f0f0e3a07..ccfca5028c 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/MetadataBar.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/MetadataBar/MetadataBar.tsx
@@ -187,10 +187,10 @@ const MetadataBar = ({ items, tooltipPlacement = 'top' }: 
MetadataBarProps) => {
   const sortedItems = uniqueItems.sort((a, b) => ORDER[a.type] - 
ORDER[b.type]);
   const count = sortedItems.length;
   if (count < MIN_NUMBER_ITEMS) {
-    throw Error('The minimum number of items for the metadata bar is 2.');
+    throw new Error('The minimum number of items for the metadata bar is 2.');
   }
   if (count > MAX_NUMBER_ITEMS) {
-    throw Error('The maximum number of items for the metadata bar is 6.');
+    throw new Error('The maximum number of items for the metadata bar is 6.');
   }
 
   const onResize = useCallback(
diff --git 
a/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/TTestTable.tsx
 
b/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/TTestTable.tsx
index abc2a2ccff..95922fd32d 100644
--- 
a/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/TTestTable.tsx
+++ 
b/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/TTestTable.tsx
@@ -181,7 +181,7 @@ class TTestTable extends Component<TTestTableProps, 
TTestTableState> {
     const { control, liftValues, pValues } = this.state;
 
     if (!Array.isArray(groups) || groups.length === 0) {
-      throw Error('Group by param is required');
+      throw new Error('Group by param is required');
     }
 
     // Render column header for each group
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/controlPanel.tsx 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/controlPanel.tsx
index a84a466e20..993bdf56f8 100644
--- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/controlPanel.tsx
@@ -87,31 +87,29 @@ function getQueryMode(controls: ControlStateMapping): 
QueryMode {
 }
 
 const processComparisonColumns = (columns: any[], suffix: string) =>
-  columns
-    .map(col => {
-      if (!col.label.includes(suffix)) {
-        return [
-          {
-            label: `${t('Main')} ${col.label}`,
-            value: `${t('Main')} ${col.value}`,
-          },
-          {
-            label: `# ${col.label}`,
-            value: `# ${col.value}`,
-          },
-          {
-            label: `△ ${col.label}`,
-            value: `△ ${col.value}`,
-          },
-          {
-            label: `% ${col.label}`,
-            value: `% ${col.value}`,
-          },
-        ];
-      }
-      return [];
-    })
-    .flat();
+  columns.flatMap(col => {
+    if (!col.label.includes(suffix)) {
+      return [
+        {
+          label: `${t('Main')} ${col.label}`,
+          value: `${t('Main')} ${col.value}`,
+        },
+        {
+          label: `# ${col.label}`,
+          value: `# ${col.value}`,
+        },
+        {
+          label: `△ ${col.label}`,
+          value: `△ ${col.value}`,
+        },
+        {
+          label: `% ${col.label}`,
+          value: `% ${col.value}`,
+        },
+      ];
+    }
+    return [];
+  });
 
 /**
  * Visibility check
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts
index d793f2c27a..c22b6ff46c 100644
--- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts
@@ -216,93 +216,91 @@ const processComparisonColumns = (
   props: TableChartProps,
   comparisonSuffix: string,
 ) =>
-  columns
-    .map(col => {
-      const {
-        datasource: { columnFormats, currencyFormats },
-        rawFormData: { column_config: columnConfig = {} },
-      } = props;
-      const savedFormat = columnFormats?.[col.key];
-      const savedCurrency = currencyFormats?.[col.key];
-      const originalLabel = col.label;
-      if (
-        (col.isMetric || col.isPercentMetric) &&
-        !col.key.includes(comparisonSuffix) &&
-        col.isNumeric
-      ) {
-        return [
-          {
-            ...col,
-            originalLabel,
-            metricName: col.key,
-            label: t('Main'),
-            key: `${t('Main')} ${col.key}`,
-            config: getComparisonColConfig(t('Main'), col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              t('Main'),
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-          {
-            ...col,
-            originalLabel,
-            metricName: col.key,
-            label: `#`,
-            key: `# ${col.key}`,
-            config: getComparisonColConfig(`#`, col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              `#`,
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-          {
-            ...col,
-            originalLabel,
-            metricName: col.key,
-            label: `△`,
-            key: `△ ${col.key}`,
-            config: getComparisonColConfig(`△`, col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              `△`,
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-          {
-            ...col,
-            originalLabel,
-            metricName: col.key,
-            label: `%`,
-            key: `% ${col.key}`,
-            config: getComparisonColConfig(`%`, col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              `%`,
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-        ];
-      }
-      if (
-        !col.isMetric &&
-        !col.isPercentMetric &&
-        !col.key.includes(comparisonSuffix)
-      ) {
-        return [col];
-      }
-      return [];
-    })
-    .flat();
+  columns.flatMap(col => {
+    const {
+      datasource: { columnFormats, currencyFormats },
+      rawFormData: { column_config: columnConfig = {} },
+    } = props;
+    const savedFormat = columnFormats?.[col.key];
+    const savedCurrency = currencyFormats?.[col.key];
+    const originalLabel = col.label;
+    if (
+      (col.isMetric || col.isPercentMetric) &&
+      !col.key.includes(comparisonSuffix) &&
+      col.isNumeric
+    ) {
+      return [
+        {
+          ...col,
+          originalLabel,
+          metricName: col.key,
+          label: t('Main'),
+          key: `${t('Main')} ${col.key}`,
+          config: getComparisonColConfig(t('Main'), col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            t('Main'),
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+        {
+          ...col,
+          originalLabel,
+          metricName: col.key,
+          label: `#`,
+          key: `# ${col.key}`,
+          config: getComparisonColConfig(`#`, col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            `#`,
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+        {
+          ...col,
+          originalLabel,
+          metricName: col.key,
+          label: `△`,
+          key: `△ ${col.key}`,
+          config: getComparisonColConfig(`△`, col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            `△`,
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+        {
+          ...col,
+          originalLabel,
+          metricName: col.key,
+          label: `%`,
+          key: `% ${col.key}`,
+          config: getComparisonColConfig(`%`, col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            `%`,
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+      ];
+    }
+    if (
+      !col.isMetric &&
+      !col.isPercentMetric &&
+      !col.key.includes(comparisonSuffix)
+    ) {
+      return [col];
+    }
+    return [];
+  });
 
 const serverPageLengthMap = new Map();
 
diff --git 
a/superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx
 
b/superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx
index 006409c6ec..19f2193c51 100644
--- 
a/superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx
+++ 
b/superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx
@@ -87,7 +87,7 @@ Handlebars.registerHelper('dateFormat', function (context, 
block) {
 Handlebars.registerHelper('stringify', (obj: any, obj2: any) => {
   // calling without an argument
   if (obj2 === undefined)
-    throw Error('Please call with an object. Example: `stringify myObj`');
+    throw new Error('Please call with an object. Example: `stringify myObj`');
   return isPlainObject(obj) ? JSON.stringify(obj) : String(obj);
 });
 
diff --git 
a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx 
b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
index 553a260859..7bc0e8aa8d 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
@@ -489,6 +489,7 @@ export default typedMemo(function DataTable<D extends 
object>({
   function hashString(s: string): string {
     let h = 0;
     for (let i = 0; i < s.length; i += 1) {
+      // oxlint-disable-next-line unicorn/prefer-math-trunc -- | 0 is 
intentional for 32-bit integer wrapping in hash
       h = (h * 31 + s.charCodeAt(i)) | 0;
     }
     return String(h);
diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx 
b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
index 7de658c718..c951887175 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
@@ -189,31 +189,29 @@ const percentMetricCalculationControl: 
ControlConfig<'SelectControl'> = {
 };
 
 const processComparisonColumns = (columns: any[], suffix: string) =>
-  columns
-    .map(col => {
-      if (!col.label.includes(suffix)) {
-        return [
-          {
-            label: `${t('Main')} ${col.label}`,
-            value: `${t('Main')} ${col.value}`,
-          },
-          {
-            label: `# ${col.label}`,
-            value: `# ${col.value}`,
-          },
-          {
-            label: `△ ${col.label}`,
-            value: `△ ${col.value}`,
-          },
-          {
-            label: `% ${col.label}`,
-            value: `% ${col.value}`,
-          },
-        ];
-      }
-      return [];
-    })
-    .flat();
+  columns.flatMap(col => {
+    if (!col.label.includes(suffix)) {
+      return [
+        {
+          label: `${t('Main')} ${col.label}`,
+          value: `${t('Main')} ${col.value}`,
+        },
+        {
+          label: `# ${col.label}`,
+          value: `# ${col.value}`,
+        },
+        {
+          label: `△ ${col.label}`,
+          value: `△ ${col.value}`,
+        },
+        {
+          label: `% ${col.label}`,
+          value: `% ${col.value}`,
+        },
+      ];
+    }
+    return [];
+  });
 
 /*
 Options for row limit control
diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts 
b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
index 08e832fce6..7edd806cf4 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
@@ -384,89 +384,87 @@ const processComparisonColumns = (
   props: TableChartProps,
   comparisonSuffix: string,
 ) =>
-  columns
-    .map(col => {
-      const {
-        datasource: { columnFormats, currencyFormats },
-        rawFormData: { column_config: columnConfig = {} },
-      } = props;
-      const savedFormat = columnFormats?.[col.key];
-      const savedCurrency = currencyFormats?.[col.key];
-      const originalLabel = col.label;
-      if (
-        (col.isMetric || col.isPercentMetric) &&
-        !col.key.includes(comparisonSuffix) &&
-        col.isNumeric
-      ) {
-        return [
-          {
-            ...col,
-            originalLabel,
-            label: t('Main'),
-            key: `${t('Main')} ${col.key}`,
-            config: getComparisonColConfig(t('Main'), col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              t('Main'),
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-          {
-            ...col,
-            originalLabel,
-            label: `#`,
-            key: `# ${col.key}`,
-            config: getComparisonColConfig(`#`, col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              `#`,
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-          {
-            ...col,
-            originalLabel,
-            label: `△`,
-            key: `△ ${col.key}`,
-            config: getComparisonColConfig(`△`, col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              `△`,
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-          {
-            ...col,
-            originalLabel,
-            label: `%`,
-            key: `% ${col.key}`,
-            config: getComparisonColConfig(`%`, col.key, columnConfig),
-            formatter: getComparisonColFormatter(
-              `%`,
-              col,
-              columnConfig,
-              savedFormat,
-              savedCurrency,
-            ),
-          },
-        ];
-      }
-      if (
-        !col.isMetric &&
-        !col.isPercentMetric &&
-        !col.key.includes(comparisonSuffix)
-      ) {
-        return [col];
-      }
-      return [];
-    })
-    .flat();
+  columns.flatMap(col => {
+    const {
+      datasource: { columnFormats, currencyFormats },
+      rawFormData: { column_config: columnConfig = {} },
+    } = props;
+    const savedFormat = columnFormats?.[col.key];
+    const savedCurrency = currencyFormats?.[col.key];
+    const originalLabel = col.label;
+    if (
+      (col.isMetric || col.isPercentMetric) &&
+      !col.key.includes(comparisonSuffix) &&
+      col.isNumeric
+    ) {
+      return [
+        {
+          ...col,
+          originalLabel,
+          label: t('Main'),
+          key: `${t('Main')} ${col.key}`,
+          config: getComparisonColConfig(t('Main'), col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            t('Main'),
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+        {
+          ...col,
+          originalLabel,
+          label: `#`,
+          key: `# ${col.key}`,
+          config: getComparisonColConfig(`#`, col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            `#`,
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+        {
+          ...col,
+          originalLabel,
+          label: `△`,
+          key: `△ ${col.key}`,
+          config: getComparisonColConfig(`△`, col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            `△`,
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+        {
+          ...col,
+          originalLabel,
+          label: `%`,
+          key: `% ${col.key}`,
+          config: getComparisonColConfig(`%`, col.key, columnConfig),
+          formatter: getComparisonColFormatter(
+            `%`,
+            col,
+            columnConfig,
+            savedFormat,
+            savedCurrency,
+          ),
+        },
+      ];
+    }
+    if (
+      !col.isMetric &&
+      !col.isPercentMetric &&
+      !col.key.includes(comparisonSuffix)
+    ) {
+      return [col];
+    }
+    return [];
+  });
 
 /**
  * Automatically set page size based on number of cells.
diff --git a/superset-frontend/scripts/check-custom-rules.js 
b/superset-frontend/scripts/check-custom-rules.js
index b0ddfe0625..5606fe9691 100755
--- a/superset-frontend/scripts/check-custom-rules.js
+++ b/superset-frontend/scripts/check-custom-rules.js
@@ -30,9 +30,9 @@ const parser = require('@babel/parser');
 const traverse = require('@babel/traverse').default;
 
 // ANSI color codes
-const RED = '\x1b[31m';
-const YELLOW = '\x1b[33m';
-const RESET = '\x1b[0m';
+const RED = '\x1B[31m';
+const YELLOW = '\x1B[33m';
+const RESET = '\x1B[0m';
 
 let errorCount = 0;
 let warningCount = 0;
diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.ts 
b/superset-frontend/src/SqlLab/reducers/getInitialState.ts
index d11d32eb75..a594196c5d 100644
--- a/superset-frontend/src/SqlLab/reducers/getInitialState.ts
+++ b/superset-frontend/src/SqlLab/reducers/getInitialState.ts
@@ -242,7 +242,7 @@ export default function getInitialState({
             }
           });
         }
-        lastUpdatedActiveTab = tabHistory.slice(tabHistory.length - 1)[0] || 
'';
+        lastUpdatedActiveTab = tabHistory.slice(-1)[0] || '';
       }
     }
   } catch (error) {
diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx 
b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 6660bf8363..47820e475b 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -304,7 +304,7 @@ export function DatabaseSelector({
       if (schemas.length === 1) {
         changeSchema(schemas[0]);
       } else if (
-        !schemas.find(schemaOption => schemaRef.current === schemaOption.value)
+        !schemas.some(schemaOption => schemaRef.current === schemaOption.value)
       ) {
         changeSchema(undefined);
       }
@@ -345,7 +345,7 @@ export function DatabaseSelector({
       } else if (catalogs.length === 1) {
         changeCatalog(catalogs[0]);
       } else if (
-        !catalogs.find(
+        !catalogs.some(
           catalogOption => catalogRef.current === catalogOption.value,
         )
       ) {
diff --git a/superset-frontend/src/components/GridTable/Header.tsx 
b/superset-frontend/src/components/GridTable/Header.tsx
index 5b6e341fc0..f47d9432d6 100644
--- a/superset-frontend/src/components/GridTable/Header.tsx
+++ b/superset-frontend/src/components/GridTable/Header.tsx
@@ -118,8 +118,9 @@ export const Header: React.FC<Params> = ({
   );
 
   const onSortChanged = useCallback(() => {
-    const hasMultiSort =
-      api.getAllDisplayedColumns().findIndex(c => c.getSortIndex()) !== -1;
+    const hasMultiSort = api
+      .getAllDisplayedColumns()
+      .some(c => c.getSortIndex());
     const updatedSortIndex = column.getSortIndex();
     sortOption.current = SORT_DIRECTION.indexOf(column.getSort() ?? null);
     setCurrentSort(column.getSort() ?? null);
diff --git a/superset-frontend/src/components/ListView/utils.ts 
b/superset-frontend/src/components/ListView/utils.ts
index 37b15e8780..718f270ca4 100644
--- a/superset-frontend/src/components/ListView/utils.ts
+++ b/superset-frontend/src/components/ListView/utils.ts
@@ -117,7 +117,7 @@ export function convertFilters(fts: InternalFilter[]): 
FilterValue[] {
           (Array.isArray(f.value) && !f.value.length)
         ),
     )
-    .map(({ value, operator, id }) => {
+    .flatMap(({ value, operator, id }) => {
       // handle between filter using 2 api filters
       if (operator === 'between' && Array.isArray(value)) {
         return [
@@ -138,8 +138,7 @@ export function convertFilters(fts: InternalFilter[]): 
FilterValue[] {
         operator,
         id,
       };
-    })
-    .flat();
+    });
 }
 
 // convertFilters but to handle new decoded rison format
diff --git a/superset-frontend/src/components/MessageToasts/reducers.ts 
b/superset-frontend/src/components/MessageToasts/reducers.ts
index 9209164c57..94fa7dc925 100644
--- a/superset-frontend/src/components/MessageToasts/reducers.ts
+++ b/superset-frontend/src/components/MessageToasts/reducers.ts
@@ -41,7 +41,7 @@ export default function messageToastsReducer(
     case ADD_TOAST: {
       const { payload: toast } = action;
       const result = toasts.slice();
-      if (!toast.noDuplicate || !result.find(x => x.text === toast.text)) {
+      if (!toast.noDuplicate || !result.some(x => x.text === toast.text)) {
         return [toast, ...toasts];
       }
       return toasts;
diff --git 
a/superset-frontend/src/dashboard/components/RefreshFrequency/RefreshFrequencySelect.tsx
 
b/superset-frontend/src/dashboard/components/RefreshFrequency/RefreshFrequencySelect.tsx
index 453a063ed6..25847fab35 100644
--- 
a/superset-frontend/src/dashboard/components/RefreshFrequency/RefreshFrequencySelect.tsx
+++ 
b/superset-frontend/src/dashboard/components/RefreshFrequency/RefreshFrequencySelect.tsx
@@ -81,11 +81,11 @@ export const RefreshFrequencySelect = ({
 }: RefreshFrequencySelectProps) => {
   // Separate radio selection state from value state
   const [radioSelection, setRadioSelection] = useState(() =>
-    REFRESH_FREQUENCY_OPTIONS.find(opt => opt.value === value) ? value : -1,
+    REFRESH_FREQUENCY_OPTIONS.some(opt => opt.value === value) ? value : -1,
   );
 
   const [customValue, setCustomValue] = useState(() =>
-    REFRESH_FREQUENCY_OPTIONS.find(opt => opt.value === value)
+    REFRESH_FREQUENCY_OPTIONS.some(opt => opt.value === value)
       ? ''
       : value.toString(),
   );
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts
index ed6a7634f1..9fd165f99a 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts
@@ -141,7 +141,7 @@ export const useInitialization = () => {
     }
 
     if (
-      Object.values(filters).find(
+      Object.values(filters).some(
         filter => 'requiredFirst' in filter && filter.requiredFirst,
       )
     ) {
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index 23fa95482d..e7800e583c 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -1579,7 +1579,7 @@ const FiltersConfigForm = (
                                             prevErroredFilters => {
                                               if (
                                                 prevErroredFilters.length &&
-                                                !formValidationFields.find(
+                                                !formValidationFields.some(
                                                   f => f.errors.length > 0,
                                                 )
                                               ) {
diff --git a/superset-frontend/src/dashboard/util/getOverwriteItems.ts 
b/superset-frontend/src/dashboard/util/getOverwriteItems.ts
index 7492b8aeac..f6c7451b68 100644
--- a/superset-frontend/src/dashboard/util/getOverwriteItems.ts
+++ b/superset-frontend/src/dashboard/util/getOverwriteItems.ts
@@ -31,7 +31,7 @@ function extractValue(object: JsonObject, keyPath: string) {
 export default function getOverwriteItems(prev: JsonObject, next: JsonObject) {
   return OVERWRITE_INSPECT_FIELDS.map(keyPath => ({
     keyPath,
-    ...(keyPath.split('.').find(key => JSON_KEYS.has(key))
+    ...(keyPath.split('.').some(key => JSON_KEYS.has(key))
       ? {
           oldValue:
             JSON.stringify(extractValue(prev, keyPath), null, 2) || '{}',
diff --git a/superset-frontend/src/dashboard/util/isValidChild.test.ts 
b/superset-frontend/src/dashboard/util/isValidChild.test.ts
index 0fb51d1c88..33af311285 100644
--- a/superset-frontend/src/dashboard/util/isValidChild.test.ts
+++ b/superset-frontend/src/dashboard/util/isValidChild.test.ts
@@ -145,7 +145,7 @@ describe('isValidChild', () => {
           const parentType = example[i - 1];
 
           if (typeof parentType !== 'string')
-            throw TypeError('parent must be string');
+            throw new TypeError('parent must be string');
 
           test(`(${exampleIdx})${getIndentation(
             childDepth,
diff --git 
a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx
 
b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx
index 9a8613cb96..16b085071c 100644
--- 
a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx
+++ 
b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx
@@ -921,7 +921,7 @@ class AnnotationLayer extends PureComponent<
     if (
       color &&
       color !== AUTOMATIC_COLOR &&
-      !colorScheme.find(x => x.toLowerCase() === color.toLowerCase())
+      !colorScheme.some(x => x.toLowerCase() === color.toLowerCase())
     ) {
       colorScheme.push(color);
     }
diff --git 
a/superset-frontend/src/explore/components/controls/SelectAsyncControl/index.tsx
 
b/superset-frontend/src/explore/components/controls/SelectAsyncControl/index.tsx
index 6a12cdc853..c9095b5899 100644
--- 
a/superset-frontend/src/explore/components/controls/SelectAsyncControl/index.tsx
+++ 
b/superset-frontend/src/explore/components/controls/SelectAsyncControl/index.tsx
@@ -90,7 +90,7 @@ const SelectAsyncControl = ({
       value || (props.default !== undefined ? props.default : undefined);
 
     // safety check - the value is intended to be undefined but null was used
-    if (currentValue === null && !options.find(o => o.value === null)) {
+    if (currentValue === null && !options.some(o => o.value === null)) {
       return undefined;
     }
     return currentValue;
diff --git 
a/superset-frontend/src/explore/components/controls/SelectControl.tsx 
b/superset-frontend/src/explore/components/controls/SelectControl.tsx
index 751546c7f5..f13164db20 100644
--- a/superset-frontend/src/explore/components/controls/SelectControl.tsx
+++ b/superset-frontend/src/explore/components/controls/SelectControl.tsx
@@ -284,7 +284,7 @@ export default class SelectControl extends PureComponent<
       // safety check - the value is intended to be undefined but null was used
       if (
         currentValue === null &&
-        !this.state.options.find(o => o.value === null)
+        !this.state.options.some(o => o.value === null)
       ) {
         return undefined;
       }
diff --git a/superset-frontend/src/views/CRUD/utils.tsx 
b/superset-frontend/src/views/CRUD/utils.tsx
index f234527fa6..9db1ae58ee 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -480,50 +480,45 @@ export const isAlreadyExists = (payload: any) =>
 
 export const getPasswordsNeeded = (errors: Record<string, any>[]) =>
   errors
-    .map(error =>
+    .flatMap(error =>
       Object.entries(error.extra)
         .filter(([, payload]) => isNeedsPassword(payload))
         .map(([fileName]) => fileName),
-    )
-    .flat();
+    );
 
 export const getSSHPasswordsNeeded = (errors: Record<string, any>[]) =>
   errors
-    .map(error =>
+    .flatMap(error =>
       Object.entries(error.extra)
         .filter(([, payload]) => isNeedsSSHPassword(payload))
         .map(([fileName]) => fileName),
-    )
-    .flat();
+    );
 
 export const getSSHPrivateKeysNeeded = (errors: Record<string, any>[]) =>
   errors
-    .map(error =>
+    .flatMap(error =>
       Object.entries(error.extra)
         .filter(([, payload]) => isNeedsSSHPrivateKey(payload))
         .map(([fileName]) => fileName),
-    )
-    .flat();
+    );
 
 export const getSSHPrivateKeyPasswordsNeeded = (
   errors: Record<string, any>[],
 ) =>
   errors
-    .map(error =>
+    .flatMap(error =>
       Object.entries(error.extra)
         .filter(([, payload]) => isNeedsSSHPrivateKeyPassword(payload))
         .map(([fileName]) => fileName),
-    )
-    .flat();
+    );
 
 export const getAlreadyExists = (errors: Record<string, any>[]) =>
   errors
-    .map(error =>
+    .flatMap(error =>
       Object.entries(error.extra)
         .filter(([, payload]) => isAlreadyExists(payload))
         .map(([fileName]) => fileName),
-    )
-    .flat();
+    );
 
 export const hasTerminalValidation = (errors: Record<string, any>[]) =>
   errors.some(error => {


Reply via email to