diff --git a/.editorconfig b/.editorconfig index f654551..4bd3bd8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,12 @@ -[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}] +root = true + +[*] charset = utf-8 -indent_size = 2 indent_style = space +indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2df27f4..055cb8d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,10 +4,8 @@ ## Vérifié -- [ ] `pnpm lint` -- [ ] `pnpm typecheck` -- [ ] `pnpm test` -- [ ] `pnpm build` — le seul à typer `src/` +- [ ] `pnpm verifie` — lint, typage et tests +- [ ] `pnpm build` — Quasar sait produire le bundle - [ ] Essayé dans l'application, pas seulement en test ## À savoir diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ed7d75..5d450c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,10 @@ # système de fichiers, les noms courts 8.3, l'énumération des processus. Les faire tourner # ailleurs vérifierait autre chose que ce qui est livré. # -# `build` en plus de `typecheck` : `pnpm typecheck` ne couvre que `server/` et `test/`. Le -# front n'est typé que par `vue-tsc`, que seul le build déclenche. Sans cette étape, une -# rupture de types dans `src/` passerait la CI. +# `build` en plus de `typecheck` : depuis la mise au socle, `pnpm typecheck` ouvre la marche +# par `vue-tsc` et couvre donc le front, ce qu'il ne faisait pas — le build ne rattrape plus +# une rupture de types. Il reste parce qu'il vérifie autre chose : que Quasar sait produire +# le bundle, résoudre les alias et les imports dynamiques du manuel. name: CI @@ -27,16 +28,14 @@ jobs: steps: - uses: actions/checkout@v7 - # 11 et non 10 : les gardes de `pnpm-workspace.yaml` sont des fonctions de - # pnpm 11. Sous la 10, `pnpm install` ne vérifie pas le lockfile contre - # elles — la quarantaine ne protégeait donc que la machine qui résout, et - # un lockfile fabriqué ailleurs avec une dérogation locale passait la CI - # sans un mot. C'est aussi la version qui écrit le lockfile en local : - # laisser la CI le relire avec une majeure de retard est une panne qui - # attend son heure. + # Pas de version ici : `packageManager` dans `package.json` la porte + # désormais, et l'action refuse de démarrer si les deux se contredisent. + # Ce qui se joue sur ce numéro : les gardes de `pnpm-workspace.yaml` sont + # des fonctions de pnpm 11. Sous la 10, `pnpm install` ne vérifie pas le + # lockfile contre elles — la quarantaine ne protégeait donc que la machine + # qui résout, et un lockfile fabriqué ailleurs avec une dérogation locale + # passait la CI sans un mot. - uses: pnpm/action-setup@v6 - with: - version: 11 - uses: actions/setup-node@v7 with: diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index e0847c6..0440e5b 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -23,11 +23,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - # Même version que `ci.yml`, et pour la même raison : voir le commentaire - # qui s'y trouve. + # Même version que `ci.yml`, et par le même chemin : `packageManager` dans + # `package.json`. Voir le commentaire qui s'y trouve. - uses: pnpm/action-setup@v6 - with: - version: 11 - uses: actions/setup-node@v7 with: node-version: 24.19.0 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a951207 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,17 @@ +# Regénéré par pnpm à chaque `pnpm add` : le formater le remet en écart au +# geste suivant, pour un fichier que personne ne lit à la main. +pnpm-lock.yaml + +# Ce que le dépôt ne versionne pas. `prettier .` ne lit pas le .gitignore : sans +# ces lignes, il reformaterait la sortie de build, la copie du manuel recopiée +# dans le site vitrine et le harnais Playwright. +dist/ +.quasar/ +harness/ +site/guide/ +site/en/guide/ +site/.vitepress/cache/ +site/.vitepress/dist/ +llm règles/ +idea/ +.local/ diff --git a/.prettierrc.json b/.prettierrc.json index 4ab7f6d..c0ab21d 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,9 @@ { "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, "singleQuote": true, - "printWidth": 100 + "endOfLine": "auto", + "printWidth": 150, + "vueIndentScriptAndStyle": true, + "trailingComma": "all" } diff --git a/CHANGELOG.md b/CHANGELOG.md index bdadf69..5f095a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,33 @@ par cas. Une version se pose quand il y a quelque chose à annoncer, pas à chaque fusion. Une journée entière de montées de dépendances n'en produit aucune. +## [1.3.1] — 2026-08-25 + +Une version d'entretien : rien de visible à l'écran, tout sous le capot. Elle réunit deux +chantiers — ce qu'il faut pour installer AURA sans surprise, et l'adoption du socle de +qualité commun aux dépôts du parc. + +### Ce qui change pour vous + +- **L'installation dit ce qu'elle exige.** Le gestionnaire de paquets est épinglé dans le + manifeste, le socle exigé nomme la version que le projet fige, et la politique + d'installation dit ce qu'elle refuse plutôt que de le laisser découvrir à l'échec. +- **Les dépendances sont à jour.** Quasar, Vite, Fastify, Vitest, ESLint, vue-i18n, mermaid, + dompurify et le reste. Aucune ne change ce que vous voyez. + +### Sous le capot + +- **Le typecheck ne laisse plus rien dehors.** `pnpm typecheck` ne regardait que le BFF et + les tests : une rupture de types dans un composant du front passait pour vérifiée. Il + ouvre maintenant la marche par `vue-tsc`. +- **Un seul geste avant de pousser.** `pnpm verifie` enchaîne le lint, le typage et les + tests. `pnpm lint` relit les 384 fichiers du dépôt au lieu de quatre dossiers. +- **La mise en forme est vérifiée, pas seulement disponible.** Prettier est lancé comme une + règle ESLint, aux réglages du parc. Le dépôt est reformaté en entier. +- **Des règles de style que le dépôt ne portait pas.** `eqeqeq`, `curly`, `func-style`, + `no-console`, `ban-ts-comment`, plus les règles Vue et Vitest du parc. Le lint ne relève + plus rien : zéro erreur, zéro avertissement. + ## [1.3.0] — 2026-08-20 Une version d'une seule capacité, et elle est grande : la **Passerelle**, qui relie une @@ -173,6 +200,7 @@ collées, reprise d'une session existante. externe, aucun secret dans le navigateur. Voir [SECURITY.md](SECURITY.md). - Windows, exclusivement — la seule plateforme sur laquelle l'application a tourné. +[1.3.1]: https://github.com/Shaenn/aura/releases/tag/v1.3.1 [1.3.0]: https://github.com/Shaenn/aura/releases/tag/v1.3.0 [1.2.0]: https://github.com/Shaenn/aura/releases/tag/v1.2.0 [1.1.0]: https://github.com/Shaenn/aura/releases/tag/v1.1.0 diff --git a/CLAUDE.md b/CLAUDE.md index 506271c..de5a40f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,14 +42,23 @@ un téléphone, ce qui supposerait de l'adapter à cet écran. ```bash pnpm dev:all # front :9100 + BFF :8800 (proxy /api → :8800) pnpm test # vitest, environnement node, test/**/*.test.ts -pnpm typecheck # tsc sur server/ et test/ — ne couvre PAS src/ -pnpm lint -pnpm format +pnpm typecheck # vue-tsc sur src/, puis tsc sur server/ et test/ +pnpm lint # eslint . — 384 fichiers, mise en forme comprise +pnpm lint:fix +pnpm format # prettier . --write puis pnpm lint:fix +pnpm format:check +pnpm verifie # lint + typecheck + test : le geste avant de pousser ``` -`src/` est typé par `vue-tsc` via `vite-plugin-checker`, donc uniquement pendant -`pnpm dev` ou `pnpm build`. Une modification du front qui casse les types ne sera **pas** -vue par `pnpm typecheck`. +`pnpm typecheck` couvre tout ce que le dépôt contient : le front par `vue-tsc`, le BFF et +les tests par `tsc`. `vite-plugin-checker` continue de typer `src/` pendant `pnpm dev`, +mais il ne rattrape plus rien que la ligne de commande ne verrait pas. + +Prettier est lancé **comme une règle ESLint** (`eslint-plugin-prettier/recommended`) : la +mise en forme se relève par `pnpm lint` et se corrige par le même `--fix`. Une seule +réserve : le correcteur de `local/import-order` remplace le bloc des imports par les seuls +imports triés, et efface ce qui vit entre eux — un `export … from`, un commentaire de +groupe. Ordonner les imports à la main plutôt que de laisser `pnpm format` s'en charger. `server/` et `test/` ont leur propre `tsconfig.json` (Node, pas de `lib: dom`) ; la racine les exclut. `exactOptionalPropertyTypes` est actif côté `src/`, inactif côté `server/`. diff --git a/CONTRIBUTING.en.md b/CONTRIBUTING.en.md index 309a31e..e8ef29c 100644 --- a/CONTRIBUTING.en.md +++ b/CONTRIBUTING.en.md @@ -175,14 +175,15 @@ rather than copying their markup/CSS. ## Quality ```bash -pnpm lint # ESLint -pnpm format # Prettier -pnpm typecheck # tsc over server/ and test/ +pnpm verifie # lint + typecheck + test — run this before pushing +pnpm lint # ESLint, formatting included +pnpm format # Prettier, then ESLint --fix +pnpm typecheck # vue-tsc over src/, then tsc over server/ and test/ pnpm test # vitest ``` -`src/` is **not** covered by `pnpm typecheck`: the front is typed by `vue-tsc` through -`vite-plugin-checker`, therefore during `pnpm dev` or `pnpm build`. +`pnpm typecheck` covers the front, the BFF and the tests. Prettier runs as an ESLint rule: +formatting is reported by `pnpm lint` and fixed by the same `--fix`. `server/` and `test/` have their own `tsconfig.json` (Node, `lib: esnext` without DOM, `.ts` extensions allowed on import); the root tsconfig excludes them. `exactOptionalPropertyTypes` is on diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00f4416..9982955 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -174,14 +174,15 @@ plutôt que de recopier leur markup/CSS. ## Qualité ```bash -pnpm lint # ESLint -pnpm format # Prettier -pnpm typecheck # tsc sur server/ et test/ +pnpm verifie # lint + typecheck + test — le geste avant de pousser +pnpm lint # ESLint, mise en forme comprise +pnpm format # Prettier puis ESLint --fix +pnpm typecheck # vue-tsc sur src/, puis tsc sur server/ et test/ pnpm test # vitest ``` -`src/` n'est **pas** couvert par `pnpm typecheck` : le front est typé par `vue-tsc` via -`vite-plugin-checker`, donc pendant `pnpm dev` ou `pnpm build`. +`pnpm typecheck` couvre le front, le BFF et les tests. Prettier est lancé comme une règle +ESLint : la mise en forme se relève par `pnpm lint` et se corrige par le même `--fix`. `server/` et `test/` ont leur propre `tsconfig.json` (Node, `lib: esnext` sans DOM, extensions `.ts` autorisées à l'import) ; le tsconfig racine les exclut. `exactOptionalPropertyTypes` est actif côté diff --git a/INSTALL.en.md b/INSTALL.en.md index 80d7162..5a88ade 100644 --- a/INSTALL.en.md +++ b/INSTALL.en.md @@ -11,7 +11,7 @@ system. ## Requirements - **Node.js 24** installed on the machine. -- **pnpm** ≥ 10 (`npm i -g pnpm`). +- **pnpm** ≥ 11 (`npm i -g pnpm`). - **Claude Code** installed and run at least once, with an existing **`~/.claude`** folder (on Windows: `C:\Users\\.claude`). That folder is what AURA reads and changes. diff --git a/INSTALL.md b/INSTALL.md index 0d9c534..b7a879c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -11,7 +11,7 @@ supposent ce système. ## Pré-requis - **Node.js 24** installé sur le poste. -- **pnpm** ≥ 10 (`npm i -g pnpm`). +- **pnpm** ≥ 11 (`npm i -g pnpm`). - **Claude Code** installé et lancé au moins une fois, avec un dossier **`~/.claude`** existant (sous Windows : `C:\Users\\.claude`). C'est ce dossier qu'AURA lit et modifie. diff --git a/README.en.md b/README.en.md index 81afefe..60cd4d5 100644 --- a/README.en.md +++ b/README.en.md @@ -308,7 +308,9 @@ report a flaw. - **Node.js 24** — a single major version: the one the application is developed, tested and shipped on, and the one CI runs. Node 24 is under long-term support. -- **pnpm** ≥ 10 — `npm i -g pnpm`. +- **pnpm** ≥ 11 — `npm i -g pnpm`. The exact version is pinned by `packageManager` in + `package.json`: the guards in `pnpm-workspace.yaml` are pnpm 11 features, and an earlier + major would ignore them silently. - **Claude Code** installed and run at least once, so an existing `~/.claude` folder. On Windows: `C:\Users\\.claude`. @@ -478,8 +480,8 @@ accessibility and the write contract, and they are documented. Issues and PRs ar either language. ```bash -pnpm lint -pnpm typecheck # server/ and test/ +pnpm verifie # lint + typecheck + test +pnpm typecheck # src/, server/ and test/ pnpm test # vitest ``` diff --git a/README.md b/README.md index c293be8..4db609b 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,9 @@ comment signaler une faille. - **Node.js 24** — une seule version majeure, celle sur laquelle l'application est développée, testée et livrée. C'est aussi celle que la CI fait tourner. Node 24 est en support long terme. -- **pnpm** ≥ 10 — `npm i -g pnpm`. +- **pnpm** ≥ 11 — `npm i -g pnpm`. La version exacte est épinglée par `packageManager` dans + `package.json` : les gardes de `pnpm-workspace.yaml` sont des fonctions de pnpm 11, et une + majeure antérieure les ignorerait en silence. - **Claude Code** installé et lancé au moins une fois, donc un dossier `~/.claude` existant — `C:\Users\\.claude`. @@ -525,8 +527,8 @@ Les issues et les pull requests sont bienvenues. Avant d'ouvrir une PR, lisez design, l'accessibilité et le contrat d'écriture, et elles sont documentées. ```bash -pnpm lint -pnpm typecheck # server/ et test/ +pnpm verifie # lint + typecheck + test +pnpm typecheck # src/, server/ et test/ pnpm test # vitest ``` diff --git a/eslint-rules/import-order.js b/eslint-rules/import-order.js new file mode 100644 index 0000000..59f1c4a --- /dev/null +++ b/eslint-rules/import-order.js @@ -0,0 +1,113 @@ +import { builtinModules } from 'node:module' + +const builtins = new Set(builtinModules.flatMap((m) => [m, `node:${m}`])) + +/** + * @typedef {'builtin' | 'external' | 'parent' | 'sibling' | 'index'} ImportGroup + */ + +/** @type {ImportGroup[]} */ +const GROUP_ORDER = ['builtin', 'external', 'parent', 'sibling', 'index'] + +/** + * @param {string} source + * @returns {ImportGroup} + */ +function getGroup(source) { + if (builtins.has(source)) { + return 'builtin' + } + if (source === '.' || source === './' || source === './index') { + return 'index' + } + if (source.startsWith('./')) { + return 'sibling' + } + if (source.startsWith('../')) { + return 'parent' + } + return 'external' +} + +/** + * @param {string} source + * @returns {number} + */ +function groupRank(source) { + return GROUP_ORDER.indexOf(getGroup(source)) +} + +/** + * @param {import('estree').ImportDeclaration} a + * @param {import('estree').ImportDeclaration} b + * @returns {number} + */ +function compareImports(a, b) { + const srcA = /** @type {string} */ (a.source.value) + const srcB = /** @type {string} */ (b.source.value) + const rankDiff = groupRank(srcA) - groupRank(srcB) + if (rankDiff !== 0) { + return rankDiff + } + return srcA.localeCompare(srcB) +} + +/** @type {import('eslint').Rule.RuleModule} */ +const rule = { + meta: { + type: 'suggestion', + docs: { + description: 'Enforce import ordering by group (builtin, external, parent, sibling, index) then alphabetically within each group', + }, + fixable: 'code', + schema: [], + messages: { + outOfOrder: "'{{ current }}' ({{ currentGroup }}) should come before '{{ previous }}' ({{ previousGroup }}).", + }, + }, + + create(context) { + return { + Program(node) { + /** @type {import('estree').ImportDeclaration[]} */ + const imports = node.body.filter((/** @type {import('estree').Statement} */ s) => s.type === 'ImportDeclaration') + + if (imports.length < 2) { + return + } + + for (let i = 1; i < imports.length; i++) { + if (compareImports(imports[i - 1], imports[i]) > 0) { + const prev = /** @type {string} */ (imports[i - 1].source.value) + const curr = /** @type {string} */ (imports[i].source.value) + + context.report({ + node: imports[i], + messageId: 'outOfOrder', + data: { + current: curr, + currentGroup: getGroup(curr), + previous: prev, + previousGroup: getGroup(prev), + }, + fix(fixer) { + const sourceCode = context.sourceCode + const sorted = [...imports].sort(compareImports) + + /** @type {[number, number]} */ + const originalRange = [imports[0].range[0], imports[imports.length - 1].range[1]] + + const sortedText = sorted.map((imp) => sourceCode.getText(imp)).join('\n') + + return fixer.replaceTextRange(originalRange, sortedText) + }, + }) + break + } + } + }, + } + }, +} + +export default rule diff --git a/eslint.config.js b/eslint.config.js index 5896f4f..92df764 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,91 +1,151 @@ -import js from '@eslint/js'; -import globals from 'globals'; -import pluginVue from 'eslint-plugin-vue'; -import pluginQuasar from '@quasar/app-vite/eslint'; -import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'; -import prettierSkipFormatting from '@vue/eslint-config-prettier/skip-formatting'; - -export default defineConfigWithVueTs( +import js from '@eslint/js' +import vitestConfig from '@lehoczky/eslint-config-vitest' +import pluginQuasar from '@quasar/app-vite/eslint' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' +import pluginPrettier from 'eslint-plugin-prettier/recommended' +import pluginVue from 'eslint-plugin-vue' +import globals from 'globals' +import importOrder from './eslint-rules/import-order.js' + +export default [ { - /** - * Ignore the following files. - * Please note that pluginQuasar.configs.recommended() already ignores - * the "node_modules" folder for you (and all other Quasar project - * relevant folders and files). - * - * ESLint requires "ignores" key to be the only one in this object - */ - // ignores: [] + // `pluginQuasar.configs.recommended()` ignore déjà node_modules, .quasar et dist. + // Restent les dossiers que le dépôt ne versionne pas — le harnais Playwright, le + // site vitrine et les notes de travail : les relire n'apprendrait rien, et leur + // donner un tsconfig pour que les règles typées s'y exécutent en apprendrait moins + // encore. + name: 'aura/hors-perimetre', + ignores: ['harness/**', 'site/**', 'llm règles/**', 'idea/**', '.local/**'], }, - pluginQuasar.configs.recommended(), + ...pluginQuasar.configs.recommended(), + + // Les règles cœur d'ESLint. En configuration à plat, ESLint n'active RIEN de + // lui-même : sans cette ligne, `no-debugger`, `no-duplicate-case`, + // `no-constant-condition` et une quarantaine d'autres ne tournent tout + // simplement pas. js.configs.recommended, - /** - * https://eslint.vuejs.org - * - * pluginVue.configs.base - * -> Settings and rules to enable correct ESLint parsing. - * pluginVue.configs[ 'flat/essential'] - * -> base, plus rules to prevent errors or unintended behavior. - * pluginVue.configs["flat/strongly-recommended"] - * -> Above, plus rules to considerably improve code readability and/or dev experience. - * pluginVue.configs["flat/recommended"] - * -> Above, plus rules to enforce subjective community defaults to ensure consistency. - */ - pluginVue.configs['flat/essential'], + // Tout ce qui touche aux règles TYPÉES passe à l'intérieur de + // `defineConfigWithVueTs`, y compris nos propres surcharges. + // + // Ce n'est pas cosmétique. Le paquet énumère les .vue sans bloc diff --git a/src/components/ConfirmDiffDialog.vue b/src/components/ConfirmDiffDialog.vue index 62ad3a6..4488259 100644 --- a/src/components/ConfirmDiffDialog.vue +++ b/src/components/ConfirmDiffDialog.vue @@ -20,9 +20,7 @@
{{ t('diff.noChange') }}
- + {{ l.text || ' ' }}
@@ -50,168 +48,159 @@ diff --git a/src/components/OrphanPlans.vue b/src/components/OrphanPlans.vue index 95dc048..8d7eea7 100644 --- a/src/components/OrphanPlans.vue +++ b/src/components/OrphanPlans.vue @@ -35,9 +35,7 @@ @click="select(p.name)" > {{ p.title }} - - {{ fmtDate(p.mtime) }} · {{ fmtBytes(p.size) }} - + {{ fmtDate(p.mtime) }} · {{ fmtBytes(p.size) }} @@ -49,20 +47,10 @@