Skip to content

Commit

Permalink
feat: Allow config to be provided via command-line (#304)
Browse files Browse the repository at this point in the history
Configuration file can be provided via a -c or --config commandline parameter. 
This allows users to be explicit with where the configuration lives rather 
than relying on cosmiconfig path search.
  • Loading branch information
cameronhunter authored and sudo-suhas committed Oct 18, 2017
1 parent eacb3d2 commit 54809ae
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Starting with v3.1 you can now use different ways of configuring it:
* `lint-staged` object in your `package.json`
* `.lintstagedrc` file in JSON or YML format
* `lint-staged.config.js` file in JS format
* Pass a configuration file using the `--config` or `-c` flag

See [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) for more details on what formats are supported.

Expand Down
10 changes: 9 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@

'use strict'

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

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

require('./src')(console, cmdline.config)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"dependencies": {
"app-root-path": "^2.0.0",
"chalk": "^2.1.0",
"commander": "^2.11.0",
"cosmiconfig": "^1.1.0",
"execa": "^0.8.0",
"is-glob": "^4.0.0",
Expand Down
13 changes: 8 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ const errConfigNotFound = new Error('Config could not be found')
/**
* Root lint-staged function that is called from .bin
*/
module.exports = function lintStaged() {
module.exports = function lintStaged(injectedLogger, configPath) {
const logger = injectedLogger || console

return cosmiconfig('lint-staged', {
configPath,
rc: '.lintstagedrc',
rcExtensions: true
})
Expand All @@ -38,7 +41,7 @@ module.exports = function lintStaged() {
const config = validateConfig(getConfig(result.config))

if (config.verbose) {
console.log(`
logger.log(`
Running lint-staged with the following config:
${stringifyObject(config)}
`)
Expand All @@ -59,15 +62,15 @@ ${stringifyObject(config)}
})
.catch(err => {
if (err === errConfigNotFound) {
console.error(`${err.message}.`)
logger.error(`${err.message}.`)
} else {
// It was probably a parsing error
console.error(`Could not parse lint-staged config.
logger.error(`Could not parse lint-staged config.
${err}`)
}
// Print helpful message for all errors
console.error(`
logger.error(`
Please make sure you have created it correctly.
See https://github.com/okonet/lint-staged#configuration.
`)
Expand Down
12 changes: 12 additions & 0 deletions test/__mocks__/cosmiconfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const actual = require.requireActual('cosmiconfig')
const mock = jest.genMockFromModule('cosmiconfig')

function cosmiconfig(name, options) {
if (options.configPath) {
return actual(name, options)
}

return mock(name, options)
}

module.exports = jest.fn(cosmiconfig)
6 changes: 6 additions & 0 deletions test/__mocks__/my-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"verbose": true,
"linters": {
"*": "mytask"
}
}
35 changes: 34 additions & 1 deletion test/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`lintStaged should load config file when specified 1`] = `
"
LOG
Running lint-staged with the following config:
{
verbose: true,
linters: {
'*': 'mytask'
},
concurrent: true,
chunkSize: 9007199254740991,
gitDir: '.',
globOptions: {
matchBase: true,
dot: true
},
subTaskConcurrency: 1,
renderer: 'verbose'
}
"
`;

exports[`lintStaged should not output config in non verbose mode 1`] = `""`;

exports[`lintStaged should ouput config in verbose mode 1`] = `
exports[`lintStaged should output config in verbose mode 1`] = `
"
LOG
Running lint-staged with the following config:
Expand Down Expand Up @@ -32,3 +54,14 @@ Please make sure you have created it correctly.
See https://github.com/okonet/lint-staged#configuration.
"
`;

exports[`lintStaged should print helpful error message when explicit config file is not found 1`] = `
ERROR Could not parse lint-staged config.
Error: ENOENT: no such file or directory, open 'fake-config-file.yml'
ERROR
Please make sure you have created it correctly.
See https://github.com/okonet/lint-staged#configuration.
`;
44 changes: 36 additions & 8 deletions test/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,66 @@
import { makeConsoleMock } from 'consolemock'
import cosmiconfig from 'cosmiconfig'
import path from 'path'
import lintStaged from '../src/index'

const replaceSerializer = (from, to) => ({
test: val => typeof val === 'string' && from.test(val),
print: val => val.replace(from, to)
})

jest.mock('cosmiconfig')

describe('lintStaged', () => {
let logger

beforeEach(() => {
console = makeConsoleMock()
logger = makeConsoleMock()
})

it('should ouput config in verbose mode', async () => {
it('should output config in verbose mode', async () => {
const config = {
verbose: true,
linters: {
'*': 'mytask'
}
}
cosmiconfig.mockImplementationOnce(() => Promise.resolve({ config }))
await lintStaged()
expect(console.printHistory()).toMatchSnapshot()
await lintStaged(logger)
expect(logger.printHistory()).toMatchSnapshot()
})

it('should not output config in non verbose mode', async () => {
const config = {
'*': 'mytask'
}
cosmiconfig.mockImplementationOnce(() => Promise.resolve({ config }))
await lintStaged()
expect(console.printHistory()).toMatchSnapshot()
await lintStaged(logger)
expect(logger.printHistory()).toMatchSnapshot()
})

it('should load config file when specified', async () => {
await lintStaged(logger, path.join(__dirname, '__mocks__', 'my-config.json'))
expect(logger.printHistory()).toMatchSnapshot()
})

it('should print helpful error message when config file is not found', async () => {
cosmiconfig.mockImplementationOnce(() => Promise.resolve(null))
await lintStaged()
expect(console.printHistory()).toMatchSnapshot()
await lintStaged(logger)
expect(logger.printHistory()).toMatchSnapshot()
})

it('should print helpful error message when explicit config file is not found', async () => {
const nonExistentConfig = 'fake-config-file.yml'

// Serialize Windows, Linux and MacOS paths consistently
expect.addSnapshotSerializer(
replaceSerializer(
/Error: ENOENT: no such file or directory, open '([^']+)'/,
`Error: ENOENT: no such file or directory, open '${nonExistentConfig}'`
)
)

await lintStaged(logger, nonExistentConfig)
expect(logger.printHistory()).toMatchSnapshot()
})
})

0 comments on commit 54809ae

Please sign in to comment.