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