UNPKG

@pequity/eslint-config

Version:
259 lines (254 loc) 7.61 kB
import js from '@eslint/js'; import pluginVitest from '@vitest/eslint-plugin'; import eslintConfigPrettier from 'eslint-config-prettier'; import pluginHtml from 'eslint-plugin-html'; import pluginPrettier from 'eslint-plugin-prettier/recommended'; import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort'; import neostandard, { plugins, resolveIgnoresFromGitignore } from 'neostandard'; export { plugins } from 'neostandard'; import pluginVue from 'eslint-plugin-vue'; const typescript = [ ...plugins["typescript-eslint"].configs.recommended, { name: "pequity/typescript", rules: { // Prefer T[] instead of Array<T> "@typescript-eslint/array-type": ["error", { default: "array" }], // Prefer type over interface for objects "@typescript-eslint/consistent-type-definitions": ["error", "type"], // Fix type imports "@typescript-eslint/consistent-type-imports": [ "error", { prefer: "type-imports", fixStyle: "inline-type-imports" } ], // Allow _ for unused variables "@typescript-eslint/no-unused-vars": [ "error", { args: "none", caughtErrors: "none", ignoreRestSiblings: true, vars: "all", argsIgnorePattern: "^_" } ], // Allow optional chaining and nullish coalescing in expressions "@typescript-eslint/no-unused-expressions": [ "error", { allowShortCircuit: true, allowTernary: true } ], // Disallow non-null assertions using the ! postfix operator "@typescript-eslint/no-non-null-assertion": "error", // Disallow enums "no-restricted-syntax": [ "error", { selector: "TSEnumDeclaration", message: `Enums introduce unexpected runtime behavior, break TypeScript's structural typing, and add unnecessary complexity. Use union types or 'as const' objects instead.` } ] } } ]; const vue = (options) => { const opts = { version: 3, ts: true, ...options }; const vueConfig = opts.version === 3 ? pluginVue.configs["flat/recommended"] : pluginVue.configs["flat/vue2-recommended"]; const languageOptions = opts.ts ? { languageOptions: { parserOptions: { parser: plugins["typescript-eslint"].parser } } } : {}; const config = [ ...vueConfig, { name: "pequity/vue", files: ["*.vue", "**/*.vue"], ...languageOptions, rules: { // Overrides for vue/(vue3-)recommended preset "vue/max-attributes-per-line": "off", "vue/singleline-html-element-content-newline": "off", // Strengthen vue/(vue3-)recommended preset for autofix // https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/configs/recommended.js "vue/attributes-order": "error", "vue/block-order": "error", "vue/no-lone-template": "error", "vue/no-multiple-slot-args": "error", "vue/no-v-html": "error", "vue/order-in-components": "error", "vue/this-in-template": "error", "vue/require-prop-types": "error", // Enforce PascalCase for Vue components "vue/component-name-in-template-casing": [ "error", "PascalCase", { registeredComponentsOnly: false, ignores: [] } ], // Do not allow inline styles "vue/no-static-inline-styles": ["error", { allowBinding: false }], // Require explicit emits "vue/require-explicit-emits": "error", // Require component to have a name property "vue/require-name-property": "error", // Require components that don't have any content to self-close "vue/html-self-closing": [ "error", { html: { void: "always", normal: "never", component: "always" }, svg: "always", math: "always" } ], // Enforce dot notation whenever possible in `<template>` "vue/dot-notation": ["error"] } } ]; return config; }; const config = (options) => { const opts = { noJsx: true, noStyle: true, semi: true, ts: true, vue: true, vueVersion: 3, vitest: true, prettier: true, ...options }; const linterConfig = [ { name: "pequity/ignores", ignores: [ "**/node_modules/**", "{tmp,temp}/**", "**/*.min.js", "vendor/**", "dist/**", "public/**", ...resolveIgnoresFromGitignore() ] }, // JavaScript { name: "pequity/javascript", ...js.configs.recommended }, // TypeScript ...opts.ts ? typescript : [], // Neostandard ...neostandard(opts), { name: "pequity/neostandard-overrides", rules: { // Console and debugger settings depending whether we're on production or not "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", // Enforce consistent brace style for all control statements curly: ["error", "all"], // Disable relative imports - https://stackoverflow.com/questions/65670432/eslint-only-allow-absolute-import-paths-not-relative "no-restricted-imports": [ "error", { patterns: [ { group: ["./", "../"], message: "Relative imports are not allowed." } ] } ], // Enforce dot notation "dot-notation": ["error"], // Import rules // TypeScript compilation already ensures that named imports exist in the referenced module // and has its own version of no-unused-vars ...opts.ts ? { "import-x/named": "off", "no-unused-vars": "off" } : {}, "import-x/no-mutable-exports": "error", "import-x/newline-after-import": ["error", { count: 1 }], "import-x/no-self-import": "error", "import-x/no-relative-packages": "error" } }, // Sort imports { name: "pequity/sort-imports", plugins: { "simple-import-sort": pluginSimpleImportSort }, rules: { "simple-import-sort/imports": [ "error", // Remove all blank lines between imports { groups: [["^\\u0000", "^node:", "^@?\\w", "^", "^\\."]] } ], "simple-import-sort/exports": "error" } }, // HTML { name: "pequity/html", files: ["**/*.html"], plugins: { html: pluginHtml } }, // Vue ...opts.vue ? vue({ version: opts.vueVersion, ts: opts.ts }) : [], // Vitest ...opts.vitest ? [ { name: "pequity/vitest", files: ["test/**", "tests/**", "**/*.test.{js,ts}*", "**/*.spec.{js,ts}"], plugins: { vitest: pluginVitest }, rules: { ...pluginVitest.configs.recommended.rules }, languageOptions: { globals: { ...pluginVitest.environments.env.globals } } } ] : [], // Prettier ...opts.prettier ? [ { name: "pequity/plugin-prettier", ...pluginPrettier } ] : [ { name: "pequity/config-prettier", ...eslintConfigPrettier } ] ]; return linterConfig; }; export { config };