Skip to content

Commit

Permalink
feat: Add debug mode, deprecate verbose option (#344)
Browse files Browse the repository at this point in the history
* feat: Add debug logs

* feat: Command line option --debug

- Parse command line option `--debug` and enable debug logs if it was passed.
- Propagate debug mode - `src/index.js -> runAll -> runScript -> findBin`.
- Set npm `--silent` flag using debug mode.
- Remove `verbose` from default config, add entry to deprecated options.
- Set `listr` renderer using debug mode.
- Tweak logs in `src/index.js` depending on debug mode.
- Update tests.
- Update readme, add section on command line flags.

BREAKING CHANGE: `verbose` config option has been deprecated and is superseded
by the command line option `--debug`.
  • Loading branch information
sudo-suhas committed Dec 1, 2017
1 parent c22fbc2 commit 8f214f0
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 105 deletions.
23 changes: 22 additions & 1 deletion README.md
Expand Up @@ -50,6 +50,28 @@ See [examples](#examples) and [configuration](#configuration) below.
>
> To mitigate this rename your `commit` npm script to something non git hook namespace like, for example `{ cz: git-cz }`
## Command line flags

```
$ ./node_modules/.bin/lint-staged --help
Usage: lint-staged [options]
Options:
-V, --version output the version number
-c, --config [path] Path to configuration file
-d, --debug Enable debug mode
-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.
* **`--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`.
- Do not pass `--silent` to npm scripts.

## Configuration

Starting with v3.1 you can now use different ways of configuring it:
Expand Down Expand Up @@ -102,7 +124,6 @@ To set options and keep lint-staged extensible, advanced format can be used. Thi
* `concurrent`*true* — runs linters for each glob pattern simultaneously. If you don’t want this, you can set `concurrent: false`
* `chunkSize` — Max allowed chunk size based on number of files for glob pattern. This is important on windows based systems to avoid command length limitations. See [#147](https://github.com/okonet/lint-staged/issues/147)
* `subTaskConcurrency``1` — Controls concurrency for processing chunks generated for each linter. Execution is **not** concurrent by default(see [#225](https://github.com/okonet/lint-staged/issues/225))
* `verbose`*false* — runs lint-staged in verbose mode. When `true` it will use https://github.com/SamVerschueren/listr-verbose-renderer.
* `globOptions``{ matchBase: true, dot: true }`[minimatch options](https://github.com/isaacs/minimatch#options) to customize how glob patterns match files.

## Filtering files
Expand Down
12 changes: 11 additions & 1 deletion index.js
Expand Up @@ -3,11 +3,21 @@
'use strict'

const cmdline = require('commander')
const debugLib = require('debug')
const pkg = require('./package.json')

const debug = debugLib('lint-staged:bin')

cmdline
.version(pkg.version)
.option('-c, --config [path]', 'Path to configuration file')
.option('-d, --debug', 'Enable debug mode')
.parse(process.argv)

require('./src')(console, cmdline.config)
if (cmdline.debug) {
debugLib.enable('lint-staged*')
}

debug('Running `lint-staged@%s`', pkg.version)

require('./src')(console, cmdline.config, cmdline.debug)
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -31,6 +31,7 @@
"chalk": "^2.1.0",
"commander": "^2.11.0",
"cosmiconfig": "^3.1.0",
"debug": "^3.1.0",
"dedent": "^0.7.0",
"execa": "^0.8.0",
"find-parent-dir": "^0.3.0",
Expand Down
10 changes: 8 additions & 2 deletions src/findBin.js
Expand Up @@ -2,11 +2,14 @@

const npmWhich = require('npm-which')(process.cwd())

module.exports = function findBin(cmd, scripts, options) {
const debug = require('debug')('lint-staged:find-bin')

module.exports = function findBin(cmd, scripts, debugMode) {
debug('Resolving binary for command `%s`', cmd)
const npmArgs = (bin, args) =>
// We always add `--` even if args are not defined. This is required
// because we pass filenames later.
['run', options && options.verbose ? undefined : '--silent', bin, '--']
['run', debugMode ? undefined : '--silent', bin, '--']
// args could be undefined but we filter that out.
.concat(args)
.filter(arg => arg !== undefined)
Expand Down Expand Up @@ -39,6 +42,7 @@ module.exports = function findBin(cmd, scripts, options) {
*/
if (scripts[cmd] !== undefined) {
// Support for scripts from package.json
debug('`%s` resolved to npm script - `%s`', cmd, scripts[cmd])
return { bin: 'npm', args: npmArgs(cmd) }
}

Expand All @@ -47,6 +51,7 @@ module.exports = function findBin(cmd, scripts, options) {
const args = parts.splice(1)

if (scripts[bin] !== undefined) {
debug('`%s` resolved to npm script - `%s`', bin, scripts[bin])
return { bin: 'npm', args: npmArgs(bin, args) }
}

Expand Down Expand Up @@ -76,5 +81,6 @@ module.exports = function findBin(cmd, scripts, options) {
throw new Error(`${bin} could not be found. Try \`npm install ${bin}\`.`)
}

