Skip to content

Commit

Permalink
feat: add support for concurrent CLI option
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffreyffs authored and iiroj committed Nov 27, 2019
1 parent 99c317a commit 6af8307
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 13 deletions.
19 changes: 12 additions & 7 deletions README.md
Expand Up @@ -70,13 +70,14 @@ $ npx lint-staged --help
Usage: lint-staged [options]

Options:
-V, --version output the version number
-c, --config [path] Path to configuration file
-r, --relative Pass relative filepaths to tasks
-x, --shell Skip parsing of tasks for better shell support
-q, --quiet Disable lint-staged’s own console output
-d, --debug Enable debug mode
-h, --help output usage information
-V, --version output the version number
-c, --config [path] Path to configuration file
-r, --relative Pass relative filepaths to tasks
-x, --shell Skip parsing of tasks for better shell support
-q, --quiet Disable lint-staged’s own console output
-d, --debug Enable debug mode
-p, --concurrent [parallel tasks] The number of tasks to run concurrently, or false to run tasks sequentially
-h, --help output usage information
```

- **`--config [path]`**: This can be used to manually specify the `lint-staged` config file location. However, if the specified file cannot be found, it will error out instead of performing the usual search. You may pass a npm package name for configuration also.
Expand All @@ -86,6 +87,10 @@ Options:
- **`--debug`**: Enabling the debug mode does the following:
- `lint-staged` uses the [debug](https://github.com/visionmedia/debug) module internally to log information about staged files, commands being executed, location of binaries, etc. Debug logs, which are automatically enabled by passing the flag, can also be enabled by setting the environment variable `$DEBUG` to `lint-staged*`.
- Use the [`verbose` renderer](https://github.com/SamVerschueren/listr-verbose-renderer) for `listr`.
- **`--concurrent [number | (true/false)]`**: Controls the concurrency of tasks being run by lint-staged. **NOTE**: This does NOT affect the concurrency of subtasks (they will always be run sequentially). Possible values are:
- `false`: Run all tasks serially
- `true` (default) : _Infinite_ concurrency. Runs as many tasks in parallel as possible.
- `{number}`: Run the specified number of tasks in parallel, where `1` is equivalent to `false`.

## Configuration

Expand Down
8 changes: 7 additions & 1 deletion bin/lint-staged
Expand Up @@ -34,6 +34,11 @@ cmdline
.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')
.option(
'-p, --concurrent <parallel tasks>',
'The number of tasks to run concurrently, or false to run tasks serially',
true
)
.parse(process.argv)

if (cmdline.debug) {
Expand All @@ -47,7 +52,8 @@ lintStaged({
relative: !!cmdline.relative,
shell: !!cmdline.shell,
quiet: !!cmdline.quiet,
debug: !!cmdline.debug
debug: !!cmdline.debug,
concurrent: cmdline.concurrent
})
.then(passed => {
process.exitCode = passed ? 0 : 1
Expand Down
13 changes: 11 additions & 2 deletions src/index.js
Expand Up @@ -48,12 +48,21 @@ function loadConfig(configPath) {
* @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 {boolean | number} [options.concurrent] - The number of tasks to run concurrently, or false to run tasks serially
* @param {Logger} [logger]
*
* @returns {Promise<boolean>} Promise of whether the linting passed or failed
*/
module.exports = function lintStaged(
{ configPath, config, relative = false, shell = false, quiet = false, debug = false } = {},
{
configPath,
config,
relative = false,
shell = false,
quiet = false,
debug = false,
concurrent = true
} = {},
logger = console
) {
debugLog('Loading config using `cosmiconfig`')
Expand All @@ -76,7 +85,7 @@ module.exports = function lintStaged(
debugLog('lint-staged config:\n%O', config)
}

return runAll({ config, relative, shell, quiet, debug }, logger)
return runAll({ config, relative, shell, quiet, debug, concurrent }, logger)
.then(() => {
debugLog('tasks were executed successfully!')
return Promise.resolve(true)
Expand Down
14 changes: 11 additions & 3 deletions src/runAll.js
Expand Up @@ -33,15 +33,23 @@ const MAX_ARG_LENGTH =
* @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 {boolean | number} [options.concurrent] - The number of tasks to run concurrently, or false to run tasks serially
* @param {Logger} logger
* @returns {Promise}
*/
module.exports = async function runAll(
{ config, cwd = process.cwd(), debug = false, quiet = false, relative = false, shell = false },
{
config,
cwd = process.cwd(),
debug = false,
quiet = false,
relative = false,
shell = false,
concurrent = true
},
logger = console
) {
debugLog('Running all linter scripts')

const gitDir = await resolveGitDir({ cwd })

if (!gitDir) {
Expand Down Expand Up @@ -120,7 +128,7 @@ https://github.com/okonet/lint-staged#using-js-functions-to-customize-linter-com
},
{
title: 'Running tasks...',
task: () => new Listr(tasks, { ...listrOptions, concurrent: true, exitOnError: false })
task: () => new Listr(tasks, { ...listrOptions, concurrent, exitOnError: false })
},
{
title: 'Updating stash...',
Expand Down
75 changes: 75 additions & 0 deletions test/runAll.unmocked.spec.js
Expand Up @@ -89,6 +89,81 @@ describe('runAll', () => {
expect(await readFile('test.js')).toEqual(testJsFilePretty)
})

it('Should succeed when conflicting tasks sequentially edit a file', async () => {
await appendFile('test.js', testJsFileUgly)

await fs.mkdir(cwd + '/files')
await appendFile('file.js', testJsFileUgly, cwd + '/files')

await execGit(['add', 'test.js'])
await execGit(['add', 'files'])

const success = await gitCommit({
config: {
'file.js': ['prettier --write', 'git add'],
'test.js': files => {
// concurrent: false, means this should still work
fs.removeSync(`${cwd}/files`)
return [`prettier --write ${files.join(' ')}`, `git add ${files.join(' ')}`]
}
},
concurrent: false
})

expect(success).toEqual(true)
})

it('Should fail when conflicting tasks concurrently edit a file', async () => {
await appendFile('test.js', testJsFileUgly)
await appendFile('test2.js', testJsFileUgly)

await fs.mkdir(cwd + '/files')
await appendFile('file.js', testJsFileUgly, cwd + '/files')

await execGit(['add', 'test.js'])
await execGit(['add', 'test2.js'])
await execGit(['add', 'files'])

const success = await gitCommit({
config: {
'file.js': ['prettier --write', 'git add'],
'test.js': ['prettier --write', 'git add'],
'test2.js': files => {
// remove `files` so the 1st command should fail
fs.removeSync(`${cwd}/files`)
return [`prettier --write ${files.join(' ')}`, `git add ${files.join(' ')}`]
}
},
concurrent: true
})

expect(success).toEqual(false)
})

it('Should succeed when conflicting tasks concurrently (max concurrency 1) edit a file', async () => {
await appendFile('test.js', testJsFileUgly)

await fs.mkdir(cwd + '/files')
await appendFile('file.js', testJsFileUgly, cwd + '/files')

await execGit(['add', 'test.js'])
await execGit(['add', 'files'])

const success = await gitCommit({
config: {
'file.js': ['prettier --write', 'git add'],
'test2.js': files => {
// concurrency of one should prevent save this operation
fs.removeSync(`${cwd}/files`)
return [`prettier --write ${files.join(' ')}`, `git add ${files.join(' ')}`]
}
},
concurrent: 1
})

expect(success).toEqual(true)
})

it('Should commit entire staged file when no errors and linter modifies file', async () => {
// Stage ugly file
await appendFile('test.js', testJsFileUgly)
Expand Down

0 comments on commit 6af8307

Please sign in to comment.