UNPKG

@coderwyd/eslint-config

Version:
1,578 lines (1,553 loc) 61.1 kB
// src/configs/command.ts import createCommand from "eslint-plugin-command/config"; function command() { return [ { ...createCommand(), name: "coderwyd/command/rules" } ]; } // src/plugins/index.ts import { default as default2 } from "@eslint-community/eslint-plugin-eslint-comments"; import { default as default3 } from "eslint-plugin-antfu"; import { default as default4 } from "eslint-plugin-de-morgan"; import * as pluginImport from "eslint-plugin-import-x"; import { default as default5 } from "eslint-plugin-n"; import { default as default6 } from "eslint-plugin-perfectionist"; import { default as default7 } from "eslint-plugin-unicorn"; import { default as default8 } from "eslint-plugin-unused-imports"; // src/configs/comments.ts function comments() { return [ { name: "coderwyd/eslint-comments/rules", plugins: { "eslint-comments": default2 }, rules: { "eslint-comments/no-aggregating-enable": "error", "eslint-comments/no-duplicate-disable": "error", "eslint-comments/no-unlimited-disable": "error", "eslint-comments/no-unused-enable": "error" } } ]; } // src/configs/de-morgan.ts function deMorgan() { return [ { name: "coderwyd/de-morgan/rules", plugins: { "de-morgan": default4 }, rules: { "de-morgan/no-negated-conjunction": "error", "de-morgan/no-negated-disjunction": "error" } } ]; } // src/constants/glob.ts var GLOB_SRC_EXT = "?([cm])[jt]s?(x)"; var GLOB_SRC = "**/*.?([cm])[jt]s?(x)"; var GLOB_TS = "**/*.?([cm])ts"; var GLOB_DTS = "**/*.d.?([cm])ts"; var GLOB_TSX = "**/*.?([cm])tsx"; var GLOB_VUE = "**/*.vue"; var GLOB_ASTRO_TS = "**/*.astro/*.ts"; var GLOB_SVELTE = "**/*.svelte"; var GLOB_JSON = "**/*.json"; var GLOB_JSON5 = "**/*.json5"; var GLOB_JSONC = "**/*.jsonc"; var GLOB_MARKDOWN = "**/*.md"; var GLOB_YAML = "**/*.y?(a)ml"; var GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`; var GLOB_TESTS = [ `**/__tests__/**/*.${GLOB_SRC_EXT}`, `**/*.spec.${GLOB_SRC_EXT}`, `**/*.test.${GLOB_SRC_EXT}`, `**/*.bench.${GLOB_SRC_EXT}`, `**/*.benchmark.${GLOB_SRC_EXT}` ]; var GLOB_EXCLUDE = [ "**/node_modules", "**/dist", "**/package-lock.json", "**/yarn.lock", "**/pnpm-lock.yaml", "**/bun.lockb", "**/output", "**/coverage", "**/temp", "**/.temp", "**/tmp", "**/.tmp", "**/.history", "**/.vitepress/cache", "**/.nuxt", "**/.next", "**/.svelte-kit", "**/.vercel", "**/.changeset", "**/.idea", "**/.cache", "**/.output", "**/.vite-inspect", "**/.yarn", "**/vite.config.*.timestamp-*", "**/CHANGELOG*.md", "**/*.min.*", "**/LICENSE*", "**/__snapshots__", "**/auto-import?(s).d.ts", "**/components.d.ts" ]; // src/configs/ignores.ts function ignores(userIgnores = []) { return [ { ignores: [...GLOB_EXCLUDE, ...userIgnores], name: "coderwyd/ignores" } ]; } // src/configs/imports.ts function imports() { return [ { name: "coderwyd/imports/rules", plugins: { antfu: default3, import: pluginImport }, rules: { "antfu/import-dedupe": "error", "antfu/no-import-dist": "error", "antfu/no-import-node-modules-by-path": "error", "import/consistent-type-specifier-style": ["error", "prefer-top-level"], "import/first": "error", "import/newline-after-import": ["error", { count: 1 }], "import/no-duplicates": "error", "import/no-mutable-exports": "error", "import/no-named-default": "error", "import/no-self-import": "error", "import/no-webpack-loader-syntax": "error" } } ]; } // src/configs/javascript.ts import globals from "globals"; function javascript(options = {}) { const { isInEditor: isInEditor2 = false, overrides = {} } = options; return [ { languageOptions: { ecmaVersion: 2022, globals: { ...globals.browser, ...globals.es2021, ...globals.node, document: "readonly", navigator: "readonly", window: "readonly" }, parserOptions: { ecmaFeatures: { jsx: true }, ecmaVersion: 2022, sourceType: "module" }, sourceType: "module" }, linterOptions: { reportUnusedDisableDirectives: true }, name: "coderwyd/javascript/setup" }, { name: "coderwyd/javascript/rules", plugins: { "unused-imports": default8 }, rules: { "accessor-pairs": [ "error", { enforceForClassMembers: true, setWithoutGet: true } ], "antfu/no-top-level-await": "error", "antfu/top-level-function": "error", "array-callback-return": "error", "block-scoped-var": "error", "constructor-super": "error", "default-case-last": "error", "dot-notation": ["error", { allowKeywords: true }], eqeqeq: ["error", "smart"], "for-direction": "error", "getter-return": "error", "new-cap": [ "error", { capIsNew: false, newIsCap: true, properties: true } ], "no-alert": "error", "no-array-constructor": "error", "no-async-promise-executor": "error", "no-caller": "error", "no-case-declarations": "error", "no-class-assign": "error", "no-compare-neg-zero": "error", "no-cond-assign": ["error", "always"], "no-console": ["error", { allow: ["warn", "error"] }], "no-const-assign": "error", "no-constant-binary-expression": "error", "no-constant-condition": "error", "no-control-regex": "error", "no-debugger": "error", "no-delete-var": "error", "no-dupe-args": "error", "no-dupe-class-members": "error", "no-dupe-keys": "error", "no-duplicate-case": "error", "no-empty": ["error", { allowEmptyCatch: true }], "no-empty-character-class": "error", "no-empty-pattern": "error", "no-eval": "error", "no-ex-assign": "error", "no-extend-native": "error", "no-extra-bind": "error", "no-extra-boolean-cast": "error", "no-fallthrough": "error", "no-func-assign": "error", "no-global-assign": "error", "no-implied-eval": "error", "no-import-assign": "error", "no-invalid-regexp": "error", "no-irregular-whitespace": "error", "no-iterator": "error", "no-labels": ["error", { allowLoop: false, allowSwitch: false }], "no-lone-blocks": "error", "no-loss-of-precision": "error", "no-misleading-character-class": "error", "no-multi-str": "error", "no-new": "error", "no-new-func": "error", "no-new-native-nonconstructor": "error", "no-new-wrappers": "error", "no-obj-calls": "error", "no-object-constructor": "error", "no-octal": "error", "no-octal-escape": "error", "no-proto": "error", "no-prototype-builtins": "error", "no-redeclare": ["error", { builtinGlobals: false }], "no-regex-spaces": "error", "no-restricted-globals": [ "error", { message: "Use `globalThis` instead.", name: "global" }, { message: "Use `globalThis` instead.", name: "self" } ], "no-restricted-properties": [ "error", { message: "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.", property: "__proto__" }, { message: "Use `Object.defineProperty` instead.", property: "__defineGetter__" }, { message: "Use `Object.defineProperty` instead.", property: "__defineSetter__" }, { message: "Use `Object.getOwnPropertyDescriptor` instead.", property: "__lookupGetter__" }, { message: "Use `Object.getOwnPropertyDescriptor` instead.", property: "__lookupSetter__" } ], "no-restricted-syntax": [ "error", "TSEnumDeclaration[const=true]", "TSExportAssignment" ], "no-self-assign": ["error", { props: true }], "no-self-compare": "error", "no-sequences": "error", "no-shadow-restricted-names": "error", "no-sparse-arrays": "error", "no-template-curly-in-string": "error", "no-this-before-super": "error", "no-throw-literal": "error", "no-undef": "error", "no-undef-init": "error", "no-unexpected-multiline": "error", "no-unmodified-loop-condition": "error", "no-unneeded-ternary": ["error", { defaultAssignment: false }], "no-unreachable": "error", "no-unreachable-loop": "error", "no-unsafe-finally": "error", "no-unsafe-negation": "error", "no-unused-expressions": [ "error", { allowShortCircuit: true, allowTaggedTemplates: true, allowTernary: true } ], "no-unused-vars": "off", "no-use-before-define": [ "error", { classes: false, functions: false, variables: true } ], "no-useless-backreference": "error", "no-useless-call": "error", "no-useless-catch": "error", "no-useless-computed-key": "error", "no-useless-constructor": "error", "no-useless-rename": "error", "no-useless-return": "error", "no-var": "error", "no-with": "error", "object-shorthand": [ "error", "always", { avoidQuotes: true, ignoreConstructors: false } ], "one-var": ["error", { initialized: "never" }], "prefer-arrow-callback": [ "error", { allowNamedFunctions: false, allowUnboundThis: true } ], "prefer-const": [ "error", { destructuring: "all", ignoreReadBeforeAssign: true } ], "prefer-exponentiation-operator": "error", // 'prefer-promise-reject-errors': 'error', "prefer-regex-literals": ["error", { disallowRedundantWrapping: true }], "prefer-rest-params": "error", "prefer-spread": "error", "prefer-template": "error", "require-await": "error", "require-yield": "error", "symbol-description": "error", "unicode-bom": ["error", "never"], "unused-imports/no-unused-imports": isInEditor2 ? "off" : "error", "unused-imports/no-unused-vars": [ "error", { args: "after-used", argsIgnorePattern: "^_", ignoreRestSiblings: true, vars: "all", varsIgnorePattern: "^_" } ], "use-isnan": [ "error", { enforceForIndexOf: true, enforceForSwitchCase: true } ], "valid-typeof": ["error", { requireStringLiterals: true }], "vars-on-top": "error", yoda: ["error", "never"], ...overrides } } ]; } // src/shared/index.ts import process from "node:process"; import { fileURLToPath } from "node:url"; import { getPackageInfoSync, isPackageExists } from "local-pkg"; var scopeUrl = fileURLToPath(new URL(".", import.meta.url)); var isCwdInScope = isPackageExists("@coderwyd/eslint-config"); async function combine(...configs2) { const resolved = await Promise.all(configs2); return resolved.flat(); } function renameRules(rules, map) { return Object.fromEntries( Object.entries(rules).map(([key, value]) => { for (const [from, to] of Object.entries(map)) { if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value]; } return [key, value]; }) ); } function renamePluginInConfigs(configs2, map) { return configs2.map((i) => { const clone = { ...i }; if (clone.rules) clone.rules = renameRules(clone.rules, map); if (clone.plugins) { clone.plugins = Object.fromEntries( Object.entries(clone.plugins).map(([key, value]) => { if (key in map) return [map[key], value]; return [key, value]; }) ); } return clone; }); } function getVueVersion() { const pkg = getPackageInfoSync("vue", { paths: [process.cwd()] }); if (pkg && typeof pkg.version === "string" && !Number.isNaN(+pkg.version[0])) return +pkg.version[0]; return 3; } function toArray(value) { return Array.isArray(value) ? value : [value]; } async function interopDefault(m) { const resolved = await m; return resolved.default || resolved; } function isPackageInScope(name) { return isPackageExists(name, { paths: [scopeUrl] }); } async function ensurePackages(packages) { if (process.env.CI || process.stdout.isTTY === false || isCwdInScope === false) return; const nonExistingPackages = packages.filter((i) => !isPackageInScope(i)); if (nonExistingPackages.length === 0) return; const { default: prompts } = await import("prompts"); const { result } = await prompts([ { message: `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?`, name: "result", type: "confirm" } ]); if (result) { await import("@antfu/install-pkg").then( (i) => i.installPackage(nonExistingPackages, { dev: true }) ); } } function resolveSubOptions(options, key) { return typeof options[key] === "boolean" ? {} : options[key] || {}; } function getOverrides(options, key) { const subOptions = resolveSubOptions(options, key); return { ..."overrides" in subOptions && subOptions.overrides ? subOptions.overrides : {} }; } function isInEditorEnv() { if (process.env.CI) return false; if (isInGitHooksOrLintStaged()) { return false; } return !!(process.env.VSCODE_PID || process.env.VSCODE_CWD || process.env.JETBRAINS_IDE || process.env.VIM || process.env.NVIM || false); } function isInGitHooksOrLintStaged() { return !!(process.env.GIT_PARAMS || process.env.VSCODE_GIT_COMMAND || process.env.npm_lifecycle_script?.startsWith("lint-staged") || process.env.npm_lifecycle_script?.startsWith("nano-staged") || false); } // src/configs/jsdoc.ts async function jsdoc() { return [ { name: "coderwyd/jsdoc/rules", plugins: { jsdoc: await interopDefault(import("eslint-plugin-jsdoc")) }, rules: { "jsdoc/check-access": "warn", "jsdoc/check-param-names": "warn", "jsdoc/check-property-names": "warn", "jsdoc/check-types": "warn", "jsdoc/empty-tags": "warn", "jsdoc/implements-on-classes": "warn", "jsdoc/no-defaults": "warn", "jsdoc/no-multi-asterisks": "warn", "jsdoc/require-param-name": "warn", "jsdoc/require-property": "warn", "jsdoc/require-property-description": "warn", "jsdoc/require-property-name": "warn", "jsdoc/require-returns-check": "warn", "jsdoc/require-returns-description": "warn", "jsdoc/require-yields-check": "warn" } } ]; } // src/configs/jsonc.ts async function jsonc(options = {}) { const { files = [GLOB_JSON, GLOB_JSON5, GLOB_JSONC], overrides = {} } = options || {}; const [pluginJsonc, parserJsonc] = await Promise.all([ interopDefault(import("eslint-plugin-jsonc")), interopDefault(import("jsonc-eslint-parser")) ]); return [ { name: "coderwyd/jsonc/setup", plugins: { jsonc: pluginJsonc } }, { files, languageOptions: { parser: parserJsonc }, name: "coderwyd/jsonc/rules", rules: { "jsonc/no-bigint-literals": "error", "jsonc/no-binary-expression": "error", "jsonc/no-binary-numeric-literals": "error", "jsonc/no-dupe-keys": "error", "jsonc/no-escape-sequence-in-identifier": "error", "jsonc/no-floating-decimal": "error", "jsonc/no-hexadecimal-numeric-literals": "error", "jsonc/no-infinity": "error", "jsonc/no-multi-str": "error", "jsonc/no-nan": "error", "jsonc/no-number-props": "error", "jsonc/no-numeric-separators": "error", "jsonc/no-octal": "error", "jsonc/no-octal-escape": "error", "jsonc/no-octal-numeric-literals": "error", "jsonc/no-parenthesized": "error", "jsonc/no-plus-sign": "error", "jsonc/no-regexp-literals": "error", "jsonc/no-sparse-arrays": "error", "jsonc/no-template-literals": "error", "jsonc/no-undefined-value": "error", "jsonc/no-unicode-codepoint-escapes": "error", "jsonc/no-useless-escape": "error", "jsonc/space-unary-ops": "error", "jsonc/valid-json-number": "error", "jsonc/vue-custom-block/no-parsing-error": "error", ...overrides } } ]; } // src/configs/node.ts function node() { return [ { name: "coderwyd/node/rules", plugins: { node: default5 }, rules: { "node/handle-callback-err": ["error", "^(err|error)$"], "node/no-deprecated-api": "error", "node/no-exports-assign": "error", "node/no-new-require": "error", "node/no-path-concat": "error", "node/no-unsupported-features/es-builtins": "error", "node/prefer-global/buffer": ["error", "never"], "node/prefer-global/process": ["error", "never"], "node/process-exit-as-throw": "error" } } ]; } // src/configs/perfectionist.ts function perfectionist() { return [ { name: "coderwyd/perfectionist/rules", plugins: { perfectionist: default6 }, rules: { "perfectionist/sort-exports": [ "error", { order: "asc", type: "natural" } ], "perfectionist/sort-imports": [ "warn", { groups: [ "builtin", "external", "type", ["internal", "internal-type"], ["parent", "sibling", "index"], ["parent-type", "sibling-type", "index-type"], "object", "side-effect", "side-effect-style", "style", "unknown" ], internalPattern: ["^[~@#]/.*"], newlinesBetween: "ignore", order: "asc", type: "natural" } ], "perfectionist/sort-named-exports": [ "warn", { groupKind: "values-first", order: "asc", type: "natural" } ], "perfectionist/sort-named-imports": [ "warn", { groupKind: "values-first", order: "asc", type: "natural" } ] } } ]; } // src/configs/prettier.ts import prettierRules from "eslint-config-prettier"; var { rules: eslintRules } = prettierRules; function prettier() { return [ { name: "coderwyd/prettier/rules", rules: { ...eslintRules, "arrow-body-style": "off", "prefer-arrow-callback": "off" } } ]; } // src/env.ts import process2 from "node:process"; import { isPackageExists as isPackageExists2 } from "local-pkg"; var isInEditor = !!((process2.env.VSCODE_PID || process2.env.VSCODE_CWD || process2.env.JETBRAINS_IDE || process2.env.VIM || process2.env.NVIM) && !process2.env.CI); var VueJsPackages = ["vue", "nuxt", "vitepress", "@slidev/cli"]; var RemixPackages = [ "@remix-run/node", "@remix-run/react", "@remix-run/serve", "@remix-run/dev" ]; var ReactRouterPackages = [ "@react-router/node", "@react-router/react", "@react-router/serve", "@react-router/dev" ]; var NextJsPackages = ["next"]; var ReactRefreshAllowConstantExportPackages = ["vite"]; var isUsingTypeScript = isPackageExists2("typescript"); var isUsingVue = hasPackages(VueJsPackages); var isUsingRemix = hasPackages(RemixPackages); var isUsingReactRouter = hasPackages(ReactRouterPackages); var isUsingNext = hasPackages(NextJsPackages); var isAllowConstantExport = hasPackages( ReactRefreshAllowConstantExportPackages ); function hasPackages(packages) { return packages.some((name) => isPackageExists2(name)); } // src/configs/react.ts async function react(options = {}) { const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS], overrides = {}, tsconfigPath } = options; await ensurePackages([ "@eslint-react/eslint-plugin", "eslint-plugin-react-hooks", "eslint-plugin-react-refresh" ]); const isTypeAware = !!tsconfigPath; const typeAwareRules = { "react/no-leaked-conditional-rendering": "warn" }; const [ pluginReact, pluginReactHooks, pluginReactRefresh, pluginReactCompiler ] = await Promise.all([ interopDefault(import("@eslint-react/eslint-plugin")), interopDefault(import("eslint-plugin-react-hooks")), interopDefault(import("eslint-plugin-react-refresh")), interopDefault(import("eslint-plugin-react-compiler")) ]); const plugins = pluginReact.configs.all.plugins; return [ { name: "coderwyd/react/setup", plugins: { react: plugins["@eslint-react"], "react-compiler": pluginReactCompiler, "react-dom": plugins["@eslint-react/dom"], "react-hooks": pluginReactHooks, "react-hooks-extra": plugins["@eslint-react/hooks-extra"], "react-naming-convention": plugins["@eslint-react/naming-convention"], "react-refresh": pluginReactRefresh, "react-web-api": plugins["@eslint-react/web-api"] } }, { files, languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } }, sourceType: "module" }, name: "coderwyd/react/rules", rules: { "react-compiler/react-compiler": "warn", // recommended rules from eslint-plugin-react-x https://eslint-react.xyz/docs/rules/overview#core-rules "react/no-access-state-in-setstate": "error", "react/no-array-index-key": "warn", "react/no-children-count": "warn", "react/no-children-for-each": "warn", "react/no-children-map": "warn", "react/no-children-only": "warn", "react/no-children-to-array": "warn", "react/no-clone-element": "warn", "react/no-comment-textnodes": "warn", "react/no-component-will-mount": "error", "react/no-component-will-receive-props": "error", "react/no-component-will-update": "error", "react/no-context-provider": "warn", "react/no-create-ref": "error", "react/no-default-props": "error", "react/no-direct-mutation-state": "error", "react/no-duplicate-jsx-props": "warn", "react/no-duplicate-key": "warn", "react/no-forward-ref": "warn", "react/no-implicit-key": "warn", "react/no-missing-key": "error", "react/no-nested-component-definitions": "error", "react/no-prop-types": "error", "react/no-redundant-should-component-update": "error", "react/no-set-state-in-component-did-mount": "warn", "react/no-set-state-in-component-did-update": "warn", "react/no-set-state-in-component-will-update": "warn", "react/no-string-refs": "error", "react/no-unsafe-component-will-mount": "warn", "react/no-unsafe-component-will-receive-props": "warn", "react/no-unsafe-component-will-update": "warn", "react/no-unstable-context-value": "warn", "react/no-unstable-default-props": "warn", "react/no-unused-class-component-members": "warn", "react/no-unused-state": "warn", "react/no-use-context": "warn", "react/no-useless-forward-ref": "warn", "react/use-jsx-vars": "warn", // recommended rules from eslint-plugin-react-dom https://eslint-react.xyz/docs/rules/overview#dom-rules "react-dom/no-dangerously-set-innerhtml": "warn", "react-dom/no-dangerously-set-innerhtml-with-children": "error", "react-dom/no-find-dom-node": "error", "react-dom/no-flush-sync": "error", "react-dom/no-hydrate": "error", "react-dom/no-missing-button-type": "warn", "react-dom/no-missing-iframe-sandbox": "warn", "react-dom/no-namespace": "error", "react-dom/no-render": "error", "react-dom/no-render-return-value": "error", "react-dom/no-script-url": "warn", "react-dom/no-unsafe-iframe-sandbox": "warn", "react-dom/no-unsafe-target-blank": "warn", "react-dom/no-use-form-state": "error", "react-dom/no-void-elements-with-children": "error", // recommended rules eslint-plugin-react-hooks https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks/src/rules "react-hooks/exhaustive-deps": "warn", "react-hooks/rules-of-hooks": "error", // recommended rules from eslint-plugin-react-hooks-extra https://eslint-react.xyz/docs/rules/overview#hooks-extra-rules "react-hooks-extra/no-direct-set-state-in-use-effect": "warn", "react-hooks-extra/no-unnecessary-use-prefix": "warn", // recommended rules from eslint-plugin-react-web-api https://eslint-react.xyz/docs/rules/overview#web-api-rules "react-web-api/no-leaked-event-listener": "warn", "react-web-api/no-leaked-interval": "warn", "react-web-api/no-leaked-resize-observer": "warn", "react-web-api/no-leaked-timeout": "warn", // preconfigured rules from eslint-plugin-react-refresh https://github.com/ArnaudBarre/eslint-plugin-react-refresh/tree/main/src "react-refresh/only-export-components": [ "warn", { allowConstantExport: isAllowConstantExport, allowExportNames: [ ...isUsingNext ? [ "dynamic", "dynamicParams", "revalidate", "fetchCache", "runtime", "preferredRegion", "maxDuration", "config", "generateStaticParams", "metadata", "generateMetadata", "viewport", "generateViewport" ] : [], ...isUsingRemix || isUsingReactRouter ? ["meta", "links", "headers", "loader", "action"] : [] ] } ], // overrides ...overrides } }, ...isTypeAware ? [ { files: filesTypeAware, ignores: ignoresTypeAware, name: "antfu/react/type-aware-rules", rules: { ...typeAwareRules } } ] : [] ]; } // src/configs/regexp.ts import { configs } from "eslint-plugin-regexp"; function regexp(options = {}) { const config = configs["flat/recommended"]; const rules = { ...config.rules }; if (options.level === "warn") { Object.keys(rules).forEach((key) => { if (rules[key] === "error") rules[key] = "warn"; }); } return [ { ...config, name: "coderwyd/regexp/rules", rules: { ...rules, ...options.overrides } } ]; } // src/configs/sort.ts function sortPackageJson() { return [ { files: ["**/package.json"], name: "coderwyd/sort/package-json", rules: { "jsonc/sort-array-values": [ "error", { order: { type: "asc" }, pathPattern: "^files$" } ], "jsonc/sort-keys": [ "error", { order: [ "publisher", "name", "displayName", "type", "version", "private", "packageManager", "description", "author", "license", "funding", "homepage", "repository", "bugs", "keywords", "categories", "sideEffects", "exports", "main", "module", "unpkg", "jsdelivr", "types", "typesVersions", "bin", "icon", "files", "engines", "activationEvents", "contributes", "scripts", "peerDependencies", "peerDependenciesMeta", "dependencies", "optionalDependencies", "devDependencies", "pnpm", "overrides", "resolutions", "husky", "simple-git-hooks", "lint-staged", "nano-staged", "eslintConfig" ], pathPattern: "^$" }, { order: { type: "asc" }, pathPattern: "^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$" }, { order: { type: "asc" }, pathPattern: "^(?:resolutions|overrides|pnpm.overrides)$" }, { order: ["types", "import", "require", "default"], pathPattern: "^exports.*$" }, { order: [ // client hooks only "pre-commit", "prepare-commit-msg", "commit-msg", "post-commit", "pre-rebase", "post-rewrite", "post-checkout", "post-merge", "pre-push", "pre-auto-gc" ], pathPattern: "^(?:gitHooks|husky|simple-git-hooks)$" } ] } } ]; } function sortTsconfig() { return [ { files: ["**/tsconfig.json", "**/tsconfig.*.json"], name: "coderwyd/sort/tsconfig-json", rules: { "jsonc/sort-keys": [ "error", { order: [ "extends", "compilerOptions", "references", "files", "include", "exclude" ], pathPattern: "^$" }, { order: [ /* Projects */ "incremental", "composite", "tsBuildInfoFile", "disableSourceOfProjectReferenceRedirect", "disableSolutionSearching", "disableReferencedProjectLoad", /* Language and Environment */ "target", "jsx", "jsxFactory", "jsxFragmentFactory", "jsxImportSource", "lib", "moduleDetection", "noLib", "reactNamespace", "useDefineForClassFields", "emitDecoratorMetadata", "experimentalDecorators", "libReplacement", /* Modules */ "baseUrl", "rootDir", "rootDirs", "customConditions", "module", "moduleResolution", "moduleSuffixes", "noResolve", "paths", "resolveJsonModule", "resolvePackageJsonExports", "resolvePackageJsonImports", "typeRoots", "types", "allowArbitraryExtensions", "allowImportingTsExtensions", "allowUmdGlobalAccess", /* JavaScript Support */ "allowJs", "checkJs", "maxNodeModuleJsDepth", /* Type Checking */ "strict", "strictBindCallApply", "strictFunctionTypes", "strictNullChecks", "strictPropertyInitialization", "allowUnreachableCode", "allowUnusedLabels", "alwaysStrict", "exactOptionalPropertyTypes", "noFallthroughCasesInSwitch", "noImplicitAny", "noImplicitOverride", "noImplicitReturns", "noImplicitThis", "noPropertyAccessFromIndexSignature", "noUncheckedIndexedAccess", "noUnusedLocals", "noUnusedParameters", "useUnknownInCatchVariables", /* Emit */ "declaration", "declarationDir", "declarationMap", "downlevelIteration", "emitBOM", "emitDeclarationOnly", "importHelpers", "importsNotUsedAsValues", "inlineSourceMap", "inlineSources", "mapRoot", "newLine", "noEmit", "noEmitHelpers", "noEmitOnError", "outDir", "outFile", "preserveConstEnums", "preserveValueImports", "removeComments", "sourceMap", "sourceRoot", "stripInternal", /* Interop Constraints */ "allowSyntheticDefaultImports", "esModuleInterop", "forceConsistentCasingInFileNames", "isolatedDeclarations", "isolatedModules", "preserveSymlinks", "verbatimModuleSyntax", "erasableSyntaxOnly", /* Completeness */ "skipDefaultLibCheck", "skipLibCheck" ], pathPattern: "^compilerOptions$" } ] } } ]; } // src/configs/specials.ts function specials() { return [ { files: [`**/scripts/${GLOB_SRC}`], name: "coderwyd/specials/scripts", rules: { "antfu/no-top-level-await": "off", "no-console": "off", "ts/explicit-function-return-type": "off" } }, { files: [`**/cli/${GLOB_SRC}`, `**/cli.${GLOB_SRC_EXT}`], name: "coderwyd/specials/cli", rules: { "antfu/no-top-level-await": "off", "no-console": "off" } }, { files: ["**/bin/**/*", `**/bin.${GLOB_SRC_EXT}`], name: "coderwyd/specials/bin", rules: { "antfu/no-import-dist": "off", "antfu/no-import-node-modules-by-path": "off" } }, { files: [GLOB_DTS], name: "coderwyd/specials/dts", rules: { "eslint-comments/no-unlimited-disable": "off", "import/no-duplicates": "off", "no-restricted-syntax": "off", "unused-imports/no-unused-vars": "off" } }, { files: ["**/*.{test,spec}.([tj])s?(x)"], name: "coderwyd/specials/test", rules: { "antfu/no-top-level-await": "off", "no-unused-expressions": "off" } }, { files: ["**/*.js", "**/*.cjs"], name: "coderwyd/specials/cjs", rules: { "ts/no-require-imports": "off" } } ]; } // src/configs/svelte.ts async function svelte(options = {}) { const { files = [GLOB_SVELTE], overrides = {} } = options; await ensurePackages(["eslint-plugin-svelte"]); const [pluginSvelte, parserSvelte] = await Promise.all([ interopDefault(import("eslint-plugin-svelte")), interopDefault(import("svelte-eslint-parser")) ]); return [ { name: "coderwyd/svelte/setup", plugins: { svelte: pluginSvelte } }, { files, languageOptions: { parser: parserSvelte, parserOptions: { extraFileExtensions: [".svelte"], parser: options.typescript ? await interopDefault( import("@typescript-eslint/parser") ) : null } }, name: "coderwyd/svelte/rules", processor: pluginSvelte.processors[".svelte"], rules: { "import/no-mutable-exports": "off", "no-undef": "off", // incompatible with most recent (attribute-form) generic types RFC "no-unused-vars": [ "error", { args: "none", caughtErrors: "none", ignoreRestSiblings: true, vars: "all", varsIgnorePattern: "^(\\$\\$Props$|\\$\\$Events$|\\$\\$Slots$)" } ], "svelte/comment-directive": "error", "svelte/no-at-debug-tags": "warn", "svelte/no-at-html-tags": "error", "svelte/no-dupe-else-if-blocks": "error", "svelte/no-dupe-style-properties": "error", "svelte/no-dupe-use-directives": "error", "svelte/no-dynamic-slot-name": "error", "svelte/no-export-load-in-svelte-module-in-kit-pages": "error", "svelte/no-inner-declarations": "error", "svelte/no-not-function-handler": "error", "svelte/no-object-in-text-mustaches": "error", "svelte/no-reactive-functions": "error", "svelte/no-reactive-literals": "error", "svelte/no-shorthand-style-property-overrides": "error", "svelte/no-unknown-style-directive-property": "error", "svelte/no-unused-svelte-ignore": "error", "svelte/no-useless-mustaches": "error", "svelte/require-store-callbacks-use-set-param": "error", "svelte/system": "error", "svelte/valid-each-key": "error", "unused-imports/no-unused-vars": [ "error", { args: "after-used", argsIgnorePattern: "^_", vars: "all", varsIgnorePattern: "^(_|\\$\\$Props$|\\$\\$Events$|\\$\\$Slots$)" } ], ...overrides } } ]; } // src/configs/tailwindcss.ts async function tailwindcss(options = {}) { const { overrides } = options; await ensurePackages(["eslint-plugin-tailwindcss"]); const pluginTailwindcss = await interopDefault( import("eslint-plugin-tailwindcss") ); return [ { name: "coderwyd/tailwindcss/rules", plugins: { tailwindcss: pluginTailwindcss }, rules: { "tailwindcss/classnames-order": "warn", "tailwindcss/enforces-negative-arbitrary-values": "warn", "tailwindcss/enforces-shorthand": "warn", "tailwindcss/migration-from-tailwind-2": "warn", "tailwindcss/no-arbitrary-value": "off", "tailwindcss/no-contradicting-classname": "warn", "tailwindcss/no-custom-classname": "off", "tailwindcss/no-unnecessary-arbitrary-value": "warn", ...overrides } } ]; } // src/configs/test.ts var _pluginTest; async function test(options = {}) { const { files = GLOB_TESTS, isInEditor: isInEditor2 = false, overrides = {} } = options; const [pluginVitest, pluginNoOnlyTests] = await Promise.all([ interopDefault(import("@vitest/eslint-plugin")), // @ts-expect-error missing types interopDefault(import("eslint-plugin-no-only-tests")) ]); _pluginTest = _pluginTest || { ...pluginVitest, rules: { ...pluginVitest.rules, // extend `test/no-only-tests` rule ...pluginNoOnlyTests.rules } }; return [ { name: "coderwyd/test/setup", plugins: { test: _pluginTest } }, { files, name: "coderwyd/test/rules", rules: { "node/prefer-global/process": "off", "test/consistent-test-it": [ "error", { fn: "it", withinDescribe: "it" } ], "test/no-identical-title": "error", "test/no-import-node-test": "error", "test/no-only-tests": isInEditor2 ? "off" : "error", "test/prefer-hooks-in-order": "error", "test/prefer-lowercase-title": "error", "ts/explicit-function-return-type": "off", ...overrides } } ]; } // src/configs/typescript.ts import process3 from "node:process"; async function typescript(options = {}) { const { componentExts = [], overrides = {}, overridesTypeAware = {}, parserOptions = {} } = options; const files = options.files ?? [ GLOB_TS, GLOB_TSX, ...componentExts.map((ext) => `**/*.${ext}`) ]; const filesTypeAware = options.filesTypeAware ?? [GLOB_TS, GLOB_TSX]; const ignoresTypeAware = options.ignoresTypeAware ?? [ `${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS ]; const tsconfigPath = options.tsconfigPath; const isTypeAware = !!tsconfigPath; const typeAwareRules = { "dot-notation": "off", "no-implied-eval": "off", "ts/await-thenable": "error", "ts/dot-notation": ["error", { allowKeywords: true }], "ts/no-floating-promises": "error", "ts/no-for-in-array": "error", "ts/no-implied-eval": "error", "ts/no-misused-promises": "error", "ts/no-unnecessary-type-assertion": "error", "ts/no-unsafe-argument": "error", "ts/no-unsafe-assignment": "error", "ts/no-unsafe-call": "error", "ts/no-unsafe-member-access": "error", "ts/no-unsafe-return": "error", "ts/promise-function-async": "error", "ts/restrict-plus-operands": "error", "ts/restrict-template-expressions": "error", "ts/return-await": "error", "ts/strict-boolean-expressions": "error", "ts/switch-exhaustiveness-check": "error", "ts/unbound-method": "error" }; const [pluginTs, parserTs] = await Promise.all([ interopDefault(import("@typescript-eslint/eslint-plugin")), interopDefault(import("@typescript-eslint/parser")) ]); function makeParser(typeAware, files2, ignores2) { return { files: files2, ...ignores2 ? { ignores: ignores2 } : {}, languageOptions: { parser: parserTs, parserOptions: { extraFileExtensions: componentExts.map((ext) => `.${ext}`), sourceType: "module", ...typeAware ? { projectService: { allowDefaultProject: ["./*.js"], defaultProject: tsconfigPath }, tsconfigRootDir: process3.cwd() } : {}, ...parserOptions } }, name: `coderwyd/typescript/${typeAware ? "type-aware-parser" : "parser"}` }; } return [ { // Install the plugins without globs, so they can be configured separately. name: "coderwyd/typescript/setup", plugins: { antfu: default3, ts: pluginTs } }, // assign type-aware parser for type-aware files and type-unaware parser for the rest ...isTypeAware ? [ makeParser(false, files), makeParser(true, filesTypeAware, ignoresTypeAware) ] : [makeParser(false, files)], { files, name: "coderwyd/typescript/rules", rules: { ...renameRules( pluginTs.configs["eslint-recommended"].overrides[0].rules, { "@typescript-eslint": "ts" } ), ...renameRules(pluginTs.configs.strict.rules, { "@typescript-eslint": "ts" }), "no-dupe-class-members": "off", "no-redeclare": "off", "no-use-before-define": "off", "no-useless-constructor": "off", "ts/ban-ts-comment": [ "error", { "ts-expect-error": "allow-with-description" } ], "ts/consistent-type-definitions": ["error", "interface"], "ts/consistent-type-imports": [ "error", { disallowTypeAnnotations: false, fixStyle: "separate-type-imports", prefer: "type-imports" } ], "ts/method-signature-style": ["error", "property"], // https://www.totaltypescript.com/method-shorthand-syntax-considered-harmful "ts/no-dupe-class-members": "error", "ts/no-dynamic-delete": "off", "ts/no-empty-object-type": [ "error", { allowInterfaces: "with-single-extends", // interface Derived extends Base {} allowObjectTypes: "never", allowWithName: "Props$" } ], "ts/no-explicit-any": "off", "ts/no-extraneous-class": "off", "ts/no-import-type-side-effects": "error", "ts/no-invalid-void-type": "off", "ts/no-non-null-assertion": "off", "ts/no-redeclare": ["error", { builtinGlobals: false }], "ts/no-require-imports": "error", "ts/no-unused-expressions": [ "error", { allowShortCircuit: true, allowTaggedTemplates: true, allowTernary: true } ], "ts/no-unused-vars": "off", "ts/no-use-before-define": [ "error", { classes: false, functions: false, variables: true } ], "ts/no-useless-constructor": "off", "ts/no-wrapper-object-types": "error", "ts/triple-slash-reference": "off", "ts/unified-signatures": "off", ...overrides } }, ...isTypeAware ? [ { files: filesTypeAware, ignores: ignoresTypeAware, name: "coderwyd/typescript/rules-type-aware", rules: { ...typeAwareRules, ...overridesTypeAware } } ] : [] ]; } // src/configs/unicorn.ts function unicorn() { return [ { name: "coderwyd/unicorn/rules", plugins: { unicorn: default7 }, rules: { // 'unicorn/better-regex': 'error', "unicorn/catch-error-name": "error", "unicorn/consistent-empty-array-spread": "error", "unicorn/consistent-existence-index-check": "error", "unicorn/custom-error-definition": "error", "unicorn/error-message": "error", // 'unicorn/explicit-length-check': 'error', // 'unicorn/filename-case': [ // 'error', // { // cases: { kebabCase: true, pascalCase: true }, // ignore: [/^[A-Z]+\..*$/], // }, "unicorn/escape-case": "error", // ], "unicorn/new-for-builtins": "error", // 'unicorn/no-array-callback-reference': 'error', "unicorn/no-array-method-this-argument": "error", "unicorn/no-array-push-push": "error", "unicorn/no-await-in-promise-methods": "error", "unicorn/no-console-spaces": "error", "unicorn/no-for-loop": "error", "unicorn/no-hex-escape": "error", "unicorn/no-instanceof-builtins": "error", "unicorn/no-invalid-remove-event-listener": "error", "unicorn/no-length-as-slice-end": "error", "unicorn/no-lonely-if": "error", "unicorn/no-negation-in-equality-check": "error", "unicorn/no-new-array": "error", "unicorn/no-new-buffer": "error", "unicorn/no-single-promise-in-promise-methods": "error", "unicorn/no-static-only-class": "error", "unicorn/no-unnecessary-await": "error", "unicorn/no-zero-fractions": "error", "unicorn/prefer-add-event-listener": "error", "unicorn/prefer-array-find": "error", "unicorn/prefer-array-flat-map": "error", "unicorn/prefer-array-index-of": "error", "unicorn/prefer-array-some": "error", "unicorn/prefer-at": "error", "unicorn/prefer-blob-reading-methods": "error", "unicorn/prefer-date-now": "error", "unicorn/prefer-dom-node-append": "error", "unicorn/prefer-dom-node-dataset": "error", "unicorn/prefer-dom-node-remove": "error", "unicorn/prefer-dom-node-text-content": "error", "unicorn/prefer-includes": "error", "unicorn/prefer-keyboard-event-key": "error", "unicorn/prefer-math-min-max": "error", "unicorn/prefer-math-trunc": "error", "unicorn/prefer-modern-dom-apis": "error", "unicorn/prefer-modern-math-apis": "error", "unicorn/prefer-negative-index": "error", "unicorn/prefer-node-protocol": "error", "unicorn/prefer-number-properties": "error", "unicorn/prefer-optional-catch-binding": "error", "unicorn/prefer-prototype-methods": "error", "unicorn/prefer-query-selector": "error", "unicorn/prefer-reflect-apply": "error", // 'unicorn/prefer-regexp-test': 'error', "unicorn/prefer-string-replace-all": "error", "unicorn/prefer-string-slice": "error", "unicorn/prefer-string-starts-ends-with": "error", "unicorn/prefer-string-trim-start-end": "error", // top level await is not supported in all environments // 'unicorn/prefer-top-level-await': 'error', "unicorn/prefer-type-error": "error", "unicorn/throw-new-error": "error" } } ]; } // src/configs/unocss.ts async function unocss(options = {}) { const { attributify = true, strict = false } = options; await ensurePackages(["@unocss/eslint-plugin"]); const [pluginUnoCSS] = await Promise.all([ interopDefault(import("@unocss/eslint-plugin")) ]); return [ { name: "coderwyd/unocss/rules", plugins: { unocss: pluginUnoCSS }, rules: { "unocss/order": "warn", ...attributify ? { "unocss/order-attributify": "warn" } : {}, ...strict ? { "unocss/blocklist": "error" } : {} } } ]; } // src/configs/vue.ts async function vue(options = {}) { const { files = [GLOB_VUE], overrides = {} } = options; const [pluginVue, parserVue] = await Promise.all([ interopDefault(import("eslint-plugin-vue")), interopDefault(import("vue-eslint-parser")) ]); const isVue3 = getVueVersion() === 3; const configKeys = isVue3 ? ["essential", "strongly-reco