diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..a0765e1 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..b77951a --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,19 @@ +--- +engines: + duplication: + config: + languages: + - javascript + enabled: true + eslint: + enabled: true + fixme: + enabled: true +exclude_paths: + - spec/**/** + - atom/**/** + - node_modules/**/** + - coverage/**/** +ratings: + paths: + - lib/** diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..96212a3 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +**/*{.,-}min.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..441747f --- /dev/null +++ b/.eslintrc @@ -0,0 +1,213 @@ +ecmaFeatures: + modules: true + jsx: true + +env: + amd: true + browser: true + es6: true + jquery: true + node: true + +# http://eslint.org/docs/rules/ +rules: + # Possible Errors + comma-dangle: [2, never] + no-cond-assign: 2 + no-console: 0 + no-constant-condition: 2 + no-control-regex: 2 + no-debugger: 2 + no-dupe-args: 2 + no-dupe-keys: 2 + no-duplicate-case: 2 + no-empty: 2 + no-empty-character-class: 2 + no-ex-assign: 2 + no-extra-boolean-cast: 2 + no-extra-parens: 0 + no-extra-semi: 2 + no-func-assign: 2 + no-inner-declarations: [2, functions] + no-invalid-regexp: 2 + no-irregular-whitespace: 2 + no-negated-in-lhs: 2 + no-obj-calls: 2 + no-regex-spaces: 2 + no-sparse-arrays: 2 + no-unexpected-multiline: 2 + no-unreachable: 2 + use-isnan: 2 + valid-jsdoc: 0 + valid-typeof: 2 + + # Best Practices + accessor-pairs: 2 + block-scoped-var: 0 + complexity: [2, 6] + consistent-return: 0 + curly: 0 + default-case: 0 + dot-location: 0 + dot-notation: 0 + eqeqeq: 2 + guard-for-in: 2 + no-alert: 2 + no-caller: 2 + no-case-declarations: 2 + no-div-regex: 2 + no-else-return: 0 + no-empty-label: 0 + no-empty-pattern: 2 + no-eq-null: 2 + no-eval: 2 + no-extend-native: 2 + no-extra-bind: 2 + no-fallthrough: 2 + no-floating-decimal: 0 + no-implicit-coercion: 0 + no-implied-eval: 2 + no-invalid-this: 0 + no-iterator: 2 + no-labels: 0 + no-lone-blocks: 2 + no-loop-func: 2 + no-magic-number: 0 + no-multi-spaces: 0 + no-multi-str: 0 + no-native-reassign: 2 + no-new-func: 2 + no-new-wrappers: 2 + no-new: 2 + no-octal-escape: 2 + no-octal: 2 + no-proto: 2 + no-redeclare: 2 + no-return-assign: 2 + no-script-url: 2 + no-self-compare: 2 + no-sequences: 0 + no-throw-literal: 0 + no-unused-expressions: 2 + no-useless-call: 2 + no-useless-concat: 2 + no-void: 2 + no-warning-comments: 0 + no-with: 2 + radix: 2 + vars-on-top: 0 + wrap-iife: 2 + yoda: 0 + + # Strict + strict: 0 + + # Variables + init-declarations: 0 + no-catch-shadow: 2 + no-delete-var: 2 + no-label-var: 2 + no-shadow-restricted-names: 2 + no-shadow: 0 + no-undef-init: 2 + no-undef: 0 + no-undefined: 0 + no-unused-vars: 0 + no-use-before-define: 0 + + # Node.js and CommonJS + callback-return: 0 + global-require: 0 + handle-callback-err: 2 + no-mixed-requires: 0 + no-new-require: 0 + no-path-concat: 2 + no-process-exit: 2 + no-restricted-modules: 0 + no-sync: 0 + + # Stylistic Issues + array-bracket-spacing: 0 + block-spacing: 0 + brace-style: 0 + camelcase: 0 + comma-spacing: 0 + comma-style: 0 + computed-property-spacing: 0 + consistent-this: 0 + eol-last: 0 + func-names: 0 + func-style: 0 + id-length: 0 + id-match: 0 + indent: 0 + jsx-quotes: 0 + key-spacing: 0 + linebreak-style: 0 + lines-around-comment: 0 + max-depth: 0 + max-len: 0 + max-nested-callbacks: 0 + max-params: 0 + max-statements: [2, 30] + new-cap: 0 + new-parens: 0 + newline-after-var: 0 + no-array-constructor: 0 + no-bitwise: 0 + no-continue: 0 + no-inline-comments: 0 + no-lonely-if: 0 + no-mixed-spaces-and-tabs: 0 + no-multiple-empty-lines: 0 + no-negated-condition: 0 + no-nested-ternary: 0 + no-new-object: 0 + no-plusplus: 0 + no-restricted-syntax: 0 + no-spaced-func: 0 + no-ternary: 0 + no-trailing-spaces: 0 + no-underscore-dangle: 0 + no-unneeded-ternary: 0 + object-curly-spacing: 0 + one-var: 0 + operator-assignment: 0 + operator-linebreak: 0 + padded-blocks: 0 + quote-props: 0 + quotes: 0 + require-jsdoc: 0 + semi-spacing: 0 + semi: 0 + sort-vars: 0 + space-after-keywords: 0 + space-before-blocks: 0 + space-before-function-paren: 0 + space-before-keywords: 0 + space-in-parens: 0 + space-infix-ops: 0 + space-return-throw-case: 0 + space-unary-ops: 0 + spaced-comment: 0 + wrap-regex: 0 + + # ECMAScript 6 + arrow-body-style: 0 + arrow-parens: 0 + arrow-spacing: 0 + constructor-super: 0 + generator-star-spacing: 0 + no-arrow-condition: 0 + no-class-assign: 0 + no-const-assign: 0 + no-dupe-class-members: 0 + no-this-before-super: 0 + no-var: 0 + object-shorthand: 0 + prefer-arrow-callback: 0 + prefer-const: 0 + prefer-reflect: 0 + prefer-spread: 0 + prefer-template: 0 + require-yield: 0 diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..b088eff --- /dev/null +++ b/.jshintrc @@ -0,0 +1,97 @@ +{ + // JSHint Default Configuration File (as on JSHint website) + // See http://jshint.com/docs/ for more details + + "maxerr": 50, // {int} Maximum error before stopping + + // Enforcing + "bitwise": true, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase": false, // true: Identifiers must be in camelCase + "curly": true, // true: Require {} for every new block or scope + "eqeqeq": true, // true: Require triple equals (===) for comparison + "forin": true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "freeze": true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. + "immed": false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "indent": 4, // {int} Number of spaces to use for indentation + "latedef": false, // true: Require variables/functions to be defined before being used + "newcap": false, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg": true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty": true, // true: Prohibit use of empty blocks + "nonbsp": true, // true: Prohibit "non-breaking whitespace" characters. + "nonew": false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus": false, // true: Prohibit use of `++` & `--` + "quotmark": false, // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef": true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused": true, // Unused variables: + // true : all variables, last function parameter + // "vars" : all variables only + // "strict": true, // all variables, all function parameters + "strict": false, // true: Requires all functions run in ES5 Strict Mode + "maxparams": false, // {int} Max number of formal params allowed per function + "maxdepth": false, // {int} Max depth of nested blocks (within functions) + "maxstatements": false, // {int} Max number statements per function + "maxcomplexity": false, // {int} Max cyclomatic complexity per function + "maxlen": false, // {int} Max number of characters per line + "varstmt": false, // true: Disallow any var statements. Only `let` and `const` are allowed. + + // Relaxing + "asi": false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss": false, // true: Tolerate assignments where comparisons would be expected + "debug": false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull": false, // true: Tolerate use of `== null` + "es5": false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext": false, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz": false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil": false, // true: Tolerate use of `eval` and `new Function()` + "expr": false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope": false, // true: Tolerate defining variables inside control statements + "globalstrict": false, // true: Allow global "use strict" (also enables 'strict') + "iterator": false, // true: Tolerate using the `__iterator__` property + "lastsemic": false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak": false, // true: Tolerate possibly unsafe line breakings + "laxcomma": false, // true: Tolerate comma-first style coding + "loopfunc": false, // true: Tolerate functions being defined in loops + "multistr": false, // true: Tolerate multi-line strings + "noyield": false, // true: Tolerate generator functions with no yield statement in them. + "notypeof": false, // true: Tolerate invalid typeof operator values + "proto": false, // true: Tolerate using the `__proto__` property + "scripturl": false, // true: Tolerate script-targeted URLs + "shadow": false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub": false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew": false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis": false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser": true, // Web Browser (window, document, etc) + "browserify": false, // Browserify (node.js code in the browser) + "couch": false, // CouchDB + "devel": true, // Development/debugging (alert, confirm, etc) + "dojo": false, // Dojo Toolkit + "jasmine": true, // Jasmine + "jquery": true, // jQuery + "mocha": true, // Mocha + "mootools": false, // MooTools + "node": true, // Node.js + "nonstandard": false, // Widely adopted globals (escape, unescape, etc) + "phantom": false, // PhantomJS + "prototypejs": false, // Prototype and Scriptaculous + "qunit": false, // QUnit + "rhino": false, // Rhino + "shelljs": false, // ShellJS + "typed": false, // Globals for typed array constructions + "worker": false, // Web Workers + "wsh": false, // Windows Scripting Host + "yui": false, // Yahoo User Interface + + "esversion": 6, + + // Custom Globals + "globals": { + "atom": true + } // additional predefined global variables +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ca99254 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: node_js + +node_js: + - "6.1" + +before_install: + - npm install -g codeclimate-test-reporter + - npm install -g codacy-coverage + +env: + - COVERAGE=false + - COVERAGE=true + +notifications: + email: + on_success: never + on_failure: change + +script: + - './test.sh' diff --git a/README.md b/README.md index d5106be..1eb4ab5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ # node-maven-api -Run maven commands via a node js api + +[![Build Status](https://api.travis-ci.org/concon121/node-maven-api.png)](https://api.travis-ci.org/concon121/node-maven-api) +[![Dependency Status](https://david-dm.org/concon121/node-maven-api.svg)](https://david-dm.org/concon121/node-maven-api) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9b9b60c42152461a9ec4e29d84848b01)](https://www.codacy.com/app/connor-bray/node-maven-api?utm_source=github.com&utm_medium=referral&utm_content=concon121/node-maven-api&utm_campaign=Badge_Grade) +[![Code Climate](https://codeclimate.com/github/concon121/node-maven-api/badges/gpa.svg)](https://codeclimate.com/github/concon121/node-maven-api) +[![Issue Count](https://codeclimate.com/github/concon121/node-maven-api/badges/issue_count.svg)](https://codeclimate.com/github/concon121/node-maven-api) +[![Test Coverage](https://codeclimate.com/github/concon121/node-maven-api/badges/coverage.svg)](https://codeclimate.com/github/concon121/node-maven-api/coverage) +[![Downloads](https://img.shields.io/npm/dt/node-maven-api.svg?maxAge=2592000)](https://www.npmjs.com/package/node-maven-api) + +Run maven commands via a Node JS API! + +## Usage + +To create the maven handler, you need to call the create method providing the path to the POM file to be handled. + +``` +var mvn = require('node-maven-api').create('/workspace/project/pom.xml'); + +mvn.clean(); +mvn.install(); +``` + +### Clean + +Invoke a maven clean on your project. + +``` +mvn.clean(); +``` + +### Install + +Invoke a maven install on your project. + +``` +mvn.install(); +``` + +### Test + +Invoke a maven test on your project. + +``` +mvn.test(); +``` + +### Effective pom + +Get the effective pom for your project. A promise will be returned which will be resolved to a javascript object which represents the xml effective pom. + +``` +var promise = mvn.effectivePom(); + +promise.then((result) => { + console.log('Effective POM is: ', result); +}); +``` diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..ab54895 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,16 @@ +'use babel'; +'use strict'; + +const Maven = require('./maven'); + +module.exports = { + + create: function (pom) { + if (pom) { + return new Maven(pom); + } else { + console.error('No pom specified.'); + } + } + +}; diff --git a/lib/maven.js b/lib/maven.js new file mode 100644 index 0000000..29e5e26 --- /dev/null +++ b/lib/maven.js @@ -0,0 +1,66 @@ +'use babel'; +'use strict'; + +const process = require('child_process'); +const fs = require('fs'); +const xml2js = require('xml2js').Parser({ + explicitArray: false +}); + +class Maven { + constructor(pom) { + this.pom = pom; + } +} + +Maven.prototype.clean = function () { + var cmd = 'mvn clean -f ' + this.pom; + this.exec(cmd); +}; + +Maven.prototype.install = function () { + var cmd = 'mvn install -f ' + this.pom; + this.exec(cmd); +}; + +Maven.prototype.test = function () { + var cmd = 'mvn test -f ' + this.pom; + this.exec(cmd); +}; + +Maven.prototype.effectivePom = function (output) { + return new Promise(function (resolve, reject) { + if (output) { + var cmd = 'mvn help:effective-pom -f ' + this.pom + ' -Doutput=' + output; + this.execSync(cmd, () => { + xml2js.parseString(fs.readFileSync(output, 'UTF-8'), (err, result) => { + if (err) { + console.error(err); + reject(Error(err)); + } + resolve(result); + }); + }); + } else { + reject(Error('Please provide a file to write effective pom to.')); + } + }); +}; + +Maven.prototype.exec = function (cmd) { + process.exec(cmd, function (error, stdout, stderr) { + if (error) { + console.error(`exec error: ${error}`); + return; + } + console.debug(`stdout: ${stdout}`); + console.debug(`stderr: ${stderr}`); + }); +}; + +Maven.prototype.execSync = function (cmd, callback) { + process.execSync(cmd); + return callback(); +}; + +module.exports = Maven; diff --git a/package.json b/package.json new file mode 100644 index 0000000..9ea557a --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "node-maven-api", + "main": "./lib/index", + "version": "0.0.1", + "description": "Run maven commands via a node js api!", + "keywords": [ + "apache", + "maven", + "node" + ], + "repository": "https://github.com/concon121/node-maven-api", + "license": "MIT", + "dependencies": { + "jshint": "^2.9.4", + "underscore": "^1.8.3", + "xml2js": "^0.4.17" + }, + "devDependencies": { + "babel": "^6.5.2", + "babel-cli": "^6.9.0", + "babel-core": "^6.3.17", + "babel-preset-es2015": "^6.3.13", + "babel-register": "^6.9.0", + "codeclimate-test-reporter": "^0.3.1", + "happiness": "^6.0.7", + "istanbul": "^1.0.0-alpha.2", + "jasmine": "^2.5.2", + "jshint": "^2.9.2" + }, + "scripts": { + "test": "node ./node_modules/jasmine/bin/jasmine spec/*-spec.js", + "coverage": "node ./node_modules/istanbul/lib/cli.js cover -v --include-all-sources node_modules/jasmine/bin/jasmine.js -R spec/*-spec.js", + "quality": "node ./node_modules/jshint/bin/jshint lib/index.js lib/mvn.js" + }, + "standard": { + "globals": [ + "it", + "spyOn", + "describe", + "expect", + "beforeEach" + ] + } +} diff --git a/spec/index-spec.js b/spec/index-spec.js new file mode 100644 index 0000000..a8c720a --- /dev/null +++ b/spec/index-spec.js @@ -0,0 +1,23 @@ +'use strict'; +'use babel'; + +if (process.env.COVERAGE && process.env.COVERAGE.indexOf('true') >= 0) { + require('babel-register'); +} + +var index = require('../lib/index'); + +describe('When calling create without providing a pom path', function () { + it('should not return anything', function () { + var actual = index.create(); + expect(actual).toEqual(undefined); + }); +}); + +describe('When calling create with a pom path', function () { + it('should return an mvn object', function () { + var actual = index.create('made/up/pom/path'); + expect(actual).toBeDefined(); + expect(actual.pom).toBeDefined(); + }); +}); diff --git a/spec/maven-spec.js b/spec/maven-spec.js new file mode 100644 index 0000000..ef626dd --- /dev/null +++ b/spec/maven-spec.js @@ -0,0 +1,65 @@ +'use strict'; +'use babel'; + +if (process.env.COVERAGE && process.env.COVERAGE.indexOf('true') >= 0) { + require('babel-register'); +} + +var index = require('../lib/index'); + +describe('When calling clean, insall or test.', function () { + + var actual; + + beforeEach(function () { + actual = index.create('/workspace/made/up'); + spyOn(actual, 'exec').and.returnValue(undefined); + }); + + it('Maven clean is invoked.', function () { + expect(actual).toBeDefined(); + expect(actual.pom).toBeDefined(); + actual.clean(); + expect(actual.exec).toHaveBeenCalled(); + }); + + it('Maven install is invoked.', function () { + expect(actual).toBeDefined(); + expect(actual.pom).toBeDefined(); + actual.install(); + expect(actual.exec).toHaveBeenCalled(); + }); + +}); + +describe('When calling effectivePom', function () { + + var actual; + var expected = 123; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; + + beforeEach(function () { + actual = index.create('/workspace/made/up'); + spyOn(actual, 'execSync').and.returnValue(expected); + }); + + var failTest = function (error) { + expect(error).toBeDefined(); + }; + + it('A JSON effective pom is not returned when no output location is provided.', function (done) { + expect(actual).toBeDefined(); + expect(actual.pom).toBeDefined(); + var effectivePomPromise = actual.effectivePom(); + expect(actual.execSync).not.toHaveBeenCalled(); + expect(effectivePomPromise).toBeDefined(); + effectivePomPromise.then(function (result) { + expect(result).not.toBeDefined(); + done(); + }, function (err) { + expect(err).toBeDefined(); + done(); + }).catch(failTest); + }); + +}); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..388d745 --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,14 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*[sS]pec.js" + ], + "helpers": [ + "../../node_modules/jasmine-es6/lib/install.js", + "../../node_modules/babel-core/register.js", + "**/node_modules/jasmine-es6/lib/install.js", + "**/node_modules/babel-core/register.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..f28f929 --- /dev/null +++ b/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +npm install + +if [ $COVERAGE == true ] +then + npm run coverage + codeclimate-test-reporter < coverage/lcov.info + codacy-coverage < coverage/lcov.info +else + npm test + npm run quality +fi