From 922ccf0ce7597ce419fe208e67078b4ed7a0d047 Mon Sep 17 00:00:00 2001 From: Paul O'Neill Date: Sun, 5 Feb 2017 13:52:48 -0800 Subject: [PATCH] Tidy up parameter naming, add samples (#102) * Updating parameter names, deprecating old names * Fix double-logging of tslint exec * Fixing relative path mapping from tslint output * Adding a basic sample covering a tslint run and coverage import * Adding sample on re-using existing tslint output * Versioning for 1.0.0 RC 1 --- pom.xml | 2 +- samples/basic-setup/.gitignore | 6 ++ samples/basic-setup/README.md | 40 +++++++ samples/basic-setup/coverage/lcov.info | 55 ++++++++++ samples/basic-setup/karma.conf.js | 25 +++++ samples/basic-setup/package.json | 25 +++++ samples/basic-setup/sonar-project.properties | 12 +++ samples/basic-setup/src/MyApp.ts | 30 ++++++ samples/basic-setup/test/MyAppTests.ts | 12 +++ samples/basic-setup/tsconfig.json | 16 +++ samples/basic-setup/tslint.json | 102 ++++++++++++++++++ .../using-existing-tslint-output/.gitignore | 6 ++ .../using-existing-tslint-output/README.md | 35 ++++++ .../coverage/lcov.info | 55 ++++++++++ .../using-existing-tslint-output/issues.json | 1 + .../karma.conf.js | 25 +++++ .../using-existing-tslint-output/package.json | 25 +++++ .../sonar-project.properties | 13 +++ .../using-existing-tslint-output/src/MyApp.ts | 30 ++++++ .../test/MyAppTests.ts | 12 +++ .../tsconfig.json | 16 +++ .../using-existing-tslint-output/tslint.json | 102 ++++++++++++++++++ .../pablissimo/sonar/TsLintExecutorImpl.java | 4 - .../com/pablissimo/sonar/TsLintSensor.java | 22 ++-- .../pablissimo/sonar/TypeScriptPlugin.java | 91 ++++++++++------ .../pablissimo/sonar/TsLintSensorTest.java | 25 +++++ 26 files changed, 743 insertions(+), 44 deletions(-) create mode 100644 samples/basic-setup/.gitignore create mode 100644 samples/basic-setup/README.md create mode 100644 samples/basic-setup/coverage/lcov.info create mode 100644 samples/basic-setup/karma.conf.js create mode 100644 samples/basic-setup/package.json create mode 100644 samples/basic-setup/sonar-project.properties create mode 100644 samples/basic-setup/src/MyApp.ts create mode 100644 samples/basic-setup/test/MyAppTests.ts create mode 100644 samples/basic-setup/tsconfig.json create mode 100644 samples/basic-setup/tslint.json create mode 100644 samples/using-existing-tslint-output/.gitignore create mode 100644 samples/using-existing-tslint-output/README.md create mode 100644 samples/using-existing-tslint-output/coverage/lcov.info create mode 100644 samples/using-existing-tslint-output/issues.json create mode 100644 samples/using-existing-tslint-output/karma.conf.js create mode 100644 samples/using-existing-tslint-output/package.json create mode 100644 samples/using-existing-tslint-output/sonar-project.properties create mode 100644 samples/using-existing-tslint-output/src/MyApp.ts create mode 100644 samples/using-existing-tslint-output/test/MyAppTests.ts create mode 100644 samples/using-existing-tslint-output/tsconfig.json create mode 100644 samples/using-existing-tslint-output/tslint.json diff --git a/pom.xml b/pom.xml index e5d92a4..57286ab 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.pablissimo.sonar sonar-typescript-plugin sonar-plugin - 0.99.1-SNAPSHOT + 1.0.0-RC1 TypeScript Analyse TypeScript projects diff --git a/samples/basic-setup/.gitignore b/samples/basic-setup/.gitignore new file mode 100644 index 0000000..28b9360 --- /dev/null +++ b/samples/basic-setup/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +tmp/ +src/*.js +test/*.js +*.map +html/ \ No newline at end of file diff --git a/samples/basic-setup/README.md b/samples/basic-setup/README.md new file mode 100644 index 0000000..af66a2e --- /dev/null +++ b/samples/basic-setup/README.md @@ -0,0 +1,40 @@ +#Basic setup example +This sample project can be analysed by SonarQube to demonstrate: + +* Linting information from ```tslint```, configured via a ```tslint.json``` file +* Code coverage metrics from an LCOV file + +You can see a live example of the results of analysing this project at [https://sonar.pablissimo.com](https://sonar.pablissimo.com/dashboard?id=com.pablissimo.sonar%3Abasic-setup). + +##Building and analysing + +* Run ```npm install``` from the cloned repo folder +* Run ```npm test``` to regenerate code coverage information + * You don't *need* to perform this step, a coverage file is already checked into the repository + +To analyse with SonarQube just run ```sonar-scanner -X``` from the cloned repo folder. + +* The -X flag will give us diagnostic information during the run, so you can see what the plugin is up to + +##Breaking down the sonar-project.properties file + +The sample has a ```sonar-project.properties``` file that controls how the analysis gets run. We haven't specified anything here that isn't its default or automatically detected - these are explained under the table. + + + + + + + + + + + + + +
LineDescription
sonar.projectKey=com.pablissimo.sonar:basic-setupUnique string that identifies this project to SonarQube, differentiating it from any other project that's being analysed by the same server
sonar.projectName=Basic project setup exampleName of the project, which will show in the SonarQube admin interface
sonar.projectVersion=1.0Version of the project
sonar.sources=./srcThe path to the source code for your project - essentially describing the files to be analysed. Specified relative to the sonar-project.properties file.
sonar.sourceEncoding=UTF-8The encoding of your source files - typically UTF-8 is fine, unless you know you're using a different encoding
sonar.exclusions=node_modules/**The files that should be excluded from analysis - here, we don't want to include anything in the node_modules folder as it's full of TypeScript and JavaScript that's got nothing to do with us and would muddy the analysis
sonar.tests=./testThe path to any test code you have for your project, which will be excluded when calculating code coverage metrics - but these files are still analysed by the plugin for linting issues
sonar.ts.coverage.lcovReportPath=coverage/lcov.infoThe path to an LCOV-format file describing code coverage for your project. Typically generated by tools like Karma, Istanbul (with Istanbul-remap), Chutzpah etc
+ +Several settings are automatically detected (and you'll see these listed in the LCOV output): + +* The path to ```tslint``` is automatically detected as within the node_modules folder +* The location to the ```tslint.json``` file that configures tslint is automatically assumed to be the same folder as the ```sonar-project.properties``` file diff --git a/samples/basic-setup/coverage/lcov.info b/samples/basic-setup/coverage/lcov.info new file mode 100644 index 0000000..e93d841 --- /dev/null +++ b/samples/basic-setup/coverage/lcov.info @@ -0,0 +1,55 @@ +TN: +SF:C:\Projects\SonarTsPlugin\samples\basic-setup\src\MyApp.ts +FN:1,(anonymous_3) +FN:7,(anonymous_4) +FN:8,MyStringUtils +FN:11,(anonymous_6) +FN:24,(anonymous_7) +FN:25,(anonymous_9) +FNF:6 +FNH:5 +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +FNDA:1,MyStringUtils +FNDA:1,(anonymous_6) +FNDA:1,(anonymous_7) +FNDA:0,(anonymous_9) +DA:1,2 +DA:7,1 +DA:8,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:16,0 +DA:18,1 +DA:24,2 +DA:25,1 +DA:26,0 +DA:29,1 +LF:12 +LH:10 +BRDA:12,1,0,1 +BRDA:12,1,1,0 +BRDA:1,2,0,1 +BRDA:1,2,1,1 +BRF:4 +BRH:3 +end_of_record +TN: +SF:C:\Projects\SonarTsPlugin\samples\basic-setup\test\MyAppTests.ts +FN:3,(anonymous_3) +FN:4,(anonymous_4) +FNF:2 +FNH:2 +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +DA:1,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:10,1 +LF:5 +LH:5 +BRF:0 +BRH:0 +end_of_record diff --git a/samples/basic-setup/karma.conf.js b/samples/basic-setup/karma.conf.js new file mode 100644 index 0000000..0febe1c --- /dev/null +++ b/samples/basic-setup/karma.conf.js @@ -0,0 +1,25 @@ +module.exports = function (config) { + config.set({ + frameworks: ["jasmine", "karma-typescript"], + files: [ + { pattern: "src/**/*.ts" }, + { pattern: "test/**/*.ts" } + ], + preprocessors: { + "**/*.ts": ["karma-typescript"] + }, + reporters: ["progress", "karma-typescript"], + browsers: ["PhantomJS"], + + karmaTypescriptConfig: { + reports: { + "lcovonly": { + directory: "coverage", + subdirectory: ".", + filename: "lcov.info" + } + }, + tsconfig: "./tsconfig.json" + } + }); +}; \ No newline at end of file diff --git a/samples/basic-setup/package.json b/samples/basic-setup/package.json new file mode 100644 index 0000000..0813bd8 --- /dev/null +++ b/samples/basic-setup/package.json @@ -0,0 +1,25 @@ +{ + "name": "basic-app", + "version": "1.0.0", + + "scripts": { + "test": "karma start --single-run --code-coverage" + }, + + "dependencies": { + "@types/jasmine": "^2.5.35", + "jasmine-core": "^2.5.2", + "karma": "^1.4.1", + "karma-phantomjs-launcher": "^1.0.2", + "karma-cli": "^1.0.1", + "karma-jasmine": "^1.1.0", + "karma-typescript": "^2.1.7", + "typescript": "latest", + "tslint": "^4.4.2" + }, + + "devDependencies": { + "gulp": "^3.9.1", + "karma-typescript": "^2.1.7" + } +} diff --git a/samples/basic-setup/sonar-project.properties b/samples/basic-setup/sonar-project.properties new file mode 100644 index 0000000..a2e5a12 --- /dev/null +++ b/samples/basic-setup/sonar-project.properties @@ -0,0 +1,12 @@ +sonar.projectKey=com.pablissimo.sonar:basic-setup +sonar.projectName=Basic project setup example +sonar.projectVersion=1.0 + +sonar.sources=./src +sonar.sourceEncoding=UTF-8 +sonar.exclusions=node_modules/** + +sonar.tests=./test + +# To import the LCOV report +sonar.ts.coverage.lcovReportPath=coverage/lcov.info \ No newline at end of file diff --git a/samples/basic-setup/src/MyApp.ts b/samples/basic-setup/src/MyApp.ts new file mode 100644 index 0000000..9f9525c --- /dev/null +++ b/samples/basic-setup/src/MyApp.ts @@ -0,0 +1,30 @@ +export module MyApp { + /* + Contains a couple of methods that our test class will call, and + some awful code that will cause tslint to flag a couple of basic + issues + */ + export class MyStringUtils { + constructor(public dummy1: string, public dummy2: string) { + } + + public areStringsEqual(s1: string, s2: string) { + if (s1 == s2) { + return true; + } + else + return false; + } + } + + /* + Contains a method that none of the test code hits, and + that tslint will flag another couple of issues on + */ + export class MyUncoveredClass { + public justReturn42(param1:string,param2: number) { + return 42; + } + + } +} \ No newline at end of file diff --git a/samples/basic-setup/test/MyAppTests.ts b/samples/basic-setup/test/MyAppTests.ts new file mode 100644 index 0000000..ff5b343 --- /dev/null +++ b/samples/basic-setup/test/MyAppTests.ts @@ -0,0 +1,12 @@ +import { MyApp } from "../src/MyApp"; + +describe("areStringsEqual", () => { + it("should return true if two strings are the same", () => { + var result = + new MyApp + .MyStringUtils("whatever", "whoever") + .areStringsEqual("t", "t"); + + expect(result).toEqual(true); + }); +}); \ No newline at end of file diff --git a/samples/basic-setup/tsconfig.json b/samples/basic-setup/tsconfig.json new file mode 100644 index 0000000..6ae601a --- /dev/null +++ b/samples/basic-setup/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": true, + "outDir": "tmp", + "sourceMap": true, + "target": "ES5", + "types": [ + "jasmine" + ] + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/samples/basic-setup/tslint.json b/samples/basic-setup/tslint.json new file mode 100644 index 0000000..1645da0 --- /dev/null +++ b/samples/basic-setup/tslint.json @@ -0,0 +1,102 @@ +{ + "jsRules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "indent": [ + true, + "spaces" + ], + "no-duplicate-variable": true, + "no-eval": true, + "no-trailing-whitespace": true, + "no-unsafe-finally": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "double" + ], + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + }, + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "indent": [ + true, + "spaces" + ], + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": true, + "no-unsafe-finally": true, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "double" + ], + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/samples/using-existing-tslint-output/.gitignore b/samples/using-existing-tslint-output/.gitignore new file mode 100644 index 0000000..28b9360 --- /dev/null +++ b/samples/using-existing-tslint-output/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +tmp/ +src/*.js +test/*.js +*.map +html/ \ No newline at end of file diff --git a/samples/using-existing-tslint-output/README.md b/samples/using-existing-tslint-output/README.md new file mode 100644 index 0000000..93b46ee --- /dev/null +++ b/samples/using-existing-tslint-output/README.md @@ -0,0 +1,35 @@ +#Using existing tslint output example +This sample project can be analysed by SonarQube to demonstrate re-using the output of a build-step ```tslint``` pass, instead of having the plugin perform the analysis itself. + +You can see a live example of the results of analysing this project at [https://sonar.pablissimo.com](https://sonar.pablissimo.com/dashboard?id=com.pablissimo.sonar%3Ausing-existing-tslint-output). + +This sample is identical to the basic-setup sample but with reuse of tslint output. + +##Building and analysing + +If you want, you can just analyse this project directly as the tslint output has already been generated. However, to rebuild fully: + +* Run ```npm install``` from the cloned repo folder +* Run ```npm test``` to run unit tests and ```tslint``` analysis, building the ```issues.json``` output file. + +To analyse with SonarQube just run ```sonar-scanner -X``` from the cloned repo folder. + +* The -X flag will give us diagnostic information during the run, so you can see what the plugin is up to + +##Breaking down the sonar-project.properties file + +The sample has a ```sonar-project.properties``` file that controls how the analysis gets run. This file differs from the basic-setup example only in one interesting respect: + + + + + + +
LineDescription
sonar.ts.tslint.outputPath=issues.jsonTells the plugin to skip running tslint itself, and instead just parse the output of the previous tslint path found in the issues.json file
+ +See the basic-setup example for detail on the other configured settings. + +##Retionale +It's possible that your CI build already performs a ```tslint``` path, since a tslint failure might be considered important enough to break your build (or at least otherwise report on). Since a ```tslint``` path on a large project can take a while, the ```sonar.ts.tslint.outputPath``` setting can be set to reuse the output of the CI call to ```tslint```, reducing the time it takes to perform analysis. + +It also allows you to call ```tslint``` with parameters or configuration that the plugin may not easily handle, or otherwise filter or transform the ```tslint``` output before it gets consumed by the plugin. \ No newline at end of file diff --git a/samples/using-existing-tslint-output/coverage/lcov.info b/samples/using-existing-tslint-output/coverage/lcov.info new file mode 100644 index 0000000..8ef3c15 --- /dev/null +++ b/samples/using-existing-tslint-output/coverage/lcov.info @@ -0,0 +1,55 @@ +TN: +SF:C:\Projects\SonarTsPlugin\samples\using-existing-tslint-output\src\MyApp.ts +FN:1,(anonymous_3) +FN:7,(anonymous_4) +FN:8,MyStringUtils +FN:11,(anonymous_6) +FN:24,(anonymous_7) +FN:25,(anonymous_9) +FNF:6 +FNH:5 +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +FNDA:1,MyStringUtils +FNDA:1,(anonymous_6) +FNDA:1,(anonymous_7) +FNDA:0,(anonymous_9) +DA:1,2 +DA:7,1 +DA:8,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:16,0 +DA:18,1 +DA:24,2 +DA:25,1 +DA:26,0 +DA:29,1 +LF:12 +LH:10 +BRDA:12,1,0,1 +BRDA:12,1,1,0 +BRDA:1,2,0,1 +BRDA:1,2,1,1 +BRF:4 +BRH:3 +end_of_record +TN: +SF:C:\Projects\SonarTsPlugin\samples\using-existing-tslint-output\test\MyAppTests.ts +FN:3,(anonymous_3) +FN:4,(anonymous_4) +FNF:2 +FNH:2 +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +DA:1,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:10,1 +LF:5 +LH:5 +BRF:0 +BRH:0 +end_of_record diff --git a/samples/using-existing-tslint-output/issues.json b/samples/using-existing-tslint-output/issues.json new file mode 100644 index 0000000..d561357 --- /dev/null +++ b/samples/using-existing-tslint-output/issues.json @@ -0,0 +1 @@ +[{"endPosition":{"character":29,"line":15,"position":497},"failure":"else statements must be braced","name":"src/MyApp.ts","ruleName":"curly","startPosition":{"character":12,"line":14,"position":462}},{"endPosition":{"character":13,"line":0,"position":13},"failure":"The internal 'module' syntax is deprecated, use the 'namespace' keyword instead.","name":"src/MyApp.ts","ruleName":"no-internal-module","startPosition":{"character":7,"line":0,"position":7}},{"endPosition":{"character":21,"line":11,"position":397},"failure":"== should be ===","name":"src/MyApp.ts","ruleName":"triple-equals","startPosition":{"character":19,"line":11,"position":395}},{"endPosition":{"character":36,"line":24,"position":731},"failure":"missing whitespace","fix":{"innerRuleName":"whitespace","innerReplacements":[{"innerStart":730,"innerLength":0,"innerText":" "}]},"name":"src/MyApp.ts","ruleName":"whitespace","startPosition":{"character":35,"line":24,"position":730}},{"endPosition":{"character":43,"line":24,"position":738},"failure":"missing whitespace","fix":{"innerRuleName":"whitespace","innerReplacements":[{"innerStart":737,"innerLength":0,"innerText":" "}]},"name":"src/MyApp.ts","ruleName":"whitespace","startPosition":{"character":42,"line":24,"position":737}},{"endPosition":{"character":11,"line":4,"position":155},"failure":"Forbidden 'var' keyword, use 'let' or 'const' instead","fix":{"innerRuleName":"no-var-keyword","innerReplacements":[{"innerStart":152,"innerLength":3,"innerText":"let"}]},"name":"test/MyAppTests.ts","ruleName":"no-var-keyword","startPosition":{"character":8,"line":4,"position":152}}] \ No newline at end of file diff --git a/samples/using-existing-tslint-output/karma.conf.js b/samples/using-existing-tslint-output/karma.conf.js new file mode 100644 index 0000000..0febe1c --- /dev/null +++ b/samples/using-existing-tslint-output/karma.conf.js @@ -0,0 +1,25 @@ +module.exports = function (config) { + config.set({ + frameworks: ["jasmine", "karma-typescript"], + files: [ + { pattern: "src/**/*.ts" }, + { pattern: "test/**/*.ts" } + ], + preprocessors: { + "**/*.ts": ["karma-typescript"] + }, + reporters: ["progress", "karma-typescript"], + browsers: ["PhantomJS"], + + karmaTypescriptConfig: { + reports: { + "lcovonly": { + directory: "coverage", + subdirectory: ".", + filename: "lcov.info" + } + }, + tsconfig: "./tsconfig.json" + } + }); +}; \ No newline at end of file diff --git a/samples/using-existing-tslint-output/package.json b/samples/using-existing-tslint-output/package.json new file mode 100644 index 0000000..ef5d5fe --- /dev/null +++ b/samples/using-existing-tslint-output/package.json @@ -0,0 +1,25 @@ +{ + "name": "basic-app", + "version": "1.0.0", + + "scripts": { + "test": "karma start --single-run --code-coverage && tslint --force --format json --config tslint.json --out issues.json src/MyApp.ts test/MyAppTests.ts" + }, + + "dependencies": { + "@types/jasmine": "^2.5.35", + "jasmine-core": "^2.5.2", + "karma": "^1.4.1", + "karma-phantomjs-launcher": "^1.0.2", + "karma-cli": "^1.0.1", + "karma-jasmine": "^1.1.0", + "karma-typescript": "^2.1.7", + "typescript": "latest", + "tslint": "^4.4.2" + }, + + "devDependencies": { + "gulp": "^3.9.1", + "karma-typescript": "^2.1.7" + } +} diff --git a/samples/using-existing-tslint-output/sonar-project.properties b/samples/using-existing-tslint-output/sonar-project.properties new file mode 100644 index 0000000..7f3c4c7 --- /dev/null +++ b/samples/using-existing-tslint-output/sonar-project.properties @@ -0,0 +1,13 @@ +sonar.projectKey=com.pablissimo.sonar:using-existing-tslint-output +sonar.projectName=Simulating reusing the output of a build-step tslint pass +sonar.projectVersion=1.0 + +sonar.sources=./src +sonar.sourceEncoding=UTF-8 +sonar.exclusions=node_modules/** + +sonar.tests=./test + +sonar.ts.tslint.outputPath=issues.json +# To import the LCOV report +sonar.ts.coverage.lcovReportPath=coverage/lcov.info \ No newline at end of file diff --git a/samples/using-existing-tslint-output/src/MyApp.ts b/samples/using-existing-tslint-output/src/MyApp.ts new file mode 100644 index 0000000..9f9525c --- /dev/null +++ b/samples/using-existing-tslint-output/src/MyApp.ts @@ -0,0 +1,30 @@ +export module MyApp { + /* + Contains a couple of methods that our test class will call, and + some awful code that will cause tslint to flag a couple of basic + issues + */ + export class MyStringUtils { + constructor(public dummy1: string, public dummy2: string) { + } + + public areStringsEqual(s1: string, s2: string) { + if (s1 == s2) { + return true; + } + else + return false; + } + } + + /* + Contains a method that none of the test code hits, and + that tslint will flag another couple of issues on + */ + export class MyUncoveredClass { + public justReturn42(param1:string,param2: number) { + return 42; + } + + } +} \ No newline at end of file diff --git a/samples/using-existing-tslint-output/test/MyAppTests.ts b/samples/using-existing-tslint-output/test/MyAppTests.ts new file mode 100644 index 0000000..ff5b343 --- /dev/null +++ b/samples/using-existing-tslint-output/test/MyAppTests.ts @@ -0,0 +1,12 @@ +import { MyApp } from "../src/MyApp"; + +describe("areStringsEqual", () => { + it("should return true if two strings are the same", () => { + var result = + new MyApp + .MyStringUtils("whatever", "whoever") + .areStringsEqual("t", "t"); + + expect(result).toEqual(true); + }); +}); \ No newline at end of file diff --git a/samples/using-existing-tslint-output/tsconfig.json b/samples/using-existing-tslint-output/tsconfig.json new file mode 100644 index 0000000..6ae601a --- /dev/null +++ b/samples/using-existing-tslint-output/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": true, + "outDir": "tmp", + "sourceMap": true, + "target": "ES5", + "types": [ + "jasmine" + ] + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/samples/using-existing-tslint-output/tslint.json b/samples/using-existing-tslint-output/tslint.json new file mode 100644 index 0000000..1645da0 --- /dev/null +++ b/samples/using-existing-tslint-output/tslint.json @@ -0,0 +1,102 @@ +{ + "jsRules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "indent": [ + true, + "spaces" + ], + "no-duplicate-variable": true, + "no-eval": true, + "no-trailing-whitespace": true, + "no-unsafe-finally": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "double" + ], + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + }, + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "indent": [ + true, + "spaces" + ], + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": true, + "no-unsafe-finally": true, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "double" + ], + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/src/main/java/com/pablissimo/sonar/TsLintExecutorImpl.java b/src/main/java/com/pablissimo/sonar/TsLintExecutorImpl.java index 8834505..506a963 100644 --- a/src/main/java/com/pablissimo/sonar/TsLintExecutorImpl.java +++ b/src/main/java/com/pablissimo/sonar/TsLintExecutorImpl.java @@ -166,10 +166,6 @@ else if (files == null) { } private String getCommandOutput(Command thisCommand, StreamConsumer stdOutConsumer, StreamConsumer stdErrConsumer, File tslintOutputFile, Integer timeoutMs) { - LOG.debug("Executing TsLint with command: " + thisCommand.toCommandLine()); - - // Timeout is specified per file, not per batch (which can vary a lot) - // so multiply it up this.createExecutor().execute(thisCommand, stdOutConsumer, stdErrConsumer, timeoutMs); return getFileContent(tslintOutputFile); diff --git a/src/main/java/com/pablissimo/sonar/TsLintSensor.java b/src/main/java/com/pablissimo/sonar/TsLintSensor.java index b6cac4b..65eaeea 100644 --- a/src/main/java/com/pablissimo/sonar/TsLintSensor.java +++ b/src/main/java/com/pablissimo/sonar/TsLintSensor.java @@ -23,8 +23,7 @@ public class TsLintSensor implements Sensor { private TsLintExecutor executor; private TsLintParser parser; - public TsLintSensor(Settings settings, PathResolver resolver, - TsLintExecutor executor, TsLintParser parser) { + public TsLintSensor(Settings settings, PathResolver resolver, TsLintExecutor executor, TsLintParser parser) { this.settings = settings; this.resolver = resolver; this.executor = executor; @@ -65,7 +64,8 @@ else if (config.getConfigFile() == null && config.getPathToTsConfig() == null) { } List paths = new ArrayList(); - HashMap fileMap = new HashMap(); + HashMap absoluteFileMap = new HashMap(); + HashMap relativeFileMap = new HashMap(); for (InputFile file : ctx.fileSystem().inputFiles(ctx.fileSystem().predicates().hasLanguage(TypeScriptLanguage.LANGUAGE_KEY))) { if (skipTypeDefFiles && file.file().getName().toLowerCase().endsWith("." + TypeScriptLanguage.LANGUAGE_DEFINITION_EXTENSION)) { @@ -74,7 +74,8 @@ else if (config.getConfigFile() == null && config.getPathToTsConfig() == null) { String pathAdjusted = file.absolutePath().replace('\\', '/'); paths.add(pathAdjusted); - fileMap.put(pathAdjusted, file); + absoluteFileMap.put(pathAdjusted, file); + relativeFileMap.put(file.relativePath().replace('\\', '/'), file); } List jsonResults = this.executor.execute(config, paths); @@ -94,13 +95,16 @@ else if (config.getConfigFile() == null && config.getPathToTsConfig() == null) { continue; } - if (!fileMap.containsKey(filePath)) { + InputFile matchingFile = absoluteFileMap.get(filePath); + if (matchingFile == null) { + matchingFile = relativeFileMap.get(filePath); + } + + if (matchingFile == null) { LOG.warn("TsLint reported issues against a file that wasn't sent to it - will be ignored: " + filePath); continue; } - InputFile file = fileMap.get(filePath); - for (TsLintIssue issue : batchIssues) { // Make sure the rule we're violating is one we recognise - if not, we'll // fall back to the generic 'tslint-issue' rule @@ -117,9 +121,9 @@ else if (config.getConfigFile() == null && config.getPathToTsConfig() == null) { NewIssueLocation newIssueLocation = newIssue .newLocation() - .on(file) + .on(matchingFile) .message(issue.getFailure()) - .at(file.selectLine(issue.getStartPosition().getLine() + 1)); + .at(matchingFile.selectLine(issue.getStartPosition().getLine() + 1)); newIssue.at(newIssueLocation); newIssue.save(); diff --git a/src/main/java/com/pablissimo/sonar/TypeScriptPlugin.java b/src/main/java/com/pablissimo/sonar/TypeScriptPlugin.java index 92e2181..bf3bceb 100644 --- a/src/main/java/com/pablissimo/sonar/TypeScriptPlugin.java +++ b/src/main/java/com/pablissimo/sonar/TypeScriptPlugin.java @@ -5,35 +5,38 @@ @Properties({ @Property( key = TypeScriptPlugin.SETTING_TS_LINT_ENABLED, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_ENABLED_PREV, type = PropertyType.BOOLEAN, defaultValue = "true", - name = "Enable TSLint", - description = "Run TSLint on SonarQube analysis", + name = "Enable tslint", + description = "Specifies whether to run tslint on SonarQube analysis", project = true ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_PATH, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_PATH_PREV, defaultValue = "", - name = "Path to TSLint", - description = "Path to installed Node TSLint", + name = "Path to tslint", + description = "Path to installed copy of tslint to use to analyse the project", project = true, global = true ), @Property( key = TypeScriptPlugin.SETTING_TS_RULE_CONFIGS, + deprecatedKey = TypeScriptPlugin.SETTING_TS_RULE_CONFIGS_PREV, name = "TsLint Rule-Collections", - description = "A collection of configurations for mapping TsLint rules to Sonar rules", + description = "A collection of configurations for mapping tslint rules to Sonar rules", project = false, global = true, fields = { @PropertyField( key = "name", - name = "rule collection name", + name = "Rule collection name", type = PropertyType.STRING ), @PropertyField( key = "config", - name = "rule configs & parameters", + name = "Rule configuration (see documentation)", type = PropertyType.TEXT, indicativeSize = 120 ) @@ -41,6 +44,7 @@ ), @Property( key = TypeScriptPlugin.SETTING_EXCLUDE_TYPE_DEFINITION_FILES, + deprecatedKey = TypeScriptPlugin.SETTING_EXCLUDE_TYPE_DEFINITION_FILES_PREV, type = PropertyType.BOOLEAN, defaultValue = "true", name = "Exclude .d.ts files", @@ -50,23 +54,26 @@ ), @Property( key = TypeScriptPlugin.SETTING_LCOV_REPORT_PATH, + deprecatedKey = TypeScriptPlugin.SETTING_LCOV_REPORT_PATH_PREV, type = PropertyType.STRING, name = "LCOV report path", - description = "LCOV report path", + description = "Path to the LCOV report for code coverage, if one is available", project = true, global = false ), @Property( key = TypeScriptPlugin.SETTING_FORCE_ZERO_COVERAGE, + deprecatedKey = TypeScriptPlugin.SETTING_FORCE_ZERO_COVERAGE_PREV, defaultValue = "false", type = PropertyType.BOOLEAN, - name = "Force 0 coverage", - description = "Force coverage to be set to 0 when no report is provided.", + name = "Force zero coverage", + description = "Forces coverage of all source file to be set to 0% when no coverage report is provided", project = true, global = false ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_CONFIG_PATH, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_CONFIG_PATH_PREV, defaultValue = "tslint.json", type = PropertyType.STRING, name = "Path to tslint.json rule configuration file", @@ -76,65 +83,89 @@ ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_RULES_DIR, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_RULES_DIR_PREV, defaultValue = "", type = PropertyType.STRING, name = "Custom rules dir", - description = "Path to any custom rules directory to be supplied to TsLint", + description = "Path to any custom rules directory to be supplied to tslint", project = true, global = false ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_TIMEOUT, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_TIMEOUT_PREV, defaultValue = "60000", type = PropertyType.INTEGER, - name = "Max TsLint wait time (milliseconds)", - description = "Maximum time to wait for TsLint execution to finish before aborting (in milliseconds)", + name = "Max TsLint wait time (milliseconds) per analysed file", + description = "Maximum time to wait for tslint execution to finish before aborting (in milliseconds) per file sent to tslint", project = true, global = false ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_TYPECHECK, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_TYPECHECK_PREV, defaultValue = "false", type = PropertyType.BOOLEAN, name = "Forces tslint to run a type-check", - description = "Equivalent to --type-check tslint argument - requires tslintconfigpath also set", + description = "Equivalent to --type-check tslint argument - requires " + TypeScriptPlugin.SETTING_TS_LINT_PROJECT_PATH + " to also be set", project = true, global = false ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_PROJECT_PATH, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_PROJECT_PATH_PREV, defaultValue = "", type = PropertyType.STRING, name = "Path to tsconfig.json file, if required", - description = "Required if tslinttypecheck parameter specified, the path to the tsconfig.json file that describes the files to lint and build", + description = "Required if " + TypeScriptPlugin.SETTING_TS_LINT_TYPECHECK + " setting specified, the path to the tsconfig.json file that describes the files to lint and build", project = true, global = false ), @Property( key = TypeScriptPlugin.SETTING_TS_LINT_OUTPUT_PATH, + deprecatedKey = TypeScriptPlugin.SETTING_TS_LINT_OUTPUT_PATH_PREV, defaultValue = "", type = PropertyType.STRING, - name = "Path to TSLint JSON output file", - description = "If set, the contents of this file will be used to discover linting issues rather than the plugin running tslint itself", + name = "Path to tslint JSON output file", + description = "If set, the contents of this file will parsed for linting issues rather than the plugin running tslint itself", project = true, global = false ) }) public class TypeScriptPlugin implements Plugin { - public static final String SETTING_EXCLUDE_TYPE_DEFINITION_FILES = "sonar.ts.excludetypedefinitionfiles"; - public static final String SETTING_FORCE_ZERO_COVERAGE = "sonar.ts.forceZeroCoverage"; - public static final String SETTING_IGNORE_NOT_FOUND = "sonar.ts.ignoreNotFound"; - public static final String SETTING_TS_LINT_ENABLED = "sonar.ts.tslintenabled"; - public static final String SETTING_TS_LINT_PATH = "sonar.ts.tslintpath"; - public static final String SETTING_TS_LINT_CONFIG_PATH = "sonar.ts.tslintconfigpath"; - public static final String SETTING_TS_LINT_TIMEOUT = "sonar.ts.tslinttimeout"; - public static final String SETTING_TS_LINT_RULES_DIR = "sonar.ts.tslintrulesdir"; - public static final String SETTING_LCOV_REPORT_PATH = "sonar.ts.lcov.reportpath"; - public static final String SETTING_TS_RULE_CONFIGS = "sonar.ts.ruleconfigs"; - public static final String SETTING_TS_LINT_TYPECHECK = "sonar.ts.tslinttypecheck"; - public static final String SETTING_TS_LINT_PROJECT_PATH = "sonar.ts.tslintprojectpath"; - public static final String SETTING_TS_LINT_OUTPUT_PATH = "sonar.ts.tslintoutputpath"; + // Deprecated settings + public static final String SETTING_EXCLUDE_TYPE_DEFINITION_FILES_PREV = "sonar.ts.excludetypedefinitionfiles"; + public static final String SETTING_FORCE_ZERO_COVERAGE_PREV = "sonar.ts.forceZeroCoverage"; + public static final String SETTING_IGNORE_NOT_FOUND_PREV = "sonar.ts.ignoreNotFound"; + public static final String SETTING_TS_LINT_ENABLED_PREV = "sonar.ts.tslintenabled"; + public static final String SETTING_TS_LINT_PATH_PREV = "sonar.ts.tslintpath"; + public static final String SETTING_TS_LINT_CONFIG_PATH_PREV = "sonar.ts.tslintconfigpath"; + public static final String SETTING_TS_LINT_TIMEOUT_PREV = "sonar.ts.tslinttimeout"; + public static final String SETTING_TS_LINT_RULES_DIR_PREV = "sonar.ts.tslintrulesdir"; + public static final String SETTING_LCOV_REPORT_PATH_PREV = "sonar.ts.lcov.reportpath"; + public static final String SETTING_TS_RULE_CONFIGS_PREV = "sonar.ts.ruleconfigs"; + public static final String SETTING_TS_LINT_TYPECHECK_PREV = "sonar.ts.tslinttypecheck"; + public static final String SETTING_TS_LINT_PROJECT_PATH_PREV = "sonar.ts.tslintprojectpath"; + public static final String SETTING_TS_LINT_OUTPUT_PATH_PREV = "sonar.ts.tslintoutputpath"; + // Current settings + public static final String SETTING_EXCLUDE_TYPE_DEFINITION_FILES = "sonar.ts.excludeTypeDefinitionFiles"; + + public static final String SETTING_FORCE_ZERO_COVERAGE = "sonar.ts.coverage.forceZeroIfUnspecified"; + public static final String SETTING_IGNORE_NOT_FOUND = "sonar.ts.coverage.ignoreNotFound"; + public static final String SETTING_LCOV_REPORT_PATH = "sonar.ts.coverage.lcovReportPath"; + + public static final String SETTING_TS_LINT_ENABLED = "sonar.ts.tslint.enabled"; + public static final String SETTING_TS_LINT_PATH = "sonar.ts.tslint.path"; + public static final String SETTING_TS_LINT_CONFIG_PATH = "sonar.ts.tslint.configPath"; + public static final String SETTING_TS_LINT_TIMEOUT = "sonar.ts.tslint.timeout"; + public static final String SETTING_TS_LINT_RULES_DIR = "sonar.ts.tslint.rulesDir"; + public static final String SETTING_TS_RULE_CONFIGS = "sonar.ts.tslint.ruleConfigs"; + public static final String SETTING_TS_LINT_TYPECHECK = "sonar.ts.tslint.typeCheck"; + public static final String SETTING_TS_LINT_PROJECT_PATH = "sonar.ts.tslint.projectPath"; + public static final String SETTING_TS_LINT_OUTPUT_PATH = "sonar.ts.tslint.outputPath"; + + @Override public void define(Context ctx) { // Core components - the actual sensors doing the work or configuring diff --git a/src/test/java/com/pablissimo/sonar/TsLintSensorTest.java b/src/test/java/com/pablissimo/sonar/TsLintSensorTest.java index 4bc7919..da0cf30 100644 --- a/src/test/java/com/pablissimo/sonar/TsLintSensorTest.java +++ b/src/test/java/com/pablissimo/sonar/TsLintSensorTest.java @@ -134,6 +134,31 @@ public void execute_addsIssues() { assertEquals("rule name", this.context.allIssues().iterator().next().ruleKey().rule()); } + @Test + public void execute_addsIssues_evenIfReportedAgainstRelativePaths() { + TsLintIssue issue = new TsLintIssue(); + issue.setFailure("failure"); + issue.setRuleName("rule name"); + issue.setName(this.file.relativePath().replace("\\", "/")); + + TsLintPosition startPosition = new TsLintPosition(); + startPosition.setLine(0); + + issue.setStartPosition(startPosition); + + List issueList = new ArrayList(); + issueList.add(issue); + + Map> issues = new HashMap>(); + issues.put(issue.getName(), issueList); + + when(this.parser.parse(any(List.class))).thenReturn(issues); + this.sensor.execute(this.context); + + assertEquals(1, this.context.allIssues().size()); + assertEquals("rule name", this.context.allIssues().iterator().next().ruleKey().rule()); + } + @Test public void execute_doesNotThrow_ifParserReturnsNoResult() { when(this.parser.parse(any(List.class))).thenReturn(null);