Skip to content

Commit bed9127

Browse files
iirojokonet
authored andcommittedJul 1, 2019
refactor: use execa's shell option to run commands
BREAKING CHANGE: Local commands are no longer resolved by lint-staged, but execa will do this instead. In effect, there are no longer pretty error messages when commands are not found.
1 parent d3f6475 commit bed9127

12 files changed

+95
-303
lines changed
 

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ Supported are any executables installed locally or globally via `npm` as well as
214214

215215
> Using globally installed scripts is discouraged, since lint-staged may not work for someone who doesn’t have it installed.
216216
217-
`lint-staged` is using [npm-which](https://github.com/timoxley/npm-which) to locate locally installed scripts. So in your `.lintstagedrc` you can write:
217+
`lint-staged` uses [execa](https://github.com/sindresorhus/execa#preferlocal) to locate locally installed scripts. So in your `.lintstagedrc` you can write:
218218

219219
```json
220220
{

‎package.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,15 @@
3333
"debug": "^3.1.0",
3434
"dedent": "^0.7.0",
3535
"del": "^4.1.1",
36-
"execa": "^1.0.0",
36+
"execa": "^2.0.1",
3737
"is-glob": "^4.0.0",
3838
"listr": "^0.14.2",
3939
"listr-update-renderer": "^0.5.0",
4040
"lodash": "^4.17.11",
4141
"log-symbols": "^2.2.0",
4242
"micromatch": "^3.1.8",
43-
"npm-which": "^3.0.1",
4443
"path-is-inside": "^1.0.2",
4544
"please-upgrade-node": "^3.0.2",
46-
"string-argv": "^0.0.2",
4745
"stringify-object": "^3.2.2",
4846
"yup": "^0.27.0"
4947
},

‎src/checkPkgScripts.js

-46
This file was deleted.

‎src/findBin.js

-54
This file was deleted.

‎src/resolveTaskFn.js

+12-20
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,19 @@ const dedent = require('dedent')
55
const execa = require('execa')
66
const symbols = require('log-symbols')
77

8-
const findBin = require('./findBin')
9-
108
const debug = require('debug')('lint-staged:task')
119

1210
/**
13-
* Execute the given linter binary with arguments using execa and
11+
* Execute the given linter cmd using execa and
1412
* return the promise.
1513
*
16-
* @param {string} bin
17-
* @param {Array<string>} args
18-
* @param {Object} execaOptions
14+
* @param {string} cmd
1915
* @return {Promise} child_process
2016
*/
21-
function execLinter(bin, args, execaOptions) {
22-
debug('bin:', bin)
23-
debug('args: %O', args)
24-
debug('opts: %o', execaOptions)
25-
26-
return execa(bin, args, { ...execaOptions })
17+
const execLinter = (cmd, execaOptions = {}) => {
18+
debug('cmd:', cmd)
19+
debug('execaOptions:', execaOptions)
20+
return execa(cmd, execaOptions)
2721
}
2822

2923
const successMsg = linter => `${symbols.success} ${linter} passed!`
@@ -84,7 +78,7 @@ function makeErr(linter, result, context = {}) {
8478
* @returns {function(): Promise<Array<string>>}
8579
*/
8680
module.exports = function resolveTaskFn(options) {
87-
const { linter, gitDir, pathsToLint } = options
81+
const { gitDir, linter, pathsToLint } = options
8882

8983
// If `linter` is a function, it should return a string when evaluated with `pathsToLint`.
9084
// Else, it's a already a string
@@ -94,20 +88,18 @@ module.exports = function resolveTaskFn(options) {
9488
const linters = Array.isArray(linterString) ? linterString : [linterString]
9589

9690
const tasks = linters.map(command => {
97-
const { bin, args } = findBin(command)
98-
99-
// If `linter` is a function, args already include `pathsToLint`.
100-
const argsWithPaths = fnLinter ? args : args.concat(pathsToLint)
91+
// If `linter` is a function, cmd already includes `pathsToLint`.
92+
const cmdWithPaths = fnLinter ? command : `${command} ${pathsToLint.join(' ')}`
10193

102-
const execaOptions = { reject: false }
10394
// Only use gitDir as CWD if we are using the git binary
10495
// e.g `npm` should run tasks in the actual CWD
105-
if (/git(\.exe)?$/i.test(bin) && gitDir !== process.cwd()) {
96+
const execaOptions = { preferLocal: true, reject: false, shell: true }
97+
if (/^git(\.exe)?/i.test(command) && gitDir !== process.cwd()) {
10698
execaOptions.cwd = gitDir
10799
}
108100

109101
return ctx =>
110-
execLinter(bin, argsWithPaths, execaOptions).then(result => {
102+
execLinter(cmdWithPaths, execaOptions).then(result => {
111103
if (result.failed || result.killed || result.signal != null) {
112104
throw makeErr(linter, result, ctx)
113105
}

‎test/__snapshots__/checkPkgScripts.spec.js.snap

-19
This file was deleted.

‎test/__snapshots__/findBin.spec.js.snap

-10
This file was deleted.

‎test/checkPkgScripts.spec.js

-50
This file was deleted.

‎test/findBin.spec.js

-56
This file was deleted.

‎test/makeCmdTasks.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ describe('makeCmdTasks', () => {
3737
expect(taskPromise).toBeInstanceOf(Promise)
3838
await taskPromise
3939
expect(execa).toHaveBeenCalledTimes(1)
40-
expect(execa).lastCalledWith('test', ['test.js'], { reject: false })
40+
expect(execa).lastCalledWith('test test.js', { preferLocal: true, reject: false, shell: true })
4141
taskPromise = linter2.task()
4242
expect(taskPromise).toBeInstanceOf(Promise)
4343
await taskPromise
4444
expect(execa).toHaveBeenCalledTimes(2)
45-
expect(execa).lastCalledWith('test2', ['test.js'], { reject: false })
45+
expect(execa).lastCalledWith('test2 test.js', { preferLocal: true, reject: false, shell: true })
4646
})
4747
})

‎test/resolveTaskFn.spec.js

+25-20
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@ describe('resolveTaskFn', () => {
88
execa.mockClear()
99
})
1010

11-
it('should throw for non-existent script', () => {
12-
expect(() => {
13-
resolveTaskFn({
14-
...defaultOpts,
15-
linter: 'missing-module'
16-
})
17-
}).toThrow()
18-
})
19-
2011
it('should support non npm scripts', async () => {
2112
expect.assertions(2)
2213
const taskFn = resolveTaskFn({
@@ -26,8 +17,10 @@ describe('resolveTaskFn', () => {
2617

2718
await taskFn()
2819
expect(execa).toHaveBeenCalledTimes(1)
29-
expect(execa).lastCalledWith('node', ['--arg=true', './myscript.js', 'test.js'], {
30-
reject: false
20+
expect(execa).lastCalledWith('node --arg=true ./myscript.js test.js', {
21+
preferLocal: true,
22+
reject: false,
23+
shell: true
3124
})
3225
})
3326

@@ -40,8 +33,10 @@ describe('resolveTaskFn', () => {
4033

4134
await taskFn()
4235
expect(execa).toHaveBeenCalledTimes(1)
43-
expect(execa).lastCalledWith('node', ['--arg=true', './myscript.js', 'test.js'], {
44-
reject: false
36+
expect(execa).lastCalledWith('node --arg=true ./myscript.js test.js', {
37+
preferLocal: true,
38+
reject: false,
39+
shell: true
4540
})
4641
})
4742

@@ -55,11 +50,15 @@ describe('resolveTaskFn', () => {
5550

5651
await taskFn()
5752
expect(execa).toHaveBeenCalledTimes(2)
58-
expect(execa).nthCalledWith(1, 'node', ['--arg=true', './myscript.js', 'foo.js'], {
59-
reject: false
53+
expect(execa).nthCalledWith(1, 'node --arg=true ./myscript.js foo.js', {
54+
preferLocal: true,
55+
reject: false,
56+
shell: true
6057
})
61-
expect(execa).nthCalledWith(2, 'node', ['--arg=true', './myscript.js', 'bar.js'], {
62-
reject: false
58+
expect(execa).nthCalledWith(2, 'node --arg=true ./myscript.js bar.js', {
59+
preferLocal: true,
60+
reject: false,
61+
shell: true
6362
})
6463
})
6564

@@ -73,9 +72,11 @@ describe('resolveTaskFn', () => {
7372

7473
await taskFn()
7574
expect(execa).toHaveBeenCalledTimes(1)
76-
expect(execa).lastCalledWith('git', ['add', 'test.js'], {
75+
expect(execa).lastCalledWith('git add test.js', {
7776
cwd: '../',
78-
reject: false
77+
preferLocal: true,
78+
reject: false,
79+
shell: true
7980
})
8081
})
8182

@@ -85,7 +86,11 @@ describe('resolveTaskFn', () => {
8586

8687
await taskFn()
8788
expect(execa).toHaveBeenCalledTimes(1)
88-
expect(execa).lastCalledWith('jest', ['test.js'], { reject: false })
89+
expect(execa).lastCalledWith('jest test.js', {
90+
preferLocal: true,
91+
reject: false,
92+
shell: true
93+
})
8994
})
9095

9196
it('should throw error for failed linters', async () => {

‎yarn.lock

+54-22
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
12201220
dependencies:
12211221
delayed-stream "~1.0.0"
12221222

1223-
commander@^2.11.0, commander@^2.14.1, commander@^2.9.0, commander@~2.20.0:
1223+
commander@^2.11.0, commander@^2.14.1, commander@~2.20.0:
12241224
version "2.20.0"
12251225
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
12261226
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
@@ -1330,7 +1330,7 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
13301330
shebang-command "^1.2.0"
13311331
which "^1.2.9"
13321332

1333-
cross-spawn@^6.0.0:
1333+
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
13341334
version "6.0.5"
13351335
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
13361336
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
@@ -1894,6 +1894,20 @@ execa@^1.0.0:
18941894
signal-exit "^3.0.0"
18951895
strip-eof "^1.0.0"
18961896

1897+
execa@^2.0.1:
1898+
version "2.0.1"
1899+
resolved "https://registry.yarnpkg.com/execa/-/execa-2.0.1.tgz#546a5be56388953409cbf24972d2fd1bb36dbfcd"
1900+
integrity sha512-pHGXlV7S7ilDda3eaCTcr6zmFTMA3wJo7j+RtNg0uH9sbAasJfVug5RkYOTBLj5g4MOqlsaPUn3HKa/UfTDw8w==
1901+
dependencies:
1902+
cross-spawn "^6.0.5"
1903+
get-stream "^5.0.0"
1904+
is-stream "^2.0.0"
1905+
merge-stream "^2.0.0"
1906+
npm-run-path "^3.0.0"
1907+
p-finally "^2.0.0"
1908+
signal-exit "^3.0.2"
1909+
strip-final-newline "^2.0.0"
1910+
18971911
exit-hook@^1.0.0:
18981912
version "1.1.1"
18991913
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
@@ -2300,6 +2314,13 @@ get-stream@^4.0.0:
23002314
dependencies:
23012315
pump "^3.0.0"
23022316

2317+
get-stream@^5.0.0:
2318+
version "5.1.0"
2319+
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
2320+
integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
2321+
dependencies:
2322+
pump "^3.0.0"
2323+
23032324
get-value@^2.0.3, get-value@^2.0.6:
23042325
version "2.0.6"
23052326
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -2943,6 +2964,11 @@ is-stream@^1.0.1, is-stream@^1.1.0:
29432964
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
29442965
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
29452966

2967+
is-stream@^2.0.0:
2968+
version "2.0.0"
2969+
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
2970+
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
2971+
29462972
is-symbol@^1.0.2:
29472973
version "1.0.2"
29482974
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
@@ -3789,6 +3815,11 @@ merge-stream@^1.0.1:
37893815
dependencies:
37903816
readable-stream "^2.0.1"
37913817

3818+
merge-stream@^2.0.0:
3819+
version "2.0.0"
3820+
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
3821+
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
3822+
37923823
merge@^1.2.0:
37933824
version "1.2.1"
37943825
resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
@@ -4053,28 +4084,19 @@ npm-packlist@^1.1.6:
40534084
ignore-walk "^3.0.1"
40544085
npm-bundled "^1.0.1"
40554086

4056-
npm-path@^2.0.2:
4057-
version "2.0.4"
4058-
resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64"
4059-
integrity sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==
4060-
dependencies:
4061-
which "^1.2.10"
4062-
40634087
npm-run-path@^2.0.0:
40644088
version "2.0.2"
40654089
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
40664090
integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
40674091
dependencies:
40684092
path-key "^2.0.0"
40694093

4070-
npm-which@^3.0.1:
4071-
version "3.0.1"
4072-
resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
4073-
integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=
4094+
npm-run-path@^3.0.0:
4095+
version "3.1.0"
4096+
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-3.1.0.tgz#7f91be317f6a466efed3c9f2980ad8a4ee8b0fa5"
4097+
integrity sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==
40744098
dependencies:
4075-
commander "^2.9.0"
4076-
npm-path "^2.0.2"
4077-
which "^1.2.10"
4099+
path-key "^3.0.0"
40784100

40794101
npmlog@^4.0.2:
40804102
version "4.1.2"
@@ -4256,6 +4278,11 @@ p-finally@^1.0.0:
42564278
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
42574279
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
42584280

4281+
p-finally@^2.0.0:
4282+
version "2.0.1"
4283+
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561"
4284+
integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==
4285+
42594286
p-limit@^1.1.0:
42604287
version "1.3.0"
42614288
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@@ -4373,6 +4400,11 @@ path-key@^2.0.0, path-key@^2.0.1:
43734400
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
43744401
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
43754402

4403+
path-key@^3.0.0:
4404+
version "3.1.0"
4405+
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3"
4406+
integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==
4407+
43764408
path-parse@^1.0.5, path-parse@^1.0.6:
43774409
version "1.0.6"
43784410
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
@@ -5198,11 +5230,6 @@ stealthy-require@^1.1.1:
51985230
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
51995231
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
52005232

5201-
string-argv@^0.0.2:
5202-
version "0.0.2"
5203-
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736"
5204-
integrity sha1-2sMECGkMIfPDYwo/86BYd73L1zY=
5205-
52065233
string-length@^2.0.0:
52075234
version "2.0.0"
52085235
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
@@ -5280,6 +5307,11 @@ strip-eof@^1.0.0:
52805307
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
52815308
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
52825309

5310+
strip-final-newline@^2.0.0:
5311+
version "2.0.0"
5312+
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
5313+
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
5314+
52835315
strip-json-comments@2.0.1, strip-json-comments@~2.0.1:
52845316
version "2.0.1"
52855317
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -5631,7 +5663,7 @@ which-module@^2.0.0:
56315663
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
56325664
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
56335665

5634-
which@^1.2.10, which@^1.2.12, which@^1.2.9, which@^1.3.0:
5666+
which@^1.2.12, which@^1.2.9, which@^1.3.0:
56355667
version "1.3.1"
56365668
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
56375669
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==

0 commit comments

Comments
 (0)
Please sign in to comment.