npm-groovy-lint 14.6.0 → 14.6.1-beta202408252305.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +13 -0
  2. package/lib/codenarc-caller.js +54 -53
  3. package/lib/codenarc-factory.js +41 -50
  4. package/lib/config.js +22 -19
  5. package/lib/filter.js +8 -15
  6. package/lib/groovy-lint-fix.js +32 -34
  7. package/lib/groovy-lint-rules.js +12 -12
  8. package/lib/groovy-lint.js +40 -41
  9. package/lib/index.js +3 -3
  10. package/lib/java/{CodeNarc-3.4.0-alpha+3346775f.jar → CodeNarc-3.5.0.jar} +0 -0
  11. package/lib/java/CodeNarcServer.jar +0 -0
  12. package/lib/options.js +45 -54
  13. package/lib/output.js +23 -23
  14. package/lib/rules/AssignmentInConditional.js +7 -7
  15. package/lib/rules/BlankLineBeforePackage.js +11 -11
  16. package/lib/rules/BlockEndsWithBlankLine.js +6 -6
  17. package/lib/rules/BlockStartsWithBlankLine.js +6 -6
  18. package/lib/rules/BracesForClass.js +8 -8
  19. package/lib/rules/BracesForForLoop.js +9 -9
  20. package/lib/rules/BracesForIfElse.js +9 -9
  21. package/lib/rules/BracesForMethod.js +11 -11
  22. package/lib/rules/BracesForTryCatchFinally.js +7 -7
  23. package/lib/rules/CatchException.js +4 -4
  24. package/lib/rules/ClassEndsWithBlankLine.js +6 -6
  25. package/lib/rules/ClassStartsWithBlankLine.js +6 -6
  26. package/lib/rules/ClosingBraceNotAlone.js +8 -8
  27. package/lib/rules/ConsecutiveBlankLines.js +7 -7
  28. package/lib/rules/DuplicateImport.js +6 -6
  29. package/lib/rules/DuplicateNumberLiteral.js +6 -6
  30. package/lib/rules/DuplicateStringLiteral.js +6 -6
  31. package/lib/rules/ElseBlockBraces.js +10 -10
  32. package/lib/rules/ExplicitArrayListInstantiation.js +7 -7
  33. package/lib/rules/ExplicitLinkedListInstantiation.js +7 -7
  34. package/lib/rules/FileEndsWithoutNewline.js +7 -7
  35. package/lib/rules/GStringExpressionWithinString.js +7 -7
  36. package/lib/rules/IfStatementBraces.js +10 -10
  37. package/lib/rules/Indentation.js +14 -14
  38. package/lib/rules/IndentationClosingBraces.js +8 -8
  39. package/lib/rules/IndentationComments.js +6 -6
  40. package/lib/rules/InsecureRandom.js +14 -14
  41. package/lib/rules/JavaIoPackageAccess.js +6 -6
  42. package/lib/rules/MethodCount.js +6 -6
  43. package/lib/rules/MethodParameterTypeRequired.js +6 -6
  44. package/lib/rules/MethodReturnTypeRequired.js +6 -6
  45. package/lib/rules/MisorderedStaticImports.js +14 -14
  46. package/lib/rules/MissingBlankLineAfterImports.js +6 -6
  47. package/lib/rules/MissingBlankLineAfterPackage.js +6 -6
  48. package/lib/rules/NoDef.js +4 -4
  49. package/lib/rules/NoJavaUtilDate.js +4 -4
  50. package/lib/rules/NoTabCharacter.js +7 -7
  51. package/lib/rules/SimpleDateFormatMissingLocale.js +4 -4
  52. package/lib/rules/SpaceAfterCatch.js +7 -7
  53. package/lib/rules/SpaceAfterComma.js +9 -9
  54. package/lib/rules/SpaceAfterFor.js +8 -8
  55. package/lib/rules/SpaceAfterIf.js +10 -10
  56. package/lib/rules/SpaceAfterMethodCallName.js +7 -7
  57. package/lib/rules/SpaceAfterOpeningBrace.js +9 -9
  58. package/lib/rules/SpaceAfterSemicolon.js +8 -8
  59. package/lib/rules/SpaceAfterSwitch.js +8 -8
  60. package/lib/rules/SpaceAfterWhile.js +7 -7
  61. package/lib/rules/SpaceAroundOperator.js +14 -14
  62. package/lib/rules/SpaceBeforeClosingBrace.js +8 -8
  63. package/lib/rules/SpaceBeforeOpeningBrace.js +9 -9
  64. package/lib/rules/SpaceInsideParentheses.js +17 -17
  65. package/lib/rules/SystemExit.js +4 -4
  66. package/lib/rules/TrailingWhitespace.js +8 -8
  67. package/lib/rules/UnnecessaryDefInFieldDeclaration.js +8 -8
  68. package/lib/rules/UnnecessaryDefInMethodDeclaration.js +7 -7
  69. package/lib/rules/UnnecessaryDefInVariableDeclaration.js +9 -9
  70. package/lib/rules/UnnecessaryDotClass.js +7 -7
  71. package/lib/rules/UnnecessaryFinalOnPrivateMethod.js +7 -7
  72. package/lib/rules/UnnecessaryGString.js +14 -14
  73. package/lib/rules/UnnecessaryGroovyImport.js +6 -6
  74. package/lib/rules/UnnecessaryPackageReference.js +9 -9
  75. package/lib/rules/UnnecessaryParenthesesForMethodCallWithClosure.js +9 -9
  76. package/lib/rules/UnnecessaryPublicModifier.js +4 -4
  77. package/lib/rules/UnnecessarySemicolon.js +10 -13
  78. package/lib/rules/UnnecessaryToString.js +8 -8
  79. package/lib/rules/UnusedImport.js +10 -10
  80. package/lib/rules/UnusedMethodParameter.js +6 -6
  81. package/lib/rules/UnusedVariable.js +6 -6
  82. package/lib/rules/VariableName.js +11 -11
  83. package/lib/rules/VariableTypeRequired.js +11 -11
  84. package/lib/utils.js +62 -43
  85. package/package.json +22 -19
