Compare commits

...

No commits in common. "1faaed08153e354815a1c46fcdc7cf1d8c4d4aea" and "6cc2b478a100c74f4cd723796743a8639485bb2d" have entirely different histories.

30 changed files with 15 additions and 6551 deletions

8
.gitignore vendored
View File

@ -1,8 +0,0 @@
/node_modules/
/prod/
# Ignore packed files so that npm pack can be run locally
*.tgz
# Sonarcloud
.scannerwork

View File

@ -1,7 +0,0 @@
/.forgejo/
.prettierrc.json
.gitattributes
/src/
# Ignore packed files so that npm pack can be run locally
*.tgz

12
.vscode/settings.json vendored
View File

@ -1,12 +0,0 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"typescript"
],
"sonarlint.connectedMode.project": {
"connectionId": "nhcarrigan",
"projectKey": "nhcarrigan_eslint-config"
}
}

105
README.md
View File

@ -1,95 +1,20 @@
# Naomi's ESLint Config
# New Repository Template
This package holds my ESLint configuration for easy installation and syncing changes across projects.
This template contains all of our basic files for a new GitHub repository. There is also a handy workflow that will create an issue on a new repository made from this template, with a checklist for the steps we usually take in setting up a new repository.
If you're starting a Node.JS project with TypeScript, we have a [specific template](https://github.com/naomi-lgbt/nodejs-typescript-template) for that purpose.
## Readme
Delete all of the above text (including this line), and uncomment the below text to use our standard readme template.
<!-- # Project Name
Project Description
## Live Version
This package is currently published. [View the `npm` page](https://www.npmjs.com/package/@nhcarrigan/eslint-config).
## Installation
To install this package, run the following command:
```bash
npm i @nhcarrigan/eslint-config eslint
```
## Compatibility
This package is compatible with ESLint 9.
## Usage
To use this package, add the following to your `eslint.config.js` file:
```js
import NaomisConfig from "@nhcarrigan/eslint-config";
export default [
...NaomisConfig,
// Any overrides you need, such as:
{
rules: {
complexity: "off",
"max-lines-per-function": "off",
"max-statements": "off",
"jsdoc/require-file-overview": "off"
},
},
{
files: ["test/__mocks__/Database.mock.ts"],
rules: {
"require-await": "off"
}
},
];
```
Then set up these two scripts in your `package.json`:
```json
{
"format": "eslint src test --max-warnings 0 --fix",
"lint": "eslint src test --max-warnings 0",
}
```
### Formatting
Our rulesets include the `stylistic` package, which enforces quite a bit of specific formatting. With this being the case, projects should NOT use Prettier in tandem with this config.
Instead, set your editor to run the ESLint formatter on save. For example, in VSCodium, you can add a `.vscode/settings.json` file to your project:
```json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["typescript"]
}
```
## Stylistic Standards
This configuration does not extend or incorporate any external rulesets. Every rule this config sets has been added deliberately and with reason.
### Warnings and Errors
A rule is set to be a warning when it is something that is okay during development (e.g. using a `console.log`, or not having a JSDoc definition yet) but should not make it to production code.
A rule is set to an error when it is something that should not occur in development or production (e.g. missing semi-colons, using loose equality).
### No Deactivated Rules
Because this config is built from scratch, there is no need to explicitly deactivate any rules. Everything is "off" by default, and turned on as desired by this package.
The tests will not allow you to explicitly disable rules.
The only exception is the `disabledEslintRules` object, which explicitly turns off built-in ESLint rules to avoid conflicts with external packages like `@typescript-eslint`.
### Proposing Style Changes
All style changes should be proposed in our [chat server](https://chat.nhcarrigan.com).
This page is currently deployed. [View the live website.]
## Feedback and Bugs
@ -105,10 +30,10 @@ Before interacting with our community, please read our [Code of Conduct](CODE_OF
## License
This software is licensed under our [global software license](https://docs.nhcarrigan.com/legal/license/).
This software is licensed under our [global software license](https://docs.nhcarrigan.com/#/license).
Copyright held by Naomi Carrigan.
## Contact
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`.
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`. -->

View File

@ -1,17 +0,0 @@
import naomisRules from "./prod/index.js";
export default [
...naomisRules,
{
rules: {
"@typescript-eslint/naming-convention": "off",
"import/no-default-export": "off",
"import/namespace": "off",
"import/no-deprecated": "off",
"@typescript-eslint/consistent-type-assertions": "off",
"max-lines-per-function": "off",
"complexity": "off",
"max-nested-callbacks": "off"
},
},
];

View File

@ -1,64 +0,0 @@
{
"name": "@nhcarrigan/eslint-config",
"version": "5.1.0",
"description": "Global config for ESLint",
"main": "prod/index.js",
"type": "module",
"scripts": {
"prepublish": "pnpm format && pnpm test",
"build": "tsc",
"format": "pnpm build && eslint src test --max-warnings 0 --fix",
"lint": "pnpm build && eslint src test --max-warnings 0",
"test": "vitest run",
"scan": "SONAR_TOKEN='op://Environment Variables - Development/SonarCloud/eslint-config' op run -- sonar-scanner -Dsonar.organization=nhcarrigan -Dsonar.projectKey=nhcarrigan_eslint-config -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io"
},
"repository": {
"type": "git",
"url": "git@codeberg.org:naomi-lgbt/eslint-config.git"
},
"engines": {
"node": ">=22",
"pnpm": ">=9"
},
"keywords": [
"eslint"
],
"author": "Naomi Carrigan",
"license": "SEE LICENSE IN https://docs.nhcarrigan.com/#/license",
"bugs": {
"url": "https://codeberg.org/naomi-lgbt/eslint-config/issues"
},
"homepage": "https://codeberg.org/naomi-lgbt/eslint-config",
"dependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
"@eslint/compat": "1.2.4",
"@eslint/eslintrc": "3.2.0",
"@eslint/js": "9.17.0",
"@stylistic/eslint-plugin": "2.12.1",
"@typescript-eslint/eslint-plugin": "8.19.0",
"@typescript-eslint/parser": "8.19.0",
"@vitest/eslint-plugin": "1.1.24",
"eslint-plugin-deprecation": "3.0.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsdoc": "50.6.1",
"eslint-plugin-playwright": "2.1.0",
"eslint-plugin-react": "7.37.3",
"eslint-plugin-sort-keys-fix": "1.1.2",
"eslint-plugin-unicorn": "56.0.1",
"globals": "15.14.0"
},
"peerDependencies": {
"eslint": ">=9",
"playwright": ">=1",
"react": ">=18",
"typescript": ">=5",
"vitest": ">=2"
},
"devDependencies": {
"@nhcarrigan/typescript-config": "4.0.0",
"@types/eslint": "9.6.1",
"@types/node": "22.10.5",
"typescript": "5.7.2",
"vitest": "2.1.8"
}
}

4594
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,214 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { fixupPluginRules } from "@eslint/compat";
import comments from "@eslint-community/eslint-plugin-eslint-comments";
import stylistic from "@stylistic/eslint-plugin";
import tslint from "@typescript-eslint/eslint-plugin";
import parser from "@typescript-eslint/parser";
import vitest from "@vitest/eslint-plugin";
import deprecation from "eslint-plugin-deprecation";
import importPlugin from "eslint-plugin-import";
import jsdoc from "eslint-plugin-jsdoc";
import playwright from "eslint-plugin-playwright";
import react from "eslint-plugin-react";
import sortKeysFix from "eslint-plugin-sort-keys-fix";
import unicorn from "eslint-plugin-unicorn";
import globals from "globals";
import { commentsRules } from "./rules/comments.js";
import { deprecationRules } from "./rules/deprecation.js";
import { disabledEslintRules, eslintRules } from "./rules/eslint.js";
import { importRules } from "./rules/import.js";
import { jsdocRules } from "./rules/jsdoc.js";
import { playwrightRules } from "./rules/playwright.js";
import { reactRules } from "./rules/react.js";
import { sortKeysFixRules } from "./rules/sortKeysFix.js";
import { stylisticRules } from "./rules/stylistic.js";
import { typescriptEslintRules, typescriptEslintRulesWithTypes }
from "./rules/typescriptEslint.js";
import { unicornRules } from "./rules/unicorn.js";
import { vitestRules } from "./rules/vitest.js";
import type { ESLint, Linter } from "eslint";
const config: Array<Linter.Config> = [
// #region Typescript Config
{
files: [ "src/**/*.ts" ],
languageOptions: {
globals: {
...globals.node,
},
parser: parser,
parserOptions: {
ecmaVersion: 11,
project: true,
sourceType: "module",
tsconfigRootDir: process.cwd(),
},
},
plugins: {
// @ts-expect-error It's a config. It's just not the narrow config. SMH.
"@typescript-eslint": tslint,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Not unsafe, just not typed.
"comments": comments,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"deprecation": fixupPluginRules(deprecation),
"import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc,
"sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn,
},
rules: {
...eslintRules,
...disabledEslintRules,
...typescriptEslintRules,
...typescriptEslintRulesWithTypes,
...importRules,
...jsdocRules,
...deprecationRules,
...stylisticRules,
...unicornRules,
...sortKeysFixRules,
...commentsRules,
},
},
// #endregion
// #region Vitest Config
{
files: [ "test/**/*.spec.ts" ],
languageOptions: {
globals: {
...globals.node,
},
parser: parser,
parserOptions: {
ecmaVersion: 11,
sourceType: "module",
},
},
plugins: {
// @ts-expect-error It's a config. It's just not the narrow config. SMH.
"@typescript-eslint": tslint,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Not unsafe, just not typed.
"comments": comments,
"import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc,
"sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn,
"vitest": vitest,
},
rules: {
...eslintRules,
...disabledEslintRules,
...typescriptEslintRules,
...vitestRules,
...importRules,
...jsdocRules,
...stylisticRules,
...unicornRules,
...sortKeysFixRules,
...commentsRules,
},
},
// #endregion
// #region Playwright Config
{
files: [ "e2e/**/*.spec.ts" ],
languageOptions: {
globals: {
...globals.node,
},
parser: parser,
parserOptions: {
ecmaVersion: 11,
sourceType: "module",
},
},
plugins: {
// @ts-expect-error It's a config. It's just not the narrow config. SMH.
"@typescript-eslint": tslint,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Not unsafe, just not typed.
"comments": comments,
"import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc,
"playwright": playwright,
"sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn,
},
rules: {
...eslintRules,
...disabledEslintRules,
...typescriptEslintRules,
...playwrightRules,
...importRules,
...jsdocRules,
...stylisticRules,
...unicornRules,
...sortKeysFixRules,
...commentsRules,
},
},
// #endregion
// #region React Config
{
files: [ "src/**/*.tsx" ],
languageOptions: {
globals: {
...globals.browser,
},
parser: parser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 11,
project: true,
sourceType: "module",
tsconfigRootDir: process.cwd(),
},
},
plugins: {
// @ts-expect-error It's a config. It's just not the narrow config. SMH.
"@typescript-eslint": tslint,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Not unsafe, just not typed.
"comments": comments,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"deprecation": fixupPluginRules(deprecation),
"import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc,
"react": react as ESLint.Plugin,
"sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn,
},
rules: {
...eslintRules,
...disabledEslintRules,
...typescriptEslintRules,
...typescriptEslintRulesWithTypes,
...importRules,
...jsdocRules,
...deprecationRules,
...stylisticRules,
...unicornRules,
...sortKeysFixRules,
...reactRules,
...commentsRules,
},
},
];
export default config;

View File

@ -1,12 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const commentsRules: Linter.RulesRecord = {
"comments/no-unlimited-disable": "warn",
"comments/require-description": "warn",
};

View File

@ -1,11 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const deprecationRules: Linter.RulesRecord = {
"deprecation/deprecation": "error",
};

View File

@ -1,202 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
const maxComplexity = 10;
const maxClasses = 1;
const maxDepth = 5;
const maxCallbacks = 2;
const maxStatements = 20;
/**
* Rules explicitly disabled to be overridden
* by external packages.
*/
const disabledEslintRules: Linter.RulesRecord = {
"class-methods-use-this": "off",
"default-param-last": "off",
"dot-notation": "off",
"init-declarations": "off",
"max-params": "off",
"no-array-constructor": "off",
"no-empty-function": "off",
"no-implied-eval": "off",
"no-loop-func": "off",
"no-loss-of-precision": "off",
"no-return-await": "off",
"no-shadow": "off",
"no-throw-literal": "off",
"no-unused-expressions": "off",
"no-unused-vars": "off",
"no-use-before-define": "off",
"no-useless-constructor": "off",
"prefer-destructuring": "off",
"prefer-promise-reject-errors": "off",
"require-await": "off",
};
/**
* Rules for the base eslint.
*/
const eslintRules: Linter.RulesRecord = {
"accessor-pairs": "error",
"array-callback-return": [ "error", { allowImplicit: true } ],
"arrow-body-style": [ "warn", "always" ],
"block-scoped-var": "error",
"capitalized-comments": "warn",
"complexity": [ "error", maxComplexity ],
"consistent-return": "error",
"curly": "error",
"default-case": "error",
"default-case-last": "warn",
"eqeqeq": "error",
"for-direction": "error",
"func-name-matching": "error",
"func-names": [ "warn", "always" ],
"func-style":
[ "warn", "declaration", { allowArrowFunctions: true } ],
"getter-return": [ "error", { allowImplicit: false } ],
"grouped-accessor-pairs": "warn",
"logical-assignment-operators": [ "error", "never" ],
"max-classes-per-file": [ "error", maxClasses ],
"max-depth": [ "warn", maxDepth ],
"max-lines":
[ "warn", { max: 300, skipBlankLines: true, skipComments: true } ],
"max-lines-per-function": [
"warn",
{ IIFEs: true, max: 50, skipBlankLines: true, skipComments: false },
],
"max-nested-callbacks": [ "warn", maxCallbacks ],
"max-statements": [ "warn", maxStatements ],
"new-cap": "warn",
"no-alert": "warn",
"no-async-promise-executor": "error",
"no-await-in-loop": "warn",
"no-bitwise": "error",
"no-caller": "error",
"no-case-declarations": "warn",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-console": "warn",
// Technically covered by ts
"no-const-assign": "error",
"no-constant-binary-expression": "error",
"no-constant-condition": "error",
"no-constructor-return": "error",
"no-control-regex": "error",
"no-debugger": "warn",
"no-delete-var": "error",
"no-div-regex": "warn",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-duplicate-imports": "warn",
"no-else-return": "warn",
"no-empty": "warn",
"no-empty-character-class": "warn",
"no-empty-pattern": "warn",
"no-empty-static-block": "warn",
"no-eq-null": "error",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "error",
"no-extra-bind": "warn",
"no-extra-boolean-cast": "warn",
"no-extra-label": "warn",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-implicit-coercion": "warn",
"no-implicit-globals": "error",
"no-import-assign": "error",
"no-inner-declarations": "warn",
"no-invalid-regexp": "warn",
"no-invalid-this": "error",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "warn",
"no-lone-blocks": "warn",
"no-lonely-if": "warn",
"no-misleading-character-class": "warn",
"no-multi-assign": "warn",
"no-multi-str": "warn",
"no-negated-condition": "warn",
"no-nested-ternary": "warn",
"no-new": "warn",
"no-new-native-nonconstructor": "error",
"no-new-wrappers": "error",
"no-nonoctal-decimal-escape": "warn",
"no-obj-calls": "warn",
"no-object-constructor": "error",
"no-octal": "warn",
"no-octal-escape": "warn",
"no-param-reassign": "error",
"no-plusplus": "warn",
"no-promise-executor-return": "warn",
"no-proto": "warn",
"no-prototype-builtins": "error",
"no-regex-spaces": "warn",
"no-return-assign": "error",
"no-script-url": "warn",
"no-self-assign": "error",
"no-self-compare": "error",
"no-sequences": "warn",
"no-setter-return": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "warn",
"no-this-before-super": "error",
"no-undef": "error",
"no-undef-init": "warn",
"no-underscore-dangle": "warn",
"no-unexpected-multiline": "warn",
"no-unmodified-loop-condition": "warn",
"no-unneeded-ternary": "warn",
"no-unreachable": "warn",
"no-unreachable-loop": "warn",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": "error",
"no-unused-private-class-members": "warn",
"no-useless-assignment": "warn",
"no-useless-backreference": "warn",
"no-useless-call": "warn",
"no-useless-catch": "warn",
"no-useless-computed-key": "warn",
"no-useless-concat": "warn",
"no-useless-escape": "warn",
"no-useless-rename": "warn",
"no-useless-return": "warn",
"no-var": "error",
"no-warning-comments": "warn",
"no-with": "error",
"object-shorthand": [ "warn", "consistent-as-needed" ],
"operator-assignment": [ "warn", "never" ],
"prefer-arrow-callback": "warn",
"prefer-const": "error",
"prefer-named-capture-group": "warn",
"prefer-object-has-own": "warn",
"prefer-object-spread": "warn",
"prefer-regex-literals": "warn",
"prefer-rest-params": "warn",
"prefer-spread": "warn",
"prefer-template": "warn",
"radix": "error",
"require-atomic-updates": "error",
"require-yield": "warn",
"sort-keys": "warn",
"sort-vars": "warn",
"symbol-description": "warn",
"use-isnan": "warn",
"valid-typeof": "error",
"yoda": [ "warn", "never" ],
};
export { disabledEslintRules, eslintRules };

View File

@ -1,62 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const importRules: Linter.RulesRecord = {
"import/default": "error",
"import/export": "error",
"import/exports-last": "warn",
"import/extensions": [ "warn", "ignorePackages" ],
"import/first": "warn",
"import/group-exports": "warn",
"import/newline-after-import": [ "warn", { count: 1 } ],
"import/no-absolute-path": [ "error" ],
"import/no-amd": "error",
"import/no-anonymous-default-export": [ "warn" ],
"import/no-commonjs": [ "error" ],
"import/no-cycle": [ "error" ],
"import/no-default-export": "warn",
"import/no-duplicates": [ "error" ],
"import/no-dynamic-require": "error",
"import/no-empty-named-blocks": "error",
"import/no-extraneous-dependencies": [
"error",
{ devDependencies: [ "**/*.spec.ts" ] },
],
"import/no-import-module-exports": [ "error" ],
"import/no-mutable-exports": "error",
"import/no-named-as-default": "warn",
"import/no-named-as-default-member": "warn",
"import/no-namespace": [ "warn" ],
"import/no-relative-packages": "warn",
"import/no-self-import": "error",
"import/no-unassigned-import": [ "error" ],
"import/no-unused-modules": [ "warn" ],
"import/no-useless-path-segments": [ "warn" ],
"import/no-webpack-loader-syntax": "error",
"import/order": [
"warn",
{
"alphabetize": {
caseInsensitive: true,
order: "asc",
},
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"object",
"type",
"unknown",
],
"newlines-between": "never",
},
],
};

View File

@ -1,102 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const jsdocRules: Linter.RulesRecord = {
"jsdoc/check-access": "warn",
"jsdoc/check-alignment": "warn",
"jsdoc/check-indentation": "warn",
"jsdoc/check-param-names": "warn",
"jsdoc/check-property-names": "warn",
"jsdoc/check-syntax": "warn",
"jsdoc/check-tag-names": "warn",
"jsdoc/check-template-names": "warn",
"jsdoc/check-values": [
"warn",
{
allowedLicenses: [ "Naomi's Public License" ],
},
],
"jsdoc/convert-to-jsdoc-comments": [
"warn",
{
enableFixer: true,
enforceJsdocLineStyle: "multi",
lineOrBlockStyle: "both",
},
],
"jsdoc/empty-tags": "warn",
"jsdoc/implements-on-classes": "warn",
"jsdoc/imports-as-dependencies": "warn",
"jsdoc/informative-docs": "warn",
"jsdoc/match-description": "warn",
"jsdoc/multiline-blocks":
[ "warn", { noSingleLineBlocks: true } ],
"jsdoc/no-bad-blocks": "warn",
"jsdoc/no-blank-block-descriptions": "warn",
"jsdoc/no-blank-blocks": "warn",
"jsdoc/no-defaults": "warn",
"jsdoc/no-types": "warn",
"jsdoc/require-asterisk-prefix": "warn",
"jsdoc/require-description": "warn",
"jsdoc/require-description-complete-sentence": "warn",
"jsdoc/require-file-overview": [
"warn",
{
tags: {
author: {
mustExist: true,
preventDuplicates: true,
},
copyright: {
mustExist: true,
preventDuplicates: true,
},
license: {
mustExist: true,
preventDuplicates: true,
},
},
},
],
"jsdoc/require-hyphen-before-param-description": "warn",
"jsdoc/require-jsdoc": [
"warn",
{
publicOnly: true,
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionDeclaration: true,
FunctionExpression: true,
MethodDefinition: true,
},
},
],
"jsdoc/require-param": "warn",
"jsdoc/require-param-description": "warn",
"jsdoc/require-param-name": "warn",
"jsdoc/require-property": "warn",
"jsdoc/require-property-description": "warn",
"jsdoc/require-property-name": "warn",
"jsdoc/require-returns": "warn",
"jsdoc/require-returns-check": "warn",
"jsdoc/require-returns-description": "warn",
"jsdoc/require-template": "warn",
"jsdoc/require-throws": "warn",
"jsdoc/require-yields": "warn",
"jsdoc/require-yields-check": "warn",
"jsdoc/sort-tags": [
"warn",
{
linesBetween: 0,
},
],
"jsdoc/tag-lines": [ "warn", "never" ],
"jsdoc/valid-types": "warn",
};

View File

@ -1,53 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const playwrightRules: Linter.RulesRecord = {
"playwright/expect-expect": "warn",
"playwright/max-nested-describe": [ "warn", { max: 2 } ],
"playwright/missing-playwright-await": "warn",
"playwright/no-commented-out-tests": "warn",
"playwright/no-conditional-expect": "warn",
"playwright/no-conditional-in-test": "warn",
"playwright/no-duplicate-hooks": "warn",
"playwright/no-element-handle": "warn",
"playwright/no-eval": "warn",
"playwright/no-focused-test": "warn",
"playwright/no-force-option": "warn",
"playwright/no-get-by-title": "warn",
"playwright/no-nested-step": "warn",
"playwright/no-networkidle": "warn",
"playwright/no-nth-methods": "warn",
"playwright/no-page-pause": "warn",
"playwright/no-raw-locators": "warn",
"playwright/no-skipped-test": "warn",
"playwright/no-standalone-expect": "warn",
"playwright/no-unsafe-references": "warn",
"playwright/no-useless-await": "warn",
"playwright/no-useless-not": "warn",
"playwright/no-wait-for-selector": "warn",
"playwright/no-wait-for-timeout": "warn",
"playwright/prefer-comparison-matcher": "warn",
"playwright/prefer-equality-matcher": "warn",
"playwright/prefer-hooks-in-order": "warn",
"playwright/prefer-hooks-on-top": "warn",
"playwright/prefer-lowercase-title": "warn",
"playwright/prefer-strict-equal": "warn",
"playwright/prefer-to-be": "warn",
"playwright/prefer-to-contain": "warn",
"playwright/prefer-to-have-count": "warn",
"playwright/prefer-to-have-length": "warn",
"playwright/prefer-web-first-assertions": "warn",
"playwright/require-hook": "warn",
"playwright/require-to-throw-message": "warn",
"playwright/require-top-level-describe": "warn",
"playwright/valid-describe-callback": "warn",
"playwright/valid-expect":
[ "warn", { maxArgs: 2, minArgs: 2 } ],
"playwright/valid-expect-in-promise": "warn",
"playwright/valid-title": "warn",
};

View File

@ -1,87 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const reactRules: Linter.RulesRecord = {
"react/boolean-prop-naming": "warn",
"react/button-has-type": "error",
"react/checked-requires-onchange-or-readonly": "error",
"react/default-props-match-prop-types": "warn",
"react/destructuring-assignment": "warn",
"react/display-name": "warn",
"react/forbid-foreign-prop-types": "warn",
"react/forbid-prop-types": "warn",
"react/forward-ref-uses-ref": "warn",
"react/function-component-definition":
[ "warn",
{
namedComponents: "arrow-function",
unnamedComponents: "arrow-function",
} ],
"react/hook-use-state": "warn",
"react/iframe-missing-sandbox": "error",
"react/jsx-boolean-value": [ "warn", "always" ],
"react/jsx-child-element-spacing": "warn",
"react/jsx-filename-extension":
[ "warn", { allow: "as-needed", extensions: [ ".jsx", ".tsx" ] } ],
"react/jsx-fragments": [ "warn", "syntax" ],
"react/jsx-handler-names": "warn",
"react/jsx-key": "error",
"react/jsx-no-bind": "error",
"react/jsx-no-comment-textnodes": "error",
"react/jsx-no-constructed-context-values": "error",
"react/jsx-no-duplicate-props": "error",
"react/jsx-no-leaked-render": "error",
"react/jsx-no-literals": "warn",
"react/jsx-no-script-url": "error",
"react/jsx-no-target-blank": "error",
"react/jsx-no-undef": "error",
"react/jsx-no-useless-fragment": "warn",
"react/jsx-pascal-case": "warn",
"react/jsx-props-no-spread-multi": "warn",
"react/jsx-sort-props": "warn",
"react/jsx-uses-vars": "warn",
"react/no-access-state-in-setstate": "error",
"react/no-array-index-key": "error",
"react/no-arrow-function-lifecycle": "warn",
"react/no-children-prop": "error",
"react/no-danger": "error",
"react/no-danger-with-children": "error",
"react/no-deprecated": "warn",
"react/no-did-mount-set-state": "error",
"react/no-did-update-set-state": "error",
"react/no-direct-mutation-state": "error",
"react/no-find-dom-node": "error",
"react/no-is-mounted": "error",
"react/no-multi-comp": "warn",
"react/no-object-type-as-default-prop": "error",
"react/no-redundant-should-component-update": "warn",
"react/no-render-return-value": "error",
"react/no-string-refs": "error",
"react/no-this-in-sfc": "error",
"react/no-typos": "warn",
"react/no-unescaped-entities": "error",
"react/no-unknown-property": "error",
"react/no-unsafe": "warn",
"react/no-unstable-nested-components": "warn",
"react/no-unused-class-component-methods": "warn",
"react/no-unused-prop-types": "warn",
"react/no-unused-state": "warn",
"react/no-will-update-set-state": "error",
"react/prefer-es6-class": "warn",
"react/prefer-read-only-props": "error",
"react/prefer-stateless-function": "warn",
"react/prop-types": "warn",
"react/require-default-props": "warn",
"react/require-render-return": "error",
"react/self-closing-comp":
[ "warn", { component: false, html: false } ],
"react/state-in-constructor": "warn",
"react/static-property-placement": "warn",
"react/style-prop-object": "warn",
"react/void-dom-elements-no-children": "error",
};

View File

@ -1,11 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const sortKeysFixRules: Linter.RulesRecord = {
"sort-keys-fix/sort-keys-fix": "warn",
};

View File

@ -1,105 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
const spacesPerIndent = 2;
export const stylisticRules: Linter.RulesRecord = {
"stylistic/array-bracket-newline": [ "warn", "consistent" ],
"stylistic/array-bracket-spacing": [ "warn", "always" ],
"stylistic/array-element-newline": [ "warn", "consistent" ],
"stylistic/arrow-parens": [ "warn", "always" ],
"stylistic/arrow-spacing":
[ "warn", { after: true, before: true } ],
"stylistic/block-spacing": [ "warn", "always" ],
"stylistic/brace-style": [ "warn", "1tbs" ],
"stylistic/comma-dangle": [ "warn", "always-multiline" ],
"stylistic/comma-spacing": [ "warn" ],
"stylistic/comma-style": [ "warn", "last" ],
"stylistic/computed-property-spacing": [ "warn", "never" ],
"stylistic/dot-location": [ "warn", "object" ],
"stylistic/eol-last": [ "warn", "always" ],
"stylistic/function-call-argument-newline": [ "warn", "consistent" ],
"stylistic/function-call-spacing": [ "warn", "never" ],
"stylistic/function-paren-newline": [ "warn", "consistent" ],
"stylistic/generator-star-spacing": [ "warn", "after" ],
"stylistic/indent": [ "warn", spacesPerIndent ],
"stylistic/key-spacing": [
"warn",
{ afterColon: true,
align: "value",
beforeColon: false,
mode: "strict" },
],
"stylistic/keyword-spacing":
[ "warn", { after: true, before: true } ],
"stylistic/line-comment-position": [ "warn", "above" ],
"stylistic/linebreak-style": [ "warn", "unix" ],
"stylistic/lines-around-comment": [
"warn",
{ afterBlockComment: false, beforeBlockComment: true },
],
"stylistic/max-len": [
"warn",
{ code: 80,
ignoreComments: true,
ignoreTemplateLiterals: true,
tabWidth: 2 },
],
"stylistic/max-statements-per-line": [ "warn", { max: 1 } ],
"stylistic/member-delimiter-style": "warn",
"stylistic/multiline-comment-style": [ "warn", "starred-block" ],
"stylistic/multiline-ternary": [ "warn", "always" ],
"stylistic/new-parens": [ "warn", "always" ],
"stylistic/newline-per-chained-call":
[ "warn", { ignoreChainWithDepth: 2 } ],
"stylistic/no-confusing-arrow": [
"warn",
{ allowParens: false, onlyOneSimpleParam: false },
],
"stylistic/no-extra-parens": [ "warn", "all" ],
"stylistic/no-extra-semi": "warn",
"stylistic/no-floating-decimal": "warn",
"stylistic/no-mixed-operators":
[ "warn", { allowSamePrecedence: false } ],
"stylistic/no-mixed-spaces-and-tabs": [ "warn" ],
"stylistic/no-multi-spaces":
[ "warn", { exceptions: { Property: true, TSTypeAnnotation: true } } ],
"stylistic/no-multiple-empty-lines": [ "warn", { max: 1 } ],
"stylistic/no-tabs": "warn",
"stylistic/no-trailing-spaces": "warn",
"stylistic/no-whitespace-before-property": "warn",
"stylistic/object-curly-newline":
[ "warn", { consistent: true } ],
"stylistic/object-curly-spacing": [ "warn", "always" ],
"stylistic/one-var-declaration-per-line": [ "warn", "always" ],
"stylistic/operator-linebreak": [ "warn", "before" ],
"stylistic/padded-blocks": [ "warn", "never" ],
"stylistic/quote-props": [ "warn", "consistent-as-needed" ],
"stylistic/quotes":
[ "warn", "double", { allowTemplateLiterals: true } ],
"stylistic/rest-spread-spacing": [ "warn", "never" ],
"stylistic/semi": [ "warn", "always" ],
"stylistic/semi-spacing":
[ "warn", { after: true, before: false } ],
"stylistic/semi-style": [ "warn", "last" ],
"stylistic/space-before-blocks": [ "warn", "always" ],
"stylistic/space-before-function-paren": [ "warn", "never" ],
"stylistic/space-in-parens": [ "warn", "never" ],
"stylistic/space-infix-ops": [ "warn" ],
"stylistic/spaced-comment": [ "warn", "always" ],
"stylistic/switch-colon-spacing":
[ "warn", { after: true, before: false } ],
"stylistic/template-curly-spacing": [ "warn", "never" ],
"stylistic/template-tag-spacing": [ "warn", "never" ],
"stylistic/type-annotation-spacing":
[ "warn", { after: true, before: false } ],
"stylistic/type-generic-spacing": "warn",
"stylistic/type-named-tuple-spacing": "warn",
"stylistic/wrap-iife": [ "warn", "inside" ],
"stylistic/yield-star-spacing": [ "warn", "after" ],
};

View File

@ -1,231 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
/**
* Rules that require type definitions.
* These CANNOT run on the test directory as our typescript
* configuration excludes tests from compliation.
*/
const typescriptEslintRulesWithTypes: Linter.RulesRecord = {
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/consistent-type-exports": [
"warn",
{ fixMixedExportsWithInlineTypeSpecifier: true },
],
"@typescript-eslint/dot-notation": [ "error" ],
"@typescript-eslint/no-array-delete": "error",
"@typescript-eslint/no-base-to-string": [ "error" ],
"@typescript-eslint/no-confusing-void-expression": [ "error" ],
"@typescript-eslint/no-duplicate-type-constituents": [ "error" ],
"@typescript-eslint/no-floating-promises": [ "error" ],
"@typescript-eslint/no-implied-eval": [ "error" ],
"@typescript-eslint/no-meaningless-void-operator": [ "warn" ],
"@typescript-eslint/no-misused-promises":
[ "error" ],
"@typescript-eslint/no-mixed-enums": "error",
"@typescript-eslint/no-redundant-type-constituents": "warn",
"@typescript-eslint/no-unnecessary-boolean-literal-compare": [ "warn" ],
"@typescript-eslint/no-unnecessary-condition": [ "warn" ],
"@typescript-eslint/no-unnecessary-qualifier": "warn",
"@typescript-eslint/no-unnecessary-template-expression": "warn",
"@typescript-eslint/no-unnecessary-type-arguments": "warn",
"@typescript-eslint/no-unnecessary-type-assertion": [ "warn" ],
"@typescript-eslint/no-unnecessary-type-parameters": "warn",
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-declaration-merging": "error",
"@typescript-eslint/no-unsafe-enum-comparison": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/no-unsafe-unary-minus": "error",
"@typescript-eslint/only-throw-error": [ "error" ],
"@typescript-eslint/prefer-destructuring": [ "warn" ],
"@typescript-eslint/prefer-find": "error",
"@typescript-eslint/prefer-includes": "warn",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-optional-chain": [ "warn" ],
"@typescript-eslint/prefer-promise-reject-errors": [ "error" ],
"@typescript-eslint/prefer-readonly": "warn",
"@typescript-eslint/prefer-reduce-type-parameter": "warn",
"@typescript-eslint/prefer-regexp-exec": "error",
"@typescript-eslint/prefer-return-this-type": "warn",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async":
[ "error", { allowAny: false } ],
"@typescript-eslint/require-array-sort-compare": [
"error",
{ ignoreStringArrays: false },
],
"@typescript-eslint/require-await": "error",
"@typescript-eslint/restrict-plus-operands": [
"error",
{
allowAny: false,
allowBoolean: false,
allowNullish: false,
allowNumberAndString: false,
allowRegExp: false,
},
],
"@typescript-eslint/restrict-template-expressions": [
"error",
{
allowAny: false,
allowBoolean: false,
allowNever: false,
allowNullish: false,
allowNumber: false,
allowRegExp: false,
},
],
"@typescript-eslint/return-await": [ "error", "always" ],
"@typescript-eslint/strict-boolean-expressions": [
"error",
{
allowNullableObject: true,
allowNumber: false,
allowString: false,
},
],
"@typescript-eslint/switch-exhaustiveness-check": [
"error",
{ requireDefaultForNonUnion: true },
],
"@typescript-eslint/unbound-method": "error",
"@typescript-eslint/use-unknown-in-catch-callback-variable": "error",
};
/**
* Rules that do not require type definitions.
* These can run on both the src and test directories.
*/
const typescriptEslintRules: Linter.RulesRecord = {
"@typescript-eslint/adjacent-overload-signatures": "warn",
"@typescript-eslint/array-type":
[ "warn", { default: "generic" } ],
"@typescript-eslint/ban-ts-comment": [
"error",
{ "ts-expect-error": "allow-with-description" },
],
"@typescript-eslint/class-literal-property-style": [ "error", "getters" ],
"@typescript-eslint/class-methods-use-this": [ "error" ],
"@typescript-eslint/consistent-generic-constructors": [
"error",
"constructor",
],
"@typescript-eslint/consistent-indexed-object-style": [ "warn", "record" ],
"@typescript-eslint/consistent-type-assertions": [
"warn",
{ assertionStyle: "never" },
],
"@typescript-eslint/consistent-type-definitions": [ "warn", "interface" ],
"@typescript-eslint/consistent-type-imports": [
"warn",
{ fixStyle: "inline-type-imports", prefer: "type-imports" },
],
"@typescript-eslint/default-param-last": "error",
"@typescript-eslint/explicit-function-return-type": [ "warn" ],
"@typescript-eslint/explicit-member-accessibility": [
"warn",
{ accessibility: "explicit" },
],
"@typescript-eslint/explicit-module-boundary-types": [ "warn" ],
"@typescript-eslint/init-declarations": [ "error", "always" ],
// Anything more than 3 and we should be using a params object
"@typescript-eslint/max-params":
[ "error", { max: 3 } ],
"@typescript-eslint/member-ordering": [ "warn" ],
"@typescript-eslint/method-signature-style": [ "warn", "property" ],
"@typescript-eslint/naming-convention": [
"warn",
{
format: [ "camelCase" ],
leadingUnderscore: "allow",
selector: "default",
trailingUnderscore: "forbid",
},
{
format: [ "PascalCase" ],
leadingUnderscore: "forbid",
selector: "typeLike",
trailingUnderscore: "forbid",
},
{
format: [ "PascalCase" ],
leadingUnderscore: "forbid",
selector: "class",
trailingUnderscore: "forbid",
},
],
"@typescript-eslint/no-array-constructor": [ "error" ],
"@typescript-eslint/no-confusing-non-null-assertion": "warn",
"@typescript-eslint/no-duplicate-enum-values": "error",
"@typescript-eslint/no-dynamic-delete": "error",
"@typescript-eslint/no-empty-function": [ "error" ],
"@typescript-eslint/no-empty-interface": [ "warn" ],
"@typescript-eslint/no-empty-object-type": [ "warn" ],
"@typescript-eslint/no-explicit-any":
[ "error", { fixToUnknown: true } ],
"@typescript-eslint/no-extraneous-class": [ "error" ],
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-import-type-side-effects": "error",
"@typescript-eslint/no-inferrable-types": [ "warn" ],
"@typescript-eslint/no-invalid-void-type":
[ "error" ],
"@typescript-eslint/no-loop-func": "error",
"@typescript-eslint/no-loss-of-precision": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-require-imports":
[ "error" ],
"@typescript-eslint/no-shadow":
[ "error" ],
"@typescript-eslint/no-this-alias": [ "warn" ],
"@typescript-eslint/no-unnecessary-parameter-property-assignment": "warn",
"@typescript-eslint/no-unnecessary-type-constraint": "warn",
"@typescript-eslint/no-unused-expressions": [ "warn" ],
"@typescript-eslint/no-unused-vars": [
"warn",
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "all",
vars: "all",
varsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-use-before-define": [ "error" ],
"@typescript-eslint/no-useless-constructor": "warn",
"@typescript-eslint/no-useless-empty-export": "warn",
"@typescript-eslint/no-var-requires": [ "error" ],
"@typescript-eslint/no-wrapper-object-types": "warn",
"@typescript-eslint/parameter-properties": [
"warn",
{ prefer: "parameter-property" },
],
"@typescript-eslint/prefer-as-const": "warn",
"@typescript-eslint/prefer-enum-initializers": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-literal-enum-member": [ "error" ],
"@typescript-eslint/triple-slash-reference": [
"warn",
{ lib: "never", path: "never", types: "prefer-import" },
],
"@typescript-eslint/unified-signatures": [ "warn" ],
};
export {
typescriptEslintRules,
typescriptEslintRulesWithTypes,
};

View File

@ -1,85 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const unicornRules: Linter.RulesRecord = {
"unicorn/better-regex": "warn",
"unicorn/catch-error-name":
[ "warn", { name: "error" } ],
"unicorn/consistent-destructuring": "warn",
"unicorn/consistent-empty-array-spread": "error",
"unicorn/consistent-function-scoping": "error",
"unicorn/error-message": "error",
"unicorn/escape-case": "warn",
"unicorn/filename-case":
[ "warn", { case: "camelCase" } ],
"unicorn/no-array-callback-reference": "error",
"unicorn/no-array-for-each": "warn",
"unicorn/no-array-method-this-argument": "error",
"unicorn/no-array-push-push": "warn",
"unicorn/no-array-reduce": "warn",
"unicorn/no-await-expression-member": "warn",
"unicorn/no-await-in-promise-methods": "error",
"unicorn/no-empty-file": "error",
"unicorn/no-for-loop": "warn",
"unicorn/no-hex-escape": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-invalid-fetch-options": "warn",
"unicorn/no-keyword-prefix": "warn",
"unicorn/no-length-as-slice-end": "warn",
"unicorn/no-lonely-if": "warn",
"unicorn/no-negation-in-equality-check": "error",
"unicorn/no-new-buffer": "error",
"unicorn/no-object-as-default-parameter": "warn",
"unicorn/no-single-promise-in-promise-methods": "warn",
"unicorn/no-static-only-class": "warn",
"unicorn/no-thenable": "warn",
"unicorn/no-this-assignment": "warn",
"unicorn/no-typeof-undefined": "error",
"unicorn/no-unnecessary-await": "warn",
"unicorn/no-unreadable-array-destructuring": "warn",
"unicorn/no-unreadable-iife": "warn",
"unicorn/no-useless-promise-resolve-reject": "warn",
"unicorn/no-useless-spread": "warn",
"unicorn/no-useless-switch-case": "warn",
"unicorn/no-zero-fractions": "warn",
"unicorn/number-literal-case": "warn",
"unicorn/numeric-separators-style": "warn",
"unicorn/prefer-array-flat": "warn",
"unicorn/prefer-array-flat-map": "warn",
"unicorn/prefer-array-index-of": "warn",
"unicorn/prefer-array-some": "warn",
"unicorn/prefer-at": "warn",
"unicorn/prefer-code-point": "error",
"unicorn/prefer-date-now": "warn",
"unicorn/prefer-default-parameters": "warn",
"unicorn/prefer-includes": "warn",
"unicorn/prefer-math-trunc": "warn",
"unicorn/prefer-modern-math-apis": "warn",
"unicorn/prefer-module": "error",
"unicorn/prefer-native-coercion-functions": "error",
"unicorn/prefer-negative-index": "warn",
"unicorn/prefer-node-protocol": "warn",
"unicorn/prefer-number-properties": "error",
"unicorn/prefer-object-from-entries": "warn",
"unicorn/prefer-optional-catch-binding": "warn",
"unicorn/prefer-prototype-methods": "warn",
"unicorn/prefer-regexp-test": "warn",
"unicorn/prefer-set-has": "warn",
"unicorn/prefer-set-size": "warn",
"unicorn/prefer-spread": "warn",
"unicorn/prefer-string-replace-all": "warn",
"unicorn/prefer-string-slice": "warn",
"unicorn/prefer-string-starts-ends-with": "warn",
"unicorn/prefer-string-trim-start-end": "warn",
"unicorn/prefer-structured-clone": "warn",
"unicorn/prefer-top-level-await": "warn",
"unicorn/prefer-type-error": "warn",
"unicorn/prevent-abbreviations": "warn",
"unicorn/require-array-join-separator": "error",
"unicorn/require-number-to-fixed-digits-argument": "error",
};

View File

@ -1,57 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import type { Linter } from "eslint";
export const vitestRules: Linter.RulesRecord = {
"vitest/consistent-test-filename":
[ "warn", { pattern: /^[\da-z-]+\.spec\.ts$/i } ],
"vitest/consistent-test-it": "warn",
"vitest/expect-expect": "warn",
"vitest/max-nested-describe": [ "warn", { max: 2 } ],
"vitest/no-alias-methods": "warn",
"vitest/no-commented-out-tests": "warn",
"vitest/no-conditional-expect": "warn",
"vitest/no-conditional-in-test": "warn",
"vitest/no-conditional-tests": "warn",
"vitest/no-disabled-tests": "warn",
"vitest/no-done-callback": "warn",
"vitest/no-duplicate-hooks": "warn",
"vitest/no-focused-tests": "warn",
"vitest/no-identical-title": "warn",
"vitest/no-import-node-test": "warn",
"vitest/no-interpolation-in-snapshots": "warn",
"vitest/no-restricted-vi-methods": "warn",
"vitest/no-standalone-expect": "warn",
"vitest/no-test-prefixes": "warn",
"vitest/no-test-return-statement": "warn",
"vitest/prefer-called-with": "warn",
"vitest/prefer-comparison-matcher": "warn",
"vitest/prefer-each": "warn",
"vitest/prefer-equality-matcher": "warn",
"vitest/prefer-expect-assertions": "warn",
"vitest/prefer-expect-resolves": "warn",
"vitest/prefer-hooks-in-order": "warn",
"vitest/prefer-hooks-on-top": "warn",
"vitest/prefer-lowercase-title": "warn",
"vitest/prefer-mock-promise-shorthand": "warn",
"vitest/prefer-spy-on": "warn",
"vitest/prefer-strict-equal": "warn",
"vitest/prefer-to-be": "warn",
"vitest/prefer-to-be-falsy": "warn",
"vitest/prefer-to-be-object": "warn",
"vitest/prefer-to-be-truthy": "warn",
"vitest/prefer-to-contain": "warn",
"vitest/prefer-to-have-length": "warn",
"vitest/prefer-todo": "warn",
"vitest/require-hook": "warn",
"vitest/require-to-throw-message": "warn",
"vitest/require-top-level-describe": "warn",
"vitest/valid-describe-callback": "warn",
"vitest/valid-expect":
[ "warn", { maxArgs: 2, minArgs: 2 } ],
"vitest/valid-title": "warn",
};

10
src/types.d.ts vendored
View File

@ -1,10 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
declare module "eslint-plugin-no-only-tests";
declare module "eslint-plugin-import";
declare module "eslint-plugin-sort-keys-fix";
declare module "@eslint-community/eslint-plugin-eslint-comments";

View File

@ -1,270 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import config from "../src/index.ts";
import { commentsRules } from "../src/rules/comments.ts";
import { deprecationRules } from "../src/rules/deprecation.ts";
import { eslintRules } from "../src/rules/eslint.ts";
import { importRules } from "../src/rules/import.ts";
import { jsdocRules } from "../src/rules/jsdoc.ts";
import { playwrightRules } from "../src/rules/playwright.ts";
import { reactRules } from "../src/rules/react.ts";
import { sortKeysFixRules } from "../src/rules/sortKeysFix.ts";
import { stylisticRules } from "../src/rules/stylistic.ts";
import {
typescriptEslintRules,
typescriptEslintRulesWithTypes,
} from "../src/rules/typescriptEslint.ts";
import { unicornRules } from "../src/rules/unicorn.ts";
import { vitestRules } from "../src/rules/vitest.ts";
import type { Linter } from "eslint";
// #region Custom matcher
expect.extend({
toContainRules(received, expected) {
const missingRules: Array<string> = [];
const mismatchedRules: Array<string> = [];
for (const [ key, value ] of Object.entries(expected)) {
if (!(key in received)) {
missingRules.push(key);
} else if (!this.equals(received[key], value)) {
mismatchedRules.push(key);
}
}
const pass = missingRules.length === 0 && mismatchedRules.length === 0;
let message = "";
// eslint-disable-next-line no-negated-condition -- This is a test.
if (!pass) {
message = `${message}Expected rules to contain all specified rules.\n`;
if (missingRules.length > 0) {
message = `${message}Missing rules: ${missingRules.join(", ")}\n`;
}
if (mismatchedRules.length > 0) {
message = `${message}Mismatched rules: ${mismatchedRules.join(", ")}\n`;
}
} else {
message = "Expected rules not to contain all specified rules";
}
return {
message: (): string => {
return message;
},
pass: pass,
};
},
});
// #endregion
// #region Helper function
const baseProperties = (
ruleset: Linter.Config<Linter.RulesRecord>,
isNoTypes: boolean,
): void => {
expect(ruleset?.plugins, "missing @typescript-eslint plugin").toHaveProperty(
"@typescript-eslint",
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- This is a test.
isNoTypes
? expect(
ruleset?.plugins,
"should not have deprecation plugin",
).not.toHaveProperty("deprecation")
: expect(ruleset?.plugins, "missing deprecation plugin").toHaveProperty(
"deprecation",
);
expect(ruleset?.plugins, "missing import plugin").toHaveProperty("import");
expect(ruleset?.plugins, "missing jsdoc plugin").toHaveProperty("jsdoc");
expect(ruleset?.plugins, "missing sort-keys-fix plugin").toHaveProperty(
"sort-keys-fix",
);
expect(ruleset?.plugins, "missing stylistic plugin").toHaveProperty(
"stylistic",
);
expect(ruleset?.plugins, "missing unicorn plugin").toHaveProperty("unicorn");
expect(ruleset?.rules, "missing eslint rules").toContainRules(eslintRules);
expect(ruleset?.rules, "missing typescript-eslint rules").toContainRules(
typescriptEslintRules,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- This is a test.
isNoTypes
? expect(
ruleset?.rules,
"should not have deprecation rules",
).not.toContainRules(deprecationRules)
: expect(ruleset?.rules, "missing deprecation rules").toContainRules(
deprecationRules,
);
expect(ruleset?.rules, "missing import rules").toContainRules(importRules);
expect(ruleset?.rules, "missing jsdoc rules").toContainRules(jsdocRules);
expect(ruleset?.rules, "missing sort-keys-fix rules").toContainRules(
sortKeysFixRules,
);
expect(ruleset?.rules, "missing stylistic rules").toContainRules(
stylisticRules,
);
expect(ruleset?.rules, "missing unicorn rules").toContainRules(unicornRules);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- This is a test.
isNoTypes
? expect(
ruleset?.rules,
"should not have typescript-eslint rules with types",
).not.toContainRules(typescriptEslintRulesWithTypes)
: expect(
ruleset?.rules,
"missing typescript-eslint rules with types",
).toContainRules(typescriptEslintRulesWithTypes);
expect(ruleset?.rules, "missing comment rules").toContainRules(commentsRules);
};
// #endregion
describe("global config", () => {
// #region Typescript
it("should apply the expected plugins for typescript", () => {
expect.assertions(27);
const ruleset = config.find((rule) => {
return rule?.files?.includes("src/**/*.ts");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(
ruleset?.plugins,
"should not have vitest plugin",
).not.toHaveProperty("vitest");
expect(
ruleset?.plugins,
"should not have playwright plugin",
).not.toHaveProperty("playwright");
expect(ruleset?.plugins, "should not have react plugin").not.toHaveProperty(
"react",
);
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "should not have vitest rules").not.toContainRules(
vitestRules,
);
expect(
ruleset?.rules,
"should not have playwright rules",
).not.toContainRules(playwrightRules);
expect(ruleset?.rules, "should not have react rules").not.toContainRules(
reactRules,
);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, false);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(8);
});
// #endregion
// #region Vitest
it("should apply the expected plugins for vitest files", () => {
expect.assertions(27);
const ruleset = config.find((rule) => {
return rule?.files?.includes("test/**/*.spec.ts");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(ruleset?.plugins, "missing vitest plugin").toHaveProperty("vitest");
expect(
ruleset?.plugins,
"should not have playwright plugin",
).not.toHaveProperty("playwright");
expect(ruleset?.plugins, "should not have react plugin").not.toHaveProperty(
"react",
);
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "missing vitest rules").toContainRules(vitestRules);
expect(
ruleset?.rules,
"should not have playwright rules",
).not.toContainRules(playwrightRules);
expect(ruleset?.rules, "should not have react rules").not.toContainRules(
reactRules,
);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, true);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(8);
});
// #endregion
// #region Playwright
it("should apply the expected plugins for playwright", () => {
expect.assertions(27);
const ruleset = config.find((rule) => {
return rule?.files?.includes("e2e/**/*.spec.ts");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(
ruleset?.plugins,
"should not have vitest plugin",
).not.toHaveProperty("vitest");
expect(ruleset?.plugins, "missing playwright plugin").toHaveProperty(
"playwright",
);
expect(ruleset?.plugins, "should not have react plugin").not.toHaveProperty(
"react",
);
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "should not have vitest rules").not.toContainRules(
vitestRules,
);
expect(ruleset?.rules, "missing playwright rules").toContainRules(
playwrightRules,
);
expect(ruleset?.rules, "should not have react rules").not.toContainRules(
reactRules,
);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, true);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(8);
});
// #endregion
// #region React
it("should apply the expected plugins for react", () => {
expect.assertions(27);
const ruleset = config.find((rule) => {
return rule?.files?.includes("src/**/*.tsx");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(
ruleset?.plugins,
"should not have vitest plugin",
).not.toHaveProperty("vitest");
expect(
ruleset?.plugins,
"should not have playwright plugin",
).not.toHaveProperty("playwright");
expect(ruleset?.plugins, "missing react plugin").toHaveProperty("react");
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "should not have vitest rules").not.toContainRules(
vitestRules,
);
expect(
ruleset?.rules,
"should not have playwright rules",
).not.toContainRules(playwrightRules);
expect(ruleset?.rules, "missing react rules").toContainRules(reactRules);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, false);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(9);
});
// #endregion
});

View File

@ -1,31 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import { disabledEslintRules, eslintRules } from "../src/rules/eslint.ts";
describe("eslint configs", () => {
it("should not enable disabled rules", () => {
expect.assertions(20);
const disabled = Object.keys(disabledEslintRules);
const enabled = Object.keys(eslintRules);
for (const key of disabled) {
expect(enabled, `Disabled rule ${key} has been re-enabled!`).not.toContain(key);
}
});
it("all disabled rules should be off", () => {
expect.assertions(20);
const rules = Object.entries(disabledEslintRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned on - disabled rules should be explicitly turned off.`).toBe("off");
continue;
}
});
});

View File

@ -1,108 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import { eslintRules } from "../src/rules/eslint.ts";
import { importRules } from "../src/rules/import.js";
import { jsdocRules } from "../src/rules/jsdoc.js";
import { playwrightRules } from "../src/rules/playwright.ts";
import { reactRules } from "../src/rules/react.js";
import { stylisticRules } from "../src/rules/stylistic.ts";
import { typescriptEslintRules } from "../src/rules/typescriptEslint.js";
import { unicornRules } from "../src/rules/unicorn.js";
import { vitestRules } from "../src/rules/vitest.js";
describe("no rules should be turned off in", () => {
it("eslint rules", () => {
expect.assertions(148);
const rules = Object.entries(eslintRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("import rules", () => {
expect.assertions(29);
const rules = Object.entries(importRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("jsdoc rules", () => {
expect.assertions(43);
const rules = Object.entries(jsdocRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("playwright rules", () => {
expect.assertions(42);
const rules = Object.entries(playwrightRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("react rules", () => {
expect.assertions(70);
const rules = Object.entries(reactRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("stylistic rules", () => {
expect.assertions(65);
const rules = Object.entries(stylisticRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("typescript-eslint rules", () => {
expect.assertions(59);
const rules = Object.entries(typescriptEslintRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("unicorn rules", () => {
expect.assertions(73);
const rules = Object.entries(unicornRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
it("vitest rules", () => {
expect.assertions(45);
const rules = Object.entries(vitestRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be turned off - this project does not use any external configs, so all rules should be off by default.`).not.toBe("off");
}
});
});

View File

@ -1,21 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import { playwrightRules } from "../src/rules/playwright.ts";
describe("playwright configs", () => {
it("should never be an error", () => {
expect.assertions(42);
const rules = Object.entries(playwrightRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be set to an error!`).not.toBe("error");
continue;
}
});
});

View File

@ -1,21 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import { stylisticRules } from "../src/rules/stylistic.ts";
describe("stylistic configs", () => {
it("should never be an error", () => {
expect.assertions(65);
const rules = Object.entries(stylisticRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be set to an error!`).not.toBe("error");
continue;
}
});
});

19
test/vitest.d.ts vendored
View File

@ -1,19 +0,0 @@
/// <reference types="vitest" />
import type { Assertion, AsymmetricMatchersContaining } from 'vitest';
interface CustomMatchers<R = unknown> {
toContainRules(expected: Record<string, unknown>): R;
}
declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
declare module 'vitest' {
// @ts-expect-error We need to extend the TestAPI interface
export interface TestAPI {
extend: <T>(matchers: Record<string, (this: T, ...args: any[]) => any>) => void;
}
}

View File

@ -1,21 +0,0 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import { vitestRules } from "../src/rules/vitest.ts";
describe("vitest configs", () => {
it("should never be an error", () => {
expect.assertions(45);
const rules = Object.entries(vitestRules);
for (const [ name, rule ] of rules) {
expect(Array.isArray(rule)
? rule.at(0)
: rule, `${name} appears to be set to an error!`).not.toBe("error");
continue;
}
});
});

View File

@ -1,23 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"rootDir": "src",
"outDir": "prod"
},
"exclude": ["./test", "vitest.config.ts"]
}

View File

@ -1,3 +0,0 @@
import { defineConfig } from "vitest/config";
export default defineConfig({});