debug('Binary for `%s` resolved to `%s`', cmd, bin)
return { bin, args }
}
13 changes: 8 additions & 5 deletions src/generateTasks.js
Expand Up @@ -6,7 +6,11 @@ const pathIsInside = require('path-is-inside')
const getConfig = require('./getConfig').getConfig
const resolveGitDir = require('./resolveGitDir')

const debug = require('debug')('lint-staged:gen-tasks')

module.exports = function generateTasks(config, relFiles) {
debug('Generating linter tasks')

const normalizedConfig = getConfig(config) // Ensure we have a normalized config
const linters = normalizedConfig.linters
const globOptions = normalizedConfig.globOptions
Expand All @@ -29,10 +33,9 @@ module.exports = function generateTasks(config, relFiles) {
// Return absolute path after the filter is run
.map(file => path.resolve(cwd, file))

return {
pattern,
commands,
fileList
}
const task = { pattern, commands, fileList }
debug('Generated task: \n%O', task)

return task
})
}
29 changes: 18 additions & 11 deletions src/getConfig.js
Expand Up @@ -12,10 +12,12 @@ const logValidationWarning = require('jest-validate').logValidationWarning
const unknownOptionWarning = require('jest-validate/build/warnings').unknownOptionWarning
const isGlob = require('is-glob')

const debug = require('debug')('lint-staged:cfg')

/**
* Default config object
*
* @type {{concurrent: boolean, chunkSize: number, globOptions: {matchBase: boolean, dot: boolean}, linters: {}, subTaskConcurrency: number, renderer: string, verbose: boolean}}
* @type {{concurrent: boolean, chunkSize: number, globOptions: {matchBase: boolean, dot: boolean}, linters: {}, subTaskConcurrency: number, renderer: string}}
*/
const defaultConfig = {
concurrent: true,
Expand All @@ -26,8 +28,7 @@ const defaultConfig = {
},
linters: {},
subTaskConcurrency: 1,
renderer: 'update',
verbose: false
renderer: 'update'
}

