ESLint improvements (#388)

## Description

Adds various features to improve the DX with eslint:

### Webpack

- Use `eslint-webpack-plugin` for better observability of lint errors
during development.

### Ignored files

- Enable `.gitignore` support for eslint.

### Commit hook

- Add `eslint --fix` to pre-commit hook.

### Github actions
- Add eslint check to a new github action workflow.
- Use `eslint-formatter-gha` to annotate PR files with lint failures
(see the Files changed tab,
[example](https://github.com/openfrontio/OpenFrontIO/pull/388/commits/73862230be5aad7b18e122b1cd4ab05fc9570b2c)).


![image](https://github.com/user-attachments/assets/b399b92e-8eb0-48e1-92b7-ce630ccf12e1)

## Testing

These changes have been validated through local testing and through
Github workflows.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

fake.neo

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
Scott Anderson
2025-04-02 09:09:38 -07:00
committed by GitHub
co-authored by Scott Anderson
parent 8de3a5f106
commit 9a3983d014
5 changed files with 206 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
name: ESLint Check
on:
pull_request:
push:
branches: [main]
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npx eslint --format gha
+23
View File
@@ -1,13 +1,36 @@
import { includeIgnoreFile } from "@eslint/compat";
import pluginJs from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import globals from "globals";
import path from "node:path";
import { fileURLToPath } from "node:url";
import tseslint from "typescript-eslint";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, ".gitignore");
/** @type {import('eslint').Linter.Config[]} */
export default [
includeIgnoreFile(gitignorePath),
{ files: ["**/*.{js,mjs,cjs,ts}"] },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier,
{
rules: {
// Disable rules that would fail. The failures should be fixed, and the entries here removed.
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-unused-vars": "off",
"no-case-declarations": "off",
"no-useless-escape": "off",
"prefer-const": "off",
},
},
];
+157
View File
@@ -64,6 +64,7 @@
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/preset-typescript": "^7.24.7",
"@eslint/compat": "^1.2.7",
"@eslint/js": "^9.21.0",
"@types/chai": "^4.3.17",
"@types/d3": "^7.4.3",
@@ -85,6 +86,8 @@
"css-loader": "^7.1.2",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.1.1",
"eslint-formatter-gha": "^1.5.2",
"eslint-webpack-plugin": "^5.0.0",
"file-loader": "^6.2.0",
"globals": "^16.0.0",
"html-inline-script-webpack-plugin": "^3.2.1",
@@ -3555,6 +3558,24 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/compat": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.7.tgz",
"integrity": "sha512-xvv7hJE32yhegJ8xNAnb62ggiAwTYHBpUCWhRxEj/ksvgDJuSXfoDkBcRYaYNFiJ+jH0IE3K16hd+xXzhBgNbg==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": "^9.10.0"
},
"peerDependenciesMeta": {
"eslint": {
"optional": true
}
}
},
"node_modules/@eslint/config-array": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
@@ -6608,6 +6629,17 @@
"@types/trusted-types": "*"
}
},
"node_modules/@types/eslint": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -10446,6 +10478,42 @@
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-formatter-gha": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/eslint-formatter-gha/-/eslint-formatter-gha-1.5.2.tgz",
"integrity": "sha512-1TY8AYbrIP9DCcbydYW467nTP67eW79bT+oVKdUehO3WPMP8pn2oOZVkDc8yDcWf35+t2Li1elY5Dh6jTjJ3/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"eslint-formatter-json": "^8.40.0",
"eslint-formatter-stylish": "^8.40.0"
}
},
"node_modules/eslint-formatter-json": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/eslint-formatter-json/-/eslint-formatter-json-8.40.0.tgz",
"integrity": "sha512-0bXo4At1EoEU23gFfN7wcDeqRXDHLJnvDOuQKD3Q6FkBlk7L2oVNPYg/sciIWdYrUnCBcKuMit3IWXkdSfzChg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/eslint-formatter-stylish": {
"version": "8.40.0",
"resolved": "https://registry.npmjs.org/eslint-formatter-stylish/-/eslint-formatter-stylish-8.40.0.tgz",
"integrity": "sha512-blbD5ZSQnjNEUaG38VCO4WG9nfDQWE8/IOmt8DFRHXUIfZikaIXmsQTdWNFk0/e0j7RgIVRza86MpsJ+aHgFLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"strip-ansi": "^6.0.1",
"text-table": "^0.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -10472,6 +10540,88 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-webpack-plugin": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-5.0.0.tgz",
"integrity": "sha512-iDhXf2r55KO1UhMfpus8oGp93wdNF+934q5kEkwa7qn3BH9f51QEC11xQidt+8jfqRnEYYZa2/8lhac7U/vqWw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "^9.6.1",
"jest-worker": "^29.7.0",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"schema-utils": "^4.3.0"
},
"engines": {
"node": ">= 18.12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"eslint": "^8.0.0 || ^9.0.0",
"webpack": "^5.0.0"
}
},
"node_modules/eslint-webpack-plugin/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
"peerDependencies": {
"ajv": "^8.8.2"
}
},
"node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"license": "MIT"
},
"node_modules/eslint-webpack-plugin/node_modules/schema-utils": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.1.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
@@ -18193,6 +18343,13 @@
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
"license": "MIT"
},
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true,
"license": "MIT"
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+4
View File
@@ -17,6 +17,7 @@
},
"lint-staged": {
"**/*": [
"eslint --fix",
"prettier --ignore-unknown --write"
]
},
@@ -24,6 +25,7 @@
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/preset-typescript": "^7.24.7",
"@eslint/compat": "^1.2.7",
"@eslint/js": "^9.21.0",
"@types/chai": "^4.3.17",
"@types/d3": "^7.4.3",
@@ -45,6 +47,8 @@
"css-loader": "^7.1.2",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.1.1",
"eslint-formatter-gha": "^1.5.2",
"eslint-webpack-plugin": "^5.0.0",
"file-loader": "^6.2.0",
"globals": "^16.0.0",
"html-inline-script-webpack-plugin": "^3.2.1",
+4
View File
@@ -1,4 +1,5 @@
import CopyPlugin from "copy-webpack-plugin";
import ESLintPlugin from "eslint-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import path from "path";
import { fileURLToPath } from "url";
@@ -129,6 +130,9 @@ export default async (env, argv) => {
],
options: { concurrency: 100 },
}),
new ESLintPlugin({
context: __dirname,
}),
],
optimization: {
// Add optimization configuration for better caching