lint-staged
Advanced tools
Comparing version 2.0.2 to 3.0.0
@@ -0,1 +1,7 @@ | ||
# 3.0.0 | ||
- Switched to listr. Simplified code and more beautiful output. | ||
- Switched to execa. Should fix #30 | ||
- Use ES2015. Dropped support for Node < 4.x | ||
# 2.0.2 | ||
@@ -2,0 +8,0 @@ |
{ | ||
"name": "lint-staged", | ||
"version": "2.0.2", | ||
"version": "3.0.0", | ||
"description": "Lint files staged by git", | ||
@@ -17,6 +17,11 @@ "main": "index.js", | ||
"release": "npmpub", | ||
"test": "mocha --compilers js:babel-core/register ./test/*.spec.js" | ||
"test": "mocha --compilers js:babel-core/register ./test/*.spec.js", | ||
"deps": "npm-check -s", | ||
"deps:update": "npm-check -u" | ||
}, | ||
"lint-staged": { | ||
"*.js": ["eslint", "git:add"] | ||
"*.js": [ | ||
"eslint", | ||
"git:add" | ||
] | ||
}, | ||
@@ -52,4 +57,6 @@ "pre-commit": [ | ||
"batchflow": "^0.4.0", | ||
"execa": "^0.4.0", | ||
"listr": "^0.4.3", | ||
"minimatch": "^3.0.0", | ||
"npm-which": "^2.0.0", | ||
"npm-which": "^3.0.1", | ||
"object-assign": "^4.1.0", | ||
@@ -68,4 +75,5 @@ "ora": "^0.2.3", | ||
"expect": "^1.20.2", | ||
"is-promise": "^2.1.0", | ||
"mocha": "^2.5.3", | ||
"mock-spawn": "^0.2.6", | ||
"npm-check": "^5.2.2", | ||
"npmpub": "^3.1.0", | ||
@@ -72,0 +80,0 @@ "pre-commit": "^1.1.3", |
@@ -1,6 +0,8 @@ | ||
var npmWhich = require('npm-which')(process.cwd()) | ||
'use strict' | ||
module.exports = function findBin (binName, paths, config, cb) { | ||
var binPath = 'npm' | ||
var args = ['run', '-s', binName, '--'].concat(paths) | ||
const npmWhich = require('npm-which')(process.cwd()) | ||
module.exports = function findBin (binName, paths, config) { | ||
const bin = 'npm' | ||
const args = ['run', '-s', binName, '--'].concat(paths) | ||
/* | ||
@@ -12,30 +14,27 @@ * If package.json has script with binName defined | ||
// Support for scripts from package.json | ||
cb.call(this, null, binPath, args) | ||
} else { | ||
/* | ||
* If binName wasn't found in package.json scripts | ||
* we'll try to locate the binary in node_modules/.bin | ||
* This is useful for shorter configs like: | ||
* | ||
* "lint-staged": { | ||
* "*.js": "eslint" | ||
* } | ||
* | ||
* without adding | ||
* | ||
* "scripts" { | ||
* "eslint": "eslint" | ||
* } | ||
*/ | ||
npmWhich(binName, function (err, bin) { | ||
if (err) { | ||
/* | ||
* If we could not locate a binary than the config is invald | ||
* and we should warn about it... | ||
*/ | ||
cb.call(this, err, null) | ||
} | ||
cb.call(this, null, bin, ['--'].concat(paths)) | ||
}) | ||
return { | ||
bin, | ||
args | ||
} | ||
} | ||
/* | ||
* If binName wasn't found in package.json scripts | ||
* we'll try to locate the binary in node_modules/.bin | ||
* This is useful for shorter configs like: | ||
* | ||
* "lint-staged": { | ||
* "*.js": "eslint" | ||
* } | ||
* | ||
* without adding | ||
* | ||
* "scripts" { | ||
* "eslint": "eslint" | ||
* } | ||
*/ | ||
return { | ||
bin: npmWhich.sync(binName), | ||
args: ['--'].concat(paths) | ||
} | ||
} |
@@ -1,15 +0,15 @@ | ||
var sgf = require('staged-git-files') | ||
var minimatch = require('minimatch') | ||
var ora = require('ora') | ||
var assign = require('object-assign') | ||
var appRoot = require('app-root-path') | ||
var config = require(appRoot.resolve('package.json')) | ||
var runScript = require('./runScript') | ||
var getLintersAsString = require('./getLintersAsString') | ||
'use strict' | ||
var defaultLinters = {} | ||
var customLinters = config['lint-staged'] | ||
var linters = assign(defaultLinters, customLinters) | ||
var spinner = ora('Starting lint-staged').start() | ||
const sgf = require('staged-git-files') | ||
const minimatch = require('minimatch') | ||
const assign = require('object-assign') | ||
const appRoot = require('app-root-path') | ||
const config = require(appRoot.resolve('package.json')) | ||
const runScript = require('./runScript') | ||
const Listr = require('listr') | ||
const defaultLinters = {} | ||
const customLinters = config['lint-staged'] | ||
const linters = assign(defaultLinters, customLinters) | ||
sgf('ACM', function (err, results) { | ||
@@ -19,33 +19,25 @@ if (err) { | ||
} | ||
var filePaths = results.map(function (file) { | ||
return file.filename | ||
}) | ||
if (filePaths.length) { | ||
Object.keys(linters).forEach(function (key) { | ||
var linter = linters[key] | ||
var fileList = filePaths.filter(minimatch.filter(key, { matchBase: true })) | ||
if (fileList.length) { | ||
spinner.text = 'Running ' + getLintersAsString(linter) + '...' | ||
runScript(linter, fileList, config, function (error, exitCode) { | ||
if (error) { | ||
console.error(error) | ||
} | ||
if (exitCode > 0) { | ||
console.log( | ||
'š± %s found some issues. Fix them and try again.', | ||
getLintersAsString(linter) | ||
) | ||
} | ||
spinner.stop() | ||
spinner.clear() | ||
}) | ||
} else { | ||
spinner.stop() | ||
const filePaths = results.map(file => file.filename) | ||
const tasks = Object.keys(linters).map(key => { | ||
const linter = linters[key] | ||
const fileList = filePaths.filter(minimatch.filter(key, { matchBase: true })) | ||
if (fileList.length) { | ||
return { | ||
title: `Running tasks for ${key}`, | ||
task: () => { | ||
return new Listr(runScript(linter, fileList, config)) | ||
} | ||
} | ||
} | ||
}).filter(task => typeof task !== 'undefined') // Filter undefined values | ||
if (tasks.length) { | ||
new Listr(tasks).run().catch(err => { | ||
console.error(err) | ||
process.exit(1) | ||
}) | ||
} else { | ||
spinner.stop() | ||
console.log('\nš Nothing to lint. `git add` some files and try again...\n') | ||
console.log('No staged files found. Nothing to lint!') | ||
} | ||
}) | ||
@@ -1,40 +0,34 @@ | ||
var findBin = require('./findBin') | ||
var spawn = require('child_process').spawn | ||
var batch = require('batchflow') | ||
'use strict' | ||
module.exports = function runScript (linters, paths, config, cb) { | ||
var lintersArray = Array.isArray(linters) ? linters : [linters] | ||
var exitCode = 0 | ||
batch(lintersArray) | ||
.series() | ||
.each(function (i, linter, next) { | ||
// If previous process finished with non-zero code | ||
// we'll stop executing the sequence | ||
if (exitCode > 0) { | ||
return next(exitCode) | ||
} | ||
const findBin = require('./findBin') | ||
const execa = require('execa') | ||
findBin(linter, paths, config, function (err, binPath, args) { | ||
if (err) { | ||
throw err | ||
module.exports = function runScript (linters, pathsToLint, config) { | ||
const lintersArray = Array.isArray(linters) ? linters : [linters] | ||
return lintersArray.map(linter => { | ||
return { | ||
title: linter, | ||
task: () => { | ||
try { | ||
const res = findBin(linter, pathsToLint, config) | ||
return new Promise((resolve, reject) => { | ||
const npmStream = execa.spawn(res.bin, res.args, { | ||
stdio: 'inherit' | ||
}) | ||
npmStream.on('exit', code => { | ||
if (code > 0) { | ||
reject(`${linter} found some errors. Please fix them and try committing again`) | ||
} else { | ||
resolve(`${linter} passed!`) | ||
} | ||
}) | ||
}) | ||
} catch (err) { | ||
throw new Error(`${linter} not found. Try 'npm install ${linter}'`) | ||
} | ||
var npmStream = spawn(binPath, args, { | ||
stdio: 'inherit' // <== IMPORTANT: use this option to inherit the parent's environment | ||
}) | ||
npmStream.on('exit', function (code) { | ||
exitCode = code | ||
}) | ||
npmStream.on('close', next) | ||
}) | ||
}) | ||
.error(function (err) { | ||
console.error(err) | ||
cb.call(this, err, null) | ||
}) | ||
.end(function () { | ||
process.exitCode = exitCode | ||
if (typeof cb === 'function') { | ||
cb.call(this, null, exitCode) | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
@@ -12,11 +12,13 @@ /* eslint no-unused-expressions: 0 */ | ||
} | ||
const npmWichMockGood = (path, cb) => { | ||
cb(null, path) | ||
const npmWichMockGood = { | ||
sync: path => path | ||
} | ||
const npmWichMockBad = (path, cb) => { | ||
cb(true, null) | ||
const npmWichMockBad = { | ||
sync: path => { | ||
throw new Error('not found: ' + path) | ||
} | ||
} | ||
describe('findBin', () => { | ||
it('should return npm run command if it exist in both package.json and .bin/', done => { | ||
it('should return npm run command if it exist in both package.json and .bin/', () => { | ||
const packageJSONMock = { | ||
@@ -29,18 +31,12 @@ scripts: { | ||
findBin.__set__('npmWhich', npmWichMockGood) | ||
findBin('eslint', 'test.js', packageJSONMock, (err, bin, args) => { | ||
expect(err).toBe(null) | ||
expect(bin).toEqual('npm') | ||
expect(args).toEqual(['run', '-s', 'eslint', '--', 'test.js']) | ||
done() | ||
}) | ||
const { bin, args } = findBin('eslint', 'test.js', packageJSONMock) | ||
expect(bin).toEqual('npm') | ||
expect(args).toEqual(['run', '-s', 'eslint', '--', 'test.js']) | ||
}) | ||
it('should return bin from node_modules/.bin if there is no command in package.json', done => { | ||
it('should return bin from node_modules/.bin if there is no command in package.json', () => { | ||
findBin.__set__('npmWhich', npmWichMockGood) | ||
findBin('eslint', 'test.js test2.js', packageJSON, (err, bin, args) => { | ||
expect(err).toBe(null) | ||
expect(bin).toEqual('eslint') | ||
expect(args).toEqual(['--', 'test.js test2.js']) | ||
done() | ||
}) | ||
const { bin, args } = findBin('eslint', 'test.js test2.js', packageJSON) | ||
expect(bin).toEqual('eslint') | ||
expect(args).toEqual(['--', 'test.js test2.js']) | ||
}) | ||
@@ -51,7 +47,5 @@ | ||
expect(() => { | ||
findBin('eslint', 'test.js', packageJSON, (err) => { | ||
throw new Error(err) | ||
}) | ||
}).toThrow() | ||
findBin('eslint', 'test.js', packageJSON) | ||
}).toThrow('not found: eslint') | ||
}) | ||
}) |
import expect from 'expect' | ||
import mockSpawn from 'mock-spawn' | ||
import isPromise from 'is-promise' | ||
import rewire from 'rewire' | ||
const runScript = rewire('../src/runScript') | ||
expect.extend({ | ||
toBeAPromise () { | ||
expect.assert( | ||
isPromise(this.actual), | ||
'expected %s to be a Promise', | ||
this.actual | ||
) | ||
return this | ||
} | ||
}) | ||
const packageJSON = { | ||
@@ -15,99 +26,47 @@ scripts: { | ||
describe('runScript', () => { | ||
it('should run the callback with the proper exit code', done => { | ||
const spy = expect.createSpy() | ||
const mySpawn = mockSpawn() | ||
mySpawn.setDefault(mySpawn.simple(0)) | ||
runScript.__set__('spawn', mySpawn) | ||
afterEach(function () { | ||
expect.restoreSpies() | ||
}) | ||
runScript('test', 'test.js', packageJSON, spy) | ||
setTimeout(() => { | ||
expect(mySpawn.calls.length).toEqual(1) | ||
expect(mySpawn.calls[0].exitCode).toEqual(0) | ||
expect(mySpawn.calls[0].command).toEqual('npm') | ||
expect(mySpawn.calls[0].args).toEqual(['run', '-s', 'test', '--', 'test.js']) | ||
expect(spy.calls.length).toEqual(1) | ||
expect(spy).toHaveBeenCalledWith(null, 0) | ||
done() | ||
}, 10) | ||
it('should return an array', () => { | ||
expect(runScript('test', 'test.js', packageJSON)).toBeA('array') | ||
}) | ||
it('should support array of scripts as a first argument', done => { | ||
const spy = expect.createSpy() | ||
const mySpawn = mockSpawn() | ||
mySpawn.sequence.add(mySpawn.simple(0)) | ||
mySpawn.sequence.add(mySpawn.simple(1)) | ||
runScript.__set__('spawn', mySpawn) | ||
runScript(['test', 'test2'], 'test.js', packageJSON, spy) | ||
setTimeout(() => { | ||
expect(mySpawn.calls.length).toEqual(2) | ||
expect(mySpawn.calls[0].exitCode).toEqual(0) | ||
expect(mySpawn.calls[0].command).toEqual('npm') | ||
expect(mySpawn.calls[0].args).toEqual(['run', '-s', 'test', '--', 'test.js']) | ||
expect(mySpawn.calls[1].exitCode).toEqual(1) | ||
expect(mySpawn.calls[1].command).toEqual('npm') | ||
expect(mySpawn.calls[1].args).toEqual(['run', '-s', 'test2', '--', 'test.js']) | ||
expect(spy.calls.length).toEqual(1) | ||
expect(spy).toHaveBeenCalledWith(null, 1) | ||
done() | ||
}, 10) | ||
it('should return not empty array', () => { | ||
const res = runScript('test', 'test.js', packageJSON) | ||
expect(res.length).toBe(1) | ||
expect(res[0].title).toBe('test') | ||
expect(res[0].task).toBeA('function') | ||
expect(res[0].task()).toBeAPromise() | ||
}) | ||
it('should stop the sequence execution if prev process finishes with non-zero', done => { | ||
const spy = expect.createSpy() | ||
const mySpawn = mockSpawn() | ||
mySpawn.sequence.add(mySpawn.simple(1)) | ||
mySpawn.sequence.add(mySpawn.simple(0)) | ||
runScript.__set__('spawn', mySpawn) | ||
runScript(['test', 'test2'], 'test.js', packageJSON, spy) | ||
setTimeout(() => { | ||
expect(mySpawn.calls.length).toEqual(1) | ||
expect(mySpawn.calls[0].exitCode).toEqual(1) | ||
expect(spy.calls.length).toEqual(1) | ||
expect(spy).toHaveBeenCalledWith(null, 1) | ||
done() | ||
}, 10) | ||
it('should return empty array for non-existend script', () => { | ||
expect(runScript('test3', 'test.js', packageJSON)[0].task).toThrow("test3 not found. Try 'npm install test3'") | ||
}) | ||
it('should handle errors for single commands', done => { | ||
const err = new Error('linting error') | ||
const spy = expect.createSpy() | ||
const mySpawn = mockSpawn() | ||
mySpawn.sequence.add({throws: err}) | ||
mySpawn.sequence.add(mySpawn.simple(0)) | ||
runScript.__set__('spawn', mySpawn) | ||
it('should support array of scripts as a first argument', () => { | ||
const execa = runScript.__get__('execa') | ||
const spy = expect.spyOn(execa, 'spawn') | ||
const res = runScript(['test', 'test2'], 'test.js', packageJSON) | ||
expect(res.length).toBe(2) | ||
expect(res[0].title).toBe('test') | ||
expect(res[1].title).toBe('test2') | ||
runScript('test', 'test.js', packageJSON, spy) | ||
setTimeout(() => { | ||
expect(mySpawn.calls.length).toEqual(1) | ||
expect(spy.calls.length).toEqual(1) | ||
expect(spy.calls[0].arguments[0]).toBe(err) | ||
expect(spy.calls[0].arguments[1]).toNotEqual(0) | ||
done() | ||
}, 10) | ||
}) | ||
expect(res[0].task()) | ||
.toBeAPromise() | ||
.toBeFulfilled() | ||
expect(spy.calls.length).toEqual(1) | ||
expect(spy.calls[0].arguments).toEqual( | ||
['npm', ['run', '-s', 'test', '--', 'test.js'], {stdio: 'inherit'}] | ||
) | ||
it('should handle errors for sequences', done => { | ||
const err = new Error('linting error') | ||
const spy = expect.createSpy() | ||
const mySpawn = mockSpawn() | ||
mySpawn.sequence.add({throws: err}) | ||
mySpawn.sequence.add(mySpawn.simple(0)) | ||
runScript.__set__('spawn', mySpawn) | ||
runScript(['test', 'test2'], 'test.js', packageJSON, spy) | ||
setTimeout(() => { | ||
expect(mySpawn.calls.length).toEqual(1) | ||
expect(spy.calls.length).toEqual(1) | ||
expect(spy.calls[0].arguments[0]).toBe(err) | ||
expect(spy.calls[0].arguments[1]).toNotEqual(0) | ||
done() | ||
}, 10) | ||
expect(res[1].task()) | ||
.toBeAPromise() | ||
.toBeFulfilled() | ||
expect(spy.calls.length).toEqual(2) | ||
expect(spy.calls[1].arguments).toEqual( | ||
['npm', ['run', '-s', 'test2', '--', 'test.js'], {stdio: 'inherit'}] | ||
) | ||
}) | ||
}) | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
17636
9
14
16
226
+ Addedexeca@^0.4.0
+ Addedlistr@^0.4.3
+ Addedansi-escapes@1.4.0(transitive)
+ Addedcross-spawn-async@2.2.5(transitive)
+ Addedexeca@0.4.0(transitive)
+ Addedfigures@1.7.0(transitive)
+ Addedindent-string@2.1.0(transitive)
+ Addedis-finite@1.1.0(transitive)
+ Addedis-stream@1.1.0(transitive)
+ Addedlistr@0.4.3(transitive)
+ Addedlog-symbols@1.0.2(transitive)
+ Addedlog-update@1.0.2(transitive)
+ Addedlru-cache@4.1.5(transitive)
+ Addednpm-path@2.0.4(transitive)
+ Addednpm-run-path@1.0.0(transitive)
+ Addednpm-which@3.0.1(transitive)
+ Addedpath-key@1.0.0(transitive)
+ Addedpseudomap@1.0.2(transitive)
+ Addedrepeating@2.0.1(transitive)
+ Addedstream-to-observable@0.1.0(transitive)
+ Addedstrip-eof@1.0.0(transitive)
+ Addedyallist@2.1.2(transitive)
- Removednpm-path@1.1.0(transitive)
- Removednpm-which@2.0.0(transitive)
Updatednpm-which@^3.0.1