npm-groovy-lint 12.2.1-beta202311260926.0 → 13.0.1-beta202311261533.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/analytics.js CHANGED
@@ -189,7 +189,9 @@ async function buildFileStatsEvents(linterEvent, data) {
189
189
 
190
190
  async function getFileStats(file) {
191
191
  const fileStatEventProps = {};
192
- const source = await fse.readFile(file);
192
+ const source = await fse.readFile(file).catch(err => {
193
+ throw new Error(`Unable to read stats: ${err}`); // Ensure that we have a stack trace.
194
+ });
193
195
  const sourceLines = await getSourceLines(source);
194
196
  fileStatEventProps.fileId = crypto
195
197
  .createHash("sha1")
@@ -59,24 +59,12 @@ class CodeNarcCaller {
59
59
  async callCodeNarcServer(startServerTried = false) {
60
60
  // If use of --codenarcargs, get default values for CodeNarcServer host & port
61
61
  const requestUri = this.getCodeNarcServerUri() + "/request";
62
- // Remove "" around values because they won't get thru system command line parser
63
- const codeNarcArgsForServer = this.codenarcArgs.map(codeNarcArg => {
64
- if (codeNarcArg.includes('="') || codeNarcArg.includes(':"')) {
65
- codeNarcArg = codeNarcArg
66
- .replace('="', "=")
67
- .replace(':"', ":")
68
- .replace(/ /g, "%20");
69
- codeNarcArg = codeNarcArg.substring(0, codeNarcArg.length - 1);
70
- }
71
- return codeNarcArg;
72
- });
73
62
  // Call CodeNarc server
74
- const codeNarcArgsString = codeNarcArgsForServer.join(" ");
75
63
  const axiosConfig = {
76
64
  method: "post",
77
65
  url: requestUri,
78
66
  data: {
79
- codeNarcArgs: codeNarcArgsString,
67
+ codeNarcArgs: this.codenarcArgs,
80
68
  codeNarcBaseDir: this.execOpts.codeNarcBaseDir,
81
69
  codeNarcIncludes: this.execOpts.codeNarcIncludes,
82
70
  codeNarcExcludes: this.execOpts.codeNarcExcludes,
@@ -212,7 +200,7 @@ class CodeNarcCaller {
212
200
  javaCallerOpts.javaExecutable = this.javaExecutable;
213
201
  javaCallerOpts.additionalJavaArgs = this.additionalJavaArgs;
214
202
  const javaCaller = new JavaCaller(javaCallerOpts);
215
- const javaResult = await javaCaller.run(scriptArgs, { detached: false });
203
+ const javaResult = await javaCaller.run(scriptArgs, { detached: false, windowsVerbatimArguments: false });
216
204
 
217
205
  clearInterval(this.barTimer);
218
206
  this.bar.stop();
@@ -26,42 +26,48 @@ const defaultFilesPattern = ["*.groovy", "*.gvy", "Jenkinsfile", "*.gradle", "*.
26
26
  async function prepareCodeNarcCall(options) {
27
27
  const result = { codenarcArgs: [] };
28
28
 
29
+ // Protect against regressions in argument parsing.
30
+ for (const [key, value] of Object.entries(options)) {
31
+ if (typeof value === "string" && value.match(/^['"]/)) {
32
+ throw new Error(`CodeNarc doesn't supported options found ${key} = ${value}`);
33
+ }
34
+ }
35
+
29
36
  let cnPath = options.path;
30
- let cnFiles = options.files;
37
+ let cnFiles = options.file;
31
38
  const positionalArgs = options._ || [];
32
39
 
33
40
  // If source option, create a temporary Groovy file
34
41
  if (options.source) {
35
42
  cnPath = path.resolve(os.tmpdir() + "/npm-groovy-lint");
36
- await fs.ensureDir(cnPath, { mode: "0777" });
43
+ await fs.ensureDir(cnPath, { mode: "0777" }); // await is needed, ignore editor warning.
37
44
  // File path is sent (recommended): use it to create temp file name
38
45
  if (options.sourcefilepath) {
39
46
  const pathParse = path.parse(options.sourcefilepath);
40
47
  cnPath = cnPath + "/codeNarcTmpDir_" + Math.random();
41
- await fs.ensureDir(cnPath, { mode: "0777" });
48
+ await fs.ensureDir(cnPath, { mode: "0777" }); // await is needed, ignore editor warning.
42
49
  const pathBase = pathParse.base.replace(/ /g, "_");
43
50
  result.tmpGroovyFileName = path.resolve(cnPath + "/" + pathBase);
44
- cnFiles = "**/" + pathBase;
51
+ cnFiles = ["**/" + pathBase];
45
52
  }
46
53
  // Use default random file name
47
54
  else {
48
55
  const tmpFileNm = CODENARC_TMP_FILENAME_BASE + Math.random() + ".groovy";
49
56
  result.tmpGroovyFileName = path.resolve(cnPath + "/" + tmpFileNm);
50
- cnFiles = "**/" + tmpFileNm;
57
+ cnFiles = ["**/" + tmpFileNm];
51
58
  }
52
59
 
53
- await fs.writeFile(result.tmpGroovyFileName, normalizeNewLines(options.source));
60
+ await fs.writeFile(result.tmpGroovyFileName, normalizeNewLines(options.source)).catch(err => {
61
+ throw new Error(`Unable to write temp file: ${err}`); // Ensure we have a stack trace.
62
+ });
63
+
54
64
  debug(`CREATE GROOVY temp file ${result.tmpGroovyFileName} with input source, as CodeNarc requires physical files`);
55
65
  }
56
66
 
57
67
  // Define base directory
58
68
  const baseBefore = (cnPath !== "." && cnPath.startsWith("/")) || cnPath.includes(":/") || cnPath.includes(":\\") ? "" : process.cwd() + "/";
59
69
  const codeNarcBaseDir =
60
- positionalArgs.length > 0
61
- ? await getCodeNarcBaseDirFromFiles(positionalArgs)
62
- : cnPath !== "."
63
- ? baseBefore + cnPath.replace(/^"(.*)"$/, "$1")
64
- : process.cwd();
70
+ positionalArgs.length > 0 ? await getCodeNarcBaseDirFromFiles(positionalArgs) : cnPath !== "." ? baseBefore + cnPath : process.cwd();
65
71
  result.codeNarcBaseDir = path.resolve(codeNarcBaseDir);
66
72
  result.codenarcArgs.push(`-basedir=${result.codeNarcBaseDir}`);
67
73
 
@@ -74,69 +80,66 @@ async function prepareCodeNarcCall(options) {
74
80
  result.codenarcArgs.push(`-ruleset=${encodeURIComponent(options.rulesets)}`);
75
81
  } else {
76
82
  // File list format
77
- const rulesetFileArgs = options.rulesets.startsWith("file:") ? options.rulesets : `file:${options.rulesets.replace(/^"(.*)"$/, "$1")}`;
83
+ const rulesetFileArgs = options.rulesets.startsWith("file:") ? options.rulesets : `file:${options.rulesets}`;
78
84
  result.codenarcArgs.push(`-rulesetfiles=${rulesetFileArgs}`);
79
85
  }
80
86
 
81
- // Default file patterns
82
- let filePatterns = defaultFilesPattern.map(filePattern => `**/${filePattern}`).join(",");
83
-
84
87
  // Build codenarc arguments from eslint-like formatted positional arguments
85
88
  if (positionalArgs.length > 0) {
86
- if (options.ext && options.ext.length > 0) {
87
- filePatterns = `/**/*.{` + options.extensions.map(ext => ext.replace(/^\./u, "")) + "}";
89
+ const filePatterns =
90
+ options.ext && options.ext.length > 0
91
+ ? // Convert extensions with or without leading slash into an ant pattern.
92
+ options.ext.map(ext => ext.replace(/^\.?/u, "*."))
93
+ : defaultFilesPattern;
94
+ const sourceFiles = [];
95
+ const includes = [];
96
+ positionalArgs.filter(Boolean).forEach(pathname => {
97
+ const absolutePath = path.resolve(".", pathname);
98
+ const relativePath = path.relative(codeNarcBaseDir, absolutePath);
99
+ if (directoryExists(absolutePath)) {
100
+ // Directory: convert into ant patterns.
101
+ const patternBase = antPath(relativePath) + "**/";
102
+ includes.push(...filePatterns.map(filePattern => patternBase + filePattern));
103
+ } else if (fs.existsSync(absolutePath)) {
104
+ // File: add to file / patterns list.
105
+ const file = antPath(relativePath);
106
+ includes.push(file);
107
+ sourceFiles.push(absolutePath);
108
+ } else {
109
+ // Ant pattern.
110
+ includes.push(antPath(path.normalize(pathname)));
111
+ }
112
+ });
113
+
114
+ if (includes.length > 0) {
115
+ result.codenarcArgs.push(`-includes=${includes.join(",")}`);
116
+ } else if (sourceFiles.length > 0) {
117
+ // Source files override patterns, so only use if we have no patterns.
118
+ result.codenarcArgs.push(`-sourcefiles=${sourceFiles.join(",")}`);
88
119
  }
89
- const fileList = [];
90
- const patternsFinal = positionalArgs
91
- .filter(Boolean)
92
- .map(pathname => {
93
- let finalPattern = path.normalize(pathname).replace(/\\/gu, "/");
94
- // Directory: convert into ant pattern
95
- const resolvedPath = path.resolve(cnPath, pathname);
96
- if (directoryExists(resolvedPath)) {
97
- finalPattern = "**/" + path.normalize(pathname.replace(/[/\\]$/u, "")).replace(/\\/gu, "/") + filePatterns;
98
- }
99
- // Relative with codeNarcBaseDir
100
- else if (fs.existsSync(path.join(result.codeNarcBaseDir, finalPattern))) {
101
- const absolutePath = path.resolve(finalPattern).replace(/\\/gu, "/");
102
- fileList.push(absolutePath);
103
- const relativePath = finalPattern.replace(/\\/gu, "/");
104
- finalPattern = "**/" + path.normalize(relativePath.replace(/[/\\]$/u, "")).replace(/\\/gu, "/");
105
- }
106
- // Absolute or cwd - relative path file
107
- else if (fs.existsSync(finalPattern)) {
108
- const absolutePath = path.resolve(finalPattern).replace(/\\/gu, "/");
109
- fileList.push(absolutePath);
110
- const relativePath = path.relative(result.codeNarcBaseDir, path.resolve(finalPattern)).replace(/\\/gu, "/");
111
- finalPattern = "**/" + path.normalize(relativePath.replace(/[/\\]$/u, "")).replace(/\\/gu, "/");
112
- }
113
- // Directory or ant pattern
114
- return finalPattern;
115
- })
116
- .join(",");
117
- result.codenarcArgs.push(`-includes="${patternsFinal}"`);
118
- result.codeNarcIncludes = patternsFinal;
119
- result.inputFileList = fileList;
120
+
121
+ result.codeNarcIncludes = includes;
122
+ result.inputFileList = sourceFiles;
120
123
  }
121
124
  // Matching files pattern(s)
122
125
  else if (cnFiles) {
123
- const normalizedCnFiles = cnFiles.replace(/^"(.*)"$/, "$1");
124
- result.codenarcArgs.push(`-includes="${normalizedCnFiles}"`);
125
- result.codeNarcIncludes = normalizedCnFiles;
126
+ result.codenarcArgs.push(`-includes=${cnFiles.join(",")}`);
127
+ result.codeNarcIncludes = cnFiles;
126
128
  } else {
127
129
  // If files not sent, use defaultFilesPattern, guessed from options.rulesets value
128
- result.codenarcArgs.push(`-includes="${filePatterns}"`);
130
+ const filePatterns = defaultFilesPattern.map(filePattern => `**/${filePattern}`);
131
+ result.codenarcArgs.push(`-includes=${filePatterns.join(",")}`);
129
132
  result.codeNarcIncludes = filePatterns;
130
133
  }
131
134
 
132
135
  // Ignore pattern
133
136
  if (options.ignorepattern) {
134
- result.codenarcArgs.push(`-excludes="${options.ignorepattern}"`);
135
- result.codeNarcExcludes = options.ignorepattern;
137
+ result.codenarcArgs.push(`-excludes=${options.ignorepattern}`);
138
+ result.codeNarcExcludes = options.ignorepattern.split(",");
136
139
  }
137
140
 
138
141
  // Output
139
- result.output = options.output.replace(/^"(.*)"$/, "$1");
142
+ result.output = options.output;
140
143
  if (
141
144
  ["txt", "json", "sarif", "none", "stdout"].includes(result.output) ||
142
145
  result.output.endsWith(".txt") ||
@@ -181,6 +184,16 @@ async function prepareCodeNarcCall(options) {
181
184
  return result;
182
185
  }
183
186
 
187
+ /**
188
+ * Converts a path with forward slashes to backslashes.
189
+ *
190
+ * @param {string} path The path to convert
191
+ * @returns The converted path
192
+ */
193
+ function antPath(path) {
194
+ return path.replace(/\\/gu, "/");
195
+ }
196
+
184
197
  // Calculate longest base dir by analyzing the list of files
185
198
  async function getCodeNarcBaseDirFromFiles(positionalArgs) {
186
199
  // All arguments are not files
@@ -504,15 +517,6 @@ function getCodeNarcPriorityCode(ruleFromConfig) {
504
517
  return null;
505
518
  }
506
519
 
507
- async function manageDeleteTmpFiles(tmpGroovyFileName) {
508
- // Remove temporary groovy file created for source argument if provided
509
- if (tmpGroovyFileName) {
510
- await fs.remove(tmpGroovyFileName);
511
- debug(`Removed temp file ${tmpGroovyFileName} as it is not longer used`);
512
- tmpGroovyFileName = null;
513
- }
514
- }
515
-
516
520
  function directoryExists(resolvedPath) {
517
521
  try {
518
522
  return fs.statSync(resolvedPath).isDirectory();
@@ -524,4 +528,4 @@ function directoryExists(resolvedPath) {
524
528
  }
525
529
  }
526
530
 
527
- module.exports = { prepareCodeNarcCall, parseCodeNarcResult, manageDeleteTmpFiles };
531
+ module.exports = { prepareCodeNarcCall, parseCodeNarcResult };
@@ -1 +1 @@
1
- println 'This file should not be linted if --ignorepattern is **/node_modules/**' ;
1
+ println 'This file should not be linted if --ignorepattern is **/fake_node_modules/**' ;
@@ -7,7 +7,7 @@ const performance = require("perf_hooks").performance;
7
7
 
8
8
  const NpmGroovyLintFix = require("./groovy-lint-fix");
9
9
  const CodeNarcCaller = require("./codenarc-caller");
10
- const { prepareCodeNarcCall, parseCodeNarcResult, manageDeleteTmpFiles } = require("./codenarc-factory");
10
+ const { prepareCodeNarcCall, parseCodeNarcResult } = require("./codenarc-factory");
11
11
  const { NPM_GROOVY_LINT_CONSTANTS, loadConfig, getConfigFileName } = require("./config.js");
12
12
  const optionsDefinition = require("./options");
13
13
  const { computeStats, processOutput } = require("./output.js");
@@ -98,7 +98,19 @@ class NpmGroovyLint {
98
98
  this.lintResult = computeStats(this.lintResult);
99
99
  this.outputString = await processOutput(this.outputType, this.output, this.lintResult, this.options, this.fixer);
100
100
  // Delete Tmp file if existing
101
- manageDeleteTmpFiles(this.tmpGroovyFileName);
101
+ await this.manageDeleteTmpFiles();
102
+ }
103
+
104
+ // Delete tmp file if needed.
105
+ async manageDeleteTmpFiles() {
106
+ if (!this.tmpGroovyFileName) {
107
+ return;
108
+ }
109
+
110
+ await fs.remove(this.tmpGroovyFileName);
111
+ console.log(`GroovyLint: Removed temp file ${this.tmpGroovyFileName} as it is not longer used`);
112
+ debug(`Removed temp file ${this.tmpGroovyFileName} as it is not longer used`);
113
+ this.tmpGroovyFileName = null;
102
114
  }
103
115
 
104
116
  // Returns the full path of the configuration file
@@ -119,10 +131,12 @@ class NpmGroovyLint {
119
131
  async preProcess() {
120
132
  // Reset status so we don't get stale results.
121
133
  this.status = 0;
122
-
123
134
  // Manage when the user wants to use only codenarc args
124
135
  if (Array.isArray(this.args) && this.args.includes("--codenarcargs")) {
125
- this.codenarcArgs = this.args.slice(2).filter(userArg => userArg !== "--codenarcargs");
136
+ this.codenarcArgs = this.args
137
+ .slice(2)
138
+ .filter(userArg => userArg !== "--codenarcargs") // Strip codenarcargs.
139
+ .map(userArg => userArg.replace(/^-(\w+)="(.*)"$/, "-$1=$2").replace(/^-(\w+)='(.*)'$/, "-$1=$2")); // Strip quotes around values which CodeNarc doesn't support.
126
140
  this.onlyCodeNarc = true;
127
141
  return true;
128
142
  }
@@ -132,6 +146,12 @@ class NpmGroovyLint {
132
146
  if (this.parseOptions) {
133
147
  try {
134
148
  this.options = optionsDefinition.parse(this.args);
149
+ // Strip quotes around values which CodeNarc doesn't support.
150
+ for (const [key, value] of Object.entries(this.options)) {
151
+ if (typeof value === "string") {
152
+ this.options[key] = value.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
153
+ }
154
+ }
135
155
  const configProperties = await loadConfig(
136
156
  this.options.config || this.options.path,
137
157
  this.options.format ? "format" : "lint",
@@ -330,7 +350,7 @@ class NpmGroovyLint {
330
350
  }
331
351
  }
332
352
 
333
- manageDeleteTmpFiles(this.tmpGroovyFileName);
353
+ await this.manageDeleteTmpFiles();
334
354
 
335
355
  // Manage return code in case failonerror, failonwarning or failoninfo is called
336
356
  this.manageReturnCode();
Binary file
package/lib/utils.js CHANGED
@@ -255,7 +255,11 @@ function getOutOfBracesStrings(str, exclude = []) {
255
255
 
256
256
  // Split source lines to analyse
257
257
  async function getSourceLines(source, fileNm) {
258
- let fileContent = source || (await fse.readFile(fileNm));
258
+ let fileContent =
259
+ source ||
260
+ (await fse.readFile(fileNm).catch(err => {
261
+ throw new Error(`Unable to read source lines: ${err}`); // Ensure that we have a stack trace.
262
+ }));
259
263
  return normalizeNewLines(fileContent.toString()).split(os.EOL);
260
264
  }
261
265
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-groovy-lint",
3
- "version": "12.2.1-beta202311260926.0",
3
+ "version": "13.0.1-beta202311261533.0",
4
4
  "description": "Lint, format and auto-fix your Groovy / Jenkinsfile / Gradle files",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -48,7 +48,7 @@
48
48
  "dependencies": {
49
49
  "amplitude": "^5.1.6",
50
50
  "ansi-colors": "^4.1.1",
51
- "axios": "^1.6.0",
51
+ "axios": "^1.6.2",
52
52
  "chalk": "^4.1.2",
53
53
  "cli-progress": "^3.12.0",
54
54
  "commondir": "^1.0.1",
@@ -107,5 +107,8 @@
107
107
  "html"
108
108
  ],
109
109
  "all": true
110
+ },
111
+ "overrides": {
112
+ "axios": "^1.6.2"
110
113
  }
111
114
  }