@lifeofqui/lint-config 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +21 -5
  2. package/eslint.js +33 -0
  3. package/package.json +9 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @lifeofqui/lint-config
2
2
 
3
- Gemeinsames Lint- und Format-Setup für Vue-/Nuxt-Projekte: **Oxlint** für JS/TS, **ESLint** nur für das, was Oxlint nicht kann (Vue-Templates/SFCs, AST-Selector-Regeln), **Oxfmt** zum Formatieren.
3
+ Gemeinsames Lint- und Format-Setup für Vue-/Nuxt-Projekte: **Oxlint** für JS/TS, **ESLint** nur für das, was Oxlint nicht kann (Vue-Templates/SFCs inkl. Barrierefreiheit, AST-Selector-Regeln), **Oxfmt** zum Formatieren.
4
4
 
5
5
  Das Paket bringt Regeln **und** Tool-Versionen mit: `eslint`, `oxlint` und `oxfmt` sind exakt gepinnte Dependencies. Ein Update dieses Pakets aktualisiert also Regeln und Tools in einem Schritt.
6
6
 
@@ -82,13 +82,29 @@ Optionen von `defineLintConfig`:
82
82
 
83
83
  ## Updates verteilen
84
84
 
85
- 1. Regel oder Tool-Version hier ändern (Tool-Versionen immer exakt pinnen).
86
- 2. Lokal testen: `npm pack`, im Projekt `npm i -D ../lint-config/lifeofqui-lint-config-<version>.tgz`, dann `npm run lint:check`.
87
- 3. `npm version patch|minor|major` und danach `npm publish`.
88
- 4. In den Projekten öffnet Dependabot einen PR (`.github/dependabot.yml`), die CI prüft ihn mit `npm run lint:check`.
85
+ Tool-Updates kommen per Dependabot (`.github/dependabot.yml`): wöchentlich ein gesammelter PR für alle Minor- und Patch-Updates, Majors als eigene PRs. Regeländerungen werden direkt hier gemacht. Tool-Versionen immer exakt pinnen.
86
+
87
+ 1. Die CI prüft jeden PR mit `npm test` (`test/smoke.sh`). Das Skript packt das Paket, installiert es in eine Kopie von `test/fixture` und prüft, dass `eslint`, `oxlint` und `oxfmt` in `node_modules/.bin` liegen. Dann laufen alle drei Tools über das Fixture, und Oxlint und ESLint müssen bekannte Verstöße melden.
88
+ 2. Lokal gegen echten Code testen: auf `main` (der Version, von der aus aktualisiert wird) `test/sync-projects.sh` ausführen. Das Skript kopiert aus jedem Nachbarprojekt, das dieses Paket nutzt (`../*`, `../*/*`), die größte Datei je Ordner (`app/pages`, `app/components`, `server`, `tests`, …) nach `test/fixture/**/_local/`. Dort werden die Dateien mit der aktuellen Version formatiert und gefixt. Was danach noch fehlschlägt, fliegt raus. Die Kopien sind gitignored, weil die Projekte privat sind. Dann auf den Update-Branch wechseln und `npm test` ausführen: Jeder Fehler kommt jetzt vom Update.
89
+ 3. Bei größeren Änderungen zusätzlich in einem echten Projekt testen: `npm pack`, dort `npm i -D ../lint-config/lifeofqui-lint-config-<version>.tgz`, dann `npm run lint:check` und `npm run build`.
90
+ 4. Nach dem Merge lokal auf `main`: `npm version patch|minor|major` und `git push --follow-tags`. Der Tag startet `.github/workflows/publish.yml`. Der Workflow lässt `npm test` laufen und legt die Version dann per npm Trusted Publishing als Staged Publish ab.
91
+ 5. Freigeben: auf npmjs.com im Tab „Staged Packages“ auf Approve klicken, oder `npm stage approve <stage-id>` (beides mit 2FA). Erst dann ist die Version live.
92
+ 6. In den Projekten öffnet Dependabot einen PR, die CI prüft ihn mit `npm run lint:check`.
93
+
94
+ Einmalig auf npmjs.com einrichten: Paket → Settings → Trusted Publisher → GitHub Actions mit Owner `LifeOfQui`, Repository `lint-config` und Workflow `publish.yml`. „Allow npm publish“ bleibt aus, damit jede Version die Freigabe mit 2FA braucht.
89
95
 
90
96
  Semver: neue oder verschärfte Regeln und Tool-Updates mit Formatierungsänderungen → `minor`, Lockerungen und Fixes → `patch`, geänderte Datei-Struktur oder Optionen → `major`.
91
97
 
