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