npm-groovy-lint
Advanced tools
Comparing version 2.2.0-beta.8 to 2.2.0-beta.9
// Additional definition for codenarc rules ( get position & available fix) | ||
// Rule Template: | ||
/* | ||
RuleName: { | ||
// add scope = "file" if the fix impacts more than the identified line (If fix defined) | ||
scope: "file", | ||
// Define a priority at the top of groovy-lint-rules.js (If fix defined) | ||
priority: getPriority("RuleName"), | ||
// Extract variables from CodeNarc error message (optional) | ||
variables: [ | ||
{ | ||
name: "MYVAR", | ||
regex: /The (.*) is at the incorrect indent level: Expected column (.*) but was (.*)/, | ||
regexPos: 1, | ||
type: "number" | ||
}, | ||
{ | ||
name: "MYOTHERVAR", | ||
regex: /The (.*) is at the incorrect indent level: Expected column (.*) but was (.*)/, | ||
regexPos: 2, | ||
} | ||
], | ||
// Return range for UI . Input: errorLine, errorItem, evaluated variables | ||
range: { | ||
type: "function", | ||
func: (_errLine, errItem, evaluatedVars) => { | ||
const myVar = getVariable(evaluatedVars, "MYVAR"); | ||
return { | ||
start: { line: errItem.line, character: 0 }, | ||
end: { line: errItem.line, character: myVar.length } | ||
}; | ||
} | ||
}, | ||
// Fix if scope = file | ||
fix: { | ||
type: "function", | ||
func: allLines => { | ||
// update allLines here | ||
return allLines; | ||
} | ||
}, | ||
// Fix if scope = line | ||
fix: { | ||
type: "function", | ||
func: (line, evaluatedVars) => { | ||
const myVar = getVariable(evaluatedVars, "MYOTHERVAR"); | ||
line = line.replace(myVar, 'whatever'); | ||
return line; | ||
} | ||
} | ||
}, | ||
*/ | ||
"use strict"; | ||
@@ -28,3 +82,3 @@ | ||
"IfStatementBraces", | ||
"ElseStatementBraces", | ||
"ElseBlocktBraces", | ||
"ConsecutiveBlankLines", | ||
@@ -38,2 +92,12 @@ "ClosingBraceNotAlone", | ||
const npmGroovyLintRules = { | ||
// Braces for if else | ||
BracesForIfElse: { | ||
range: { | ||
type: "function", | ||
func: (_errLine, errItem, _evaluatedVars, allLines) => { | ||
return findRangeBetweenStrings(allLines, errItem, "if", "{"); | ||
} | ||
} | ||
}, | ||
// Closing brace not alone | ||
@@ -55,6 +119,6 @@ ClosingBraceNotAlone: { | ||
type: "function", | ||
func: fileLines => { | ||
func: allLines => { | ||
const newFileLines = []; | ||
let prevLine = ""; | ||
for (const line of fileLines) { | ||
for (const line of allLines) { | ||
const newLine = line.replace("{{{NEWLINECLOSINGBRACE}}}", ""); | ||
@@ -79,6 +143,6 @@ const prevLineIndent = prevLine.search(/\S/); | ||
type: "function", | ||
func: fileLines => { | ||
func: allLines => { | ||
const newFileLines = []; | ||
let prevLine = "none"; | ||
for (const line of fileLines) { | ||
for (const line of allLines) { | ||
if (!(line.trim() === "" && prevLine.trim() === "")) { | ||
@@ -95,34 +159,25 @@ // Check if previous line is empty: if not do not add line | ||
// File ends without new line | ||
FileEndsWithoutNewline: { | ||
// Missing else braces | ||
ElseBlocktBraces: { | ||
scope: "file", | ||
priority: getPriority("FileEndsWithoutNewline"), | ||
fix: { | ||
unitary: true, | ||
triggers: ["ClosingBraceNotAlone"], | ||
priority: getPriority("ElseBlocktBraces"), | ||
range: { | ||
type: "function", | ||
func: fileLines => { | ||
return (fileLines.join("\r\n") + "\r\n").split("\r\n"); | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "else", errItem); | ||
} | ||
} | ||
}, | ||
// nvuillam: Not working, especially when embedded missing If statements ... | ||
// let's let people correct that manually for now :) | ||
// Missing if braces | ||
IfStatementBraces: { | ||
scope: "file", | ||
unitary: true, | ||
triggers: ["ClosingBraceNotAlone"], | ||
priority: getPriority("IfStatementBraces"), | ||
}, | ||
fix: { | ||
type: "function", | ||
func: (fileLines, variables) => { | ||
func: (allLines, 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] && (fileLines[lineNumber + 1].includes("if") || fileLines[lineNumber + 1].includes("else"))) { | ||
return fileLines; | ||
if (allLines[lineNumber + 1] && lineNumber[lineNumber + 1].includes("else")) { | ||
return allLines; | ||
} | ||
// If line | ||
let line = fileLines[lineNumber]; | ||
let line = allLines[lineNumber]; | ||
line = line.trimEnd() + " {"; | ||
fileLines[lineNumber] = line; | ||
allLines[lineNumber] = line; | ||
// next line | ||
@@ -132,8 +187,8 @@ let match = false; | ||
let level = 0; | ||
while (!match && pos < fileLines.length) { | ||
let nextLine = fileLines[lineNumber + pos + 1]; | ||
while (!match && pos < allLines.length) { | ||
let nextLine = allLines[lineNumber + pos + 1]; | ||
if (isValidCodeLine(nextLine) && level === 0) { | ||
if (!nextLine.trim().startsWith("if") && !nextLine.includes("{")) { | ||
nextLine = nextLine + "{{{NEWLINECLOSINGBRACE}}}"; | ||
fileLines[lineNumber + pos + 1] = nextLine; | ||
allLines[lineNumber + pos + 1] = nextLine; | ||
match = true; | ||
@@ -148,3 +203,3 @@ } else if (nextLine.includes("}") && !nextLine.includes("{")) { | ||
} | ||
return fileLines; | ||
return allLines; | ||
} | ||
@@ -154,19 +209,40 @@ } | ||
// Missing else braces | ||
ElseStatementBraces: { | ||
// File ends without new line | ||
FileEndsWithoutNewline: { | ||
scope: "file", | ||
priority: getPriority("FileEndsWithoutNewline"), | ||
fix: { | ||
type: "function", | ||
func: allLines => { | ||
return (allLines.join("\r\n") + "\r\n").split("\r\n"); | ||
} | ||
} | ||
}, | ||
// nvuillam: Fix not working, especially when embedded missing If statements ... | ||
// let's let people correct that manually for now :) | ||
// Missing if braces | ||
IfStatementBraces: { | ||
scope: "file", | ||
unitary: true, | ||
triggers: ["ClosingBraceNotAlone"], | ||
priority: getPriority("ElseStatementBraces"), | ||
priority: getPriority("IfStatementBraces"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "else", errItem); | ||
} | ||
}, | ||
fix: { | ||
type: "function", | ||
func: (fileLines, variables) => { | ||
func: (allLines, 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; | ||
if (allLines[lineNumber + 1] && (allLines[lineNumber + 1].includes("if") || allLines[lineNumber + 1].includes("else"))) { | ||
return allLines; | ||
} | ||
let line = fileLines[lineNumber]; | ||
// If line | ||
let line = allLines[lineNumber]; | ||
line = line.trimEnd() + " {"; | ||
fileLines[lineNumber] = line; | ||
allLines[lineNumber] = line; | ||
// next line | ||
@@ -176,8 +252,8 @@ let match = false; | ||
let level = 0; | ||
while (!match && pos < fileLines.length) { | ||
let nextLine = fileLines[lineNumber + pos + 1]; | ||
while (!match && pos < allLines.length) { | ||
let nextLine = allLines[lineNumber + pos + 1]; | ||
if (isValidCodeLine(nextLine) && level === 0) { | ||
if (!nextLine.trim().startsWith("if") && !nextLine.includes("{")) { | ||
nextLine = nextLine + "{{{NEWLINECLOSINGBRACE}}}"; | ||
fileLines[lineNumber + pos + 1] = nextLine; | ||
allLines[lineNumber + pos + 1] = nextLine; | ||
match = true; | ||
@@ -192,3 +268,3 @@ } else if (nextLine.includes("}") && !nextLine.includes("{")) { | ||
} | ||
return fileLines; | ||
return allLines; | ||
} | ||
@@ -247,6 +323,6 @@ } | ||
type: "function", | ||
func: fileLines => { | ||
func: allLines => { | ||
const newFileLines = []; | ||
for (let i = 0; i < fileLines.length; i++) { | ||
let line = fileLines[i]; | ||
for (let i = 0; i < allLines.length; i++) { | ||
let line = allLines[i]; | ||
// Detect comment line | ||
@@ -257,5 +333,5 @@ if (line.trimStart().startsWith("//")) { | ||
let nextLineIndent = null; | ||
while (fileLines[i + j] && nextLineIndent == null) { | ||
if (!/^\s*$/.test(fileLines[i + j]) && !fileLines[i + j].trimStart().startsWith("//")) { | ||
nextLineIndent = fileLines[i + j].search(/\S/); // find first non blank character | ||
while (allLines[i + j] && nextLineIndent == null) { | ||
if (!/^\s*$/.test(allLines[i + j]) && !allLines[i + j].trimStart().startsWith("//")) { | ||
nextLineIndent = allLines[i + j].search(/\S/); // find first non blank character | ||
} | ||
@@ -282,6 +358,6 @@ j++; | ||
type: "function", | ||
func: fileLines => { | ||
func: allLines => { | ||
const newFileLines = []; | ||
for (let i = 0; i < fileLines.length; i++) { | ||
let line = fileLines[i] + ""; | ||
for (let i = 0; i < allLines.length; i++) { | ||
let line = allLines[i] + ""; | ||
// Detect closing brace line | ||
@@ -293,4 +369,4 @@ if (line.trim() === "}") { | ||
let level = 1; | ||
while ((fileLines[i - j] || fileLines[i - j] === "") && matchingLineIndent == null) { | ||
const prevLine = fileLines[i - j]; | ||
while ((allLines[i - j] || allLines[i - j] === "") && matchingLineIndent == null) { | ||
const prevLine = allLines[i - j]; | ||
if (prevLine.includes("}") && !prevLine.includes("${")) { | ||
@@ -319,2 +395,46 @@ level++; | ||
// No use of Java.io classes | ||
JavaIoPackageAccess: { | ||
variables: [ | ||
{ | ||
name: "CLASSNAME", | ||
regex: /The use of java.io.(.*) violates the Enterprise Java Bean specification/, | ||
regexPos: 1 | ||
} | ||
], | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem, evaluatedVars) => { | ||
return getVariableRange(errLine, evaluatedVars, "CLASSNAME", errItem); | ||
} | ||
} | ||
}, | ||
// Too many methods in a class | ||
MethodCount: { | ||
variables: [ | ||
{ | ||
name: "CLASSNAME", | ||
regex: /Class (.*) has 52 methods/, | ||
regexPos: 1 | ||
} | ||
], | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem, evaluatedVars) => { | ||
return getVariableRange(errLine, evaluatedVars, "CLASSNAME", errItem); | ||
} | ||
} | ||
}, | ||
// No use of Java.util.date | ||
NoJavaUtilDate: { | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "Date", errItem); | ||
} | ||
} | ||
}, | ||
// No tab character | ||
@@ -326,6 +446,6 @@ NoTabCharacter: { | ||
type: "function", | ||
func: fileLines => { | ||
func: allLines => { | ||
const newFileLines = []; | ||
const replaceChars = " ".repeat(indentLength); | ||
for (const line of fileLines) { | ||
for (const line of allLines) { | ||
newFileLines.push(line.replace(/\t/g, replaceChars)); | ||
@@ -340,2 +460,8 @@ } | ||
SpaceAfterCatch: { | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "){", errItem); | ||
} | ||
}, | ||
priority: getPriority("SpaceAfterCatch"), | ||
@@ -352,2 +478,8 @@ fix: { | ||
priority: getPriority("SpaceAfterOpeningBrace"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "{", errItem); | ||
} | ||
}, | ||
fix: { | ||
@@ -374,2 +506,8 @@ type: "function", | ||
], | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem, evaluatedVars) => { | ||
return getVariableRange(errLine, evaluatedVars, "OPERATOR", errItem); | ||
} | ||
}, | ||
fix: { | ||
@@ -391,2 +529,8 @@ type: "function", | ||
priority: getPriority("SpaceAfterComma"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, ",", errItem); | ||
} | ||
}, | ||
fix: { | ||
@@ -403,2 +547,8 @@ type: "function", | ||
priority: getPriority("SpaceBeforeOpeningBrace"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "{", errItem); | ||
} | ||
}, | ||
fix: { | ||
@@ -416,5 +566,25 @@ type: "function", | ||
// System.exit forbidden | ||
SystemExit: { | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem, evaluatedVars) => { | ||
return getVariableRange(errLine, evaluatedVars, "System.exit", errItem); | ||
} | ||
} | ||
}, | ||
// Trailing Whitespaces | ||
TrailingWhitespace: { | ||
priority: getPriority("TrailingWhitespace"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
const diff = errLine.length - errLine.trimEnd().length; | ||
return { | ||
start: { line: errItem.line, character: errLine.length - diff }, | ||
end: { line: errItem.line, character: errLine.length } | ||
}; | ||
} | ||
}, | ||
fix: { | ||
@@ -431,2 +601,8 @@ type: "function", | ||
priority: getPriority("UnnecessaryDefInFieldDeclaration"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getStringRange(errLine, "def", errItem); | ||
} | ||
}, | ||
fix: { | ||
@@ -448,2 +624,8 @@ type: "replaceString", | ||
], | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem, evaluatedVars) => { | ||
return getVariableRange(errLine, evaluatedVars, "STRING", errItem); | ||
} | ||
}, | ||
fix: { | ||
@@ -459,6 +641,15 @@ type: "replaceString", | ||
priority: getPriority("UnnecessarySemicolon"), | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem) => { | ||
return getLastStringRange(errLine, ";", errItem); | ||
} | ||
}, | ||
fix: { | ||
type: "function", | ||
func: line => { | ||
return line.split(";").join(""); | ||
if ((line.match(/;/g) || []).length === 1) { | ||
line = line.split(";").join(""); | ||
} | ||
return line; | ||
} | ||
@@ -468,5 +659,21 @@ } | ||
// Unused method parameter | ||
UnusedMethodParameter: { | ||
variables: [ | ||
{ | ||
name: "PARAMNAME", | ||
regex: /Violation in class (.*) Method parameter \[(.*)\] is never referenced in the method (.*) of class (.*)/, | ||
regexPos: 2 | ||
} | ||
], | ||
range: { | ||
type: "function", | ||
func: (errLine, errItem, evaluatedVars) => { | ||
return getVariableRange(errLine, evaluatedVars, "PARAMNAME", errItem); | ||
} | ||
} | ||
}, | ||
// Unused variable | ||
UnusedVariable: { | ||
priority: getPriority("UnusedVariable"), | ||
variables: [ | ||
@@ -481,8 +688,3 @@ { | ||
func: (errLine, errItem, evaluatedVars) => { | ||
const varName = getVariable(evaluatedVars, "VARNAME"); | ||
const varStartPos = errLine.indexOf(varName); | ||
return { | ||
start: { line: errItem.line, character: varStartPos }, | ||
end: { line: errItem.line, character: varStartPos + varName.length } | ||
}; | ||
return getVariableRange(errLine, evaluatedVars, "VARNAME", errItem); | ||
} | ||
@@ -508,2 +710,44 @@ } | ||
function getStringRange(errLine, str, errItem) { | ||
const varStartPos = errLine.indexOf(str); | ||
return { | ||
start: { line: errItem.line, character: varStartPos }, | ||
end: { line: errItem.line, character: varStartPos + str.length } | ||
}; | ||
} | ||
function getLastStringRange(errLine, str, errItem) { | ||
const varStartPos = errLine.lastIndexOf(str); | ||
return { | ||
start: { line: errItem.line, character: varStartPos }, | ||
end: { line: errItem.line, character: varStartPos + str.length } | ||
}; | ||
} | ||
function getVariableRange(errLine, evaluatedVars, variable, errItem) { | ||
const varValue = getVariable(evaluatedVars, variable); | ||
return getStringRange(errLine, varValue, errItem); | ||
} | ||
function findRangeBetweenStrings(allLines, errItem, strStart, strEnd) { | ||
let range = { | ||
start: { line: errItem.line, character: 0 }, | ||
end: { line: errItem.line, character: allLines[errItem.line - 1].length } | ||
}; | ||
let pos = errItem.line - 1; | ||
let isStartFound = false; | ||
let isEndFound = false; | ||
while ((isStartFound === false || isEndFound === false) && pos < allLines.length) { | ||
if (!isStartFound && allLines[pos].indexOf(strStart) > -1) { | ||
range.start = { line: pos, character: allLines[pos].indexOf(strStart) }; | ||
} | ||
if (!isEndFound && allLines[pos].indexOf(strEnd) > -1) { | ||
range.end = { line: pos, character: allLines[pos].indexOf(strEnd) }; | ||
isEndFound = true; | ||
} | ||
pos++; | ||
} | ||
return range; | ||
} | ||
function isValidCodeLine(line) { | ||
@@ -510,0 +754,0 @@ return line.trim() !== "" && line.trim().split("//")[0] !== ""; |
{ | ||
"name": "npm-groovy-lint", | ||
"version": "2.2.0-beta.8", | ||
"version": "2.2.0-beta.9", | ||
"description": "NPM CodeNarc wrapper to easily lint Groovy files", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
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
15919537
1782