This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch chore/lint-cleanup-tech-debt-3 in repository https://gitbox.apache.org/repos/asf/superset.git
commit 6c75540e1c856a8b62a77c83436275d72068a350 Author: Evan Rusackas <[email protected]> AuthorDate: Wed Feb 11 00:23:31 2026 -0800 chore(lint): upgrade array creation, effect, and TypeScript rules - Upgrade unicorn/no-new-array to error (all violations fixed) - Upgrade @typescript-eslint/no-use-before-define from warn to error - Add react-you-might-not-need-an-effect plugin with rules: - no-adjust-state-on-prop-change: error - no-pass-data-to-parent: error Fixed unicorn/no-new-array violations by replacing: - new Array(n).fill().map() with Array.from({ length: n }, callback) - new Array(n).fill().join() with String.repeat() - new Array(n).fill().forEach() with for loops Co-Authored-By: Claude Opus 4.5 <[email protected]> --- superset-frontend/.eslintrc.js | 8 +++++++- superset-frontend/oxlint.json | 3 ++- .../plugins/legacy-preset-chart-deckgl/src/utils.ts | 2 +- .../plugin-chart-echarts/src/Timeseries/transformers.ts | 9 +++++++-- .../src/DataTable/components/Pagination.tsx | 10 +++++----- superset-frontend/src/SqlLab/fixtures.ts | 2 +- .../src/SqlLab/utils/emptyQueryResults.test.ts | 6 ++---- .../src/components/FacePile/FacePile.stories.tsx | 2 +- .../src/components/FacePile/FacePile.test.tsx | 2 +- superset-frontend/src/components/FacePile/utils.tsx | 4 ++-- .../src/components/ListView/CardCollection.tsx | 6 +++--- .../FilterBar/FilterControls/FilterControls.tsx | 14 +++++++------- .../FiltersConfigModal/FilterConfigPane.test.tsx | 10 ++++------ .../src/pages/ExecutionLogList/ExecutionLogList.test.tsx | 2 +- superset-frontend/src/pages/Home/index.tsx | 2 +- superset-frontend/src/pages/RolesList/RolesList.test.tsx | 6 +++--- .../src/pages/SavedQueryList/SavedQueryList.test.tsx | 2 +- .../src/pages/UserRegistrations/UserRegistrations.test.tsx | 2 +- superset-frontend/src/pages/UsersList/UsersList.test.tsx | 4 ++-- 19 files changed, 52 insertions(+), 44 deletions(-) diff --git a/superset-frontend/.eslintrc.js b/superset-frontend/.eslintrc.js index 5f4b50ff9d2..2a8cf1529e1 100644 --- a/superset-frontend/.eslintrc.js +++ b/superset-frontend/.eslintrc.js @@ -136,6 +136,7 @@ module.exports = { 'i18n-strings', 'react-prefer-function-component', 'prettier', + 'react-you-might-not-need-an-effect', ], rules: { // === Essential Superset customizations === @@ -241,6 +242,11 @@ module.exports = { // File progress 'file-progress/activate': 1, + // React effect rules + 'react-you-might-not-need-an-effect/no-adjust-state-on-prop-change': + 'error', + 'react-you-might-not-need-an-effect/no-pass-data-to-parent': 'error', + // Restricted imports 'no-restricted-imports': [ 'error', @@ -350,7 +356,7 @@ module.exports = { ], '@typescript-eslint/no-empty-function': 0, '@typescript-eslint/no-explicit-any': 0, - '@typescript-eslint/no-use-before-define': 1, + '@typescript-eslint/no-use-before-define': 'error', '@typescript-eslint/no-non-null-assertion': 0, '@typescript-eslint/explicit-function-return-type': 0, '@typescript-eslint/explicit-module-boundary-types': 0, diff --git a/superset-frontend/oxlint.json b/superset-frontend/oxlint.json index 81b7523dce5..2fed1be8cb7 100644 --- a/superset-frontend/oxlint.json +++ b/superset-frontend/oxlint.json @@ -231,7 +231,7 @@ "@typescript-eslint/ban-types": "off", "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-use-before-define": "warn", + "@typescript-eslint/no-use-before-define": "error", "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-module-boundary-types": "off", @@ -250,6 +250,7 @@ ], // === Unicorn rules (bonus coverage) === + "unicorn/no-new-array": "error", "unicorn/filename-case": "off", "unicorn/prevent-abbreviations": "off", "unicorn/no-null": "off", 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 7b6399676e5..6927e6306d7 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts @@ -84,7 +84,7 @@ export function getBreakPoints( delta === 0 ? 0 : Math.max(0, Math.ceil(Math.log10(1 / delta))); // Generate breakpoints - const breakPoints = new Array(numBuckets + 1).fill(0).map((_, i) => { + const breakPoints = Array.from({ length: numBuckets + 1 }, (_, i) => { const value = minValue + i * delta; // For the first breakpoint, floor to ensure minimum is included diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts index ba114ceb0dc..dd054d42b50 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts @@ -76,7 +76,10 @@ export const getBaselineSeriesForStream = ( seriesType: EchartsTimeseriesSeriesType, ) => { const seriesLength = series[0].length; - const baselineSeriesDelta = new Array(seriesLength).fill([0, 0]); + const baselineSeriesDelta: [string | number, number][] = Array.from( + { length: seriesLength }, + () => [0, 0], + ); const getVal = (value: number | null) => value ?? 0; for (let i = 0; i < seriesLength; i += 1) { let seriesSum = 0; @@ -98,7 +101,9 @@ export const getBaselineSeriesForStream = ( } baselineSeriesDelta[i] = [series[0][i][0], -weightedSeriesSum / seriesSum]; } - const baselineSeries = baselineSeriesDelta.reduce((acc, curr, i) => { + const baselineSeries = baselineSeriesDelta.reduce< + [string | number, number][] + >((acc, curr, i) => { if (i === 0) { acc.push(curr); } else { diff --git a/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/Pagination.tsx b/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/Pagination.tsx index 87040545338..39e599d1285 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/Pagination.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/Pagination.tsx @@ -49,16 +49,16 @@ export function generatePageItems( throw new Error(`Must allow odd number of page items`); } if (total < width) { - return [...new Array(total).keys()]; + return Array.from({ length: total }, (_, i) => i); } const left = Math.max( 0, Math.min(total - width, current - Math.floor(width / 2)), ); - const items: (string | number)[] = new Array(width); - for (let i = 0; i < width; i += 1) { - items[i] = i + left; - } + const items: (string | number)[] = Array.from( + { length: width }, + (_, i) => i + left, + ); // replace non-ending items with placeholders if (typeof items[0] === 'number' && items[0] > 0) { items[0] = 0; diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index e03ecebb162..05bd77b4af7 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -775,7 +775,7 @@ export const testQuery: ISaveableDatasource = { ], }; -export const mockdatasets = new Array(3).fill(undefined).map((_, i) => ({ +export const mockdatasets = Array.from({ length: 3 }, (_, i) => ({ changed_by_name: 'user', kind: i === 0 ? 'virtual' : 'physical', // ensure there is 1 virtual changed_by: 'user', diff --git a/superset-frontend/src/SqlLab/utils/emptyQueryResults.test.ts b/superset-frontend/src/SqlLab/utils/emptyQueryResults.test.ts index 980de6c81b1..944bb135713 100644 --- a/superset-frontend/src/SqlLab/utils/emptyQueryResults.test.ts +++ b/superset-frontend/src/SqlLab/utils/emptyQueryResults.test.ts @@ -64,11 +64,9 @@ describe('reduxStateToLocalStorageHelper', () => { results: { data: [ { - jsonValue: `{"str":"${new Array( + jsonValue: `{"str":"${'0'.repeat( (LOCALSTORAGE_MAX_QUERY_RESULTS_KB / BYTES_PER_CHAR) * KB_STORAGE, - ) - .fill(0) - .join('')}"}`, + )}"}`, }, ], }, diff --git a/superset-frontend/src/components/FacePile/FacePile.stories.tsx b/superset-frontend/src/components/FacePile/FacePile.stories.tsx index 3c59fd1a50b..c0f2ed64fd0 100644 --- a/superset-frontend/src/components/FacePile/FacePile.stories.tsx +++ b/superset-frontend/src/components/FacePile/FacePile.stories.tsx @@ -49,7 +49,7 @@ const lastNames = [ 'Tzu', ]; -const users = new Array(10).fill(undefined).map((_, i) => ({ +const users = Array.from({ length: 10 }, (_, i) => ({ first_name: firstNames[Math.floor(Math.random() * firstNames.length)], last_name: lastNames[Math.floor(Math.random() * lastNames.length)], id: i, diff --git a/superset-frontend/src/components/FacePile/FacePile.test.tsx b/superset-frontend/src/components/FacePile/FacePile.test.tsx index 22954d8f43a..e37128d0b0b 100644 --- a/superset-frontend/src/components/FacePile/FacePile.test.tsx +++ b/superset-frontend/src/components/FacePile/FacePile.test.tsx @@ -39,7 +39,7 @@ const mockIsFeatureEnabled = isFeatureEnabled as jest.MockedFunction< typeof isFeatureEnabled >; -const users = new Array(10).fill(undefined).map((_, i) => ({ +const users = Array.from({ length: 10 }, (_, i) => ({ first_name: 'user', last_name: `${i}`, id: i, diff --git a/superset-frontend/src/components/FacePile/utils.tsx b/superset-frontend/src/components/FacePile/utils.tsx index 14996a6661d..4df0fa3535e 100644 --- a/superset-frontend/src/components/FacePile/utils.tsx +++ b/superset-frontend/src/components/FacePile/utils.tsx @@ -32,9 +32,9 @@ function stringAsciiPRNG(value: string, m: number) { let random = charCodes[0] % m; - new Array(len).fill(undefined).forEach(() => { + for (let i = 0; i < len; i += 1) { random = (a * random + c) % m; - }); + } return random; } diff --git a/superset-frontend/src/components/ListView/CardCollection.tsx b/superset-frontend/src/components/ListView/CardCollection.tsx index 7fe7d4505e4..8d192037f38 100644 --- a/superset-frontend/src/components/ListView/CardCollection.tsx +++ b/superset-frontend/src/components/ListView/CardCollection.tsx @@ -79,9 +79,9 @@ export default function CardCollection({ <CardContainer showThumbnails={showThumbnails}> {loading && rows.length === 0 && - new Array(25) - .fill(undefined) - .map((e, i) => <div key={i}>{renderCard({ loading })}</div>)} + Array.from({ length: 25 }, (_, i) => ( + <div key={i}>{renderCard({ loading })}</div> + ))} {rows.length > 0 && rows.map(row => { if (!renderCard) return null; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx index 1dbc0a4c86b..545496aec34 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx @@ -181,13 +181,13 @@ const FilterControls: FC<FilterControlsProps> = ({ clearAllTriggers, onClearAllComplete, ); - const portalNodes = useMemo(() => { - const nodes = new Array(filtersWithValues.length); - for (let i = 0; i < filtersWithValues.length; i += 1) { - nodes[i] = createHtmlPortalNode(); - } - return nodes; - }, [filtersWithValues.length]); + const portalNodes = useMemo( + () => + Array.from({ length: filtersWithValues.length }, () => + createHtmlPortalNode(), + ), + [filtersWithValues.length], + ); const filterIds = new Set(filtersWithValues.map(item => item.id)); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterConfigPane.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterConfigPane.test.tsx index 2c5cc0618c3..c701c19dbc4 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterConfigPane.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterConfigPane.test.tsx @@ -112,18 +112,16 @@ test('filter container should scroll to bottom when adding items', async () => { const state = { dashboardInfo: { metadata: { - native_filter_configuration: new Array(35) - .fill(0) - .map((_, index) => - buildNativeFilter(`NATIVE_FILTER-${index}`, `filter-${index}`, []), - ), + native_filter_configuration: Array.from({ length: 35 }, (_, index) => + buildNativeFilter(`NATIVE_FILTER-${index}`, `filter-${index}`, []), + ), }, }, dashboardLayout, }; const props = { ...defaultProps, - filters: new Array(35).fill(0).map((_, index) => `NATIVE_FILTER-${index}`), + filters: Array.from({ length: 35 }, (_, index) => `NATIVE_FILTER-${index}`), }; defaultRender(state, props); diff --git a/superset-frontend/src/pages/ExecutionLogList/ExecutionLogList.test.tsx b/superset-frontend/src/pages/ExecutionLogList/ExecutionLogList.test.tsx index a3f8145b60c..01bf922f331 100644 --- a/superset-frontend/src/pages/ExecutionLogList/ExecutionLogList.test.tsx +++ b/superset-frontend/src/pages/ExecutionLogList/ExecutionLogList.test.tsx @@ -25,7 +25,7 @@ const reportEndpoint = 'glob:*/api/v1/report/*'; fetchMock.delete(executionLogsEndpoint, {}); -const mockAnnotations = new Array(3).fill(undefined).map((_, i) => ({ +const mockAnnotations = Array.from({ length: 3 }, (_, i) => ({ end_dttm: new Date().toISOString, error_message: `report ${i} error message`, id: i, diff --git a/superset-frontend/src/pages/Home/index.tsx b/superset-frontend/src/pages/Home/index.tsx index 6b0d31f31f3..42a4b3be15d 100644 --- a/superset-frontend/src/pages/Home/index.tsx +++ b/superset-frontend/src/pages/Home/index.tsx @@ -135,7 +135,7 @@ const bootstrapData = getBootstrapData(); export const LoadingCards = ({ cover }: LoadingProps) => ( <CardContainer showThumbnails={cover} className="loading-cards"> - {new Array(loadingCardCount).fill(undefined).map((_, index) => ( + {Array.from({ length: loadingCardCount }, (_, index) => ( <ListViewCard key={index} cover={cover ? false : <></>} diff --git a/superset-frontend/src/pages/RolesList/RolesList.test.tsx b/superset-frontend/src/pages/RolesList/RolesList.test.tsx index 056491fbadd..23a953dbde5 100644 --- a/superset-frontend/src/pages/RolesList/RolesList.test.tsx +++ b/superset-frontend/src/pages/RolesList/RolesList.test.tsx @@ -40,20 +40,20 @@ const roleEndpoint = 'glob:*/api/v1/security/roles/*'; const permissionsEndpoint = 'glob:*/api/v1/security/permissions-resources/?*'; const usersEndpoint = 'glob:*/api/v1/security/users/?*'; -const mockRoles = new Array(3).fill(undefined).map((_, i) => ({ +const mockRoles = Array.from({ length: 3 }, (_, i) => ({ id: i, name: `role ${i}`, user_ids: [i, i + 1], permission_ids: [i, i + 1, i + 2], })); -const mockPermissions = new Array(10).fill(undefined).map((_, i) => ({ +const mockPermissions = Array.from({ length: 10 }, (_, i) => ({ id: i, permission: { name: `permission_${i}` }, view_menu: { name: `view_menu_${i}` }, })); -const mockUsers = new Array(5).fill(undefined).map((_, i) => ({ +const mockUsers = Array.from({ length: 5 }, (_, i) => ({ id: i, username: `user_${i}`, first_name: `User`, diff --git a/superset-frontend/src/pages/SavedQueryList/SavedQueryList.test.tsx b/superset-frontend/src/pages/SavedQueryList/SavedQueryList.test.tsx index 9758154b292..12cf7e6aba7 100644 --- a/superset-frontend/src/pages/SavedQueryList/SavedQueryList.test.tsx +++ b/superset-frontend/src/pages/SavedQueryList/SavedQueryList.test.tsx @@ -33,7 +33,7 @@ import SavedQueryList from '.'; // Increase default timeout jest.setTimeout(30000); -const mockQueries = new Array(3).fill(undefined).map((_, i) => ({ +const mockQueries = Array.from({ length: 3 }, (_, i) => ({ created_by: { id: i, first_name: 'user', last_name: `${i}` }, created_on: `${i}-2020`, database: { database_name: `db ${i}`, id: i }, diff --git a/superset-frontend/src/pages/UserRegistrations/UserRegistrations.test.tsx b/superset-frontend/src/pages/UserRegistrations/UserRegistrations.test.tsx index fbdaa57aefc..67856b35661 100644 --- a/superset-frontend/src/pages/UserRegistrations/UserRegistrations.test.tsx +++ b/superset-frontend/src/pages/UserRegistrations/UserRegistrations.test.tsx @@ -23,7 +23,7 @@ import UserRegistrations from '.'; const userRegistrationsEndpoint = 'glob:*/security/user_registrations/?*'; -const mockUserRegistrations = new Array(5).fill(undefined).map((_, i) => ({ +const mockUserRegistrations = Array.from({ length: 5 }, (_, i) => ({ id: i, username: `user${i}`, first_name: `User${i}`, diff --git a/superset-frontend/src/pages/UsersList/UsersList.test.tsx b/superset-frontend/src/pages/UsersList/UsersList.test.tsx index 465a0c531fc..e994bd485cc 100644 --- a/superset-frontend/src/pages/UsersList/UsersList.test.tsx +++ b/superset-frontend/src/pages/UsersList/UsersList.test.tsx @@ -39,14 +39,14 @@ const rolesEndpoint = 'glob:*/security/roles/?*'; const usersEndpoint = 'glob:*/security/users/?*'; const groupsEndpoint = 'glob:*/security/groups/*'; -const mockRoles = new Array(3).fill(undefined).map((_, i) => ({ +const mockRoles = Array.from({ length: 3 }, (_, i) => ({ id: i, name: `role ${i}`, user_ids: [i, i + 1], permission_ids: [i, i + 1, i + 2], })); -const mockUsers = new Array(5).fill(undefined).map((_, i) => ({ +const mockUsers = Array.from({ length: 5 }, (_, i) => ({ active: true, changed_by: { id: 1 }, changed_on: new Date(2025, 2, 25, 11, 4, 32 + i).toISOString(),
