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

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 534fa48f1ff chore(lint): enforce stricter eslint/oxlint rules (#37883)
534fa48f1ff is described below

commit 534fa48f1ff33d12a1fbff64847d09854f0dfd98
Author: Evan Rusackas <[email protected]>
AuthorDate: Wed Feb 11 11:07:02 2026 -0500

    chore(lint): enforce stricter eslint/oxlint rules (#37883)
    
    Co-authored-by: Claude Opus 4.5 <[email protected]>
---
 superset-frontend/.eslintrc.js                                    | 6 ++++++
 superset-frontend/oxlint.json                                     | 7 +++++--
 .../packages/superset-ui-core/src/models/ExtensibleFunction.ts    | 3 ++-
 .../packages/superset-ui-core/src/utils/html.test.tsx             | 1 +
 superset-frontend/packages/superset-ui-core/src/utils/html.tsx    | 2 ++
 .../plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx | 2 ++
 superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx   | 4 +++-
 superset-frontend/src/SqlLab/components/App/index.tsx             | 8 ++++++--
 .../src/SqlLab/components/SqlEditorTabHeader/index.tsx            | 2 ++
 9 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/superset-frontend/.eslintrc.js b/superset-frontend/.eslintrc.js
index 5f4b50ff9d2..5cbc53a4403 100644
--- a/superset-frontend/.eslintrc.js
+++ b/superset-frontend/.eslintrc.js
@@ -135,6 +135,7 @@ module.exports = {
     'icons',
     'i18n-strings',
     'react-prefer-function-component',
+    'react-you-might-not-need-an-effect',
     'prettier',
   ],
   rules: {
@@ -235,6 +236,11 @@ module.exports = {
     'jsx-a11y/mouse-events-have-key-events': 0,
     'jsx-a11y/no-static-element-interactions': 0,
 
+    // React effect best practices
+    'react-you-might-not-need-an-effect/no-empty-effect': 'error',
+    'react-you-might-not-need-an-effect/no-pass-live-state-to-parent': 'error',
+    'react-you-might-not-need-an-effect/no-initialize-state': 'error',
+
     // Lodash
     'lodash/import-scope': [2, 'member'],
 
diff --git a/superset-frontend/oxlint.json b/superset-frontend/oxlint.json
index fe2360b2219..1a9b1ac3c0b 100644
--- a/superset-frontend/oxlint.json
+++ b/superset-frontend/oxlint.json
@@ -28,7 +28,8 @@
     // === Core ESLint rules ===
     // Error prevention
     "no-console": "warn",
-    "no-alert": "warn",
+    "no-alert": "error",
+    "constructor-super": "error",
     "no-debugger": "error",
     "no-unused-vars": "off",
     "no-undef": "error",
@@ -148,7 +149,8 @@
     ],
     "react/no-array-index-key": "off",
     "react/no-children-prop": "error",
-    "react/no-danger": "warn",
+    "react/no-danger": "error",
+    "react/forbid-foreign-prop-types": "error",
     "react/no-danger-with-children": "error",
     "react/no-deprecated": "error",
     "react/no-did-update-set-state": "error",
@@ -250,6 +252,7 @@
     ],
 
     // === Unicorn rules (bonus coverage) ===
+    "unicorn/no-invalid-remove-event-listener": "error",
     "unicorn/filename-case": "off",
     "unicorn/prevent-abbreviations": "off",
     "unicorn/no-null": "off",
diff --git 
a/superset-frontend/packages/superset-ui-core/src/models/ExtensibleFunction.ts 
b/superset-frontend/packages/superset-ui-core/src/models/ExtensibleFunction.ts
index 66f82af0499..566b898091f 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/models/ExtensibleFunction.ts
+++ 
b/superset-frontend/packages/superset-ui-core/src/models/ExtensibleFunction.ts
@@ -22,7 +22,8 @@
  */
 
 export default class ExtensibleFunction extends Function {
-  // @ts-expect-error
+  // @ts-expect-error - intentionally not calling super(), using 
setPrototypeOf pattern instead
+  // eslint-disable-next-line constructor-super
   constructor(fn: Function) {
     // eslint-disable-next-line @typescript-eslint/no-unsafe-return, 
no-constructor-return
     return Object.setPrototypeOf(fn, new.target.prototype);
diff --git 
a/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx 
b/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx
index 648b2b68ce7..ea24e0c8c50 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx
@@ -106,6 +106,7 @@ describe('safeHtmlSpan', () => {
     expect(safeSpan).toEqual(
       <span
         className="safe-html-wrapper"
+        // eslint-disable-next-line react/no-danger
         dangerouslySetInnerHTML={{ __html: htmlString }}
       />,
     );
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/html.tsx 
b/superset-frontend/packages/superset-ui-core/src/utils/html.tsx
index de7267c8231..940fc232b6a 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/html.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/utils/html.tsx
@@ -167,6 +167,8 @@ export function safeHtmlSpan(possiblyHtmlString: string) {
     return (
       <span
         className="safe-html-wrapper"
+        // Safe: HTML is sanitized before rendering
+        // eslint-disable-next-line react/no-danger
         dangerouslySetInnerHTML={{ __html: sanitizeHtml(possiblyHtmlString) }}
       />
     );
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx
 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx
index 88e7bc23693..eb52f206a88 100644
--- 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx
+++ 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx
@@ -64,6 +64,8 @@ export const TextCellRenderer = (params: CellRendererProps) 
=> {
       );
     }
     if (allowRenderHtml && isProbablyHTML(value)) {
+      // Safe: HTML is sanitized before rendering
+      // eslint-disable-next-line react/no-danger
       return <div dangerouslySetInnerHTML={{ __html: sanitizeHtml(value) }} />;
     }
   }
diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx 
b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
index 08f808bcf23..9aae0231f51 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
@@ -1060,17 +1060,19 @@ export default function TableChart<D extends DataRecord 
= DataRecord>(
           };
           if (html) {
             if (truncateLongCells) {
-              // eslint-disable-next-line react/no-danger
               return (
                 <StyledCell {...cellProps}>
                   <div
                     className="dt-truncate-cell"
                     style={columnWidth ? { width: columnWidth } : undefined}
+                    // Safe: HTML is sanitized via formatColumnValue
+                    // eslint-disable-next-line react/no-danger
                     dangerouslySetInnerHTML={html}
                   />
                 </StyledCell>
               );
             }
+            // Safe: HTML is sanitized via formatColumnValue
             // eslint-disable-next-line react/no-danger
             return <StyledCell {...cellProps} dangerouslySetInnerHTML={html} 
/>;
           }
diff --git a/superset-frontend/src/SqlLab/components/App/index.tsx 
b/superset-frontend/src/SqlLab/components/App/index.tsx
index efe9597c9f7..83200616524 100644
--- a/superset-frontend/src/SqlLab/components/App/index.tsx
+++ b/superset-frontend/src/SqlLab/components/App/index.tsx
@@ -117,12 +117,16 @@ interface AppState {
 class App extends PureComponent<AppProps, AppState> {
   hasLoggedLocalStorageUsage: boolean;
 
+  private boundOnHashChanged: () => void;
+
   constructor(props: AppProps) {
     super(props);
     this.state = {
       hash: window.location.hash,
     };
 
+    this.boundOnHashChanged = this.onHashChanged.bind(this);
+
     this.showLocalStorageUsageWarning = throttle(
       this.showLocalStorageUsageWarning,
       LOCALSTORAGE_WARNING_MESSAGE_THROTTLE_MS,
@@ -131,7 +135,7 @@ class App extends PureComponent<AppProps, AppState> {
   }
 
   componentDidMount() {
-    window.addEventListener('hashchange', this.onHashChanged.bind(this));
+    window.addEventListener('hashchange', this.boundOnHashChanged);
 
     // Horrible hack to disable side swipe navigation when in SQL Lab. Even 
though the
     // docs say setting this style on any div will prevent it, turns out it 
only works
@@ -165,7 +169,7 @@ class App extends PureComponent<AppProps, AppState> {
   }
 
   componentWillUnmount() {
-    window.removeEventListener('hashchange', this.onHashChanged.bind(this));
+    window.removeEventListener('hashchange', this.boundOnHashChanged);
 
     // And now we need to reset the overscroll behavior back to the default.
     document.body.style.overscrollBehaviorX = 'auto';
diff --git 
a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx 
b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
index e63da7ad287..40c11f8a7d7 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
@@ -102,6 +102,8 @@ const SqlEditorTabHeader: FC<Props> = ({ queryEditor }) => {
   );
 
   function renameTab() {
+    // TODO: Replace native prompt with a proper modal dialog
+    // eslint-disable-next-line no-alert
     const newTitle = prompt(t('Enter a new title for the tab'));
     if (newTitle) {
       actions.queryEditorSetTitle(qe, newTitle, qe.id);

Reply via email to