]>
Commit | Line | Data |
---|---|---|
4b0098f6 DC |
1 | #!/usr/bin/env node |
2 | ||
d1e45ca9 | 3 | (async function() { |
eb39fafa DC |
4 | 'use strict'; |
5 | ||
6d505c57 | 6 | const path = require('path'); |
eb39fafa DC |
7 | const color = require('colors'); |
8 | const program = require('commander'); | |
4b0098f6 | 9 | const eslint = require('pve-eslint'); |
eb39fafa DC |
10 | |
11 | program | |
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 | |
21 | program.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 |
29 | program.parse(process.argv); |
30 | ||
31 | if (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 | 36 | if (program.args.length < 1 && !program.outputConfig) { |
177b4d5e TL |
37 | program.help(); |
38 | } | |
eb39fafa DC |
39 | |
40 | let paths = program.args; | |
41 | ||
42 | if (!paths.length) { | |
43 | paths = [process.cwd()]; | |
44 | } | |
45 | ||
aa1e5f5e DC |
46 | let threadCount = 4; |
47 | if (program.threads) { | |
48 | threadCount = program.threads; | |
49 | } | |
50 | ||
eb39fafa DC |
51 | const 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 |
269 | let pathExpand = (p) => { |
270 | if (p.match(/^[^/]/)) { | |
271 | p = process.cwd() + "/" + p; | |
272 | } | |
273 | return p; | |
274 | }; | |
275 | ||
eb39fafa DC |
276 | let config = defaultConfig; |
277 | if (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 |
286 | if (program.outputConfig) { |
287 | let cfg = JSON.stringify(config, null, 2); | |
288 | console.log(cfg); | |
289 | process.exit(0); | |
290 | } | |
291 | ||
aa1e5f5e | 292 | const cliOptions = { |
eb39fafa DC |
293 | baseConfig: config, |
294 | useEslintrc: true, | |
295 | fix: !!program.fix, | |
0803a17d | 296 | cwd: process.cwd(), |
aa1e5f5e DC |
297 | }; |
298 | ||
299 | let promises = []; | |
300 | let filesPerThread = Math.round(paths.length / threadCount); | |
301 | for (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 | |
310 | promises.push(eslint.createWorker({ | |
311 | cliOptions, | |
d1e45ca9 | 312 | files: paths, |
aa1e5f5e | 313 | })); |
eb39fafa | 314 | |
7a730cb6 | 315 | let results = (await Promise.all(promises)).flat(1); |
35543d16 | 316 | |
eb39fafa | 317 | let exitcode = 0; |
35543d16 | 318 | let files_err = [], files_warn = [], files_ok = []; |
eb39fafa DC |
319 | let fixes = 0; |
320 | console.log('------------------------------------------------------------'); | |
aa1e5f5e | 321 | results.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 | 373 | if (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 | 387 | console.log('------------------------------------------------------------'); |
eb39fafa DC |
388 | |
389 | if (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 | ||
401 | process.exit(exitcode); | |
eb39fafa | 402 | }()); |