diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..805a8d52e --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +generate/templates/templates/**/* +test/repos/**/* +dist/**/* diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..8866c3b61 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,47 @@ +{ + "env": { + "node": true, + "es6": true + }, + "root": true, + "rules": { + "no-cond-assign": [ + "error", + "except-parens" + ], + "curly": [ + "error" + ], + "no-eq-null": [ + "error" + ], + "no-eval": [ + "error" + ], + "wrap-iife": [ + "error" + ], + "max-len": [ + "error", + { + "code": 80, + "ignoreComments": true + } + ], + "no-proto": [ + "error" + ], + "quotes": [ + "error" + ], + "no-undef": [ + "error" + ], + "no-unused-vars": [ + "error" + ], + "no-invalid-this": [ + "error" + ] + } +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 0fd02f29b..000000000 --- a/.jshintrc +++ /dev/null @@ -1,31 +0,0 @@ -{ - "boss": true, - "curly": true, - "eqnull": true, - "esnext": true, - "evil": true, - "futurehostile": true, - "globals": { - "after": true, - "afterEach": true, - "before": true, - "beforeEach": true, - "define": true, - "describe": true, - "global": true, - "it": true - }, - "immed": false, - "maxlen": 80, - "node": true, - "predef": [ - "Promise", - "Set" - ], - "proto": true, - "quotmark": "double", - "trailing": true, - "undef": true, - "unused": "vars", - "validthis": true -} diff --git a/.npmignore b/.npmignore index 046bdf56a..dc0bc3ed8 100644 --- a/.npmignore +++ b/.npmignore @@ -15,6 +15,7 @@ .gitignore .gitmodules .jshintrc +.eslintrc* .travis.yml appveyor.yml diff --git a/generate/templates/templates/nodegit.js b/generate/templates/templates/nodegit.js index 246f5b26b..c52c46d98 100644 --- a/generate/templates/templates/nodegit.js +++ b/generate/templates/templates/nodegit.js @@ -1,3 +1,5 @@ +/* eslint no-unused-vars: "off" */ + var promisify = require("promisify-node"); var rawApi; @@ -17,7 +19,6 @@ catch (ex) { // Native methods do not return an identifiable function, so we // have to override them here -/* jshint ignore:start */ {% each . as idef %} {% if idef.type != "enum" %} @@ -34,14 +35,18 @@ catch (ex) { var _{{ idef.jsClassName }}_{{ fn.jsFunctionName}} = _{{ idef.jsClassName }}.prototype.{{ fn.jsFunctionName }}; _{{ idef.jsClassName }}.prototype.{{ fn.jsFunctionName }} - = promisify(_{{ idef.jsClassName }}_{{ fn.jsFunctionName}}); + = promisify( + _{{ idef.jsClassName }}_{{ fn.jsFunctionName}} + ); {% else %} var _{{ idef.jsClassName }}_{{ fn.jsFunctionName}} = _{{ idef.jsClassName }}.{{ fn.jsFunctionName }}; _{{ idef.jsClassName }}.{{ fn.jsFunctionName }} - = promisify(_{{ idef.jsClassName }}_{{ fn.jsFunctionName}}); + = promisify( + _{{ idef.jsClassName }}_{{ fn.jsFunctionName}} + ); {% endif %} @@ -66,10 +71,9 @@ _FilterRegistry.register = promisify(_FilterRegistry_register); var _FilterRegistry_unregister = _FilterRegistry.unregister; _FilterRegistry.unregister = promisify(_FilterRegistry_unregister); -/* jshint ignore:end */ // Set the exports prototype to the raw API. -exports.__proto__ = rawApi; +Object.setPrototypeOf(exports, rawApi) var importExtension = function(name) { try { @@ -101,15 +105,16 @@ importExtension("filter_registry"); importExtension("{{ filename }}"); {% endif %} {% endeach %} -/* jshint ignore:start */ {% each . as idef %} {% if idef.type != "enum" %} {% each idef.functions as fn %} {% if fn.useAsOnRootProto %} // Inherit directly from the original {{idef.jsClassName}} object. - _{{ idef.jsClassName }}.{{ fn.jsFunctionName }}.__proto__ = - _{{ idef.jsClassName }}; + Object.setPrototypeOf( + _{{ idef.jsClassName }}.{{ fn.jsFunctionName }}, + _{{ idef.jsClassName }} + ); // Ensure we're using the correct prototype. _{{ idef.jsClassName }}.{{ fn.jsFunctionName }}.prototype = @@ -123,7 +128,6 @@ importExtension("filter_registry"); {% endeach %} {% endif %} {% endeach %} -/* jshint ignore:end */ // Wrap asynchronous methods to return promises. promisify(exports); diff --git a/lifecycleScripts/lint.js b/lifecycleScripts/lint.js new file mode 100644 index 000000000..7b378cfc6 --- /dev/null +++ b/lifecycleScripts/lint.js @@ -0,0 +1,159 @@ +const eslint = require("gulp-eslint"); +const reporter = require("gulp-reporter"); +const { + Repository, + Reference, + Diff, + Blob, + Merge, +} = require("../"); +const through = require("through2"); +const Vinyl = require("vinyl"); +const debug = require("gulp-debug"); + +function getRepository(){ + return Repository.open(process.env.GIT_DIR || ".git"); +} + +function cached() { + return getRepository().then(repository => ( + Promise.all([ + Reference.nameToId(repository, "HEAD").then(head => ( + repository.getCommit(head) + )).then(commit => ( + commit.getTree() + )), + repository.index(), + ]).then(([old_tree, index]) => ( + Diff.treeToIndex(repository, old_tree, index).then(diff => ( + diff.patches() + )).then(arrayConvenientPatch => ( + arrayConvenientPatch.map(convenientPatch => ({ + repository: repository, + index: index, + convenientPatch: convenientPatch, + indexEntry: index.getByPath(convenientPatch.newFile().path()), + })) + )) + )) + )); +} + +function diff() { + return getRepository().then(repository => ( + Promise.all([ + repository.getMasterCommit().catch(()=>( + repository.getBranchCommit("origin/master") + )), + repository.getHeadCommit(), + repository.index(), + ]).then(([masterCommit, headCommit, index]) => ( + Merge.base(repository, masterCommit, headCommit).then(oid => ( + repository.getCommit(oid) + )).then(commit => ( + commit.getTree() + )).then(old_tree => ( + Diff.treeToWorkdirWithIndex(repository, old_tree) + )).then(diff => ( + diff.patches() + )).then(arrayConvenientPatch => ( + arrayConvenientPatch.filter(convenientPatch => ( + !convenientPatch.isDeleted() + )).map(convenientPatch => ({ + repository: repository, + index: index, + convenientPatch: convenientPatch, + indexEntry: index.getByPath(convenientPatch.newFile().path()), + })) + )) + )) + )); +} + +function readBlob(matcher) { + return through.obj(function (file, enc, callback) { + const { + repository, + indexEntry, + } = file.git; + Blob.lookup(repository, indexEntry.id).then(function(blob) { + if(!blob.isBinary() && matcher.test(file.path)) { + file.git.content = blob.content(); + file.contents = file.git.content; + callback(null, file); + } else { + callback(); + } + }).catch(callback); + }); +} + +function updateIndex() { + let index; + return through.obj(function (file, enc, callback) { + if(file.contents.equals(file.git.content)) { + callback(null, file); + return; + } else { + const { + repository, + indexEntry, + } = file.git; + + index = file.git.index; + + indexEntry.id = Blob.createFromBuffer( + repository, + file.contents, + Buffer.byteLength(file.contents) + ); + + index.add(indexEntry).then(() => ( + callback(null, file) + )).catch(callback); + } + }, function (cb) { + // flush function + if(index) { + index.write().then(cb); + } else { + cb(); + } + }); +} + +function toStream(fn) { + var stream = through.obj(); + fn().then(files => { + files.forEach(file => { + stream.push(new Vinyl({ + base: process.cwd(), + cwd: __dirname, + path: file.indexEntry.path, + git: file, + })); + }); + stream.end(); + }).catch(ex => { + stream.emit("error", ex); + }); + return stream; +} + +toStream(process.env.GIT_AUTHOR_DATE ? cached : diff) + .pipe(readBlob(/(jsx?|es\d*)$/)) + .pipe(debug({ + title: "eslint" + })) + .pipe(eslint({ + fix: process.argv.indexOf("--fix") > 0, + })) + .pipe(updateIndex()) + .pipe(reporter({ + // fail only for new error. + expires: new Date("2017-8-2"), + author: null, + })).resume().on("error", (e) => { + console.error(String(e)); + process.exitCode = -1; + }); diff --git a/package.json b/package.json index 9d02e56ad..82feeafe5 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,17 @@ "clean-for-publish": "~1.0.2", "combyne": "~0.8.1", "coveralls": "~2.11.4", + "eslint": "^4.4.1", + "gulp-debug": "^3.1.0", + "gulp-eslint": "^4.0.0", + "gulp-reporter": "^2.2.1", + "husky": "^0.14.3", "istanbul": "~0.3.20", "js-beautify": "~1.5.10", - "jshint": "~2.8.0", "lcov-result-merger": "~1.0.2", "mocha": "~2.3.4", + "through2": "^2.0.3", + "vinyl": "^2.1.0", "walk": "^2.3.9" }, "vendorDependencies": { @@ -75,7 +81,8 @@ "generateNativeCode": "node generate/scripts/generateNativeCode", "install": "node lifecycleScripts/preinstall && node lifecycleScripts/install", "installDebug": "BUILD_DEBUG=true npm install", - "lint": "jshint lib test/tests test/utils examples lifecycleScripts", + "lint": "node lifecycleScripts/lint", + "lint:fix": "npm run lint -- --fix", "mergecov": "lcov-result-merger 'test/**/*.info' 'test/coverage/merged.lcov' && ./lcov-1.10/bin/genhtml test/coverage/merged.lcov --output-directory test/coverage/report", "mocha": "mocha test/runner test/tests --timeout 15000", "mochaDebug": "mocha --debug-brk test/runner test/tests --timeout 15000", @@ -85,7 +92,9 @@ "rebuildDebug": "node generate && npm run babel && node-gyp configure --debug build", "recompile": "node-gyp configure build", "recompileDebug": "node-gyp configure --debug build", - "test": "npm run lint && node --expose-gc test", + "precommit": "npm run lint:fix", + "pretest": "npm run lint", + "test": "node --expose-gc test", "xcodeDebug": "node-gyp configure -- -f xcode" } } diff --git a/test/.eslintrc.json b/test/.eslintrc.json new file mode 100644 index 000000000..5075d5977 --- /dev/null +++ b/test/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "env": { + "mocha": true, + "node": true, + "es6": true + } +}