diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd0ab04 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: http://EditorConfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 + +[package.json] +indent_size = 2 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f008fe1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Logs +logs +*.log +npm-debug.log* + +# Dependency directories +node_modules +typings + +# Compiled output +*.js +*.d.ts \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ddf1027 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## 0.1.0 — 2016-05-27 + +- Initial public release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..82f2099 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Enable International Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2fcf6b4 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# gulp-tslint-vso-formatter + +A [`gulp-tslint`](https://github.com/panuhorsmalahti/gulp-tslint) reporter +for Visual Studio Online and Team Foundation Server. + +# Usage + + import gulp from "gulp"; + import tslint from "gulp-tslint"; + import vsoReporter from "gulp-tslint-vso-reporter"; + + gulp.task("typescript:lint", function() { + return gulp.src("greeter.ts") + .pipe(tslint()) + .pipe(tslint.report(vsoReporter)); + }); diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..f173514 --- /dev/null +++ b/index.ts @@ -0,0 +1,4 @@ +/*tslint:disable:no-default-export */ +import vsoReporter from "./lib/vsoReporter"; +export default vsoReporter; +/*tslint:enable:no-default-export */ diff --git a/lib/vsoReporter.ts b/lib/vsoReporter.ts new file mode 100644 index 0000000..c95506e --- /dev/null +++ b/lib/vsoReporter.ts @@ -0,0 +1,17 @@ +export default function vsoReporter(output: any, file?: any, options?: any): void { + const logType = options && !options.emitError ? "warning" : "error"; + + for (let i = 0, len = output.length; i < len; i++) { + const failure = output[i]; + + const failureString = failure.failure; + const fileName = failure.name; + const line = failure.startPosition.line + 1; + const character = failure.startPosition.character + 1; + const code = failure.ruleName; + + const properties = `sourcepath=${fileName};linenumber=${line};columnnumber=${character};code=${code};`; + + console.log(`##vso[task.logissue type=${logType};${properties}]${failureString}`); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cd00f08 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "gulp-tslint-vso-reporter", + "version": "0.1.0", + "description": "A gulp-tslint reporter for Visual Studio Online and Team Foundation Server", + "main": "index.js", + "repository": { + "type": "git", + "url": "git+https:github.com/EnableSoftware/gulp-tslint-vso-reporter.git" + }, + "bugs": { + "url": "https:github.com/EnableSoftware/gulp-tslint-vso-reporter/issues" + }, + "keywords": [ + "typescript", + "tslint", + "gulp-tslint", + "vso", + "reporter" + ], + "author": "Enable Software ", + "license": "MIT", + "devDependencies": { + "mocha": "^2.5.1", + "sinon": "^1.17.4", + "typescript": "^1.8.10" + }, + "peerDependencies": { + "gulp-tslint": "^5.0.0" + }, + "scripts": { + "postinstall": "typings install", + "prepublish": "tsc", + "test": "npm run prepublish && mocha", + "lint": "tslint lib/*.ts test/*.ts index.ts --exclude **/*.d.ts" + } +} diff --git a/test/vsoReporter.tests.ts b/test/vsoReporter.tests.ts new file mode 100644 index 0000000..0d0e41b --- /dev/null +++ b/test/vsoReporter.tests.ts @@ -0,0 +1,90 @@ +import reporter from "../lib/vsoReporter"; +import * as sinon from "sinon"; +import * as assert from "assert"; + +describe("VSO Reporter", function() { + let spy: sinon.SinonSpy; + + beforeEach(function() { + spy = sinon.stub(console, 'log'); + }); + + afterEach(function() { + spy.restore(); + }); + + it("handles no failures", function() { + reporter([]); + assert.equal(true, spy.notCalled); + }); + + it("formats failure", function() { + // Arrange + const failure = { + name: "test.ts", + failure: "rule failure message", + startPosition: + { + line: 0, + character: 0 + }, + ruleName: "rule-name" + }; + + const expectedResult = + "##vso[task.logissue type=error;sourcepath=test.ts;linenumber=1;columnnumber=1;code=rule-name;]rule failure message"; + + // Act + const actualResult = reporter([failure]); + + // Assert + assert.equal(true, spy.calledOnce); + assert.equal(true, spy.calledWith(expectedResult)); + }); + + it("formats multiple failures", function() { + // Arrange + const failures = [ + { name: "test.ts", failure: "first failure message", startPosition: { line: 0, character: 0 }, ruleName: "first-name" }, + { name: "test.ts", failure: "second failure message", startPosition: { line: 1, character: 16 }, ruleName: "second-name" }, + { name: "test.ts", failure: "last failure message", startPosition: { line: 9, character: 57 }, ruleName: "last-name" } + ]; + + const expectedResults = [ + "##vso[task.logissue type=error;sourcepath=test.ts;linenumber=1;columnnumber=1;code=first-name;]first failure message", + "##vso[task.logissue type=error;sourcepath=test.ts;linenumber=2;columnnumber=17;code=second-name;]second failure message", + "##vso[task.logissue type=error;sourcepath=test.ts;linenumber=10;columnnumber=58;code=last-name;]last failure message"]; + + // Act + const actualResult = reporter(failures); + + // Assert + assert.equal(true, spy.calledWith(expectedResults[0])); + assert.equal(true, spy.calledWith(expectedResults[1])); + assert.equal(true, spy.calledWith(expectedResults[2])); + }); + + it("emits warning if `options.emitError` is `false`", function() { + // Arrange + const failure = { + name: "test.ts", + failure: "rule failure message", + startPosition: + { + line: 0, + character: 0 + }, + ruleName: "rule-name" + }; + + const expectedResult = + "##vso[task.logissue type=warning;sourcepath=test.ts;linenumber=1;columnnumber=1;code=rule-name;]rule failure message"; + + // Act + const actualResult = reporter([failure], undefined, { emitError: false }); + + // Assert + assert.equal(true, spy.calledOnce); + assert.equal(true, spy.calledWith(expectedResult)); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..517af7f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "declaration": true, + "module": "commonjs", + "target": "es5", + "noImplicitAny": true, + "sourceMap": false + }, + "files": [ + "index.ts", + "test/vsoReporter.tests.ts", + "typings/index.d.ts" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..675f09d --- /dev/null +++ b/tslint.json @@ -0,0 +1,160 @@ +{ + "rules": { + "align": [ + true, + "parameters", + "arguments", + "statements" + ], + "ban": false, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "indent": [ + true, + "spaces" + ], + "interface-name": [ + false + ], + "jsdoc-format": true, + "label-position": true, + "label-undefined": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "public-before-private", + "static-before-instance", + "variables-before-functions" + ], + "new-parens": true, + "no-angle-bracket-type-assertion": true, + "no-any": false, + "no-arg": true, + "no-bitwise": true, + "no-conditional-assignment": true, + "no-consecutive-blank-lines": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-constructor-vars": false, + "no-debugger": true, + "no-default-export": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-empty": true, + "no-eval": true, + "no-inferrable-types": true, + "no-internal-module": false, + "no-invalid-this": [ + true, + "check-function-in-method" + ], + "no-null-keyword": true, + "no-reference": true, + "no-require-imports": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unreachable": true, + "no-unused-expression": true, + "no-unused-variable": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "no-var-requires": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-finally", + "check-whitespace" + ], + "one-variable-per-declaration": [true, + "ignore-for-loop" + ], + "quotemark": [ + true, + "double", + "avoid-escape" + ], + "radix": true, + "semicolon": [true, "always"], + "switch-default": true, + "trailing-comma": [ + true, + { + "multiline": "never", + "singleline": "never" + } + ], + "triple-equals": [ + true, + "allow-null-check", + "allow-undefined-check" + ], + "typedef": [ + false, + "call-signature", + "parameter", + "arrow-parameter", + "property-declaration", + "variable-declaration", + "member-variable-declaration" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + }, + { + "call-signature": "space", + "index-signature": "space", + "parameter": "space", + "property-declaration": "space", + "variable-declaration": "space" + } + ], + "use-isnan": true, + "use-strict": [ + false, + "check-module", + "check-function" + ], + "variable-name": [ + true, + "check-format", + "allow-leading-underscore", + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} diff --git a/typings.json b/typings.json new file mode 100644 index 0000000..b2cc4b5 --- /dev/null +++ b/typings.json @@ -0,0 +1,9 @@ +{ + "globalDependencies": { + "mocha": "registry:dt/mocha#2.2.5+20160317120654", + "node": "registry:dt/node#6.0.0+20160524002506" + }, + "dependencies": { + "sinon": "registry:npm/sinon#1.16.0+20160427193336" + } +}