/**
Expand Down Expand Up @@ -88,10 +89,11 @@ function unknownValidationReporter(config, example, option, options) {
*
* @param {Object} sourceConfig
* @returns {{
* concurrent: boolean, chunkSize: number, globOptions: {matchBase: boolean, dot: boolean}, linters: {}, subTaskConcurrency: number, renderer: string, verbose: boolean
* concurrent: boolean, chunkSize: number, globOptions: {matchBase: boolean, dot: boolean}, linters: {}, subTaskConcurrency: number, renderer: string
* }}
*/
function getConfig(sourceConfig) {
function getConfig(sourceConfig, debugMode) {
debug('Normalizing config')
const config = defaultsDeep(
{}, // Do not mutate sourceConfig!!!
isSimple(sourceConfig) ? { linters: sourceConfig } : sourceConfig,
Expand All @@ -100,18 +102,25 @@ function getConfig(sourceConfig) {

// Check if renderer is set in sourceConfig and if not, set accordingly to verbose
if (isObject(sourceConfig) && !sourceConfig.hasOwnProperty('renderer')) {
config.renderer = config.verbose ? 'verbose' : 'update'
config.renderer = debugMode ? 'verbose' : 'update'
}

return config
}

const optRmMsg = (opt, helpMsg) => ` Option ${chalk.bold(opt)} was removed.
${helpMsg}
Please remove ${chalk.bold(opt)} from your configuration.`

/**
* Runs config validation. Throws error if the config is not valid.
* @param config {Object}
* @returns config {Object}
*/
function validateConfig(config) {
debug('Validating config')
const exampleConfig = Object.assign({}, defaultConfig, {
linters: {
'*.js': ['eslint --fix', 'git add'],
Expand All @@ -120,11 +129,9 @@ function validateConfig(config) {
})

const deprecatedConfig = {
gitDir: () => ` Option ${chalk.bold('gitDir')} was removed.
lint-staged now automatically resolves '.git' directory.
Please remove ${chalk.bold('gitDir')} from your configuration.`
gitDir: () => optRmMsg('gitDir', "lint-staged now automatically resolves '.git' directory."),
verbose: () =>
optRmMsg('verbose', `Use the command line flag ${chalk.bold('--debug')} instead.`)
}

validate(config, {
Expand Down
20 changes: 15 additions & 5 deletions src/index.js
Expand Up @@ -11,6 +11,8 @@ const validateConfig = require('./getConfig').validateConfig
const printErrors = require('./printErrors')
const runAll = require('./runAll')

const debug = require('debug')('lint-staged')

// Find the right package.json at the root of the project
const packageJson = require(appRoot.resolve('package.json'))

Expand All @@ -25,7 +27,8 @@ const errConfigNotFound = new Error('Config could not be found')
/**
* Root lint-staged function that is called from .bin
*/
module.exports = function lintStaged(injectedLogger, configPath) {
module.exports = function lintStaged(injectedLogger, configPath, debugMode) {
debug('Loading config using `cosmiconfig`')
const logger = injectedLogger || console

const explorer = cosmiconfig('lint-staged', {
Expand All @@ -39,19 +42,26 @@ module.exports = function lintStaged(injectedLogger, configPath) {
.then(result => {
if (result == null) throw errConfigNotFound

debug('Successfully loaded config from `%s`:\n%O', result.filepath, result.config)
// result.config is the parsed configuration object
// result.filepath is the path to the config file that was found
const config = validateConfig(getConfig(result.config))

if (config.verbose) {
const config = validateConfig(getConfig(result.config, debugMode))
if (debugMode) {
// Log using logger to be able to test through `consolemock`.
logger.log('Running lint-staged with the following config:')
logger.log(stringifyObject(config, { indent: ' ' }))
} else {
// We might not be in debug mode but `DEBUG=lint-staged*` could have
// been set.
debug('Normalized config:\n%O', config)
}

const scripts = packageJson.scripts || {}
debug('Loaded scripts from package.json:\n%O', scripts)

runAll(scripts, config)
runAll(scripts, config, debugMode)
.then(() => {
debug('linters were executed successfully!')
// No errors, exiting with 0
process.exitCode = 0
})
Expand Down
13 changes: 10 additions & 3 deletions src/runAll.js
Expand Up @@ -8,29 +8,36 @@ const runScript = require('./runScript')
const generateTasks = require('./generateTasks')
const resolveGitDir = require('./resolveGitDir')

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

/**
* Executes all tasks and either resolves or rejects the promise
* @param scripts
* @param config {Object}
* @returns {Promise}
*/
module.exports = function runAll(scripts, config) {
module.exports = function runAll(scripts, config, debugMode) {
debug('Running all linter scripts')
// Config validation
if (!config || !has(config, 'concurrent') || !has(config, 'renderer')) {
throw new Error('Invalid config provided to runAll! Use getConfig instead.')
}

const concurrent = config.concurrent
const renderer = config.renderer
sgf.cwd = resolveGitDir()
const gitDir = resolveGitDir()
debug('Resolved git directory to be `%s`', gitDir)

sgf.cwd = gitDir
return pify(sgf)('ACM').then(files => {
/* files is an Object{ filename: String, status: String } */
const filenames = files.map(file => file.filename)
debug('Loaded list of staged files in git:\n%O', filenames)

const tasks = generateTasks(config, filenames).map(task => ({
title: `Running tasks for ${task.pattern}`,
task: () =>
new Listr(runScript(task.commands, task.fileList, scripts, config), {
new Listr(runScript(task.commands, task.fileList, scripts, config, debugMode), {
// In sub-tasks we don't want to run concurrently
// and we want to abort on errors
dateFormat: false,
Expand Down
12 changes: 10 additions & 2 deletions src/runScript.js
Expand Up @@ -10,7 +10,11 @@ const calcChunkSize = require('./calcChunkSize')
const findBin = require('./findBin')
const resolveGitDir = require('./resolveGitDir')

module.exports = function runScript(commands, pathsToLint, scripts, config) {
const debug = require('debug')('lint-staged:run-script')

module.exports = function runScript(commands, pathsToLint, scripts, config, debugMode) {
debug('Running script with commands %o', commands)

const normalizedConfig = getConfig(config)
const chunkSize = normalizedConfig.chunkSize
const concurrency = normalizedConfig.subTaskConcurrency
Expand All @@ -24,7 +28,7 @@ module.exports = function runScript(commands, pathsToLint, scripts, config) {
title: linter,
task: () => {
try {
const res = findBin(linter, scripts, config)
const res = findBin(linter, scripts, debugMode)

// Only use gitDir as CWD if we are using the git binary
// e.g `npm` should run tasks in the actual CWD
Expand All @@ -35,6 +39,10 @@ module.exports = function runScript(commands, pathsToLint, scripts, config) {
const mapper = pathsChunk => {
const args = res.args.concat(pathsChunk)

debug('bin:', res.bin)
debug('args: %O', args)
debug('opts: %o', execaOptions)

return (
execa(res.bin, args, Object.assign({}, execaOptions))
/* If we don't catch, pMap will terminate on first rejection */
Expand Down
1 change: 0 additions & 1 deletion test/__mocks__/my-config.json
@@ -1,5 +1,4 @@
{
"verbose": true,
"linters": {
"*": "mytask"
}
Expand Down
14 changes: 10 additions & 4 deletions test/__snapshots__/getConfig.spec.js.snap
Expand Up @@ -11,7 +11,6 @@ Object {
"linters": Object {},
"renderer": "update",
"subTaskConcurrency": 1,
"verbose": false,
}
`;

Expand All @@ -26,7 +25,6 @@ Object {
"linters": Object {},
"renderer": "update",
"subTaskConcurrency": 1,
"verbose": false,
}
`;

Expand All @@ -47,7 +45,6 @@ Object {
},
"renderer": "update",
"subTaskConcurrency": 1,
"verbose": false,
}
`;

Expand All @@ -72,7 +69,7 @@ WARN ● Validation Warning:
Please refer to https://github.com/okonet/lint-staged#configuration for more information..."
`;

exports[`validateConfig should print deprecation warning for gitDir option 1`] = `
exports[`validateConfig should print deprecation warning for deprecated options 1`] = `
"
WARN ● Deprecation Warning:
Expand All @@ -82,6 +79,15 @@ WARN ● Deprecation Warning:
Please remove gitDir from your configuration.
Please refer to https://github.com/okonet/lint-staged#configuration for more information...
WARN ● Deprecation Warning:
Option verbose was removed.
Use the command line flag --debug instead.
Please remove verbose from your configuration.
Please refer to https://github.com/okonet/lint-staged#configuration for more information..."
`;

Expand Down

0 comments on commit 8f214f0

Please sign in to comment.