]> git.proxmox.com Git - pve-eslint.git/blame - src/bin/app.js
fix eslint warnings/errors for own sources
[pve-eslint.git] / src / bin / app.js
CommitLineData
4b0098f6
DC
1#!/usr/bin/env node
2
d1e45ca9 3(async function() {
eb39fafa
DC
4'use strict';
5
6d505c57 6const path = require('path');
eb39fafa
DC
7const color = require('colors');
8const program = require('commander');
4b0098f6 9const eslint = require('pve-eslint');
eb39fafa
DC
10
11program
12 .usage('[options] [<file(s) ...>]')
13 .option('-c, --config <configfile>', 'uses <configfile> for eslint config instead.')
177b4d5e 14 .option('-e, --extend <configfile>', 'uses <configfile> ontop of default eslint config.')
4e679d18 15 .option('-f, --fix', 'if set, fixes will be applied.')
a4a25724 16 .option('-s, --strict', 'if set, also exit uncleanly on warnings')
aa1e5f5e 17 .option('-t, --threads <threads>', 'how many worker_threads should be used (default=4)')
4e679d18
TL
18 .option('--output-config', 'if set, only output the config as JSON and exit.')
19 ;
eb39fafa
DC
20
21program.on('--help', function() {
22 console.log('');
23 console.log(' Description:');
24 console.log('');
25 console.log(' lints javascript files');
eb39fafa
DC
26 console.log('');
27});
28
177b4d5e
TL
29program.parse(process.argv);
30
31if (program.config && program.extend) {
32 console.error('Cannot use both, --config and --extend, at the same time!');
33 process.exit(1);
be566999
TL
34}
35
4e679d18 36if (program.args.length < 1 && !program.outputConfig) {
177b4d5e
TL
37 program.help();
38}
eb39fafa
DC
39
40let paths = program.args;
41
42if (!paths.length) {
43 paths = [process.cwd()];
44}
45
aa1e5f5e
DC
46let threadCount = 4;
47if (program.threads) {
48 threadCount = program.threads;
49}
50
eb39fafa
DC
51const defaultConfig = {
52 parserOptions: {
931088d3 53 ecmaVersion: 2021,
eb39fafa
DC
54 ecmaFeatures: {
55 impliedStrict: true,
8e4082d9 56 },
eb39fafa
DC
57 },
58 env: {
59 browser: true,
60 node: true,
931088d3 61 es2021: true, // automatically sets ecmaVersion to 2021 and allows es2021 globals
eb39fafa
DC
62 },
63 globals: {
eb39fafa 64 Ext: "writable",
30ca1a0a
TL
65 FormData: "writable",
66 PBS: "writable",
eb39fafa 67 PMG: "writable",
30ca1a0a 68 PVE: "writable",
eb39fafa 69 PVE_vnc_console_event: "writable",
eb39fafa
DC
70 Proxmox: "writable",
71 console: "writable",
30ca1a0a
TL
72 eslint: "writable",
73 gettext: "writable",
d4b999a4
TL
74 proxmoxOnlineHelpInfo: "writable",
75 pveOnlineHelpInfo: "writable",
eb39fafa
DC
76 },
77 rules: {
78 // from eslint:recommend, with tweaks for our source
79 "constructor-super": "error",
80 "for-direction": "error",
81 "getter-return": "error",
82 "no-async-promise-executor": "error",
83 "no-case-declarations": "error",
84 "no-class-assign": "error",
85 "no-compare-neg-zero": "error",
86 "no-cond-assign": "error",
87 "no-const-assign": "error",
88 "no-constant-condition": "error",
89 "no-control-regex": "error",
90 "no-debugger": "error",
91 "no-delete-var": "error",
92 "no-dupe-args": "error",
93 "no-dupe-class-members": "error",
94 "no-dupe-else-if": "error",
95 "no-dupe-keys": "error",
96 "no-duplicate-case": "error",
97 "no-empty": "error",
98 "no-empty-character-class": "error",
99 "no-empty-pattern": "error",
100 "no-ex-assign": "error",
101 "no-extra-boolean-cast": "error",
102 "no-extra-semi": "error",
103 "no-fallthrough": "error",
104 "no-func-assign": "error",
105 "no-global-assign": "error",
106 "no-import-assign": "error",
107 "no-inner-declarations": "error",
108 "no-invalid-regexp": "error",
109 "no-irregular-whitespace": "error",
110 "no-misleading-character-class": "error",
111 "no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
112 "no-new-symbol": "error",
113 "no-obj-calls": "error",
114 "no-octal": "error",
115 "no-prototype-builtins": "error",
116 "no-redeclare": "error",
117 "no-regex-spaces": "error",
118 "no-self-assign": "error",
119 "no-setter-return": "error",
120 "no-shadow-restricted-names": "error",
121 "no-sparse-arrays": "error",
122 "no-this-before-super": "error",
123 "no-undef": "error",
124 "no-unexpected-multiline": "error",
125 "no-unreachable": "error",
126 "no-unsafe-finally": "error",
127 "no-unsafe-negation": "error",
128 "no-unused-labels": "error",
a4d939da 129 "no-unused-vars": ["error", { vars: "all", args: "none", varsIgnorePattern: "^(me|_.*)$" }],
eb39fafa
DC
130 "no-useless-catch": "error",
131 "no-useless-escape": "error",
132 "no-with": "error",
133 "require-yield": "error",
134 "use-isnan": "error",
135 "valid-typeof": "error",
136
137 // selection of best practices
138 "accessor-pairs": "error",
139 "array-callback-return": "error",
140 "block-scoped-var": "error",
141 "consistent-return": "error",
142 "curly": ["error", "multi-line"],
ca06ae98 143 "dot-location": ["error", "property"],
eb39fafa
DC
144 "dot-notation": "error",
145 "eqeqeq": "error",
146 "grouped-accessor-pairs": "error",
147 "guard-for-in": "error",
148 "no-alert": "error",
149 "no-await-in-loop": "error",
150 "no-caller": "error",
151 "no-constructor-return": "error",
152 "no-div-regex": "error",
3958e250 153 //"no-else-return": "warn", // not a real style problem, both can be OK
eb39fafa
DC
154 "no-empty-function": "error",
155 "no-eq-null": "error",
156 "no-eval": "error",
157 "no-extend-native": "error",
158 "no-extra-bind": "error",
159 "no-extra-label": "error",
e89a2e92
TL
160 "no-extra-parens": ["error", "all", {
161 "conditionalAssign": false, // useful for if ((match = str.match(/.../))) lines
162 "enforceForArrowConditionals": false,
163 "nestedBinaryExpressions": false,
164 }],
eb39fafa 165 "no-floating-decimal": "error",
5fad4d81 166 "no-implicit-coercion": ["error", { allow: ["!!"] }],
eb39fafa
DC
167 "no-implicit-globals": "error",
168 "no-implied-eval": "error",
169 "no-invalid-this": "error",
170 "no-lone-blocks": "error",
171 "no-loop-func": "error",
172 "no-multi-spaces": "error",
173 "no-multi-str": "error",
174 "no-new": "error",
175 "no-new-func": "error",
176 "no-new-wrappers": "error",
177 "no-octal-escape": "error",
178 "no-proto": "error",
179 "no-return-assign": "error",
180 "no-return-await": "error",
181 "no-script-url": "error",
182 "no-self-compare": "error",
183 "no-sequences": "error",
184 "no-template-curly-in-string": "error",
185 "no-unmodified-loop-condition": "error",
186 "no-unused-expressions": "error",
187 "no-useless-call": "error",
188 "no-useless-concat": "error",
189 "no-useless-return": "error",
190 "no-void": "error",
191 "prefer-regex-literals": "error",
192 "radix": "error",
193 "require-atomic-updates": "error",
194 "wrap-iife": "error",
195 "yoda": "error",
196
197 // variable issues
198 "no-label-var": "error",
199 "no-shadow": "error",
200 "no-undef-init": "error",
201 "no-use-before-define": "error",
202
203 // stylistic issues, only warn, most can be auto-fixed
204 // those are quite opinionated...
8e4082d9
DC
205 "array-bracket-spacing": ["warn", "never"],
206 "brace-style": ["warn", "1tbs", { allowSingleLine: true }],
207 "comma-dangle": ["warn", "always-multiline"], // maybe only-multiline?
eb39fafa
DC
208 "comma-spacing": "warn",
209 "comma-style": "warn",
210 "computed-property-spacing": "warn",
8e4082d9 211 "consistent-this": ["warn", "me"],
eb39fafa
DC
212 "eol-last": "warn",
213 "func-call-spacing": "warn",
214 "func-name-matching": "warn",
215 "func-style": "warn",
216 "key-spacing": "warn",
217 "keyword-spacing": "warn",
218 "linebreak-style": "warn",
8e4082d9 219 "max-len": ["warn", { code: 110, tabWidth: 8, ignoreComments: true, ignoreStrings: true, ignoreRegExpLiterals: true }],
eb39fafa
DC
220 "no-array-constructor": "warn",
221 "no-lonely-if": "warn",
6a98065e
TL
222 "no-mixed-operators": ["warn", {
223 "groups": [
224 //["+", "-", "*", "/", "%", "**"], // assume the devs can do basic math..
225 ["&", "|", "^", "~", "<<", ">>", ">>>"],
226 ["==", "!=", "===", "!==", ">", ">=", "<", "<="],
227 ["&&", "||"],
3a486d1d 228 ["in", "instanceof"],
6a98065e 229 ],
3a486d1d 230 "allowSamePrecedence": true,
6a98065e 231 }],
eb39fafa
DC
232 "no-multiple-empty-lines": "warn",
233 "no-trailing-spaces": "warn",
3fb7828e 234 // "no-underscore-dangle": ["warn", { allowAfterThis: true }], // we use it for unused variables, like in rust
eb39fafa
DC
235 "no-unneeded-ternary": "warn",
236 "no-whitespace-before-property": "warn",
067b576d
TL
237 "object-curly-newline": ["warn", {
238 "multiline": true,
239 //"minProperties": 2, // FIXME: enable this??
240 "consistent": true,
241 }],
8e4082d9 242 "object-curly-spacing": ["warn", "always"],
630511ae
TL
243 "operator-linebreak": ["warn", "after", {
244 "overrides": {
245 "?": "before",
246 ":": "before",
247 "+": "ignore",
248 },
249 }],
eb39fafa 250 "padded-blocks": ["warn", "never"], // not sure ...
8e4082d9 251 "quote-props": ["warn", "as-needed", { keywords: true, unnecessary: false }], // does nothing, maybe deactivate unnecessary
eb39fafa
DC
252 "semi": "warn",
253 "semi-spacing": "warn",
490226fa 254 // "semi-style": "warn", // could be useful, but for ?: we often want to allow the ; on the next line
eb39fafa
DC
255 "space-before-blocks": "warn",
256 "space-before-function-paren": ["warn", "never"],
257 "space-in-parens": "warn",
258 "space-unary-ops": "warn",
259 "switch-colon-spacing": "warn",
260 "unicode-bom": "warn",
261 "arrow-body-style": "warn",
262 "arrow-spacing": "warn",
630511ae 263 // "no-confusing-arrow": "warn", // can be useful to do and isn't really confusing
eb39fafa
DC
264 "prefer-numeric-literals": "warn",
265 "template-curly-spacing": "warn",
266 },
267};
268
756e2b86
TL
269let pathExpand = (p) => {
270 if (p.match(/^[^/]/)) {
271 p = process.cwd() + "/" + p;
272 }
273 return p;
274};
275
eb39fafa
DC
276let config = defaultConfig;
277if (program.config) {
eb39fafa 278 config = {
756e2b86 279 "extends": pathExpand(program.config),
8e4082d9 280 };
177b4d5e
TL
281} else if (program.extend) {
282 config.extends = pathExpand(program.extend);
283 console.log(`Extend with path: ${config.extends}`);
eb39fafa
DC
284}
285
4e679d18
TL
286if (program.outputConfig) {
287 let cfg = JSON.stringify(config, null, 2);
288 console.log(cfg);
289 process.exit(0);
290}
291
aa1e5f5e 292const cliOptions = {
eb39fafa
DC
293 baseConfig: config,
294 useEslintrc: true,
295 fix: !!program.fix,
0803a17d 296 cwd: process.cwd(),
aa1e5f5e
DC
297};
298
299let promises = [];
300let filesPerThread = Math.round(paths.length / threadCount);
301for (let i = 0; i < (threadCount - 1); i++) {
302 let files = paths.splice(0, filesPerThread);
303 promises.push(eslint.createWorker({
304 cliOptions,
d1e45ca9 305 files,
aa1e5f5e
DC
306 }));
307}
308
309// the remaining paths
310promises.push(eslint.createWorker({
311 cliOptions,
d1e45ca9 312 files: paths,
aa1e5f5e 313}));
eb39fafa 314
7a730cb6 315let results = (await Promise.all(promises)).flat(1);
35543d16 316
eb39fafa 317let exitcode = 0;
35543d16 318let files_err = [], files_warn = [], files_ok = [];
eb39fafa
DC
319let fixes = 0;
320console.log('------------------------------------------------------------');
aa1e5f5e 321results.forEach(function(result) {
6d505c57 322 let filename = path.relative(process.cwd(), result.filePath);
eb39fafa
DC
323 let msgs = result.messages;
324 let max_sev = 0;
325 if (!!program.fix && result.output) {
326 fixes++;
327 }
7b71dd2c 328 if (msgs.length <= 0) {
eb39fafa
DC
329 files_ok.push(filename);
330 return;
331 }
7b71dd2c
TL
332 console.error(`[./${color.bold(filename)}]:`);
333 msgs.forEach(function(e) {
334 if (max_sev < e.severity) {
335 max_sev = e.severity;
336 }
337 let msg = `: line ${color.bold(e.line)} col ${color.bold(e.column)}: ${e.ruleId}`;
338 if (e.severity === 1) {
339 msg = color.yellow("WARN" + msg);
a4a25724
TL
340 if (exitcode < 1 && !!program.strict) {
341 exitcode = 1;
342 }
7b71dd2c
TL
343 } else if (e.severity === 2) {
344 msg = color.red("ERR " + msg);
345 if (exitcode < 1) {
346 exitcode = 1;
347 }
348 } else {
349 msg = "INFO" + msg;
350 }
351 if (e.message) {
352 msg += ` - ${e.message}`;
353 }
354 if (!program.fix && e.fix) {
355 fixes++;
356 msg += ' (*)';
357 }
358 console.error(msg);
359 if (e.suggestion) {
360 console.error(e.suggestion);
361 }
362 });
363
364 if (max_sev === 1) {
365 files_warn.push(filename);
366 } else if (max_sev === 2) {
367 files_err.push(filename);
368 }
eb39fafa
DC
369
370 console.log('------------------------------------------------------------');
371});
372
aa1e5f5e 373if (results.length > 1) {
8e4082d9 374 console.log(`${color.bold(files_ok.length + files_err.length)} files:`);
eb39fafa 375 if (files_err.length > 0) {
7b71dd2c 376 console.log(color.red(` ${color.bold(files_err.length)} files have Errors`));
eb39fafa
DC
377 }
378 if (files_warn.length > 0) {
7b71dd2c 379 console.log(color.yellow(` ${color.bold(files_warn.length)} files have Warnings`));
eb39fafa
DC
380 }
381 if (files_ok.length > 0) {
7b71dd2c 382 console.log(color.green(` ${color.bold(files_ok.length)} files are OK`));
eb39fafa 383 }
eb39fafa
DC
384} else if (files_ok.length > 0) {
385 console.log(color.green(`${files_ok[0]} OK`));
eb39fafa 386}
7b71dd2c 387console.log('------------------------------------------------------------');
eb39fafa
DC
388
389if (program.fix) {
390 if (fixes > 0) {
391 console.log(`Writing ${color.bold(fixes)} fixed files...`);
dd99a169 392 await eslint.ESLint.outputFixes(results);
eb39fafa
DC
393 console.log('Done');
394 } else {
395 console.log("No fixable Errors/Warnings found.");
396 }
7b71dd2c
TL
397} else if (fixes > 0) {
398 console.log(`${color.bold(fixes)} issues marked with (*) could be auto-fixed using '--fix'.`);
eb39fafa
DC
399}
400
401process.exit(exitcode);
eb39fafa 402}());