package/README.md CHANGED
@@ -427,6 +427,19 @@ Example
427
427
  console.log(JSON.stringify(linter.lintResult));
428
428
  ```
429
429
 
430
+ ## Troubleshooting
431
+
432
+ If you have issues with MegaLinter, you can [report it on the repository](https://github.com/nvuillam/npm-groovy-lint/issues)
433
+
434
+ To help reproducing, you can access advanced logs using DEBUG env variables
435
+
436
+ Examples:
437
+
438
+ - `DEBUG=npm-groovy-lint npm-groovy-lint ....`
439
+ - `DEBUG=npm-groovy-lint,npm-groovy-lint-trace npm-groovy-lint ....`
440
+
441
+ If you want to see what happens in CodeNarc Server, you can clone the repo and run locally `npm server:run` before running npm-groovy-lint: you'll see the live logs of the to CodeNarc Server for npm-groovy-lint.
442
+
430
443
  ## Contribute
431
444
 
432
445
  Contributions are very welcome !
@@ -1,20 +1,23 @@
1
1
  // Call CodeNarc by server or java
2
- const axios = require("axios").default;
3
- const cliProgress = require("cli-progress");
4
- const debug = require("debug")("npm-groovy-lint");
5
- const trace = require("debug")("npm-groovy-lint-trace");
6
- const { JavaCaller } = require("java-caller");
7
- const optionsDefinition = require("./options");
8
- const { performance } = require("perf_hooks");
9
- const c = require("chalk");
2
+ import axios from "axios";
3
+ import * as cliProgress from "cli-progress";
4
+ import Debug from "debug";
5
+ const debug = Debug("npm-groovy-lint");
6
+ const trace = Debug("npm-groovy-lint-trace");
7
+ import { JavaCaller } from "java-caller";
8
+ import { optionsDefinition } from "./options.js";
9
+ import { performance } from "node:perf_hooks";
10
+ import c from "chalk";
11
+ import findJavaHome from "find-java-home";
12
+ import * as path from "path";
13
+ import { fileURLToPath } from "node:url";
10
14
 
11
15
  // Request over IPv4 because Java typically prefers it.
12
- const http = require("http");
16
+ import http from "http";
13
17
  axios.defaults.httpAgent = new http.Agent({ family: 4, keepAlive: true });
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
19
 
15
- class CodeNarcCaller {
16
- "use strict";
17
-
20
+ export class CodeNarcCaller {
18
21
  args = [];
19
22
  options;
20
23
  codenarcArgs;
@@ -34,14 +37,14 @@ class CodeNarcCaller {
34
37
  minimumJavaVersion: 17,
35
38
  maximumJavaVersion: 17,
36
39
  rootPath: __dirname,
37
- jar: "java/CodeNarcServer.jar"
40
+ jar: "java/CodeNarcServer.jar",
38
41
  },
39
42
  codeNarcJava: {
40
43
  minimumJavaVersion: 17,
41
44
  maximumJavaVersion: 17,
42
45
  rootPath: __dirname,
43
- jar: "java/CodeNarcServer.jar"
44
- }
46
+ jar: "java/CodeNarcServer.jar",
47
+ },
45
48
  };
46
49
 
47
50
  constructor(codenarcArgs1, serverStatus1, args1, options1, execOpts1) {
@@ -69,9 +72,9 @@ class CodeNarcCaller {
69
72
  codeNarcExcludes: this.execOpts.codeNarcExcludes,
70
73
  parse: this.options.parse !== false && this.execOpts.onlyCodeNarc === false,
71
74
  fileList: this.execOpts.groovyFileName ? [this.execOpts.groovyFileName] : this.execOpts.inputFileList,
72
- requestKey: this.execOpts.requestKey || null
75
+ requestKey: this.execOpts.requestKey || null,
73
76
  },
74
- timeout: 600000
77
+ timeout: 600000,
75
78
  };
76
79
  trace(`CALL CodeNarcServer with ${JSON.stringify(axiosConfig, null, 2)}`);
77
80
  let response;
@@ -98,8 +101,8 @@ class CodeNarcCaller {
98
101
  error: {
99
102
  msg: `exception: ${e.response.data.exceptionType} message: ${e.response.data.errorMessage}`,
100
103
  stack: e.stack,
101
- responseData: e.response.data.errorDtl
102
- }
104
+ responseData: e.response.data.errorDtl,
105
+ },
103
106
  };
104
107
  } else if (e.code === "ECONNRESET") {
105
108
  // The server was shutdown just retry.
@@ -111,13 +114,16 @@ class CodeNarcCaller {
111
114
  // respObj.status = 'cancelledByDuplicateRequest'
112
115
  // respObj.statusCode = 444
113
116
  return {
114
- status: 9
117
+ status: 9,
115
118
  };
116
119
  } else {
117
120
  console.error(
118
121
  c.red(
119
- "CodeNarcServer unexpected error:\n" + JSON.stringify(e, null, 2) + "\n" + JSON.stringify(e.response?.data?.errorDtl, null, 2)
120
- )
122
+ "CodeNarcServer unexpected error:\n" +
123
+ JSON.stringify(e, null, 2) +
124
+ "\n" +
125
+ JSON.stringify(e.response?.data?.errorDtl, null, 2),
126
+ ),
121
127
  );
122
128
  }
123
129
  this.serverStatus = "error";
@@ -126,8 +132,8 @@ class CodeNarcCaller {
126
132
  error: {
127
133
  msg: e.message,
128
134
  stack: e.stack,
129
- responseData: e.response?.data?.errorDtl
130
- }
135
+ responseData: e.response?.data?.errorDtl,
136
+ },
131
137
  };
132
138
  }
133
139
 
@@ -139,7 +145,7 @@ class CodeNarcCaller {
139
145
  parseErrors: response.data.parseErrors,
140
146
  codeNarcStdOut: response.data.stdout,
141
147
  codeNarcStdErr: undefined,
142
- status: 0
148
+ status: 0,
143
149
  };
144
150
  }
145
151
 
@@ -148,7 +154,7 @@ class CodeNarcCaller {
148
154
  return {
149
155
  codeNarcStdOut: undefined,
150
156
  codeNarcStdErr: undefined,
151
- status: 9
157
+ status: 9,
152
158
  };
153
159
  }
154
160
 
@@ -165,9 +171,9 @@ class CodeNarcCaller {
165
171
  msgDtl: {
166
172
  parseErrors: response.data.parseErrors,
167
173
  stdout: response.data.stdout,
168
- stderr: response.data.errorDtl
169
- }
170
- }
174
+ stderr: response.data.errorDtl,
175
+ },
176
+ },
171
177
  };
172
178
  }
173
179
 
@@ -183,7 +189,7 @@ class CodeNarcCaller {
183
189
  if (this.execOpts.groovyFileName) {
184
190
  scriptArgs.unshift("--file", this.execOpts.groovyFileName);
185
191
  } else if (this.execOpts.inputFileList) {
186
- this.execOpts.inputFileList.forEach(file => {
192
+ this.execOpts.inputFileList.forEach((file) => {
187
193
  scriptArgs.unshift("--file", file);
188
194
  });
189
195
  }
@@ -194,9 +200,9 @@ class CodeNarcCaller {
194
200
  {
195
201
  format: "[{bar}] Running CodeNarc for {duration_formatted}",
196
202
  hideCursor: true,
197
- clearOnComplete: true
203
+ clearOnComplete: true,
198
204
  },
199
- cliProgress.Presets.shades_classic
205
+ cliProgress.Presets.shades_classic,
200
206
  );
201
207
  this.bar.start(10, 1);
202
208
  this.barTimer = setInterval(() => {
@@ -232,22 +238,19 @@ class CodeNarcCaller {
232
238
  reason =
233
239
  "It seems node.js has not been found on your computer. Please install a recent node.js: https://nodejs.org/en/download/\nIf node is already installed, make sure your PATH contains node installation folder: https://love2dev.com/blog/node-is-not-recognized-as-an-internal-or-external-command/";
234
240
  } else {
235
- await new Promise(resolve => {
236
- require("find-java-home")(err => {
237
- if (err) {
238
- reason =
239
- "Java is required to run npm-groovy-lint, as CodeNarc is written in Java/Groovy. Please install Java (version 8 minimum) https://www.java.com/download";
240
- }
241
- resolve();
242
- });
241
+ await findJavaHome({ allowJre: true }, (err) => {
242
+ if (err) {
243
+ reason =
244
+ "Java is required to run npm-groovy-lint, as CodeNarc is written in Java/Groovy. Please install Java (version 8 minimum) https://www.java.com/download";
245
+ }
243
246
  });
244
247
  }
245
248
  return {
246
249
  codeNarcStdErr: javaResult.stderr,
247
250
  status: 2,
248
251
  error: {
249
- msg: `Fatal error while calling CodeNarc\n${reason}\n${javaResult.stderr}`
250
- }
252
+ msg: `Fatal error while calling CodeNarc\n${reason}\n${javaResult.stderr}`,
253
+ },
251
254
  };
252
255
  }
253
256
  }
@@ -259,7 +262,7 @@ class CodeNarcCaller {
259
262
  parseErrors: response.parseErrors,
260
263
  codeNarcStdOut: javaResult.stdout,
261
264
  codeNarcStdErr: javaResult.stderr,
262
- status: 0
265
+ status: 0,
263
266
  };
264
267
  }
265
268
 
@@ -295,16 +298,16 @@ class CodeNarcCaller {
295
298
  const start = performance.now();
296
299
  let notified = false;
297
300
  let interval;
298
- await new Promise(resolve => {
301
+ await new Promise((resolve) => {
299
302
  interval = setInterval(() => {
300
303
  debug(
301
304
  `pinging CodeNarcServer at ${serverPingUri} notified: ${notified}, serverStatus: ${
302
305
  this.serverStatus
303
- }, since: ${performance.now() - start}, maxAttemptTimeMs: ${maxAttemptTimeMs}`
306
+ }, since: ${performance.now() - start}, maxAttemptTimeMs: ${maxAttemptTimeMs}`,
304
307
  );