98
+ ## Barrierefreiheit
99
+
100
+ `eslint-plugin-vuejs-accessibility` läuft mit einer kuratierten Auswahl über `app/**/*.vue`. Die Auswahl wurde gegen alle Projekte geprüft. Regeln, die dort fast nur Fehlalarme liefern, sind nicht dabei (Begründung als Kommentar in `eslint.js`). Die meisten Treffer kommen von `label-has-for` und `form-control-has-label`: ein `<label>` ohne `for` neben einem `<input>` ohne `id`. Fix: das Input ins Label schachteln oder `for`/`id` setzen.
101
+
102
+ Bestehende Treffer in einem Projekt lassen sich bei der Umstellung per Bulk Suppression einfrieren, neuer Code muss die Regeln dann einhalten:
103
+
104
+ ```bash
105
+ npx eslint . --suppress-rule vuejs-accessibility/label-has-for --suppress-rule vuejs-accessibility/form-control-has-label
106
+ ```
107
+
92
108
  ## Stolperfalle: Inline-Handler mit mehreren Anweisungen
93
109
 
94
110
  Oxfmt entfernt mit `semi: false` das `;` aus Vue-Handlern wie `@click="a = 1; b = 2"` und schreibt die Anweisungen untereinander. Der Vue-Compiler erkennt mehrere Anweisungen aber nur am `;` und bricht dann den **Build** ab („Error parsing JavaScript expression“). Lint und Typecheck merken davon nichts.
package/eslint.js CHANGED
@@ -3,6 +3,7 @@ import { includeIgnoreFile } from 'eslint/config'
3
3
  import skipFormatting from 'eslint-config-prettier'
4
4
  import pluginOxlint from 'eslint-plugin-oxlint'
5
5
  import pluginVue from 'eslint-plugin-vue'
6
+ import pluginVueA11y from 'eslint-plugin-vuejs-accessibility'
6
7
  import { existsSync } from 'node:fs'
7
8
  import { resolve } from 'node:path'
8
9
  import { fileURLToPath } from 'node:url'
@@ -95,6 +96,38 @@ export function defineLintConfig({
95
96
  },
96
97
  },
97
98
 
99
+ // Accessibility (curated): accessible names keep e2e locators role-based. Left out for noise in
100
+ // real projects: key-event rules (modal backdrops), no-onchange (deprecated), media-has-caption,
101
+ // no-autofocus, interactive-supports-focus (flags aria-activedescendant listboxes).
102
+ {
103
+ files: ['app/**/*.vue'],
104
+ plugins: { 'vuejs-accessibility': pluginVueA11y },
105
+ rules: {
106
+ 'vuejs-accessibility/alt-text': 'error',
107
+ 'vuejs-accessibility/anchor-has-content': 'error',
108
+ 'vuejs-accessibility/aria-props': 'error',
109
+ 'vuejs-accessibility/aria-role': 'error',
110
+ 'vuejs-accessibility/aria-unsupported-elements': 'error',
111
+ 'vuejs-accessibility/form-control-has-label': 'error',
112
+ 'vuejs-accessibility/heading-has-content': 'error',
113
+ 'vuejs-accessibility/iframe-has-title': 'error',
114
+ 'vuejs-accessibility/label-has-for': [
115
+ 'error',
116
+ {
117
+ // PrimeVue form controls
118
+ controlComponents: ['Checkbox', 'RadioButton', 'ToggleSwitch'],
119
+ required: { some: ['nesting', 'id'] },
120
+ },
121
+ ],
122
+ 'vuejs-accessibility/no-access-key': 'error',
123
+ 'vuejs-accessibility/no-aria-hidden-on-focusable': 'error',
124
+ 'vuejs-accessibility/no-distracting-elements': 'error',
125
+ 'vuejs-accessibility/no-role-presentation-on-focusable': 'error',
126
+ 'vuejs-accessibility/role-has-required-aria-props': 'error',
127
+ 'vuejs-accessibility/tabindex-no-positive': 'error',
128
+ },
129
+ },
130
+
98
131
  // Nuxt page and layout transitions need a single root element
99
132
  {
100
133
  files: ['app/pages/**/*.vue', 'app/layouts/**/*.vue'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeofqui/lint-config",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Shared ESLint, Oxlint and Oxfmt setup for Vue/Nuxt projects",
5
5
  "license": "MIT",
6
6
  "author": "Quirin Koch",
@@ -31,14 +31,18 @@
31
31
  "publishConfig": {
32
32
  "access": "public"
33
33
  },
34
+ "scripts": {
35
+ "test": "test/smoke.sh"
36
+ },
34
37
  "dependencies": {
35
38
  "@vue/eslint-config-typescript": "14.9.0",
36
- "eslint": "10.13.0",
39
+ "eslint": "10.14.0",
37
40
  "eslint-config-prettier": "10.1.8",
38
- "eslint-plugin-oxlint": "1.90.0",
41
+ "eslint-plugin-oxlint": "1.92.0",
39
42
  "eslint-plugin-vue": "10.12.0",
40
- "oxfmt": "0.75.0",
41
- "oxlint": "1.90.0"
43
+ "eslint-plugin-vuejs-accessibility": "2.6.3",
44
+ "oxfmt": "0.77.0",
45
+ "oxlint": "1.92.0"
42
46
  },
43
47
  "engines": {
44
48
  "node": ">=22.18"