import { Linter } from "eslint"; import { defineConfig, globalIgnores } from "eslint/config"; import globals from "globals"; import * as astroEsLintParser from "astro-eslint-parser"; import astro from "eslint-plugin-astro"; import typeScriptEsLint from "typescript-eslint"; import functional from "eslint-plugin-functional"; import javascript from "@eslint/js"; import jsdoc from "eslint-plugin-jsdoc"; import sonarJs from "eslint-plugin-sonarjs"; import perfectionist from "eslint-plugin-perfectionist"; import unicorn from "eslint-plugin-unicorn"; //#region ../rules/astro.ts const getAstroPlugin = () => { const firstBaseConfig = astro.configs.base.at(0); if (firstBaseConfig === void 0) throw new Error("Impossible de récupérer la configuration de base du plugin Astro."); const astroPlugin = firstBaseConfig.plugins?.["astro"]; if (astroPlugin === void 0) throw new Error("Impossible de récupérer le plugin Astro."); return astroPlugin; }; const astroRules = [{ files: ["**/*.astro"], languageOptions: { globals: { ...globals.astro }, parser: astroEsLintParser, parserOptions: { extraFileExtensions: [".astro"], parser: typeScriptEsLint.parser, project: true, projectService: null, tsconfigRootDir: null }, sourceType: "module" }, name: "Astro", plugins: { astro: getAstroPlugin() }, processor: "astro/client-side-ts", rules: { "astro/jsx-a11y/alt-text": "error", "astro/jsx-a11y/anchor-ambiguous-text": "error", "astro/jsx-a11y/anchor-has-content": "error", "astro/jsx-a11y/anchor-is-valid": "error", "astro/jsx-a11y/aria-activedescendant-has-tabindex": "error", "astro/jsx-a11y/aria-props": "error", "astro/jsx-a11y/aria-proptypes": "error", "astro/jsx-a11y/aria-role": "error", "astro/missing-client-only-directive-value": "error", "astro/no-conflict-set-directives": "error", "astro/no-deprecated-astro-canonicalurl": "error", "astro/no-deprecated-astro-fetchcontent": "error", "astro/no-deprecated-astro-resolve": "error", "astro/no-deprecated-getentrybyslug": "error", "astro/no-exports-from-components": "error", "astro/no-set-html-directive": "error", "astro/no-set-text-directive": "error", "astro/no-unused-css-selector": "error", "astro/no-unused-define-vars-in-style": "error", "astro/prefer-class-list-directive": "error", "astro/prefer-object-class-list": "error", "astro/prefer-split-class-list": ["error", { splitLiteral: false }], "astro/sort-attributes": ["error", { ignoreCase: false, order: "asc", type: "alphabetical" }], "astro/valid-compile": "error" } }, { files: ["**/*.astro/*.ts"], languageOptions: { globals: { ...globals.browser }, parser: typeScriptEsLint.parser, parserOptions: { project: null }, sourceType: "module" }, name: "Astro/TypeScript" }]; //#endregion //#region ../rules/functional.ts const functionalRules = { name: "Programmation fonctionnelle", plugins: { functional }, rules: { ...functional.configs.noExceptions.rules, ...functional.configs.noMutations.rules, ...functional.configs.externalTypeScriptRecommended.rules, ...functional.configs.stylistic.rules, "@typescript-eslint/array-type": ["error", { default: "generic", readonly: "generic" }], "functional/prefer-immutable-types": "off", "functional/prefer-tacit": "off", "functional/readonly-type": ["error", "generic"], "functional/type-declaration-immutability": "off" } }; //#endregion //#region ../rules/javascript.ts const javascriptRules = { name: "JavaScript", rules: { ...javascript.configs.recommended.rules, /** * Impose la présence de déclarations `return` dans les _callbacks_ des méthodes `Array`. * * Il est autorisé ici l'usage de valeurs de retour en `void` et la vérification de valeurs de retour superflues * pour `forEach()`. * * @link [ESLint](https://eslint.org/docs/latest/rules/array-callback-return) */ "array-callback-return": ["error", { allowVoid: true, checkForEach: true }], /** Surchargé par une meilleur version présente dans le plugin _Unicorn_. */ "no-nested-ternary": "off", /** * Interdit l'usage de variables lues sans être préalablement assignées. * * @link [ESLint](https://eslint.org/docs/latest/rules/no-unassigned-vars) */ "no-unassigned-vars": "error", /** * Interdit l'usage d'opérateurs ternaires quand des opérations plus simples existent. Cela inclut ici l'usage de * ternaires comme opération d'assignation par défaut d'une valeur. * * @link [ESLint](https://eslint.org/docs/latest/rules/no-unneeded-ternary) */ "no-unneeded-ternary": ["error", { defaultAssignment: false }], /** * Interdit l'usage de boucles ne permettant qu'une seule itération. Par la nature de l'analyse statique de code, * certains cas peuvent ne pas être détectées. * * @link [ESLint](https://eslint.org/docs/latest/rules/no-unreachable-loop) */ "no-unreachable-loop": "error", /** * Interdit l'utilisation d'une variable avant sa définition. * * La règle est désactivée ici pour que l'ordre de déclaration au sein d'un module n'ait pas d'importance. * * @link [ESLint](https://eslint.org/docs/latest/rules/no-use-before-define) */ "no-use-before-define": "off", /** * Interdit l'assignation de variables non utilisées. Par la nature de l'analyse statique de code, certains cas * peuvent ne pas être détectées. * * @link [ESLint](https://eslint.org/docs/latest/rules/no-useless-assignment) */ "no-useless-assignment": "error", /** * Interdit ici l'usage de conditions « [Yoda](https://en.wikipedia.org/wiki/Yoda_conditions) » pour des soucis de * lisibilité du code. * * @link [ESLint](https://eslint.org/docs/latest/rules/yoda) */ yoda: ["error", "never"] } }; //#endregion //#region ../rules/jsdoc.ts const flatRecommended = jsdoc.configs["flat/recommended-typescript"]; const flatStylistic = jsdoc.configs["flat/stylistic-typescript"]; const jsDocRules = { name: "JSDoc", plugins: flatRecommended.plugins ?? {}, rules: { ...flatRecommended.rules, ...flatStylistic.rules, /** * Vérifie que l'indentation au sein des blocs _JSDoc_ est cohérente. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-indentation.md) */ "jsdoc/check-indentation": "warn", /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-line-alignment.md) */ "jsdoc/check-line-alignment": "warn", /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-tag-names.md) */ "jsdoc/check-tag-names": ["warn", { definedTags: ["link"] }], /** * TODO: Finir la description * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/lines-before-block.md) */ "jsdoc/lines-before-block": "off", /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-block-descriptions.md) */ "jsdoc/no-blank-block-descriptions": "warn", /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-blocks.md) */ "jsdoc/no-blank-blocks": "warn", /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-hyphen-before-param-description.md) */ "jsdoc/require-hyphen-before-param-description": ["warn", "never"], /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-throws.md) */ "jsdoc/require-throws": "warn", /** * TODO: Finir la description. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/sort-tags.md) */ "jsdoc/sort-tags": "warn", /** * Impose la présence de lignes (ou non) entre les _tags_. * * Désactivé ici car elle rentre en conflit avec l'extension _Prettier_ pour _JSDoc_. * * @link [GitHub](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/tag-lines.md) */ "jsdoc/tag-lines": "off" } }; //#endregion //#region ../rules/sonarjs.ts const sonarJsRules = { name: "SonarJS", plugins: sonarJs.configs.recommended.plugins ?? {}, rules: { ...sonarJs.configs.recommended.rules, "sonarjs/arguments-usage": "error", "sonarjs/no-collapsible-if": "error", "sonarjs/no-duplicate-string": "error", "sonarjs/no-inconsistent-returns": "error", "sonarjs/no-nested-switch": "error", "sonarjs/prefer-immediate-return": "error" } }; //#endregion //#region ../rules/tri.ts const sortRules = { name: "Tri", plugins: perfectionist.configs["recommended-natural"].plugins ?? {}, rules: perfectionist.configs["recommended-natural"].rules ?? {} }; //#endregion //#region ../rules/typescript.ts const findConfiguration = (configuration, nom) => configuration.find((v) => v.name === nom) ?? {}; const base = findConfiguration(typeScriptEsLint.configs.strictTypeChecked, "typescript-eslint/base"); const desactivationsJavaScript = findConfiguration(typeScriptEsLint.configs.strictTypeChecked, "typescript-eslint/eslint-recommended"); const strictTypeChecked = findConfiguration(typeScriptEsLint.configs.strictTypeChecked, "typescript-eslint/strict-type-checked"); const stylisticTypeChecked = findConfiguration(typeScriptEsLint.configs.stylisticTypeChecked, "typescript-eslint/stylistic-type-checked"); const typeScriptRules = { languageOptions: base.languageOptions ?? {}, name: "TypeScript", plugins: base.plugins ?? {}, rules: { ...desactivationsJavaScript.rules, ...strictTypeChecked.rules, ...stylisticTypeChecked.rules, /** * Impose un usage consistant entre interfaces et types. Ici préfère les déclarations de types. * * @link [typescript-eslint](https://typescript-eslint.io/rules/consistent-type-definitions) */ "@typescript-eslint/consistent-type-definitions": ["error", "type"], /** * Interdit la définition de fonctions avec plus de 3 paramètres. * * @link [typescript-eslint](https://eslint.org/docs/latest/rules/max-params) */ "@typescript-eslint/max-params": ["error", { max: 3 }], /** * Impose une syntaxe particulière pour les signatures de méthodes au sein d'interfaces et types. Ici utilise la * syntaxe « propriété ». * * ```typescript * interface Exemple { * func: (arg: string) => number; * } * ``` * * @link [typescript-eslint](https://typescript-eslint.io/rules/method-signature-style/) */ "@typescript-eslint/method-signature-style": ["off", "property"], /** * Interdit l'usage de nombres magiques. Cette règle étend * [no-magic-numbers](https://eslint.org/docs/latest/rules/no-magic-numbers) de la configuration de base _ESLint_. * * @link [typescript-eslint](https://eslint.org/docs/latest/rules/no-magic-numbers) */ "@typescript-eslint/no-magic-numbers": "off", /** * Autorise ici la comparaison avec des littéraux booléens. La désactivation de cette règle permet des comparaisons * plus claires qu'avec l'opérateur de négation `!`. * * @link [typescript-eslint](https://typescript-eslint.io/rules/no-unnecessary-boolean-literal-compare) */ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "off", /** * Interdit l'usage de paramètres de types non utilisés plusieurs fois. Désactivé ici à cause de faux positifs. * * @link [typescript-eslint](https://typescript-eslint.io/rules/no-unnecessary-type-parameters) */ "@typescript-eslint/no-unnecessary-type-parameters": "off", /** * Interdit de lever des exceptions avec des valeurs autres que `Error`. Semble déconner. * * @link [typescript-eslint](https://typescript-eslint.io/rules/only-throw-error/) */ "@typescript-eslint/only-throw-error": "off" } }; //#endregion //#region ../rules/unicorn.ts const unicornRules = { name: "Unicorn", plugins: { unicorn }, rules: { ...unicorn.configs.unopinionated.rules, /** * Impose un nom de paramètre spécifique dans les clauses de capture des Erreurs. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/catch-error-name.md) */ "unicorn/catch-error-name": "error", /** * Préfère des types consistants lors de l'étalage d'un tableau de littéraux au sein d'une condition ternaire. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-empty-array-spread.md) */ "unicorn/consistent-empty-array-spread": "error", /** * Impose l'unique manière valide d'étendre la classe `Error`. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/custom-error-definition.md) */ "unicorn/custom-error-definition": "error", /** * Impose la comparaison explicite de propriétés `length` ou `size`. Cela inclut ici l'obligation de vérifier * qu'elles ne correspondent pas à `0` avec `!==`. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/explicit-length-check.md) */ "unicorn/explicit-length-check": ["error", { "non-zero": "not-equal" }], /** * Autorise ici l'usage de la méthode `forEach()` des tableaux. * * Je préfère cette méthode (ou celle de la librairie _Effect_) aux boucles. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-for-each.md) */ "unicorn/no-array-for-each": "off", /** * Autorise ici l'usage de la méthode `reduce()` des tableaux. * * J'utilise occasionnellement cette méthode pour des opérations simples. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-for-each.md) */ "unicorn/no-array-reduce": "off", /** * Interdit l'usage de conditions négatives pour la clarté du code. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-negated-condition.md) */ "unicorn/no-negated-condition": "error", /** * Interdit l'usage d'opérateurs ternaires imbriqués. Cette règle remplace celle présente par défaut dans _ESLint_ * avec le même nom. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-nested-ternary.md) */ "unicorn/no-nested-ternary": "error", /** * Interdit l'usage de `null`. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-null.md) */ "unicorn/no-null": "error", /** * N'impose pas ici l'usage de `.dataset` pour l'interaction avec les attributs d'Éléments DOM. * * L'ergonomie est réduite comparée aux méthodes `{get,has,set,remove}Attribute()`. * * @link [GitHub](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-dataset.md) */ "unicorn/prefer-dom-node-dataset": "off" } }; //#endregion //#region ../index.ts const configTypescriptNavigateur = defineConfig([ globalIgnores(["dist/**/*"], "Ignore le dossier de compilation"), { files: ["**/*.js", "**/*.ts"], name: "Fichiers à analyser" }, { languageOptions: { ecmaVersion: "latest", globals: { ...globals.browser, ...globals.builtin, ...globals.es2026, ...globals.node }, parserOptions: { ecmaVersion: "latest", projectService: true, sourceType: "module", tsconfigRootDir: import.meta.dirname } }, name: "Configuration du projet" }, javascriptRules, typeScriptRules, functionalRules, unicornRules, sonarJsRules, jsDocRules, sortRules ]); const configAstro = defineConfig([...configTypescriptNavigateur, ...astroRules]); //#endregion export { configAstro, configTypescriptNavigateur };