Skip to content

Commit 2aa9c41

Browse files
loganrosenCopilot
andcommitted
Drop eslint-config-airbnb-base and cherry-pick rules
Remove the unmaintained eslint-config-airbnb-base dependency (last updated Nov 2021, no flat config support) along with its FlatCompat shim infrastructure. Replace with js.configs.recommended as the base config and explicitly cherry-pick ~40 high-value safety and style rules from airbnb-base that aren't already covered by other configs. Remove 27 rule disables that only existed to suppress airbnb opinions, and 5 dead TypeScript rule disables for rules no longer in the config. Fix 4 real bugs caught by the newly added no-constant-binary-expression rule where template literals were always truthy, making fallback values unreachable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 96b59c6 commit 2aa9c41

24 files changed

Lines changed: 95 additions & 394 deletions

demo/src/stubs/recorder.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
1515
const generateMeanStatistics = (
1616
start: Date,
1717
end: Date,
18-
// eslint-disable-next-line default-param-last
1918
period: "5minute" | "hour" | "day" | "month" = "hour",
2019
maxDiff: number
2120
): StatisticValue[] => {
@@ -49,7 +48,6 @@ const generateMeanStatistics = (
4948
const generateSumStatistics = (
5049
start: Date,
5150
end: Date,
52-
// eslint-disable-next-line default-param-last
5351
period: "5minute" | "hour" | "day" | "month" = "hour",
5452
initValue: number,
5553
maxDiff: number
@@ -86,7 +84,6 @@ const generateSumStatistics = (
8684
const generateCurvedStatistics = (
8785
start: Date,
8886
end: Date,
89-
// eslint-disable-next-line default-param-last
9087
_period: "5minute" | "hour" | "day" | "month" = "hour",
9188
initValue: number,
9289
maxDiff: number,

eslint.config.mjs

Lines changed: 50 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
import unusedImports from "eslint-plugin-unused-imports";
44
import globals from "globals";
5-
import path from "node:path";
6-
import { fileURLToPath } from "node:url";
75
import js from "@eslint/js";
8-
import { FlatCompat } from "@eslint/eslintrc";
96
import tseslint from "typescript-eslint";
107
import eslintConfigPrettier from "eslint-config-prettier";
118
import { configs as litConfigs } from "eslint-plugin-lit";
@@ -14,35 +11,8 @@ import { configs as a11yConfigs } from "eslint-plugin-lit-a11y";
1411
import html from "@html-eslint/eslint-plugin";
1512
import importX from "eslint-plugin-import-x";
1613

17-
const _filename = fileURLToPath(import.meta.url);
18-
const _dirname = path.dirname(_filename);
19-
const compat = new FlatCompat({
20-
baseDirectory: _dirname,
21-
recommendedConfig: js.configs.recommended,
22-
allConfig: js.configs.all,
23-
});
24-
25-
// Load airbnb-base via FlatCompat for non-import rules only.
26-
// eslint-plugin-import is incompatible with ESLint 10 (uses removed APIs),
27-
// so we strip its plugin/rules/settings and use eslint-plugin-import-x instead.
28-
const airbnbConfigs = compat.extends("airbnb-base").map((config) => {
29-
const { plugins = {}, rules = {}, settings = {}, ...rest } = config;
30-
return {
31-
...rest,
32-
plugins: Object.fromEntries(
33-
Object.entries(plugins).filter(([key]) => key !== "import")
34-
),
35-
rules: Object.fromEntries(
36-
Object.entries(rules).filter(([key]) => !key.startsWith("import/"))
37-
),
38-
settings: Object.fromEntries(
39-
Object.entries(settings).filter(([key]) => !key.startsWith("import/"))
40-
),
41-
};
42-
});
43-
4414
export default tseslint.config(
45-
...airbnbConfigs,
15+
js.configs.recommended,
4616
eslintConfigPrettier,
4717
litConfigs["flat/all"],
4818
tseslint.configs.recommended,
@@ -86,35 +56,61 @@ export default tseslint.config(
8656
},
8757

8858
rules: {
89-
"class-methods-use-this": "off",
90-
"new-cap": "off",
91-
"prefer-template": "off",
92-
"object-shorthand": "off",
93-
"func-names": "off",
94-
"no-underscore-dangle": "off",
95-
strict: "off",
96-
"no-plusplus": "off",
59+
// Cherry-picked safety rules (previously provided by airbnb-base)
60+
"array-callback-return": ["error", { allowImplicit: true }],
61+
"block-scoped-var": "error",
62+
"consistent-return": "error",
63+
curly: ["error", "multi-line"],
64+
"default-case-last": "error",
65+
eqeqeq: ["error", "always", { null: "ignore" }],
66+
"guard-for-in": "error",
67+
"no-await-in-loop": "error",
68+
"no-caller": "error",
69+
"no-constructor-return": "error",
70+
"no-eval": "error",
71+
"no-extend-native": "error",
72+
"no-implied-eval": "error",
73+
"no-iterator": "error",
74+
"no-new-func": "error",
75+
"no-new-wrappers": "error",
76+
"no-octal-escape": "error",
77+
"no-promise-executor-return": "error",
78+
"no-return-assign": ["error", "always"],
79+
"no-script-url": "error",
80+
"no-self-compare": "error",
81+
"no-sequences": "error",
82+
"no-template-curly-in-string": "error",
83+
"no-unreachable-loop": "error",
84+
85+
// Cherry-picked style rules (previously provided by airbnb-base)
86+
"no-else-return": ["error", { allowElseIf: false }],
87+
"no-lonely-if": "error",
88+
"no-unneeded-ternary": ["error", { defaultAssignment: false }],
89+
"no-useless-computed-key": "error",
90+
"no-useless-concat": "error",
91+
"no-useless-rename": "error",
92+
"no-useless-return": "error",
93+
"one-var": ["error", "never"],
94+
"operator-assignment": ["error", "always"],
95+
"prefer-arrow-callback": "error",
96+
"prefer-exponentiation-operator": "error",
97+
"prefer-object-spread": "error",
98+
"prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
99+
"symbol-description": "error",
100+
yoda: "error",
101+
102+
// TODO: Enable once violations are fixed (43 instances as of 2026-04)
103+
// "no-useless-assignment": "error",
104+
"no-useless-assignment": "off",
105+
106+
// Project rules
97107
"no-bitwise": "error",
98-
"comma-dangle": "off",
99-
"vars-on-top": "off",
100-
"no-continue": "off",
101-
"no-param-reassign": "off",
102-
"no-multi-assign": "off",
103108
"no-console": "error",
104-
radix: "off",
105-
"no-alert": "off",
106-
"no-nested-ternary": "off",
107-
"prefer-destructuring": "off",
108109
"no-restricted-globals": [2, "event"],
109-
"prefer-promise-reject-errors": "off",
110110
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
111-
"object-curly-newline": "off",
112-
"default-case": "off",
113111
"wc/no-self-class": "off",
114-
"no-shadow": "off",
115-
"no-use-before-define": "off",
116112

117-
// import-x rules (migrated from eslint-plugin-import / airbnb-base)
113+
// import-x rules
118114
"import-x/named": "off",
119115
"import-x/prefer-default-export": "off",
120116
"import-x/no-default-export": "off",
@@ -146,13 +142,9 @@ export default tseslint.config(
146142
"import-x/no-relative-packages": "error",
147143

148144
// TypeScript rules
149-
"@typescript-eslint/camelcase": "off",
150145
"@typescript-eslint/ban-ts-comment": "off",
151-
"@typescript-eslint/no-use-before-define": "off",
152146
"@typescript-eslint/no-non-null-assertion": "off",
153147
"@typescript-eslint/no-explicit-any": "off",
154-
"@typescript-eslint/explicit-function-return-type": "off",
155-
"@typescript-eslint/explicit-module-boundary-types": "off",
156148
"@typescript-eslint/no-shadow": ["error"],
157149

158150
"@typescript-eslint/naming-convention": [
@@ -216,7 +208,6 @@ export default tseslint.config(
216208
"lit-a11y/role-has-required-aria-attrs": "error",
217209
"@typescript-eslint/consistent-type-imports": "error",
218210
"@typescript-eslint/no-import-type-side-effects": "error",
219-
camelcase: "off",
220211
"@typescript-eslint/no-dynamic-delete": "off",
221212
"@typescript-eslint/no-empty-object-type": [
222213
"error",

gallery/src/pages/components/ha-input.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export class DemoHaInput extends LitElement {
2727
constructor() {
2828
super();
2929
// Provides internationalizationContext for ha-input-copy, ha-input-multi and ha-input-search
30-
// eslint-disable-next-line no-new
3130
new ContextProvider(this, {
3231
context: internationalizationContext,
3332
initialValue: {

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@
136136
"@babel/plugin-transform-runtime": "7.29.0",
137137
"@babel/preset-env": "7.29.2",
138138
"@bundle-stats/plugin-webpack-filter": "4.22.1",
139-
"@eslint/eslintrc": "3.3.5",
140139
"@eslint/js": "10.0.1",
141140
"@html-eslint/eslint-plugin": "0.59.0",
142141
"@lokalise/node-api": "15.7.1",
@@ -169,10 +168,8 @@
169168
"browserslist-useragent-regexp": "4.1.4",
170169
"del": "8.0.1",
171170
"eslint": "10.2.0",
172-
"eslint-config-airbnb-base": "15.0.0",
173171
"eslint-config-prettier": "10.1.8",
174172
"eslint-import-resolver-webpack": "0.13.11",
175-
"eslint-plugin-import": "2.32.0",
176173
"eslint-plugin-import-x": "4.16.2",
177174
"eslint-plugin-lit": "2.2.1",
178175
"eslint-plugin-lit-a11y": "5.1.1",

src/common/dom/ancestors-with-property.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ export const closestWithProperty = (
2121
own
2222
? Object.prototype.hasOwnProperty.call(element, property)
2323
: element && property in element
24-
)
24+
) {
2525
return element;
26+
}
2627
return closestWithProperty(element, property, own);
2728
};
2829

src/common/util/select-unit.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const SECS_PER_HOUR = SECS_PER_MIN * 60;
1919
// Adapted from https://github.com/formatjs/formatjs/blob/186cef62f980ec66252ee232f438a42d0b51b9f9/packages/intl-utils/src/diff.ts
2020
export function selectUnit(
2121
from: Date | number,
22-
// eslint-disable-next-line default-param-last
2322
to: Date | number = Date.now(),
2423
locale: FrontendLocaleData,
2524
thresholds: Partial<Thresholds> = {}

src/components/chart/state-history-chart-line.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ export class StateHistoryChartLine extends LitElement {
128128
if (
129129
dataset.tooltip?.show === false ||
130130
this._hiddenStats.has(dataset.id as string)
131-
)
131+
) {
132132
return;
133+
}
133134
const param = params.find(
134135
(p: Record<string, any>) => p.seriesIndex === index
135136
);

src/components/ha-ansi-to-html.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ export class HaAnsiToHtml extends LitElement {
177177
lineDiv.appendChild(span);
178178
};
179179

180-
/* eslint-disable no-cond-assign */
181180
let match;
182181

183182
while ((match = re.exec(line)) !== null) {

src/components/ha-attribute-value.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class HaAttributeValue extends LitElement {
2626
try {
2727
// If invalid URL, exception will be raised
2828
const url = new URL(attributeValue);
29-
if (url.protocol === "http:" || url.protocol === "https:")
29+
if (url.protocol === "http:" || url.protocol === "https:") {
3030
return html`
3131
<a
3232
target="_blank"
@@ -36,6 +36,7 @@ class HaAttributeValue extends LitElement {
3636
${attributeValue}
3737
</a>
3838
`;
39+
}
3940
} catch {
4041
// Nothing to do here
4142
}

src/components/ha-code-editor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,12 @@ export class HaCodeEditor extends ReactiveElement {
370370
}
371371

372372
private _fullscreenLabel(): string {
373-
if (this._isFullscreen)
373+
if (this._isFullscreen) {
374374
return (
375375
this.hass?.localize("ui.components.yaml-editor.exit_fullscreen") ||
376376
"Exit fullscreen"
377377
);
378+
}
378379
return (
379380
this.hass?.localize("ui.components.yaml-editor.enter_fullscreen") ||
380381
"Enter fullscreen"

0 commit comments

Comments
 (0)