npm-groovy-lint 14.5.0 → 14.6.1-beta202408252114.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.
- package/lib/codenarc-caller.js +54 -53
- package/lib/codenarc-factory.js +41 -50
- package/lib/config.js +22 -19
- package/lib/filter.js +8 -15
- package/lib/groovy-lint-fix.js +32 -34
- package/lib/groovy-lint-rules.js +12 -12
- package/lib/groovy-lint.js +40 -41
- package/lib/index.js +3 -3
- package/lib/java/CodeNarcServer.jar +0 -0
- package/lib/options.js +45 -54
- package/lib/output.js +23 -23
- package/lib/rules/AssignmentInConditional.js +7 -7
- package/lib/rules/BlankLineBeforePackage.js +11 -11
- package/lib/rules/BlockEndsWithBlankLine.js +6 -6
- package/lib/rules/BlockStartsWithBlankLine.js +6 -6
- package/lib/rules/BracesForClass.js +8 -8
- package/lib/rules/BracesForForLoop.js +9 -9
- package/lib/rules/BracesForIfElse.js +9 -9
- package/lib/rules/BracesForMethod.js +11 -11
- package/lib/rules/BracesForTryCatchFinally.js +7 -7
- package/lib/rules/CatchException.js +4 -4
- package/lib/rules/ClassEndsWithBlankLine.js +6 -6
- package/lib/rules/ClassStartsWithBlankLine.js +6 -6
- package/lib/rules/ClosingBraceNotAlone.js +8 -8
- package/lib/rules/ConsecutiveBlankLines.js +7 -7
- package/lib/rules/DuplicateImport.js +6 -6
- package/lib/rules/DuplicateNumberLiteral.js +6 -6
- package/lib/rules/DuplicateStringLiteral.js +6 -6
- package/lib/rules/ElseBlockBraces.js +10 -10
- package/lib/rules/ExplicitArrayListInstantiation.js +7 -7
- package/lib/rules/ExplicitLinkedListInstantiation.js +7 -7
- package/lib/rules/FileEndsWithoutNewline.js +7 -7
- package/lib/rules/GStringExpressionWithinString.js +7 -7
- package/lib/rules/IfStatementBraces.js +10 -10
- package/lib/rules/Indentation.js +14 -14
- package/lib/rules/IndentationClosingBraces.js +8 -8
- package/lib/rules/IndentationComments.js +6 -6
- package/lib/rules/InsecureRandom.js +14 -14
- package/lib/rules/JavaIoPackageAccess.js +6 -6
- package/lib/rules/MethodCount.js +6 -6
- package/lib/rules/MethodParameterTypeRequired.js +6 -6
- package/lib/rules/MethodReturnTypeRequired.js +6 -6
- package/lib/rules/MisorderedStaticImports.js +14 -14
- package/lib/rules/MissingBlankLineAfterImports.js +6 -6
- package/lib/rules/MissingBlankLineAfterPackage.js +6 -6
- package/lib/rules/NoDef.js +4 -4
- package/lib/rules/NoJavaUtilDate.js +4 -4
- package/lib/rules/NoTabCharacter.js +7 -7
- package/lib/rules/SimpleDateFormatMissingLocale.js +4 -4
- package/lib/rules/SpaceAfterCatch.js +7 -7
- package/lib/rules/SpaceAfterComma.js +9 -9
- package/lib/rules/SpaceAfterFor.js +8 -8
- package/lib/rules/SpaceAfterIf.js +10 -10
- package/lib/rules/SpaceAfterMethodCallName.js +7 -7
- package/lib/rules/SpaceAfterOpeningBrace.js +9 -9
- package/lib/rules/SpaceAfterSemicolon.js +8 -8
- package/lib/rules/SpaceAfterSwitch.js +8 -8
- package/lib/rules/SpaceAfterWhile.js +7 -7
- package/lib/rules/SpaceAroundOperator.js +14 -14
- package/lib/rules/SpaceBeforeClosingBrace.js +8 -8
- package/lib/rules/SpaceBeforeOpeningBrace.js +9 -9
- package/lib/rules/SpaceInsideParentheses.js +17 -17
- package/lib/rules/SystemExit.js +4 -4
- package/lib/rules/TrailingWhitespace.js +8 -8
- package/lib/rules/UnnecessaryDefInFieldDeclaration.js +8 -8
- package/lib/rules/UnnecessaryDefInMethodDeclaration.js +7 -7
- package/lib/rules/UnnecessaryDefInVariableDeclaration.js +9 -9
- package/lib/rules/UnnecessaryDotClass.js +7 -7
- package/lib/rules/UnnecessaryFinalOnPrivateMethod.js +7 -7
- package/lib/rules/UnnecessaryGString.js +14 -14
- package/lib/rules/UnnecessaryGroovyImport.js +6 -6
- package/lib/rules/UnnecessaryPackageReference.js +9 -9
- package/lib/rules/UnnecessaryParenthesesForMethodCallWithClosure.js +9 -9
- package/lib/rules/UnnecessaryPublicModifier.js +4 -4
- package/lib/rules/UnnecessarySemicolon.js +10 -13
- package/lib/rules/UnnecessaryToString.js +8 -8
- package/lib/rules/UnusedImport.js +10 -10
- package/lib/rules/UnusedMethodParameter.js +6 -6
- package/lib/rules/UnusedVariable.js +6 -6
- package/lib/rules/VariableName.js +11 -11
- package/lib/rules/VariableTypeRequired.js +11 -11
- package/lib/utils.js +62 -43
- package/package.json +23 -20
package/lib/codenarc-caller.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
// Call CodeNarc by server or java
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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" +
|
|
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
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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;
|
package/lib/codenarc-factory.js
CHANGED
|
@@ -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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
438
|
-
|
|
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
|
-
|
|
460
|
-
|
|
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
|
-
|
|
522
|
+
export { prepareCodeNarcCall, parseCodeNarcResult };
|
package/lib/config.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
// Configuration file management
|
|
2
|
-
"use strict";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
-
|
|
284
|
+
export { NPM_GROOVY_LINT_CONSTANTS, loadConfig, getConfigFileName, overriddenRules };
|