/
resolveTaskFn.js
121 lines (108 loc) Β· 3.81 KB
/
resolveTaskFn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
'use strict'
const chalk = require('chalk')
const dedent = require('dedent')
const execa = require('execa')
const symbols = require('log-symbols')
const stringArgv = require('string-argv')
const debug = require('debug')('lint-staged:task')
/**
* Execute the given linter cmd using execa and
* return the promise.
*
* @param {string} cmd
* @param {Array<string>} args
* @param {Object} execaOptions
* @return {Promise} child_process
*/
const execLinter = (cmd, args, execaOptions) => {
debug('cmd:', cmd)
if (args) debug('args:', args)
debug('execaOptions:', execaOptions)
return args ? execa(cmd, args, execaOptions) : execa(cmd, execaOptions)
}
const successMsg = linter => `${symbols.success} ${linter} passed!`
/**
* Create and returns an error instance with a given message.
* If we set the message on the error instance, it gets logged multiple times(see #142).
* So we set the actual error message in a private field and extract it later,
* log only once.
*
* @param {string} message
* @returns {Error}
*/
function throwError(message) {
const err = new Error()
err.privateMsg = `\n\n\n${message}`
return err
}
/**
* Create a failure message dependding on process result.
*
* @param {string} linter
* @param {Object} result
* @param {string} result.stdout
* @param {string} result.stderr
* @param {boolean} result.failed
* @param {boolean} result.killed
* @param {string} result.signal
* @param {Object} context (see https://github.com/SamVerschueren/listr#context)
* @returns {Error}
*/
function makeErr(linter, result, context = {}) {
// Indicate that some linter will fail so we don't update the index with formatting changes
context.hasErrors = true // eslint-disable-line no-param-reassign
const { stdout, stderr, killed, signal } = result
if (killed || (signal && signal !== '')) {
return throwError(
`${symbols.warning} ${chalk.yellow(`${linter} was terminated with ${signal}`)}`
)
}
return throwError(dedent`${symbols.error} ${chalk.redBright(
`${linter} found some errors. Please fix them and try committing again.`
)}
${stdout}
${stderr}
`)
}
/**
* Returns the task function for the linter. It handles chunking for file paths
* if the OS is Windows.
*
* @param {Object} options
* @param {string} options.command β Linter task
* @param {String} options.gitDir - Current git repo path
* @param {Boolean} options.isFn - Whether the linter task is a function
* @param {Array<string>} options.pathsToLint β Filepaths to run the linter task against
* @param {Boolean} [options.relative] β Whether the filepaths should be relative
* @param {Boolean} [options.shell] β Whether to skip parsing linter task for better shell support
* @returns {function(): Promise<Array<string>>}
*/
module.exports = function resolveTaskFn({ command, files, gitDir, isFn, relative, shell = false }) {
const execaOptions = { preferLocal: true, reject: false, shell }
if (relative) {
execaOptions.cwd = process.cwd()
} else if (/^git(\.exe)?/i.test(command) && gitDir !== process.cwd()) {
// Only use gitDir as CWD if we are using the git binary
// e.g `npm` should run tasks in the actual CWD
execaOptions.cwd = gitDir
}
let cmd
let args
if (shell) {
execaOptions.shell = true
// If `shell`, passed command shouldn't be parsed
// If `linter` is a function, command already includes `files`.
cmd = isFn ? command : `${command} ${files.join(' ')}`
} else {
const [parsedCmd, ...parsedArgs] = stringArgv.parseArgsStringToArgv(command)
cmd = parsedCmd
args = isFn ? parsedArgs : parsedArgs.concat(files)
}
return ctx =>
execLinter(cmd, args, execaOptions).then(result => {
if (result.failed || result.killed || result.signal != null) {
throw makeErr(command, result, ctx)
}
return successMsg(command)
})
}