123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- 'use strict'
- const { redBright, dim } = require('chalk')
- const execa = require('execa')
- const debug = require('debug')('lint-staged:task')
- const { parseArgsStringToArgv } = require('string-argv')
- const { error, info } = require('log-symbols')
- const { getInitialState } = require('./state')
- const { TaskError } = require('./symbols')
- const getTag = ({ code, killed, signal }) => signal || (killed && 'KILLED') || code || 'FAILED'
- /**
- * Handle task console output.
- *
- * @param {string} command
- * @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} ctx
- * @returns {Error}
- */
- const handleOutput = (command, result, ctx, isError = false) => {
- const { stderr, stdout } = result
- const hasOutput = !!stderr || !!stdout
- if (hasOutput) {
- const outputTitle = isError ? redBright(`${error} ${command}:`) : `${info} ${command}:`
- const output = []
- .concat(ctx.quiet ? [] : ['', outputTitle])
- .concat(stderr ? stderr : [])
- .concat(stdout ? stdout : [])
- ctx.output.push(output.join('\n'))
- } else if (isError) {
- // Show generic error when task had no output
- const tag = getTag(result)
- const message = redBright(`\n${error} ${command} failed without output (${tag}).`)
- if (!ctx.quiet) ctx.output.push(message)
- }
- }
- /**
- * Create a error output dependding on process result.
- *
- * @param {string} command
- * @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} ctx
- * @returns {Error}
- */
- const makeErr = (command, result, ctx) => {
- ctx.errors.add(TaskError)
- handleOutput(command, result, ctx, true)
- const tag = getTag(result)
- return new Error(`${redBright(command)} ${dim(`[${tag}]`)}`)
- }
- /**
- * Returns the task function for the linter.
- *
- * @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.files — 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
- * @param {Boolean} [options.verbose] — Always show task verbose
- * @returns {function(): Promise<Array<string>>}
- */
- module.exports = function resolveTaskFn({
- command,
- files,
- gitDir,
- isFn,
- relative,
- shell = false,
- verbose = false,
- }) {
- const [cmd, ...args] = parseArgsStringToArgv(command)
- debug('cmd:', cmd)
- debug('args:', args)
- const execaOptions = { preferLocal: true, reject: false, shell }
- if (relative) {
- execaOptions.cwd = process.cwd()
- } else if (/^git(\.exe)?/i.test(cmd) && 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
- }
- debug('execaOptions:', execaOptions)
- return async (ctx = getInitialState()) => {
- const result = await (shell
- ? execa.command(isFn ? command : `${command} ${files.join(' ')}`, execaOptions)
- : execa(cmd, isFn ? args : args.concat(files), execaOptions))
- if (result.failed || result.killed || result.signal != null) {
- throw makeErr(command, result, ctx)
- }
- if (verbose) {
- handleOutput(command, result, ctx)
- }
- }
- }
|