Files
GeoLibre/eslint.config.mjs
Qiusheng Wu 2fc35306ee fix(map): grid label font, Overture backfill on Mapbox, Maptoolkit logo (#2910)
- DGGS grid plugins re-apply the label font after a basemap change, so a
  label layer that survives the style swap stops asking the new glyph
  server for the old basemap's font stack.
- The vector-tile backfill samples the Overture control's own
  overture-<theme> source on Mapbox (the engine never compiles those
  rows), instead of a geolibre-mapbox-<id> source that does not exist.
- New Project keeps the Maptoolkit logo and its checkmark when the new
  project opens on a Maptoolkit basemap.
- Each new map engine gets the Maptoolkit logo state once, so a
  renderer swap no longer drops it.
- Annotate the place-search Cesium camera check and drop its
  no-renderer-kind-checks exclusion.

Part of #2869
2026-10-04 09:31:58 -04:00

159 lines
5.5 KiB
JavaScript

// 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",
},
},
];