Skip to content

Commit

Permalink
feat: add --relative option for controlling file paths
Browse files Browse the repository at this point in the history
All commands specified in the configuration will run in the current directory when using relative paths, instead of git commands running in the repo root
  • Loading branch information
iiroj authored and okonet committed Jul 10, 2019
1 parent 6572a82 commit 242deb5
Show file tree
Hide file tree
Showing 14 changed files with 199 additions and 124 deletions.
6 changes: 4 additions & 2 deletions bin/lint-staged
Expand Up @@ -27,8 +27,9 @@ const debug = debugLib('lint-staged:bin')
cmdline
.version(pkg.version)
.option('-c, --config [path]', 'Path to configuration file')
.option('-x, --shell', 'Use execa’s shell mode to execute linter commands')
.option('-q, --quiet', 'Use Listr’s silent renderer')
.option('-r, --relative', 'Pass relative filepaths to tasks')
.option('-x, --shell', 'Skip parsing of tasks for better shell support')
.option('-q, --quiet', 'Disable lint-staged’s own console output')
.option('-d, --debug', 'Enable debug mode')
.parse(process.argv)

Expand All @@ -40,6 +41,7 @@ debug('Running `lint-staged@%s`', pkg.version)

lintStaged({
configPath: cmdline.config,
relative: !!cmdline.relative,
shell: !!cmdline.shell,
quiet: !!cmdline.quiet,
debug: !!cmdline.debug
Expand Down
25 changes: 18 additions & 7 deletions src/generateTasks.js
Expand Up @@ -18,15 +18,25 @@ const isPathInside = (parent, child) => {
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
}

module.exports = async function generateTasks(linters, gitDir, stagedRelFiles) {
/**
* Generates all task commands, and filelist
*
* @param {object} options
* @param {Object} [options.config] - Task configuration
* @param {boolean} [options.gitDir] - Git root directory
* @param {boolean} [options.files] - Staged filepaths
* @param {boolean} [options.relative] - Whether filepaths to should be relative to gitDir
* @returns {Promise}
*/
module.exports = async function generateTasks({ config, gitDir, files, relative = false }) {
debug('Generating linter tasks')

const cwd = process.cwd()
const stagedFiles = stagedRelFiles.map(file => path.resolve(gitDir, file))
const stagedFiles = files.map(file => path.resolve(gitDir, file))

return Object.keys(linters).map(pattern => {
return Object.keys(config).map(pattern => {
const isParentDirPattern = pattern.startsWith('../')
const commands = linters[pattern]
const commands = config[pattern]

const fileList = micromatch(
stagedFiles
Expand All @@ -43,9 +53,10 @@ module.exports = async function generateTasks(linters, gitDir, stagedRelFiles) {
matchBase: !pattern.includes('/'),
dot: true
}
).map(file =>
// Return absolute path after the filter is run
path.resolve(cwd, file)
).map(
file =>
// Return absolute path after the filter is run
relative ? path.normalize(file) : path.resolve(cwd, file)
)

const task = { pattern, commands, fileList }
Expand Down
13 changes: 7 additions & 6 deletions src/index.js
Expand Up @@ -43,15 +43,16 @@ function loadConfig(configPath) {
*
* @param {object} options
* @param {string} [options.configPath] - Path to configuration file
* @param {boolean} [options.shell] - Use execa’s shell mode to execute linter commands
* @param {boolean} [options.quiet] - Use Listr’s silent renderer
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
* @param {boolean} [options.shell] - Skip parsing of tasks for better shell support
* @param {boolean} [options.quiet] - Disable lint-staged’s own console output
* @param {boolean} [options.debug] - Enable debug mode
* @param {Logger} [logger]
*
* @returns {Promise<boolean>} Promise of whether the linting passed or failed
*/
module.exports = function lintStaged(
{ configPath, shell = false, quiet = false, debug = false } = {},
{ configPath, relative = false, shell = false, quiet = false, debug = false } = {},
logger = console
) {
debugLog('Loading config using `cosmiconfig`')
Expand All @@ -71,12 +72,12 @@ module.exports = function lintStaged(
} else {
// We might not be in debug mode but `DEBUG=lint-staged*` could have
// been set.
debugLog('Normalized config:\n%O', config)
debugLog('lint-staged config:\n%O', config)
}

return runAll(config, shell, quiet, debug, logger)
return runAll({ config, relative, shell, quiet, debug }, logger)
.then(() => {
debugLog('linters were executed successfully!')
debugLog('tasks were executed successfully!')
return Promise.resolve(true)
})
.catch(error => {
Expand Down
8 changes: 5 additions & 3 deletions src/makeCmdTasks.js
Expand Up @@ -7,11 +7,13 @@ const debug = require('debug')('lint-staged:make-cmd-tasks')
/**
* Creates and returns an array of listr tasks which map to the given commands.
*
* @param {Array<string|Function>|string|Function} commands
* @param {object} options
* @param {Array<string|Function>|string|Function} [options.commands]
* @param {string} [options.gitDir]
* @param {Array<string>} [options.pathsToLint]
* @param {Boolean} shell
* @param {Array<string>} pathsToLint
*/
module.exports = async function makeCmdTasks(commands, shell, gitDir, pathsToLint) {
module.exports = async function makeCmdTasks({ commands, gitDir, pathsToLint, shell }) {
debug('Creating listr tasks for commands %o', commands)
const commandsArray = Array.isArray(commands) ? commands : [commands]

Expand Down
2 changes: 1 addition & 1 deletion src/resolveGitDir.js
Expand Up @@ -3,7 +3,7 @@
const execGit = require('./execGit')
const path = require('path')

module.exports = async function resolveGitDir(options) {
module.exports = async function resolveGitDir(options = {}) {
try {
// git cli uses GIT_DIR to fast track its response however it might be set to a different path
// depending on where the caller initiated this from, hence clear GIT_DIR
Expand Down
34 changes: 22 additions & 12 deletions src/resolveTaskFn.js
Expand Up @@ -77,16 +77,32 @@ function makeErr(linter, result, context = {}) {
* if the OS is Windows.
*
* @param {Object} options
* @param {string} options.gitDir
* @param {Boolean} options.isFn
* @param {string} options.linter
* @param {Array<string>} options.pathsToLint
* @param {Boolean} [options.shell]
* @param {String} [options.gitDir] - Current git repo path
* @param {Boolean} [options.isFn] - Whether the linter task is a function
* @param {string} [options.linter] — Linter task
* @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({ gitDir, isFn, linter, pathsToLint, shell = false }) {
module.exports = function resolveTaskFn({
gitDir,
isFn,
linter,
pathsToLint,
relative,
shell = false
}) {
const execaOptions = { preferLocal: true, reject: false, shell }

if (relative) {
execaOptions.cwd = process.cwd()
} else if (/^git(\.exe)?/i.test(linter) && 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

Expand All @@ -101,12 +117,6 @@ module.exports = function resolveTaskFn({ gitDir, isFn, linter, pathsToLint, she
args = isFn ? parsedArgs : parsedArgs.concat(pathsToLint)
}

// Only use gitDir as CWD if we are using the git binary
// e.g `npm` should run tasks in the actual CWD
if (/^git(\.exe)?/i.test(linter) && gitDir !== process.cwd()) {
execaOptions.cwd = gitDir
}

return ctx =>
execLinter(cmd, args, execaOptions).then(result => {
if (result.failed || result.killed || result.signal != null) {
Expand Down
46 changes: 24 additions & 22 deletions src/runAll.js
Expand Up @@ -13,7 +13,7 @@ const git = require('./gitWorkflow')
const makeCmdTasks = require('./makeCmdTasks')
const resolveGitDir = require('./resolveGitDir')

const debug = require('debug')('lint-staged:run')
const debugLog = require('debug')('lint-staged:run')

/**
* https://serverfault.com/questions/69430/what-is-the-maximum-length-of-a-command-line-in-mac-os-x
Expand All @@ -26,37 +26,36 @@ const MAX_ARG_LENGTH =
/**
* Executes all tasks and either resolves or rejects the promise
*
* @param config {Object}
* @param {Boolean} [shellMode] Use execa’s shell mode to execute linter commands
* @param {Boolean} [quietMode] Use Listr’s silent renderer
* @param {Boolean} [debugMode] Enable debug mode
* @param {object} options
* @param {Object} [options.config] - Task configuration
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
* @param {boolean} [options.shell] - Skip parsing of tasks for better shell support
* @param {boolean} [options.quiet] - Disable lint-staged’s own console output
* @param {boolean} [options.debug] - Enable debug mode
* @param {Logger} logger
* @returns {Promise}
*/
module.exports = async function runAll(
config,
shellMode = false,
quietMode = false,
debugMode = false,
{ config, relative = false, shell = false, quiet = false, debug = false },
logger = console
) {
debug('Running all linter scripts')
debugLog('Running all linter scripts')

const gitDir = await resolveGitDir(config)
const gitDir = await resolveGitDir()

if (!gitDir) {
throw new Error('Current directory is not a git directory!')
}

debug('Resolved git directory to be `%s`', gitDir)
debugLog('Resolved git directory to be `%s`', gitDir)

const files = await getStagedFiles({ cwd: gitDir })

if (!files) {
throw new Error('Unable to get staged files!')
}

debug('Loaded list of staged files in git:\n%O', files)
debugLog('Loaded list of staged files in git:\n%O', files)

const argLength = files.join(' ').length
if (argLength > MAX_ARG_LENGTH) {
Expand All @@ -70,16 +69,19 @@ https://github.com/okonet/lint-staged#using-js-functions-to-customize-linter-com
)
}

const tasks = (await generateTasks(config, gitDir, files)).map(task => ({
const tasks = (await generateTasks({ config, gitDir, files, relative })).map(task => ({
title: `Running tasks for ${task.pattern}`,
task: async () =>
new Listr(await makeCmdTasks(task.commands, shellMode, gitDir, task.fileList), {
// In sub-tasks we don't want to run concurrently
// and we want to abort on errors
dateFormat: false,
concurrent: false,
exitOnError: true
}),
new Listr(
await makeCmdTasks({ commands: task.commands, gitDir, shell, pathsToLint: task.fileList }),
{
// In sub-tasks we don't want to run concurrently
// and we want to abort on errors
dateFormat: false,
concurrent: false,
exitOnError: true
}
),
skip: () => {
if (task.fileList.length === 0) {
return `No staged files match ${task.pattern}`
Expand All @@ -90,7 +92,7 @@ https://github.com/okonet/lint-staged#using-js-functions-to-customize-linter-com

const listrOptions = {
dateFormat: false,
renderer: (quietMode && 'silent') || (debugMode && 'verbose') || 'update'
renderer: (quiet && 'silent') || (debug && 'verbose') || 'update'
}

// If all of the configured "linters" should be skipped
Expand Down
File renamed without changes.

0 comments on commit 242deb5

Please sign in to comment.