305
308
  axios
306
309
  .get(serverPingUri)
307
- .then(response => {
310
+ .then((response) => {
308
311
  if (response.status === 200) {
309
312
  // Server is correctly started, as he replied to the ping request
310
313
  this.serverStatus = "running";
@@ -322,7 +325,7 @@ class CodeNarcCaller {
322
325
  resolve();
323
326
  }
324
327
  })
325
- .catch(e => {
328
+ .catch((e) => {
326
329
  debug(`Ping code: ${e.code} message: ${e.message}`);
327
330
  let since = performance.now() - start;
328
331
  if (notified === false && this.serverStatus === "unknown" && since > maxAttemptTimeMs) {
@@ -399,15 +402,15 @@ class CodeNarcCaller {
399
402
  const serverPingUri = this.getCodeNarcServerUri() + "/ping";
400
403
 
401
404
  let interval;
402
- await new Promise(resolve => {
405
+ await new Promise((resolve) => {
403
406
  interval = setInterval(() => {
404
407
  debug(`pinging CodeNarcServer at ${serverPingUri} serverStatus: ${this.serverStatus}`);
405
408
  axios
406
409
  .get(serverPingUri)
407
- .then(response => {
410
+ .then((response) => {
408
411
  debug(`ping response: ${response.status}`);
409
412
  })
410
- .catch(e => {
413
+ .catch((e) => {
411
414
  debug(`Ping code: ${e.code} message: ${e.message}`);
412
415
  clearInterval(interval);
413
416
  resolve();
@@ -438,9 +441,7 @@ class CodeNarcCaller {
438
441
  try {
439
442
  return JSON.parse(response);
440
443
  } catch (e) {
441
- return { err: `Unable to parse ${response}` };
444
+ return { err: `Unable to parse ${response}: ${e.message}` };
442
445
  }
443
446
  }
444
447
  }
445
-
446
- module.exports = CodeNarcCaller;
@@ -1,21 +1,21 @@
1
1
  // Shared functions
2
- "use strict";
3
-
4
- const debug = require("debug")("npm-groovy-lint");
5
- const commondir = require("commondir");
6
- const fs = require("fs-extra");
7
- const os = require("os");
8
- const path = require("path");
9
- const { getConfigFileName } = require("./config.js");
10
- const { collectDisabledBlocks, isFilteredError } = require("./filter.js");
11
- const { getNpmGroovyLintRules } = require("./groovy-lint-rules.js");
12
- const { evaluateRange, evaluateRangeFromLine, evaluateVariables, getSourceLines, normalizeNewLines } = require("./utils.js");
13
2
 
3
+ import Debug from "debug";
4
+ const debug = Debug("npm-groovy-lint");
5
+ import commondir from "commondir";
6
+ import fs from "fs-extra";
7
+ import * as os from "os";
8
+ import * as path from "path";
9
+ import { getConfigFileName } from "./config.js";
10
+ import { collectDisabledBlocks, isFilteredError } from "./filter.js";
11
+ import { getNpmGroovyLintRules } from "./groovy-lint-rules.js";
12
+ import { evaluateRange, evaluateRangeFromLine, evaluateVariables, getSourceLines, normalizeNewLines } from "./utils.js";
13
+ import { fileURLToPath } from "url";
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15
  ////////////////////////////
15
16
  // Build codenarc options //
16
17
  ////////////////////////////
17
18
 
18
- const npmGroovyLintRules = getNpmGroovyLintRules();
19
19
  const CODENARC_TMP_FILENAME_BASE = "codeNarcTmpDir_";
20
20
  const CODENARC_WWW_BASE = "https://codenarc.github.io/CodeNarc";
21
21
 
@@ -57,7 +57,7 @@ async function prepareCodeNarcCall(options) {
57
57
  cnFiles = ["**/" + tmpFileNm];
58
58
  }
59
59
 
60
- await fs.writeFile(result.tmpGroovyFileName, normalizeNewLines(options.source)).catch(err => {
60
+ await fs.writeFile(result.tmpGroovyFileName, normalizeNewLines(options.source)).catch((err) => {
61
61
  throw new Error(`Unable to write temp file: ${err}`); // Ensure we have a stack trace.
62
62
  });
63
63
 
@@ -89,17 +89,17 @@ async function prepareCodeNarcCall(options) {
89
89
  const filePatterns =
90
90
  options.ext && options.ext.length > 0
91
91
  ? // Convert extensions with or without leading slash into an ant pattern.
92
- options.ext.map(ext => ext.replace(/^\.?/u, "*."))
92
+ options.ext.map((ext) => ext.replace(/^\.?/u, "*."))
93
93
  : defaultFilesPattern;
94
94
  const sourceFiles = [];
95
95
  const includes = [];
96
- positionalArgs.filter(Boolean).forEach(pathname => {
96
+ positionalArgs.filter(Boolean).forEach((pathname) => {
97
97
  const absolutePath = path.resolve(".", pathname);
98
98
  const relativePath = path.relative(codeNarcBaseDir, absolutePath);
99
99
  if (directoryExists(absolutePath)) {
100
100
  // Directory: convert into ant patterns.
101
101
  const patternBase = antPath(relativePath) + "**/";
102
- includes.push(...filePatterns.map(filePattern => patternBase + filePattern));
102
+ includes.push(...filePatterns.map((filePattern) => patternBase + filePattern));
103
103
  } else if (fs.existsSync(absolutePath)) {
104
104
  // File: add to file / patterns list.
105
105
  const file = antPath(relativePath);
@@ -127,7 +127,7 @@ async function prepareCodeNarcCall(options) {
127
127
  result.codeNarcIncludes = cnFiles;
128
128
  } else {
129
129
  // If files not sent, use defaultFilesPattern, guessed from options.rulesets value
130
- const filePatterns = defaultFilesPattern.map(filePattern => `**/${filePattern}`);
130
+ const filePatterns = defaultFilesPattern.map((filePattern) => `**/${filePattern}`);
131
131
  result.codenarcArgs.push(`-includes=${filePatterns.join(",")}`);
132
132
  result.codeNarcIncludes = filePatterns;
133
133
  }
@@ -149,23 +149,13 @@ async function prepareCodeNarcCall(options) {
149
149
  result.outputType = result.output.endsWith(".txt")
150
150
  ? "txt"
151
151
  : result.output.endsWith(".json")
152
- ? "json"
153
- : result.output.endsWith(".sarif")
154
- ? "sarif"
155
- : result.output;
152
+ ? "json"
153
+ : result.output.endsWith(".sarif")
154
+ ? "sarif"
155
+ : result.output;
156
156
  result.codenarcArgs.push(`-report=json:stdout`);
157
157
  } else if (["html", "xml"].includes(result.output.split(".").pop())) {
158
- result.outputType = result.output
159
- .split(".")
160
- .pop()
161
- .endsWith("html")
162
- ? "html"
163
- : result.output
164
- .split(".")
165
- .pop()
166
- .endsWith("xml")
167
- ? "xml"
168
- : "";
158
+ result.outputType = result.output.split(".").pop().endsWith("html") ? "html" : result.output.split(".").pop().endsWith("xml") ? "xml" : "";
169
159
  const ext = result.output.split(".").pop();
170
160
  result.codenarcArgs.push(`-report=${ext}:${result.output}`);
171
161
 
@@ -178,7 +168,7 @@ async function prepareCodeNarcCall(options) {
178
168
  const errMsg = `Output not managed: ${result.output}. (For now, only output formats are txt and json in console, and html and xml as files)`;
179
169
  console.error(errMsg);
180
170
  result.error = {
181
- msg: errMsg
171
+ msg: errMsg,
182
172
  };
183
173
  }
184
174
  return result;
@@ -197,10 +187,10 @@ function antPath(path) {
197
187
  // Calculate longest base dir by analyzing the list of files
198
188
  async function getCodeNarcBaseDirFromFiles(positionalArgs) {
199
189
  // All arguments are not files
200
- if (!positionalArgs.every(fileOrDirOrPattern => fs.existsSync(fileOrDirOrPattern) || directoryExists(fileOrDirOrPattern))) {
190
+ if (!positionalArgs.every((fileOrDirOrPattern) => fs.existsSync(fileOrDirOrPattern) || directoryExists(fileOrDirOrPattern))) {
201
191
  return process.cwd();
202
192
  }
203
- const folders = positionalArgs.map(fileOrDir => {
193
+ const folders = positionalArgs.map((fileOrDir) => {
204
194
  // Dir
205
195
  if (directoryExists(fileOrDir)) {
206
196
  return path.resolve(fileOrDir);
@@ -221,10 +211,11 @@ async function parseCodeNarcResult(options, codeNarcBaseDir, codeNarcJsonResult,
221
211
  return {
222
212
  status: 2,
223
213
  error: {
224
- msg: errMsg
225
- }
214
+ msg: errMsg,
215
+ },
226
216
  };
227
217
  }
218
+ const npmGroovyLintRules = await getNpmGroovyLintRules();
228
219
  const result = { summary: {} };
229
220
 
230
221
  // Parse main result
@@ -264,13 +255,13 @@ async function parseCodeNarcResult(options, codeNarcBaseDir, codeNarcJsonResult,
264
255
  line: parseError.cause && parseError.cause.line ? parseError.cause.line : 0,
265
256
  rule: "NglParseError",
266
257
  severity: "error",
267
- msg: msg
258
+ msg: msg,
268
259
  };
269
260
  // Add range if provided
270
261
  if (parseError.cause && parseError.cause.startColumn) {
271
262
  errItemParse.range = {
272
263
  start: { line: parseError.cause.startLine, character: parseError.cause.startColumn },
273
- end: { line: parseError.cause.endLine, character: parseError.cause.endColumn }
264
+ end: { line: parseError.cause.endLine, character: parseError.cause.endColumn },
274
265
  };
275
266
  }
276
267
 
@@ -310,7 +301,7 @@ async function parseCodeNarcResult(options, codeNarcBaseDir, codeNarcJsonResult,
310
301
  rule: violation.ruleName,
311
302
  severity:
312
303
  violation.priority === 1 ? "error" : violation.priority === 2 ? "warning" : violation.priority === 3 ? "info" : "unknown",
313
- msg: violation.message ? violation.message : ""
304
+ msg: violation.message ? violation.message : "",
314
305
  };
315
306
  errItem.msg = tmpGroovyFileNameReplace ? errItem.msg.replace(tmpGroovyFileNameReplace, "") : errItem.msg;
316
307
 
@@ -371,7 +362,7 @@ async function parseCodeNarcResult(options, codeNarcBaseDir, codeNarcJsonResult,
371
362
  // Add description from CodeNarc
372
363
  rules[ruleName] = { description: ruleDef.description };
373
364
  // Try to build codenarc url (ex: https://codenarc.github.io/CodeNarc/codenarc-rules-basic.html#bitwiseoperatorinconditional-rule )
374
- const matchRules = grooylintrcAllRules.filter(ruleNameX => ruleNameX.split(".")[1] === ruleName);
365
+ const matchRules = grooylintrcAllRules.filter((ruleNameX) => ruleNameX.split(".")[1] === ruleName);
375
366
  if (matchRules && matchRules[0]) {
376
367
  const ruleCategory = matchRules[0].split(".")[0];
377
368
  const ruleDocUrl = `${CODENARC_WWW_BASE}/codenarc-rules-${ruleCategory}.html#${ruleName.toLowerCase()}-rule`;
@@ -389,7 +380,7 @@ async function buildRuleSets(options) {
389
380
  // If RuleSet files has already been created, or is groovy file, return it
390
381
  if (options.rulesets && (options.rulesets.endsWith(".groovy") || options.rulesets.endsWith(".xml"))) {
391
382
  const rulesetSplits = options.rulesets.split(",");
392
- const normalizedRulesets = rulesetSplits.map(rulesetFile => {
383
+ const normalizedRulesets = rulesetSplits.map((rulesetFile) => {
393
384
  const fullFile = path.resolve(rulesetFile);
394
385
  // Encode file name so CodeNarc understands it
395
386
  if (fs.existsSync(fullFile)) {
@@ -416,8 +407,8 @@ async function buildRuleSets(options) {
416
407
  !options.rulesets.includes("/") &&
417
408
  !options.rulesets.includes("\\")
418
409
  ) {
419
- let ruleList = options.rulesets.split(/(,(?!"))/gm).filter(elt => elt !== ",");
420
- ruleSetsDef = ruleList.map(ruleFromArgument => {
410
+ let ruleList = options.rulesets.split(/(,(?!"))/gm).filter((elt) => elt !== ",");
411
+ ruleSetsDef = ruleList.map((ruleFromArgument) => {
421
412
  let ruleName;
422
413
  let ruleOptions = {};
423
414
  if (ruleFromArgument.includes("{")) {
@@ -434,8 +425,8 @@ async function buildRuleSets(options) {
434
425
  typeof ruleFromConfig === "object"
435
426
  ? Object.assign(ruleFromConfig, ruleOptions)
436
427
  : Object.keys(ruleOptions).length > 0
437
- ? ruleOptions
438
- : ruleFromConfig;
428
+ ? ruleOptions
429
+ : ruleFromConfig;
439
430
  const ruleDef = buildCodeNarcRule(ruleName, mergedRuleConfig);
440
431
  // If rule has been sent as argument, enable it by default
441
432
  if (ruleDef.enabled === false) {
@@ -449,15 +440,15 @@ async function buildRuleSets(options) {
449
440
  for (const ruleName of Object.keys(options.rules)) {
450
441
  let ruleDef = options.rules[ruleName];
451
442
  // If rule has been overridden in argument, set it on top of config file properties
452
- const ruleFromRuleSetsArgPos = ruleSetsDef.findIndex(ruleDef => ruleDef.ruleName === ruleName);
443
+ const ruleFromRuleSetsArgPos = ruleSetsDef.findIndex((ruleDef) => ruleDef.ruleName === ruleName);
453
444
  if (ruleFromRuleSetsArgPos > -1) {
454
445
  const ruleFromRuleSetsArg = ruleSetsDef[ruleFromRuleSetsArgPos];
455
446
  ruleDef =
456
447
  typeof ruleDef === "object"
457
448
  ? Object.assign(ruleDef, ruleFromRuleSetsArg)
458
449
  : Object.keys(ruleFromRuleSetsArg).length > 0
459
- ? ruleFromRuleSetsArg
460
- : ruleDef;
450
+ ? ruleFromRuleSetsArg
451
+ : ruleDef;
461
452
  }
462
453
  // Add in the list of rules to test , except if it is disabled
463
454
  if (!(ruleDef === "off" || ruleDef.disabled === true || ruleDef.enabled === false)) {
@@ -528,4 +519,4 @@ function directoryExists(resolvedPath) {
528
519
  }
529
520
  }
530
521
 
531
- module.exports = { prepareCodeNarcCall, parseCodeNarcResult };
522
+ export { prepareCodeNarcCall, parseCodeNarcResult };
package/lib/config.js CHANGED
@@ -1,18 +1,20 @@
1
1
  // Configuration file management
2
- "use strict";
3
2
 
4
- const debug = require("debug")("npm-groovy-lint");
5
- const fse = require("fs-extra");
6
- const importFresh = require("import-fresh");
7
- const path = require("path");
8
- const stripComments = require("strip-json-comments");
3
+ import Debug from "debug";
4
+ const debug = Debug("npm-groovy-lint");
5
+ import fs from "fs-extra";
6
+ import importFresh from "import-fresh";
7
+ import * as path from "path";
8
+ import stripJsonComments from "strip-json-comments";
9
+ import { fileURLToPath } from "url";
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
11
 
10
12
  const defaultConfigLintFileName = ".groovylintrc-recommended.json";
11
13
  const allConfigLintFileName = ".groovylintrc-all.json";
12
14
 
13
15
  const NPM_GROOVY_LINT_CONSTANTS = {
14
16
  CodeNarcVersion: "2.2.0",
15
- GroovyVersion: "3.0.9"
17
+ GroovyVersion: "3.0.9",
16
18
  };
17
19
 
18
20
  const configLintFilenames = [
@@ -22,7 +24,7 @@ const configLintFilenames = [
22
24
  ".groovylintrc.yml",
23
25
  ".groovylintrc.yaml",
24
26
  ".groovylintrc",
25
- "package.json"
27
+ "package.json",
26
28
  ];
27
29
 
28
30
  const configExtensions = ["json", "js", "cjs", "yml", "yaml", "groovylintrc"];
@@ -45,7 +47,7 @@ async function loadConfig(startPathOrFile, mode = "lint", sourcefilepath, fileNa
45
47
  configUser = await loadConfigFromFile(startPathOrFile);
46
48
  } else if (startPathOrFile.match(/^[a-zA-Z\d-_]+$/) && mode !== "format") {
47
49
  // Sent string: find a corresponding file name
48
- fileNames = configExtensions.map(ext => `.groovylintrc-${startPathOrFile}.${ext}`);
50
+ fileNames = configExtensions.map((ext) => `.groovylintrc-${startPathOrFile}.${ext}`);
49
51
  configFilePath = await getConfigFileName(sourcefilepath || process.cwd(), sourcefilepath, fileNames, "");
50
52
  configUser = await loadConfigFromFile(configFilePath);
51
53
  } else {
@@ -66,7 +68,7 @@ async function loadConfig(startPathOrFile, mode = "lint", sourcefilepath, fileNa
66
68
  // Set ruleSet file if found from config file
67
69
  configUser.rulesets = configUser.codenarcRulesets
68
70
  .split(",")
69
- .map(rulesetFile => path.resolve(path.dirname(configFilePath) + "/" + rulesetFile))
71
+ .map((rulesetFile) => path.resolve(path.dirname(configFilePath) + "/" + rulesetFile))
70
72
  .join(",");
71
73
  }
72
74
 
@@ -117,7 +119,7 @@ async function getConfigFileName(startPathOrFile, sourcefilepath, fileNames = co
117
119
  // Find one of the config file formats are the root of the linted file (if source is sent with sourcefilepath)
118
120
  if ([".", process.cwd()].includes(startPathOrFile) && sourcefilepath) {
119
121
  try {
120
- const stat = await fse.lstat(sourcefilepath);
122
+ const stat = await fs.lstat(sourcefilepath);
121
123
  const dir = stat.isDirectory() ? sourcefilepath : path.parse(sourcefilepath).dir;
122
124
  configFilePath = await findConfigInPath(dir, fileNames);
123
125
  } catch (e) {
@@ -127,7 +129,7 @@ async function getConfigFileName(startPathOrFile, sourcefilepath, fileNames = co
127
129
  // Find one of the config file formats at the root of the project or at upper directory levels
128
130
  if (configFilePath == null) {
129
131
  try {
130
- const stat = await fse.lstat(startPathOrFile);
132
+ const stat = await fs.lstat(startPathOrFile);
131
133
  const dir = stat.isDirectory ? startPathOrFile : path.parse(startPathOrFile).dir;
132
134
  configFilePath = await findConfigInPath(dir, fileNames);
133
135
  } catch (e) {
@@ -154,13 +156,14 @@ async function getConfigFileName(startPathOrFile, sourcefilepath, fileNames = co
154
156
  async function findConfigInPath(directoryPath, configFilenamesIn) {
155
157
  for (const filename of configFilenamesIn) {
156
158
  const filePath = path.join(directoryPath, filename);
157
- if (await fse.exists(filePath)) {
159
+ if (await fs.exists(filePath)) {
158
160
  if (filename === "package.json") {
159
161
  try {
160
162
  await loadPackageJSONConfigFile(filePath);
161
163
  return filePath;
162
164
  } catch (error) {
163
165
  /* ignore */
166
+ debug("Error loading JSON config file: " + error.message);
164
167
  }
165
168
  } else {
166
169
  return filePath;
@@ -217,15 +220,15 @@ async function loadJSConfigFile(filePath) {
217
220
  // JSON format
218
221
  async function loadJSONConfigFile(filePath) {
219
222
  try {
220
- const fileContent = await readFile(filePath);
221
- return JSON.parse(stripComments(fileContent));
223
+ const fileContent = await fs.readFile(filePath);
224
+ return JSON.parse(stripJsonComments(fileContent.toString()));
222
225
  } catch (e) {
223
226
  debug(`Error reading JSON file: ${filePath}`);
224
227
  e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
225
228
  e.messageTemplate = "failed-to-read-json";
226
229
  e.messageData = {
227
230
  path: filePath,
228
- message: e.message
231
+ message: e.message,
229
232
  };
230
233
  throw e;
231
234
  }
@@ -234,7 +237,7 @@ async function loadJSONConfigFile(filePath) {
234
237
  // YAML format
235
238
  async function loadYAMLConfigFile(filePath) {
236
239
  // lazy load YAML to improve performance when not used
237
- const yaml = require("js-yaml");
240
+ const yaml = await import("js-yaml");
238
241
 
239
242
  try {
240
243
  // empty YAML file can be null, so always use
@@ -264,7 +267,7 @@ async function loadPackageJSONConfigFile(filePath) {
264
267
 
265
268
  // Read file
266
269
  async function readFile(filePath) {
267
- const fileContent = await fse.readFile(filePath, "utf8");
270
+ const fileContent = await fs.readFile(filePath, "utf8");
268
271
  return fileContent.replace(/^\ufeff/u, "");
269
272
  }
270
273
 
@@ -278,4 +281,4 @@ async function shortenRuleNames(rules) {
278
281
  return shortenedRules;
279
282
  }
280
283
 
281
- module.exports = { NPM_GROOVY_LINT_CONSTANTS, loadConfig, getConfigFileName, overriddenRules };
284
+ export { NPM_GROOVY_LINT_CONSTANTS, loadConfig, getConfigFileName, overriddenRules };