npm-groovy-lint
Advanced tools
Comparing version 2.0.0-beta4 to 2.0.0
@@ -16,2 +16,3 @@ #! /usr/bin/env node | ||
npmGroovyLintRules; | ||
fixRules = null; | ||
fixableErrors = {}; | ||
@@ -28,4 +29,7 @@ fixedErrorsNumber = 0; | ||
this.verbose = optionsIn.verbose || false; | ||
// Load fix rules | ||
// Load available fix rules | ||
this.npmGroovyLintRules = this.options.groovyLintRulesOverride ? require(this.options.groovyLintRulesOverride) : npmGroovyLintRules; | ||
if (this.options.fixrules !== "all" && this.options.fixrules !== null) { | ||
this.fixRules = this.options.fixrules.split(","); | ||
} | ||
// Initialize fix counters | ||
@@ -69,3 +73,7 @@ this.updatedLintResult.summary.totalFixedErrorNumber = 0; | ||
for (const err of fileErrors) { | ||
if (this.npmGroovyLintRules[err.rule] != null && this.npmGroovyLintRules[err.rule].fix != null) { | ||
if ( | ||
this.npmGroovyLintRules[err.rule] != null && | ||
this.npmGroovyLintRules[err.rule].fix != null && | ||
(this.fixRules == null || this.fixRules.includes(err.rule)) | ||
) { | ||
// Add fixable error | ||
@@ -98,11 +106,3 @@ const fixableError = { | ||
this.fixableErrors[fileNm].sort((a, b) => { | ||
return a.rule.scope && a.rule.scope === "file" && b.rule.scope == null | ||
? 1 | ||
: b.rule.scope && b.rule.scope === "file" && a.rule.scope == null | ||
? -1 | ||
: a.rule.scope && a.rule.scope === "file" && b.rule.scope === "file" && a.rule.priority > b.rule.priority | ||
? 1 | ||
: a.rule.scope && a.rule.scope === "file" && b.rule.scope === "file" && a.rule.priority < b.rule.priority | ||
? -1 | ||
: 0; | ||
return a.rule.priority > b.rule.priority ? 1 : a.rule.priority < b.rule.priority ? -1 : 0; | ||
}); | ||
@@ -116,2 +116,3 @@ } | ||
fixableError.rule.scope === "file" && | ||
!fixableError.rule.unitary === true && | ||
this.fixableErrors[fileNm].filter(matchFixableError => matchFixableError.ruleName === fixableError.ruleName).length > 0 | ||
@@ -131,3 +132,6 @@ ) { | ||
let fileContent = await fse.readFile(fileNm); | ||
let fileLines = fileContent.toString().split("\n"); | ||
let fileLines = fileContent | ||
.toString() | ||
.replace(/\r?\n/g, "\r\n") | ||
.split("\r\n"); | ||
@@ -137,6 +141,9 @@ // Process fixes | ||
for (const fileFixableError of this.fixableErrors[fileNm]) { | ||
const lineNb = parseInt(fileFixableError.lineNb, 10) - 1; | ||
if (this.fixRules != null && !this.fixRules.includes(fileFixableError.ruleName)) { | ||
continue; | ||
} | ||
const lineNb = fileFixableError.lineNb ? parseInt(fileFixableError.lineNb, 10) - 1 : -1; | ||
// File scope violation | ||
if (fileFixableError.rule.scope === "file") { | ||
const fileLinesNew = this.applyFixRule(fileLines, lineNb, fileFixableError).slice(); // copy result lines | ||
const fileLinesNew = this.tryApplyFixRule(fileLines, lineNb, fileFixableError).slice(); // copy result lines | ||
if (JSON.stringify(fileLinesNew) !== JSON.stringify(fileLines.toString)) { | ||
@@ -152,3 +159,3 @@ fileLines = fileLinesNew; | ||
const line = fileLines[lineNb]; | ||
const fixedLine = this.applyFixRule(line, lineNb, fileFixableError); | ||
const fixedLine = this.tryApplyFixRule(line, lineNb, fileFixableError); | ||
if (fixedLine !== line) { | ||
@@ -164,3 +171,3 @@ fileLines[lineNb] = fixedLine; | ||
if (fixedInFileNb) { | ||
fse.writeFileSync(fileNm, fileLines.join("\n") + (fileLines[fileLines.length - 1] === "\n" ? "\n" : "")); | ||
fse.writeFileSync(fileNm, fileLines.join("\r\n") + "\r\n"); | ||
} | ||
@@ -171,2 +178,14 @@ }) | ||
tryApplyFixRule(line, lineNb, fixableError) { | ||
try { | ||
return this.applyFixRule(line, lineNb, fixableError); | ||
} catch (e) { | ||
if (this.verbose) { | ||
console.error(e.message); | ||
console.error(fixableError); | ||
} | ||
return line; | ||
} | ||
} | ||
// Evaluate variables and apply rule defined fixes | ||
@@ -183,2 +202,4 @@ applyFixRule(line, lineNb, fixableError) { | ||
evaluatedVars.push({ name: varDef.name, value: decodeHtml(regexRes[regexPos]) }); | ||
} else if (this.verbose) { | ||
console.error("NGL: Unable to match " + varDef.regex + " in " + fixableError.msg); | ||
} | ||
@@ -202,4 +223,4 @@ } else if (varDef.value) { | ||
newLine = newLine.replace(strBefore, strAfter); | ||
} else if (this.options.verbose) { | ||
console.debug("NGL: missing replacement variable(s):\n" + strBefore + "\n" + strAfter + "\n"); | ||
} else if (this.verbose) { | ||
console.error("NGL: missing replacement variable(s):\n" + strBefore + "\n" + strAfter + "\n" + JSON.stringify(fixableError)); | ||
} | ||
@@ -212,3 +233,3 @@ } | ||
} catch (e) { | ||
if (this.options.verbose) { | ||
if (this.verbose) { | ||
console.error("NGL: Function error: " + e.message + " / " + JSON.stringify(fixableError)); | ||
@@ -223,3 +244,3 @@ } | ||
setVariablesValues(str, variables) { | ||
let newStr = str; | ||
let newStr = str + ""; | ||
for (const variable of variables) { | ||
@@ -226,0 +247,0 @@ newStr = newStr.replace(new RegExp("{{" + variable.name + "}}", "g"), variable.value); |
@@ -6,9 +6,60 @@ // List fixable CodeNarc rules | ||
// Default indent length | ||
const indentLength = 4; | ||
// If you add a new global rule, it's very important to think about their order. | ||
// Rules modifiyng the number of lines must arrive last ! | ||
const rulesFixPriorityOrder = [ | ||
// Line rules or not changing line rules | ||
"NoTabCharacter", | ||
"TrailingWhitespace", | ||
"Indentation", | ||
"UnnecessaryGString", | ||
"SpaceBeforeOpeningBrace", | ||
"SpaceAfterOpeningBrace", | ||
"SpaceAfterCatch", | ||
"SpaceAroundOperator", | ||
"SpaceAfterComma", | ||
"UnnecessaryDefInFieldDeclaration", | ||
"UnnecessarySemicolon", | ||
// Rule that can change the numbers of lines, so they must be processed after line scope rules | ||
"IfStatementBraces", | ||
"ElseStatementBraces", | ||
"ConsecutiveBlankLines", | ||
"ClosingBraceNotAlone", | ||
"IndentationClosingBraces", | ||
"IndentationComments", | ||
"FileEndsWithoutNewline" | ||
]; | ||
const npmGroovyLintRules = { | ||
// Closing brace not alone | ||
ClosingBraceNotAlone: { | ||
scope: "file", | ||
priority: getPriority("ClosingBraceNotAlone"), | ||
fix: { | ||
type: "function", | ||
func: fileLines => { | ||
const newFileLines = []; | ||
let prevLine = ""; | ||
for (const line of fileLines) { | ||
const newLine = line.replace("{{{NEWLINECLOSINGBRACE}}}", ""); | ||
const prevLineIndent = prevLine.search(/\S/); | ||
newFileLines.push(newLine); | ||
if (newLine !== line) { | ||
newFileLines.push(" ".repeat(prevLineIndent) + "}"); | ||
} | ||
prevLine = newLine; | ||
} | ||
return newFileLines; | ||
} | ||
} | ||
}, | ||
// Consecutive blank lines | ||
ConsecutiveBlankLines: { | ||
scope: "file", | ||
priority: 999, | ||
priority: getPriority("ConsecutiveBlankLines"), | ||
fix: { | ||
@@ -18,10 +69,8 @@ type: "function", | ||
const newFileLines = []; | ||
let prevLine = "none"; | ||
for (const line of fileLines) { | ||
if (line.trim() === "") { | ||
// Check if previous line is empty: if not, add empty line | ||
if (!(newFileLines.length > 0 && newFileLines[newFileLines.length - 1].trim() === "")) { | ||
newFileLines.push(""); | ||
} | ||
} else { | ||
if (!(line.trim() === "" && prevLine.trim() === "")) { | ||
// Check if previous line is empty: if not do not add line | ||
newFileLines.push(line); | ||
prevLine = line; | ||
} | ||
@@ -37,7 +86,7 @@ } | ||
scope: "file", | ||
priority: 999, | ||
priority: getPriority("FileEndsWithoutNewline"), | ||
fix: { | ||
type: "function", | ||
func: fileLines => { | ||
return (fileLines.join("\n") + "\n").split("\n"); | ||
return (fileLines.join("\r\n") + "\r\n").split("\r\n"); | ||
} | ||
@@ -47,14 +96,41 @@ } | ||
/* nvuillam: Not working, especially when embedded missing If statements ... | ||
let's let people correct that manually for now :) | ||
// nvuillam: Not working, especially when embedded missing If statements ... | ||
// let's let people correct that manually for now :) | ||
// Missing if braces | ||
IfStatementBraces: { | ||
scope: "file", | ||
priority: 1, | ||
unitary: true, | ||
triggers: ["ClosingBraceNotAlone"], | ||
priority: getPriority("IfStatementBraces"), | ||
fix: { | ||
type: "function", | ||
func: (fileLines, variables) => { | ||
const lineNumber = getVariable(variables, 'lineNb', { mandatory: true }); | ||
fileLines[lineNumber - 1] = fileLines[lineNumber - 1] + ' {'; | ||
fileLines[lineNumber] = fileLines[lineNumber] + ' }'; | ||
const lineNumber = getVariable(variables, "lineNb", { mandatory: true }); | ||
// If next line is also a if/else, this rule can not autofix for now, it has to be done manually | ||
if (fileLines[lineNumber + 1] && (fileLines[lineNumber + 1].includes("if") || fileLines[lineNumber + 1].includes("else"))) { | ||
return fileLines; | ||
} | ||
// If line | ||
let line = fileLines[lineNumber]; | ||
line = line.trimEnd() + " {"; | ||
fileLines[lineNumber] = line; | ||
// next line | ||
let match = false; | ||
let pos = 0; | ||
let level = 0; | ||
while (!match && pos < fileLines.length) { | ||
let nextLine = fileLines[lineNumber + pos + 1]; | ||
if (isValidCodeLine(nextLine) && level === 0) { | ||
if (!nextLine.trim().startsWith("if") && !nextLine.includes("{")) { | ||
nextLine = nextLine + "{{{NEWLINECLOSINGBRACE}}}"; | ||
fileLines[lineNumber + pos + 1] = nextLine; | ||
match = true; | ||
} else if (nextLine.includes("}") && !nextLine.includes("{")) { | ||
level--; | ||
} else { | ||
level++; | ||
} | ||
} | ||
pos++; | ||
} | ||
return fileLines; | ||
@@ -64,17 +140,58 @@ } | ||
}, | ||
*/ | ||
// Missing else braces | ||
ElseStatementBraces: { | ||
scope: "file", | ||
unitary: true, | ||
triggers: ["ClosingBraceNotAlone"], | ||
priority: getPriority("ElseStatementBraces"), | ||
fix: { | ||
type: "function", | ||
func: (fileLines, variables) => { | ||
const lineNumber = getVariable(variables, "lineNb", { mandatory: true }); | ||
// If next line is also a if/else, this rule can not autofix for now, it has to be done manually | ||
if (fileLines[lineNumber + 1] && (lineNumber[lineNumber + 1].includes("if") || lineNumber[lineNumber + 1].includes("else"))) { | ||
return fileLines; | ||
} | ||
let line = fileLines[lineNumber]; | ||
line = line.trimEnd() + " {"; | ||
fileLines[lineNumber] = line; | ||
// next line | ||
let match = false; | ||
let pos = 0; | ||
let level = 0; | ||
while (!match && pos < fileLines.length) { | ||
let nextLine = fileLines[lineNumber + pos + 1]; | ||
if (isValidCodeLine(nextLine) && level === 0) { | ||
if (!nextLine.trim().startsWith("if") && !nextLine.includes("{")) { | ||
nextLine = nextLine + "{{{NEWLINECLOSINGBRACE}}}"; | ||
fileLines[lineNumber + pos + 1] = nextLine; | ||
match = true; | ||
} else if (nextLine.includes("}") && !nextLine.includes("{")) { | ||
level--; | ||
} else { | ||
level++; | ||
} | ||
} | ||
pos++; | ||
} | ||
return fileLines; | ||
} | ||
} | ||
}, | ||
// Indentation | ||
Indentation: { | ||
triggers: ["IndentationClosingBraces", "IndentationComments"], | ||
priority: getPriority("Indentation"), | ||
variables: [ | ||
{ | ||
name: "EXPECTED", | ||
regex: /The (.*) in class (.*) is at the incorrect indent level: Expected column (.*) but was (.*)/, | ||
regexPos: 3 | ||
regex: /The (.*) is at the incorrect indent level: Expected column (.*) but was (.*)/, | ||
regexPos: 2 | ||
}, | ||
{ | ||
name: "FOUND", | ||
regex: /The (.*) in class (.*) is at the incorrect indent level: Expected column (.*) but was (.*)/, | ||
regexPos: 4 | ||
regex: /The (.*) is at the incorrect indent level: Expected column (.*) but was (.*)/, | ||
regexPos: 3 | ||
} | ||
@@ -85,10 +202,10 @@ ], | ||
func: (line, evaluatedVars) => { | ||
const expectedIndent = parseInt(getVariable(evaluatedVars, "EXPECTED", { mandatory: true }), 10); | ||
const foundIndent = parseInt(getVariable(evaluatedVars, "FOUND", { mandatory: true })); | ||
if (line.trim() === "}") { | ||
// Manage Wrong info from codeNarc :/ { | ||
line = line.replace(" ".repeat(foundIndent - 1), " ".repeat(expectedIndent + indentLength * 2)); | ||
} else { | ||
line = line.replace(" ".repeat(foundIndent - 1), " ".repeat(expectedIndent - 1)); | ||
} | ||
const expectedCol = parseInt(getVariable(evaluatedVars, "EXPECTED", { mandatory: true, line: line }), 10); | ||
// const foundIndent = parseInt(getVariable(evaluatedVars, "FOUND", { mandatory: true, line: line })); | ||
/* if (line.trim() === "}") { | ||
// Manage Wrong info from codeNarc :/ { | ||
line = " ".repeat(expectedIndent + (indentLength * 2)) + line.trimStart(); | ||
} else { */ | ||
const startSpaces = expectedCol === 0 ? 0 : expectedCol - 1; | ||
line = " ".repeat(startSpaces) + line.trimStart(); | ||
return line; | ||
@@ -102,3 +219,3 @@ } | ||
scope: "file", | ||
priority: 900, | ||
priority: getPriority("IndentationComments"), | ||
fix: { | ||
@@ -117,3 +234,3 @@ type: "function", | ||
if (!/^\s*$/.test(fileLines[i + j]) && !fileLines[i + j].trimStart().startsWith("//")) { | ||
nextLineIndent = fileLines[i + j].search(/\S/); | ||
nextLineIndent = fileLines[i + j].search(/\S/); // find first non blank character | ||
} | ||
@@ -134,6 +251,6 @@ j++; | ||
// Indentation comments | ||
// Indentation closing braces | ||
IndentationClosingBraces: { | ||
scope: "file", | ||
priority: 800, | ||
priority: getPriority("IndentationClosingBraces"), | ||
fix: { | ||
@@ -144,19 +261,18 @@ type: "function", | ||
for (let i = 0; i < fileLines.length; i++) { | ||
let line = fileLines[i]; | ||
let line = fileLines[i] + ""; | ||
// Detect closing brace line | ||
if (line.trim() == "}") { | ||
// Find indentation of matching brace (CodeNarc Indentation rule does not work well :/ ) | ||
if (line.trim() === "}") { | ||
// Find indentation of matching brace (CodeNarc Indentation rule does not always work well :/ ) | ||
let j = 1; | ||
let matchingLineIndent = null; | ||
let level = 0; | ||
while (fileLines[i - j] && matchingLineIndent == null) { | ||
let level = 1; | ||
while ((fileLines[i - j] || fileLines[i - j] === "") && matchingLineIndent == null) { | ||
const prevLine = fileLines[i - j]; | ||
if (prevLine.includes("}")) { | ||
if (prevLine.includes("}") && !prevLine.includes("${")) { | ||
level++; | ||
} | ||
if (prevLine.includes("{")) { | ||
if (prevLine.includes("{") && !prevLine.includes("${")) { | ||
level--; | ||
if (level === 0) { | ||
matchingLineIndent = prevLine.search(/\S/); | ||
} else { | ||
level--; | ||
} | ||
@@ -168,3 +284,3 @@ } | ||
if (matchingLineIndent) { | ||
line = " ".repeat(matchingLineIndent) + line.trimStart(); | ||
line = (" ".repeat(matchingLineIndent) + line.trimStart()).replace(/\t/g, ""); | ||
} | ||
@@ -182,3 +298,3 @@ } | ||
scope: "file", | ||
priority: 2, | ||
priority: getPriority("NoTabCharacter"), | ||
fix: { | ||
@@ -188,4 +304,5 @@ type: "function", | ||
const newFileLines = []; | ||
const replaceChars = " ".repeat(indentLength); | ||
for (const line of fileLines) { | ||
newFileLines.push(line.replace("\t", "")); | ||
newFileLines.push(line.replace(/\t/g, replaceChars)); | ||
} | ||
@@ -199,2 +316,3 @@ return newFileLines; | ||
SpaceAfterCatch: { | ||
priority: getPriority("SpaceAfterCatch"), | ||
fix: { | ||
@@ -209,2 +327,3 @@ type: "replaceString", | ||
SpaceAfterOpeningBrace: { | ||
priority: getPriority("SpaceAfterOpeningBrace"), | ||
fix: { | ||
@@ -224,2 +343,3 @@ type: "function", | ||
SpaceAroundOperator: { | ||
priority: getPriority("SpaceAroundOperator"), | ||
variables: [ | ||
@@ -234,4 +354,8 @@ { | ||
func: (line, evaluatedVars) => { | ||
let operator = getVariable(evaluatedVars, "OPERATOR", { mandatory: true, htmlToString: true }); | ||
return addSpaceAroundChar(line, operator); | ||
let operator = getVariable(evaluatedVars, "OPERATOR", { mandatory: true, htmlToString: true, line: line }); | ||
if (!line.includes("+=") && !line.includes("++") && !line.includes("--") && !line.includes("-=")) { | ||
return addSpaceAroundChar(line, operator); | ||
} else { | ||
return line; | ||
} | ||
} | ||
@@ -243,2 +367,3 @@ } | ||
SpaceAfterComma: { | ||
priority: getPriority("SpaceAfterComma"), | ||
fix: { | ||
@@ -254,2 +379,3 @@ type: "function", | ||
SpaceBeforeOpeningBrace: { | ||
priority: getPriority("SpaceBeforeOpeningBrace"), | ||
fix: { | ||
@@ -269,6 +395,7 @@ type: "function", | ||
UnnecessaryDefInFieldDeclaration: { | ||
priority: getPriority("UnnecessaryDefInFieldDeclaration"), | ||
fix: { | ||
type: "replaceString", | ||
before: "static def ", | ||
after: "static " | ||
before: "def ", | ||
after: "" | ||
} | ||
@@ -279,2 +406,3 @@ }, | ||
UnnecessaryGString: { | ||
priority: getPriority("UnnecessaryGString"), | ||
variables: [ | ||
@@ -293,22 +421,9 @@ { | ||
// Unnecessary public declaration (public is by default) | ||
UnnecessaryPublicModifier: { | ||
fix: { | ||
type: "replaceString", | ||
before: "public ", | ||
after: "" | ||
} | ||
}, | ||
// Unnecessary semi colon at the end of a line | ||
UnnecessarySemicolon: { | ||
priority: getPriority("UnnecessarySemicolon"), | ||
fix: { | ||
type: "function", | ||
func: line => { | ||
const pos = line.lastIndexOf(";"); | ||
if (pos === line.length - 1) { | ||
return line.slice(0, -1).trimEnd(); | ||
} else { | ||
return (line.slice(0, pos) + line.slice(pos + 1)).trimEnd(); | ||
} | ||
return line.split(";").join(""); | ||
} | ||
@@ -320,2 +435,3 @@ } | ||
TrailingWhitespace: { | ||
priority: getPriority("TrailingWhitespace"), | ||
fix: { | ||
@@ -330,3 +446,7 @@ type: "function", | ||
function getVariable(evaluatedVars, name, optns = { mandatory: false, decodeHtml: false }) { | ||
function getPriority(ruleName) { | ||
return rulesFixPriorityOrder.indexOf(ruleName); | ||
} | ||
function getVariable(evaluatedVars, name, optns = { mandatory: false, decodeHtml: false, line: "" }) { | ||
const matchingVars = evaluatedVars.filter(evaluatedVar => evaluatedVar.name === name); | ||
@@ -336,3 +456,3 @@ if (matchingVars && matchingVars.length > 0) { | ||
} else if (optns.mandatory) { | ||
throw new Error("NGL fix: missing mandatory variable " + name + " in " + JSON.stringify(evaluatedVars)); | ||
throw new Error("NGL fix: missing mandatory variable " + name + " in " + JSON.stringify(evaluatedVars)) + "for line :\n" + optns.line; | ||
} else { | ||
@@ -343,10 +463,14 @@ return null; | ||
function isValidCodeLine(line) { | ||
return line.trim() !== "" && line.trim().split("//")[0] !== ""; | ||
} | ||
function addSpaceAroundChar(line, char) { | ||
let pos = 0; | ||
let pos = -1; | ||
const splits = line.split(char); | ||
const newArray = splits.map(str => { | ||
pos++; | ||
if (pos === 1) { | ||
if (pos === 0) { | ||
return str.trimEnd(); | ||
} else if (pos === splits.length) { | ||
} else if (pos === splits.length - 1) { | ||
return str.trimStart(); | ||
@@ -357,5 +481,5 @@ } else { | ||
}); | ||
return newArray.join(" " + char + " "); | ||
return newArray.join(" " + char + " ").trimEnd(); | ||
} | ||
module.exports = { npmGroovyLintRules }; |
@@ -7,2 +7,3 @@ #! /usr/bin/env node | ||
const os = require("os"); | ||
const path = require("path"); | ||
const cliProgress = require("cli-progress"); | ||
@@ -32,2 +33,4 @@ const util = require("util"); | ||
// npm-groovy-lint | ||
outputType; | ||
output; | ||
onlyCodeNarc = false; | ||
@@ -101,3 +104,7 @@ lintResult = {}; | ||
// Base directory | ||
this.codeNarcBaseDir = this.options.path != "." ? process.cwd() + "/" + this.options.path.replace(/^"(.*)"$/, "$1") : process.cwd(); | ||
const baseBefore = | ||
(this.options.path != "." && this.options.path.startsWith("/")) || this.options.path.includes(":/") || this.options.path.includes(":\\") | ||
? "" | ||
: process.cwd() + "/"; | ||
this.codeNarcBaseDir = this.options.path != "." ? baseBefore + this.options.path.replace(/^"(.*)"$/, "$1") : process.cwd(); | ||
this.codenarcArgs.push('-basedir="' + this.codeNarcBaseDir + '"'); | ||
@@ -132,8 +139,24 @@ | ||
// Output | ||
const output = this.options.output.replace(/^"(.*)"$/, "$1"); | ||
if (["txt", "json"].includes(output)) { | ||
this.output = this.options.output.replace(/^"(.*)"$/, "$1"); | ||
if (this.output.includes(".txt")) { | ||
// Disable ansi colors if output in txt file | ||
c.enabled = false; | ||
} | ||
if (["txt", "json"].includes(this.output) || this.output.endsWith(".txt") || this.output.endsWith(".json")) { | ||
this.outputType = this.output.endsWith(".txt") ? "txt" : this.output.endsWith(".json") ? "json" : this.output; | ||
this.codenarcArgs.push('-report=xml:"' + this.tmpXmlFileName + '"'); | ||
} else if (["html", "xml"].includes(output.split(".").pop())) { | ||
const ext = output.split(".").pop(); | ||
this.codenarcArgs.push('-report="' + ext + ":" + output + '"'); | ||
} else if (["html", "xml"].includes(this.output.split(".").pop())) { | ||
this.outputType = this.output | ||
.split(".") | ||
.pop() | ||
.endsWith("html") | ||
? "html" | ||
: this.output | ||
.split(".") | ||
.pop() | ||
.endsWith("xml") | ||
? "xml" | ||
: ""; | ||
const ext = this.output.split(".").pop(); | ||
this.codenarcArgs.push('-report="' + ext + ":" + this.output + '"'); | ||
} else { | ||
@@ -154,3 +177,3 @@ this.status = 2; | ||
if (this.options.verbose) { | ||
console.log("Running CodeNarc with arguments " + this.codenarcArgs.join(" ")); | ||
console.log("NGL: running CodeNarc with " + this.codenarcArgs.join(" ")); | ||
} | ||
@@ -209,3 +232,6 @@ this.bar = new cliProgress.SingleBar( | ||
if (this.options.fix) { | ||
this.fixer = new NpmGroovyLintFix(this.lintResult, { verbose: this.options.verbose }); | ||
this.fixer = new NpmGroovyLintFix(this.lintResult, { | ||
verbose: this.options.verbose, | ||
fixrules: this.options.fixrules | ||
}); | ||
await this.fixer.run(); | ||
@@ -264,4 +290,11 @@ this.lintResult = this.fixer.updatedLintResult; | ||
}; | ||
files[fileNm].errors.push(err); | ||
errId++; | ||
// Add error only if severity is matching logLevel | ||
if ( | ||
err.severity === "error" || | ||
this.options.loglevel === "info" || | ||
(this.options.loglevel === "warning" && ["error", "warning"].includes(err.severity)) | ||
) { | ||
files[fileNm].errors.push(err); | ||
errId++; | ||
} | ||
} | ||
@@ -278,3 +311,3 @@ } | ||
// Display as console log | ||
if (this.options.output === "txt") { | ||
if (this.outputType === "txt") { | ||
// Errors | ||
@@ -343,10 +376,26 @@ for (const fileNm of Object.keys(this.lintResult.files)) { | ||
// Output log | ||
console.log(this.nglOutputString); | ||
console.table(summaryTable, this.fixer ? ["Severity", "Total found", "Total fixed", "Total remaining"] : ["Severity", "Total found"]); | ||
// Output text log in file or console | ||
if (this.output.endsWith(".txt")) { | ||
const fullFileContent = this.nglOutputString; | ||
fse.writeFileSync(this.output, fullFileContent); | ||
console.table(summaryTable, this.fixer ? ["Severity", "Total found", "Total fixed", "Total remaining"] : ["Severity", "Total found"]); | ||
const absolutePath = path.resolve(".", this.output); | ||
console.info("NGL: Logged results in file " + absolutePath); | ||
} else { | ||
console.log(this.nglOutputString); | ||
console.table(summaryTable, this.fixer ? ["Severity", "Total found", "Total fixed", "Total remaining"] : ["Severity", "Total found"]); | ||
} | ||
} | ||
// Display as json | ||
else if (this.options.output === "json") { | ||
this.nglOutputString = JSON.stringify(this.lintResult); | ||
console.log(this.nglOutputString); | ||
else if (this.outputType === "json") { | ||
// Output log | ||
if (this.output.endsWith(".json")) { | ||
const fullFileContent = JSON.stringify(this.nglOutputString, null, 2); | ||
fse.writeFileSync(this.output, fullFileContent); | ||
const absolutePath = path.resolve(".", this.output); | ||
console.info("NGL: Logged results in file " + absolutePath); | ||
} else { | ||
this.nglOutputString = JSON.stringify(this.lintResult); | ||
console.log(this.nglOutputString); | ||
} | ||
} | ||
@@ -353,0 +402,0 @@ } |
@@ -0,0 +0,0 @@ ANTLR 2 License |
@@ -0,0 +0,0 @@ ANTLR 4 License |
@@ -0,0 +0,0 @@ ASM License |
@@ -0,0 +0,0 @@ BSD License |
@@ -0,0 +0,0 @@ Copyright (c) 2002-2012, the original author or authors. |
@@ -0,0 +0,0 @@ The person or persons who have associated work with this document (the |
@@ -0,0 +0,0 @@ Copyright (c) 2006, Sun Microsystems, Inc. |
@@ -0,0 +0,0 @@ Eclipse Public License - v 1.0 |
@@ -0,0 +0,0 @@ Eclipse Public License - v 2.0 |
@@ -0,0 +0,0 @@ Copyright (c) 2003-2006, Joe Walnes |
@@ -61,2 +61,11 @@ /** | ||
{ | ||
option: "loglevel", | ||
alias: "l", | ||
type: "String", | ||
enum: ["error", "warning", "info"], | ||
default: "info", | ||
description: "Log level (error,warning,info)", | ||
example: ["warning", "error"] | ||
}, | ||
{ | ||
option: "verbose", | ||
@@ -76,2 +85,9 @@ alias: "v", | ||
{ | ||
option: "fixrules", | ||
alias: "x", | ||
type: "String", | ||
default: "all", | ||
description: "List of rule identifiers to fix (if not specified, all available fixes will be applied)" | ||
}, | ||
{ | ||
heading: "Ignoring files" | ||
@@ -78,0 +94,0 @@ }, |
{ | ||
"name": "npm-groovy-lint", | ||
"version": "2.0.0-beta4", | ||
"version": "2.0.0", | ||
"description": "NPM CodeNarc wrapper to easily lint Groovy files", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -18,3 +18,3 @@ # NPM GROOVY LINT (and FIX !) | ||
Use option **--fix** to activate autofixing (mostly formatting rules for now) | ||
Use option **--fix** to activate autofixing (the function is still in experimental phase, you may have to run it several times at first so CodeNarc take in account the updates) | ||
@@ -38,7 +38,8 @@ Easy to integrate in a CD/CI process (Jenkins Pipeline,CircleCI...) to lint your groovy or Jenkinsfile at each build :) | ||
| -p<br/> --path | String | Directory containing the files to lint<br/> Example: `./path/to/my/groovy/files` | | ||
| -f<br/> --files | String | Comma-separated list of Ant-style file patterns specifying files that must be included.<br/> Default: `"**/*.groovy,**/Jenkinsfile"`, or `"**/*.groovy"` if --rulesets Groovy, or `**/Jenkinsfile` if --rulesets Jenkinsfile <br/> Examples: <br/> - `"**/Jenkinsfile"<br/>` - `"*/*.groovy"` | | ||
| -f<br/> --files | String | Comma-separated list of Ant-style file patterns specifying files that must be included.<br/> Default: `"**/*.groovy,**/Jenkinsfile"`, or `"**/*.groovy"` if --rulesets Groovy, or `**/Jenkinsfile` if --rulesets Jenkinsfile <br/> Examples: <br/> - `"**/Jenkinsfile"<br/>` - `"*/*.groovy"` | | ||
| -r<br/> --rulesets | String | RuleSet file(s) to use for linting. If it is a directory, all rulesets will be used.<br/> RuleSet file definition: http://codenarc.sourceforge.net/codenarc-creating-ruleset.html.<br/> If not specified, npm-groovy-script default ones will be used depending on file types found in --path:<br/> - [Groovy recommended rules](https://github.com/nvuillam/npm-groovy-lint/blob/master/lib/example/RuleSet-Groovy.groovy), also usable with `--rulesets Groovy`<br/> - [Jenkinsfile recommended rules](https://github.com/nvuillam/npm-groovy-lint/blob/master/lib/example/RuleSet-Jenkinsfile.groovy), also usable with `--rulesets Jenkinsfile`<br/> Examples:<br/> - `"./config/codenarc/RuleSet-Custom.groovy"`<br/> - `"./path/to/my/ruleset/files"`<br/> - `Jenkinsfile` | | ||
| -o<br/> --output | String | Output format (txt,json,html,xml), or path to a file with one of these extensions<br/> Default: `txt`<br/> Examples:<br/> - `"txt"`<br/> - `"json"`<br/> - `"./logs/myLintResults.txt"`<br/> - `"./logs/myLintResults.json"`<br/> - `"./logs/myLintResults.html"`<br/> - `"./logs/myLintResults.xml"` | | ||
| -l<br/> --loglevel | String | Log level (error,warning or info)<br/>Default: info | | ||
| -v<br/> --verbose | Boolean | More outputs in console, including performed fixes | | ||
| --fix | Boolean | Automatically fix problems when possible<br/> See [Autofixable rules](#Autofixable-rules) | | ||
| --fix | Boolean | (Experimental) Automatically fix problems when possible<br/> See [Autofixable rules](#Autofixable-rules) | | ||
| -i<br/> --ignore-pattern | String | Comma-separated list of Ant-style file patterns specifying files that must be ignored<br/> Default: none<br/> Example: `"**/test/*""` | | ||
@@ -72,3 +73,3 @@ | --failonerror | Boolean | Fails if at least one error is found | | ||
# Autofixable rules | ||
# Autofixable rules (experimental) | ||
@@ -85,3 +86,2 @@ - ConsecutiveBlankLines | ||
- UnnecessaryGString | ||
- UnnecessaryPublicModifier | ||
- UnnecessarySemicolon | ||
@@ -88,0 +88,0 @@ - TrailingWhitespace |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
15899103
1321
0