mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Add eslint pre-commit hook
This replaces the old arclint eslint setup 1:1 rule wise, only porting the configuration to a format recent eslint can read. Further remove the arclint setup as it is no longer of use. Thanks to Stan for reviewing all needed fixes to Javascript code to allow for adding this without compromises. Fixes: #7812 Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
This commit is contained in:
parent
666ffb0f18
commit
02e15da51b
10 changed files with 178 additions and 278 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -87,6 +87,10 @@ binaries/data/mods/**/*.jpg
|
|||
# Vulkan SPIR-V shaders
|
||||
binaries/data/mods/*/shaders/spirv
|
||||
|
||||
# eslint
|
||||
node_modules
|
||||
package-lock.json
|
||||
|
||||
# Windows specific data
|
||||
desktop.ini
|
||||
Thumbs.db
|
||||
|
|
|
|||
|
|
@ -72,3 +72,13 @@ repos:
|
|||
- id: yamllint
|
||||
args:
|
||||
- --strict
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v9.27.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
additional_dependencies:
|
||||
- eslint@9.27.0
|
||||
- eslint-plugin-brace-rules@0.1.6
|
||||
args:
|
||||
- --max-warnings=0
|
||||
- --no-warn-ignored
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ https://secure.phabricator.com/book/phabricator/article/arcanist_lint/
|
|||
|
||||
- `text` is configured to detect whitespace issues.
|
||||
- `json` detects JSON syntax errors.
|
||||
- `eslint`, if installed, will run on javascript files.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -21,14 +20,3 @@ Configuration is at the root of the project, under `.arclint`.
|
|||
### Installing linters
|
||||
|
||||
We provide dummy replacement for external linters, so that they are not required.
|
||||
|
||||
#### eslint
|
||||
|
||||
Installation via npm is recommended. The linter assumes a global installation
|
||||
of both eslint and the "brace-rules" plugin.
|
||||
|
||||
```
|
||||
npm install -g eslint@latest eslint-plugin-brace-rules`
|
||||
```
|
||||
|
||||
See also https://eslint.org/docs/user-guide/getting-started
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022
|
||||
},
|
||||
"plugins": [
|
||||
"brace-rules"
|
||||
],
|
||||
"rules": {
|
||||
"no-caller": 1,
|
||||
"no-cond-assign": 1,
|
||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||
"no-dupe-args": 1,
|
||||
"no-dupe-keys": 1,
|
||||
"no-duplicate-case": 1,
|
||||
"no-empty": 1,
|
||||
"no-extra-boolean-cast": 0,
|
||||
"no-extra-parens": 0,
|
||||
"no-extra-semi": 1,
|
||||
"no-floating-decimal": 1,
|
||||
"no-func-assign": 1,
|
||||
"no-negated-in-lhs": 1,
|
||||
"no-obj-calls": 1,
|
||||
"no-unreachable": 1,
|
||||
"no-use-before-define": ["error", "nofunc"],
|
||||
"use-isnan": 1,
|
||||
"valid-jsdoc": 0,
|
||||
"valid-typeof": 1,
|
||||
|
||||
"block-scoped-var": 0,
|
||||
"consistent-return": 1,
|
||||
"default-case": 1,
|
||||
"dot-notation": 1,
|
||||
"no-else-return": 1,
|
||||
"no-invalid-this": 1,
|
||||
"no-loop-func": 0,
|
||||
"no-multi-spaces": ["warn", { "ignoreEOLComments": true }],
|
||||
"no-new": 1,
|
||||
"no-redeclare": 0,
|
||||
"no-return-assign": 1,
|
||||
"no-self-assign": 1,
|
||||
"no-self-compare": 1,
|
||||
"no-unmodified-loop-condition": 1,
|
||||
"no-unused-expressions": 1,
|
||||
"no-unused-labels": 1,
|
||||
"no-useless-concat": 0,
|
||||
"yoda": 1,
|
||||
|
||||
"no-delete-var": 1,
|
||||
"no-label-var": 1,
|
||||
"no-shadow-restricted-names": 1,
|
||||
"no-shadow": 1,
|
||||
"no-undef": 0,
|
||||
"no-undef-init": 1,
|
||||
"no-unused-vars": 0,
|
||||
|
||||
"comma-spacing": 1,
|
||||
"indent": ["warn", "tab", { "outerIIFEBody": 0 }],
|
||||
"key-spacing": 1,
|
||||
"new-cap": 0,
|
||||
"new-parens": 1,
|
||||
"no-mixed-spaces-and-tabs": ["warn", "smart-tabs"],
|
||||
"no-multi-assign": 1,
|
||||
"no-trailing-spaces": 1,
|
||||
"no-unneeded-ternary": 1,
|
||||
"no-irregular-whitespace": 1,
|
||||
"object-curly-spacing": ["warn", "always"],
|
||||
"operator-assignment": 1,
|
||||
"operator-linebreak": ["warn", "after"],
|
||||
"quote-props": 1,
|
||||
"semi": 1,
|
||||
"semi-spacing": 1,
|
||||
"space-before-function-paren": ["warn", "never"],
|
||||
"space-in-parens": 1,
|
||||
"space-unary-ops": 1,
|
||||
"spaced-comment": ["warn", "always"],
|
||||
|
||||
"no-class-assign": 1,
|
||||
"no-const-assign": 1,
|
||||
"no-dupe-class-members" : 1,
|
||||
"prefer-const": 1,
|
||||
|
||||
"brace-rules/brace-on-same-line": ["warn", {
|
||||
"FunctionDeclaration": "never",
|
||||
"FunctionExpression": "ignore",
|
||||
"ArrowFunctionExpression": "always",
|
||||
"IfStatement": "never",
|
||||
"TryStatement": "ignore",
|
||||
"CatchClause": "ignore",
|
||||
"DoWhileStatement": "never",
|
||||
"WhileStatement": "never",
|
||||
"ForStatement": "never",
|
||||
"ForInStatement": "never",
|
||||
"ForOfStatement": "never",
|
||||
"SwitchStatement": "never"
|
||||
}, { "allowSingleLine": true }]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
@echo off
|
||||
php build\arclint\dummies\eslint.php
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This file replaces eslint if the former is not found, to avoid failure in 'arc lint'.
|
||||
* It is written in PHP as we can assume php is installed if arcanist is to work at all.
|
||||
* It mimics `eslint --format json`.
|
||||
* Set the VERBOSE env variable to generate an 'advice' level lint message.
|
||||
*/
|
||||
|
||||
$verbose = getenv("VERBOSE") ? getenv("VERBOSE") : false;
|
||||
|
||||
$advice = <<<EOD
|
||||
{
|
||||
"filePath": "build/arclint/dummies/eslint.php",
|
||||
"messages": [{
|
||||
"ruleId": "skipped",
|
||||
"severity": 0,
|
||||
"message": "ESLint not found - skipped",
|
||||
"line": 23,
|
||||
"column": 0
|
||||
}]
|
||||
}
|
||||
EOD;
|
||||
|
||||
fwrite(STDOUT, $verbose ? "[$advice]" : "[]");
|
||||
?>
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2016 Pinterest, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright 2021 Wildfire Games.
|
||||
* Lints JavaScript via ESlint.
|
||||
* Heavily modified from pinterest/arcanist-linters. Original licence above.
|
||||
*/
|
||||
final class ESLintLinter extends ArcanistExternalLinter {
|
||||
|
||||
private $config;
|
||||
|
||||
public function getLinterName() {
|
||||
return 'ESLINT';
|
||||
}
|
||||
|
||||
public function getLinterConfigurationName() {
|
||||
return 'eslint';
|
||||
}
|
||||
|
||||
public function getDefaultBinary() {
|
||||
return 'eslint';
|
||||
}
|
||||
|
||||
public function getInstallInstructions() {
|
||||
return pht(
|
||||
'Install eslint using npm install -g eslint.',
|
||||
'Install the brace-rule plugin using',
|
||||
'npm install -g eslint-plugin-brace-rules');
|
||||
}
|
||||
|
||||
protected function getMandatoryFlags() {
|
||||
list($err, $stdout, $stderr) = exec_manual('npm root -g');
|
||||
return array(
|
||||
'--format=json',
|
||||
'--no-color',
|
||||
'--config',
|
||||
$this->config,
|
||||
// This allows globally installing plugins even with eslint 6+
|
||||
'--resolve-plugins-relative-to',
|
||||
strtok($stdout, "\n")
|
||||
);
|
||||
}
|
||||
|
||||
public function getLinterConfigurationOptions() {
|
||||
$options = array(
|
||||
'config' => array(
|
||||
'type' => 'optional string',
|
||||
'help' => pht('Link to the config file.'),
|
||||
),
|
||||
);
|
||||
return $options + parent::getLinterConfigurationOptions();
|
||||
}
|
||||
|
||||
public function setLinterConfigurationValue($key, $value) {
|
||||
switch ($key) {
|
||||
case 'config':
|
||||
$this->config = $value;
|
||||
return;
|
||||
}
|
||||
return parent::setLinterConfigurationValue($key, $value);
|
||||
}
|
||||
|
||||
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
|
||||
// Gate on $stderr b/c $err (exit code) is expected.
|
||||
if ($stderr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$json = json_decode($stdout, true);
|
||||
$messages = array();
|
||||
|
||||
foreach ($json as $file) {
|
||||
foreach ($file['messages'] as $offense) {
|
||||
// Skip file ignored warning: if a file is ignored by .eslintingore
|
||||
// but linted explicitly (by arcanist), a warning will be reported,
|
||||
// containing only: `{fatal:false,severity:1,message:...}`.
|
||||
if (strpos($offense['message'], "File ignored ") === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = new ArcanistLintMessage();
|
||||
$message->setPath($file['filePath']);
|
||||
$message->setSeverity($this->mapSeverity(idx($offense, 'severity', '0')));
|
||||
$message->setName(nonempty(idx($offense, 'ruleId'), 'unknown'));
|
||||
$message->setDescription(idx($offense, 'message'));
|
||||
$message->setLine(idx($offense, 'line'));
|
||||
$message->setChar(idx($offense, 'column'));
|
||||
$message->setCode($this->getLinterName());
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
private function mapSeverity($eslintSeverity) {
|
||||
switch($eslintSeverity) {
|
||||
case '0':
|
||||
return ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||
case '1':
|
||||
return ArcanistLintSeverity::SEVERITY_WARNING;
|
||||
case '2':
|
||||
default:
|
||||
return ArcanistLintSeverity::SEVERITY_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
eslint.config.mjs
Normal file
139
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Hack to get eslint run via pre-commit to find the braces plugin in the pre-commit cache,
|
||||
// should be 'import braceRules from "eslint-plugin-brace-rules";'
|
||||
import { createRequire } from 'node:module';
|
||||
const require = createRequire(import.meta.url);
|
||||
const braceRules = require("eslint-plugin-brace-rules");
|
||||
|
||||
|
||||
const configIgnores = {
|
||||
"ignores": [
|
||||
"binaries/data/mods/mod/globalscripts/sprintf.js",
|
||||
"libraries/",
|
||||
"source/tools/profiler2/jquery*",
|
||||
"source/tools/replayprofile/jquery*",
|
||||
"source/tools/templatesanalyzer/tablefilter/",
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
const configEslintBase = {
|
||||
"rules": {
|
||||
"no-caller": 1,
|
||||
"no-cond-assign": 1,
|
||||
|
||||
"no-constant-condition": ["error", {
|
||||
"checkLoops": false,
|
||||
}],
|
||||
|
||||
"no-dupe-args": 1,
|
||||
"no-dupe-keys": 1,
|
||||
"no-duplicate-case": 1,
|
||||
"no-empty": 1,
|
||||
"no-extra-boolean-cast": 0,
|
||||
"no-extra-parens": 0,
|
||||
"no-extra-semi": 1,
|
||||
"no-floating-decimal": 1,
|
||||
"no-func-assign": 1,
|
||||
"no-negated-in-lhs": 1,
|
||||
"no-obj-calls": 1,
|
||||
"no-unreachable": 1,
|
||||
"no-use-before-define": ["error", "nofunc"],
|
||||
"use-isnan": 1,
|
||||
"valid-jsdoc": 0,
|
||||
"valid-typeof": 1,
|
||||
"block-scoped-var": 0,
|
||||
"consistent-return": 1,
|
||||
"default-case": 1,
|
||||
"dot-notation": 1,
|
||||
"no-else-return": 1,
|
||||
"no-invalid-this": 1,
|
||||
"no-loop-func": 0,
|
||||
|
||||
"no-multi-spaces": ["warn", {
|
||||
"ignoreEOLComments": true,
|
||||
}],
|
||||
|
||||
"no-new": 1,
|
||||
"no-redeclare": 0,
|
||||
"no-return-assign": 1,
|
||||
"no-self-assign": 1,
|
||||
"no-self-compare": 1,
|
||||
"no-unmodified-loop-condition": 1,
|
||||
"no-unused-expressions": 1,
|
||||
"no-unused-labels": 1,
|
||||
"no-useless-concat": 0,
|
||||
"yoda": 1,
|
||||
"no-delete-var": 1,
|
||||
"no-label-var": 1,
|
||||
"no-shadow-restricted-names": 1,
|
||||
"no-shadow": 1,
|
||||
"no-undef": 0,
|
||||
"no-undef-init": 1,
|
||||
"no-unused-vars": 0,
|
||||
"comma-spacing": 1,
|
||||
|
||||
"indent": ["warn", "tab", {
|
||||
"outerIIFEBody": 0,
|
||||
}],
|
||||
|
||||
"key-spacing": 1,
|
||||
"new-cap": 0,
|
||||
"new-parens": 1,
|
||||
"no-mixed-spaces-and-tabs": ["warn", "smart-tabs"],
|
||||
"no-multi-assign": 1,
|
||||
"no-trailing-spaces": 1,
|
||||
"no-unneeded-ternary": 1,
|
||||
"no-irregular-whitespace": 1,
|
||||
"object-curly-spacing": ["warn", "always"],
|
||||
"operator-assignment": 1,
|
||||
"operator-linebreak": ["warn", "after"],
|
||||
"quote-props": 1,
|
||||
"semi": 1,
|
||||
"semi-spacing": 1,
|
||||
"space-before-function-paren": ["warn", "never"],
|
||||
"space-in-parens": 1,
|
||||
"space-unary-ops": 1,
|
||||
"spaced-comment": ["warn", "always"],
|
||||
"no-class-assign": 1,
|
||||
"no-const-assign": 1,
|
||||
"no-dupe-class-members": 1,
|
||||
"prefer-const": 1,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const configBracesRules = {
|
||||
"plugins": {
|
||||
"brace-rules": braceRules
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"brace-rules/brace-on-same-line": [
|
||||
"warn",
|
||||
{
|
||||
"FunctionDeclaration": "never",
|
||||
"FunctionExpression": "ignore",
|
||||
"ArrowFunctionExpression": "always",
|
||||
"IfStatement": "never",
|
||||
"TryStatement": "ignore",
|
||||
"CatchClause": "ignore",
|
||||
"DoWhileStatement": "never",
|
||||
"WhileStatement": "never",
|
||||
"ForStatement": "never",
|
||||
"ForInStatement": "never",
|
||||
"ForOfStatement": "never",
|
||||
"SwitchStatement": "never",
|
||||
},
|
||||
{
|
||||
"allowSingleLine": true,
|
||||
}
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const configs = [configIgnores, configEslintBase];
|
||||
configs[1].plugins = { ...configBracesRules.plugins };
|
||||
Object.assign(configs[1].rules, configBracesRules.rules);
|
||||
|
||||
export default configs;
|
||||
11
package.json
Normal file
11
package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-brace-rules": "^0.1.6"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix"
|
||||
}
|
||||
}
|
||||
|
|
@ -20,3 +20,17 @@ Adding library cfg's for other deps could improve cppchecks ability to find issu
|
|||
## copyright
|
||||
|
||||
A linter for checking copyright dates in file headers are up to date.
|
||||
|
||||
## eslint
|
||||
|
||||
For eslint run 'pre-commt run eslint -a'
|
||||
|
||||
### Installation and IDE integration
|
||||
|
||||
Install Node.js and then run 'npm install' in the repo root.
|
||||
|
||||
Now you can run eslint as 'npm run-script lint' or if you want eslint to try
|
||||
fix the issues 'npm run-script lint:fix'.
|
||||
|
||||
After having installed eslint you might want to add an eslint extension to your
|
||||
editor to get inline warnings and to allow for auto-formatting.
|
||||
|
|
|
|||
Loading…
Reference in a new issue