Repository navigation
Expand file tree
/
Copy patheslint.config.mjs
More file actions
158 lines (156 loc) · 5.51 KB
/
Copy patheslint.config.mjs
File metadata and controls
158 lines (156 loc) · 5.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// ESLint flat config.
//
// Errors are reserved for bugs `tsc` cannot see: a misplaced Hook (e.g. a
// `useMemo` after an early `return`) type-checks but crashes the app at
// runtime with "Rendered more hooks than during the previous render", and
// `react-hooks/rules-of-hooks` catches exactly that class of bug statically.
//
// Everything else is a warning held by a ratchet: `npm run lint` passes
// `--max-warnings` set to today's count, so the number can only go down. When
// you fix warnings, lower the limit in package.json to the new count in the
// same PR (the lint output prints it). Never raise it to make a PR pass; fix
// the new warning or disable it on that line with a reason.
import jsxA11y from "eslint-plugin-jsx-a11y";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";
import local from "./eslint-rules/index.mjs";
// Source compiled into the app, the packages and the workers. Type-aware rules
// run only here: each file is checked against the tsconfig.json nearest to it
// (typescript-eslint's project service), and scripts/tests/configs outside a
// tsconfig are left to the syntactic rules.
const TYPED_SOURCES = [
"apps/geolibre-desktop/src/**/*.{ts,tsx}",
"packages/*/src/**/*.{ts,tsx}",
"workers/*/src/**/*.ts",
];
export default [
{
ignores: [
"**/dist/**",
"**/dist-embed/**",
"**/build/**",
"**/target/**",
"**/node_modules/**",
// Generated/vendored bundles (the embedded web app baked into the Python
// wheel, the built docs site, minified assets) must never be parsed.
"python/**",
"site/**",
"**/static/**",
"**/public/plugins/**",
"**/*.min.js",
"**/*.d.ts",
],
},
{
files: ["**/*.{ts,tsx,js,jsx,mjs,cjs}"],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: { jsx: true },
},
},
// The full @typescript-eslint plugin is registered so that existing
// `// eslint-disable @typescript-eslint/*` comments resolve to a known
// rule instead of erroring as "rule definition not found".
plugins: {
"react-hooks": reactHooks,
"@typescript-eslint": tseslint.plugin,
local,
},
// Only a handful of rules are on, so existing disable directives that
// anticipate a fuller ruleset would otherwise be reported as unused.
linterOptions: {
reportUnusedDisableDirectives: "off",
},
rules: {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
},
{
files: ["**/*.{ts,tsx}"],
rules: {
"@typescript-eslint/no-explicit-any": "warn",
},
},
{
// Accessibility: an icon-only control needs an accessible name, and a
// click handler on a plain element needs a role and keyboard support.
// eslint-plugin-jsx-a11y 6.10 declares ESLint <= 9 as a peer; it runs
// under ESLint 10, and the root package.json `overrides` entry points its
// peer at the installed ESLint.
files: ["apps/**/*.{tsx,jsx}", "packages/**/*.{tsx,jsx}"],
plugins: { "jsx-a11y": jsxA11y },
rules: {
// Form fields are labelled by a wrapping <label> or `htmlFor`, which this
// rule cannot see, so they are ignored here (the plugin's recommended
// ignoreElements); it targets unlabelled buttons, links and ARIA controls.
"jsx-a11y/control-has-associated-label": [
"warn",
{
ignoreElements: ["audio", "canvas", "embed", "input", "textarea", "tr", "video"],
ignoreRoles: [
"grid",
"listbox",
"menu",
"menubar",
"radiogroup",
"row",
"tablist",
"toolbar",
"tree",
"treegrid",
],
includeRoles: ["alert", "dialog"],
depth: 7,
},
],
"jsx-a11y/no-static-element-interactions": "warn",
},
},
{
// The UI mirrors for right-to-left locales; see docs/i18n.md.
files: ["apps/**/*.{ts,tsx,jsx}", "packages/**/*.{ts,tsx,jsx}"],
rules: {
"local/no-physical-tailwind": "warn",
},
},
{
// The map engines in @geolibre/map share code only through neutral
// modules; see eslint-rules/no-cross-engine-imports.mjs for which files
// belong to which engine. An error, not a ratcheted warning: the boundary
// has no exceptions, and a warning could hide behind an unrelated fix.
files: ["packages/map/src/**/*.{ts,tsx}"],
rules: {
"local/no-cross-engine-imports": "error",
},
},
{
// UI and plugins gate on MapEngineCapabilities, not on the renderer's name;
// see eslint-rules/no-renderer-kind-checks.mjs. packages/map hosts the
// engines and is where a name legitimately maps to behaviour. An error, so
// a new check either uses a capability or states why it is about the kind.
files: ["apps/*/src/**/*.{ts,tsx}", "packages/*/src/**/*.{ts,tsx}"],
ignores: ["packages/map/**"],
rules: {
"local/no-renderer-kind-checks": "error",
},
},
{
files: TYPED_SOURCES,
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// A promise nobody awaits or catches turns a rejection into an
// unhandled one: the failure never reaches the user or the diagnostics
// log. Mark a deliberate fire-and-forget with `void`.
"@typescript-eslint/no-floating-promises": "warn",
},
},
];