diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dac44ae..9bfd1105 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [7.5.1] 2020-09-02 + +- Fix [(#96)](https://github.com/nvuillam/npm-groovy-lint/issues/96) --fix adds redundant space into `${VARIABLE}` (SpaceBeforeOpeningBrace fix rule error) +- Fix grails framework detection +- Fix Groovy parsing parsing when multiple files +- Add `.gvy` and `.nf` in default browsed files extensions + ## [7.5.0] 2020-09-02 - Add anonymous framework usage stats for Groovy core Team diff --git a/README.md b/README.md index fda398a7..aebad3bd 100644 --- a/README.md +++ b/README.md @@ -397,6 +397,13 @@ Please follow [Contribution instructions](https://github.com/nvuillam/npm-groovy ## RELEASE NOTES +### [7.5.1] 2020-09-02 + +- Fix [(#96)](https://github.com/nvuillam/npm-groovy-lint/issues/96) --fix adds redundant space into `${VARIABLE}` (SpaceBeforeOpeningBrace fix rule error) +- Fix grails framework detection +- Fix Groovy parsing parsing when multiple files +- Add `.gvy` and `.nf` in default browsed files extensions + ### [7.4.3] 2020-08-29 - Upgrade [java-caller](https://github.com/nvuillam/node-java-caller) to v2.2.0 diff --git a/groovy/src/main/com/nvuillam/CodeNarcServer.groovy b/groovy/src/main/com/nvuillam/CodeNarcServer.groovy index 6288e931..623e1bfa 100644 --- a/groovy/src/main/com/nvuillam/CodeNarcServer.groovy +++ b/groovy/src/main/com/nvuillam/CodeNarcServer.groovy @@ -218,12 +218,18 @@ class CodeNarcServer { } else if (bodyObj.codeNarcBaseDir) { // Ant style pattern is used: list all files - println 'Ant file scanner in ' + bodyObj.codeNarcBaseDir + ', includes ' + bodyObj.codeNarcIncludes + ', excludes ' + bodyObj.codeNarcExcludes + println 'Ant file scanner in ' + bodyObj.codeNarcBaseDir + ', includes ' + bodyObj.codeNarcIncludes + ', excludes ' + ((bodyObj.codeNarcExcludes) ? bodyObj.codeNarcExcludes : 'no') def ant = new groovy.ant.AntBuilder() def scanner = ant.fileScanner { fileset(dir: bodyObj.codeNarcBaseDir) { - include(name: bodyObj.codeNarcIncludes) - exclude(name : (bodyObj.codeNarcExcludes) ? bodyObj.codeNarcExcludes : 'no') + bodyObj.codeNarcIncludes.split(',').each { includeExpr -> + include(name: includeExpr) + } + if (bodyObj.codeNarcExcludes) { + bodyObj.codeNarcIncludes.split(',').each { excludeExpr -> + exclude(name: excludeExpr) + } + } } } // Parse collected files @@ -257,17 +263,17 @@ class CodeNarcServer { catch (MultipleCompilationErrorsException ep) { def excptnJsonTxt = JsonOutput.toJson(ep) parseErrors = ep.getErrorCollector().getErrors() - println 'Parse error (MultipleCompilationErrorsException): ' + file.getAbsolutePath() + '\n' + excptnJsonTxt + println 'PARSE ERROR (MultipleCompilationErrorsException): ' + file.getAbsolutePath() + '\n' + excptnJsonTxt } catch (CompilationFailedException ep) { def excptnJsonTxt = JsonOutput.toJson(ep) parseErrors = ep.getErrorCollector().getErrors() - println 'Parse error (CompilationFailedException): ' + file.getAbsolutePath() + '\n' + excptnJsonTxt + println 'PARSE ERROR (CompilationFailedException): ' + file.getAbsolutePath() + '\n' + excptnJsonTxt } catch (Exception ep) { def excptnJsonTxt = JsonOutput.toJson(ep) parseErrors = ep.getErrorCollector().getErrors() - println 'Parse error (Other): ' + file.getAbsolutePath() + '\n' + excptnJsonTxt + println 'PARSE ERROR (Other): ' + file.getAbsolutePath() + '\n' + excptnJsonTxt } return parseErrors } diff --git a/lib/analytics.js b/lib/analytics.js index 24d4a7da..6dbeb159 100644 --- a/lib/analytics.js +++ b/lib/analytics.js @@ -203,7 +203,7 @@ async function getFileStats(file) { const frameworkDefs = [ { name: "beakerx", priority: 3 }, // UNDEFINED - { name: "codenarc", priority: 3, sourceIncludes: ["codenarc", "groovy-lint"] }, + { name: "codenarc", priority: 3, sourceIncludes: ["codenarc", "groovylint"] }, { name: "dru", priority: 2, packages: ["com.agorapulse.dru"] }, { name: "ersatz", priority: 2, packages: ["com.stehno.ersatz"] }, { name: "gaelyk", priority: 2, packages: ["groovyx.gaelyk"] }, @@ -212,7 +212,7 @@ const frameworkDefs = [ { name: "geb", priority: 3, packages: ["geb"] }, { name: "gperfutils", priority: 3, packages: ["groovyx.gbench", "groovyx.gprof"] }, { name: "gradle", priority: 1, fileExtensions: [".gradle"] }, - { name: "grails", priority: 1 }, // UNDEFINED + { name: "grails", priority: 1, filePathIncludes: ["grails-app"] }, { name: "grain", priority: 2, packages: ["com.sysgears"] }, { name: "griffon", priority: 2, sourceIncludes: ["griffon"] }, { name: "groocss", priority: 2, packages: ["org.groocss"] }, @@ -242,8 +242,10 @@ const frameworkDefs = [ function listFileUsedFrameworks(file, source) { const frameworksUsed = []; + const fileBaseName = path.basename(file); + const fileExtname = path.extname(file); for (const frameworkDef of frameworkDefs) { - if (isUsedFramework(frameworkDef, file, source)) { + if (isUsedFramework(frameworkDef, file, source, fileExtname, fileBaseName)) { frameworksUsed.push(frameworkDef); } } @@ -254,11 +256,11 @@ function listFileUsedFrameworks(file, source) { return frameworkKeys; } -function isUsedFramework(frameworkDef, file, source) { +function isUsedFramework(frameworkDef, file, source, fileExtname, fileBaseName) { // Check file extension if (frameworkDef.fileExtensions) { for (const ext of frameworkDef.fileExtensions) { - if (path.extname(file) === ext) { + if (fileExtname === ext) { return true; } } @@ -282,11 +284,21 @@ function isUsedFramework(frameworkDef, file, source) { // Check file name if (frameworkDef.fileNameIncludes) { for (const str of frameworkDef.fileNameIncludes) { - if (path.basename(file).includes(str)) { + if (fileBaseName.includes(str)) { return true; } } } + + // Check file path + if (frameworkDef.filePathIncludes) { + for (const str of frameworkDef.filePathIncludes) { + if (file.includes(str)) { + return true; + } + } + } + return false; } diff --git a/lib/codenarc-factory.js b/lib/codenarc-factory.js index 4c2dd57f..32a983b2 100644 --- a/lib/codenarc-factory.js +++ b/lib/codenarc-factory.js @@ -63,7 +63,7 @@ async function prepareCodeNarcCall(options) { } // Build ruleSet & file CodeNarc arguments - let defaultFilesPattern = "**/*.groovy,**/Jenkinsfile,**/*.gradle"; + let defaultFilesPattern = "**/*.groovy,**/*.gvy,**/Jenkinsfile,**/*.gradle,**/*.nf"; // RuleSet codeNarc arg const rulesetFileArgs = options.rulesets.startsWith("file:") ? options.rulesets : `file:${options.rulesets.replace(/^"(.*)"$/, "$1")}`; diff --git a/lib/example/SampleFileSmall.groovy b/lib/example/SampleFileSmall.groovy index 0cb753f0..6c2a3b68 100644 --- a/lib/example/SampleFileSmall.groovy +++ b/lib/example/SampleFileSmall.groovy @@ -1,7 +1,7 @@ import groovy.io.FileType import groovy.json.* import groovy.time.TimeCategory -import static groovyx.gpars.GParsPool.withPool +import static groovyx2.gpars.GParsPool.withPool def script = new GroovyScriptEngine( '.' ).with{ loadScriptByName( 'Utils.groovy' ) ; diff --git a/lib/example/grails-app/dummy.groovy b/lib/example/grails-app/dummy.groovy new file mode 100644 index 00000000..aa00e583 --- /dev/null +++ b/lib/example/grails-app/dummy.groovy @@ -0,0 +1,2 @@ +/* groovylint-disable-next-line CompileStatic */ +println 'Toto' diff --git a/lib/java/CodeNarcServer.jar b/lib/java/CodeNarcServer.jar index 66cd8409..0779fada 100644 Binary files a/lib/java/CodeNarcServer.jar and b/lib/java/CodeNarcServer.jar differ diff --git a/lib/rules/SpaceBeforeOpeningBrace.js b/lib/rules/SpaceBeforeOpeningBrace.js index eee2c8b4..cb7349f5 100644 --- a/lib/rules/SpaceBeforeOpeningBrace.js +++ b/lib/rules/SpaceBeforeOpeningBrace.js @@ -12,7 +12,7 @@ const rule = { label: "Add space before opening brace", type: "function", func: line => { - const regexMatch = line.match(new RegExp(/[^ ]{/, "g")); + const regexMatch = line.match(new RegExp(/[^ |$]{/, "g")); if (regexMatch && regexMatch[0]) { line = line.replace(regexMatch[0], regexMatch[0][0] + " {"); } @@ -28,6 +28,36 @@ class MyOtherClass extends AbstractClass{ } sourceAfter: ` class MyClass { } class MyOtherClass extends AbstractClass { } +` + }, + { + sourceBefore: ` +pipeline { + stages { + stage('CleanWorkspace') { + steps { + cleanWs() + dir("../\${JOB_NAME}@2"){ + deleteDir() + } + } + } + } +} +`, + sourceAfter: ` +pipeline { + stages { + stage('CleanWorkspace') { + steps { + cleanWs() + dir("../\${JOB_NAME}@2") { + deleteDir() + } + } + } + } +} ` } ] diff --git a/lib/test/lint-api.test.js b/lib/test/lint-api.test.js index 0545e9ee..a3703081 100644 --- a/lib/test/lint-api.test.js +++ b/lib/test/lint-api.test.js @@ -128,6 +128,7 @@ describe("Lint with API", () => { }); it("(API:files) should ignore fake_node_modules pattern", async () => { + const lintedFilesNb = 10; const npmGroovyLintConfig = { files: "**/*.groovy", ignorepattern: "**/fake_node_modules/**", @@ -138,8 +139,8 @@ describe("Lint with API", () => { const linter = await new NpmGroovyLint(npmGroovyLintConfig, {}).run(); assert(!linter.outputString.includes(`ToIgnore.groovy`), "ToIgnore.groovy has been ignored"); assert( - linter.outputString.includes(`npm-groovy-lint results in ${c.bold(9)} linted files`), - "Number of linted files is displayed in summary" + linter.outputString.includes(`npm-groovy-lint results in ${c.bold(lintedFilesNb)} linted files`), + `Number of linted files is displayed in summary: ${c.bold(lintedFilesNb)}` ); }); diff --git a/lib/test/lint-build.test.js b/lib/test/lint-build.test.js index e647fd65..520fed4f 100644 --- a/lib/test/lint-build.test.js +++ b/lib/test/lint-build.test.js @@ -39,7 +39,7 @@ describe("Lint with executable", () => { } assert(stdout, "stdout is set"); assert(!stdout.includes(`ToIgnore.groovy`), `ToIgnore.groovy has been ignored \n${stdout}`); - assert(stdout.includes(`npm-groovy-lint results in ${c.bold(9)} linted files`), `Number of linted files is displayed in summary \n${stdout}`); + assert(stdout.includes(`npm-groovy-lint results in ${c.bold(10)} linted files`), `Number of linted files is displayed in summary \n${stdout}`); }); it("(EXE:file) should generate codenarc HTML file report", async () => { diff --git a/lib/test/miscellaneous.test.js b/lib/test/miscellaneous.test.js index 5fe65263..4490382f 100644 --- a/lib/test/miscellaneous.test.js +++ b/lib/test/miscellaneous.test.js @@ -158,6 +158,17 @@ describe("Miscellaneous", function() { assert(linter.startElapse != null, "Anonymous stats has not been sent"); }); + it("(API:source) send anonymous usage statistics (2)", async () => { + const npmGroovyLintConfig = { + path: "./lib/example/grails-app", + returnrules: true, + output: "txt" + }; + const linter = await new NpmGroovyLint(npmGroovyLintConfig, {}).run(); + assert(linter.status === 0, `Linter status is 0 (${linter.status} returned)`); + assert(linter.startElapse != null, "Anonymous stats has not been sent"); + }); + it("(API:source) should use a CodeNarc ruleset defined in groovylintrc.json", async () => { const npmGroovyLintConfig = { config: "./lib/example/.groovylintrc-codenarc-rulesets.json",