Skip to content

Commit

Permalink
feat: Allow linting files outside of project folder (#495)
Browse files Browse the repository at this point in the history
Only when pattern starts with `../`.
  • Loading branch information
sudo-suhas committed Sep 20, 2018
1 parent d59fad7 commit d386c80
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 14 deletions.
24 changes: 16 additions & 8 deletions README.md
Expand Up @@ -133,15 +133,10 @@ To extend and customise lint-staged, advanced options are available. To use thes
{
"lint-staged": {
"linters": {
"*.{js,scss}": [
"some command",
"git add"
]
"*.{js,scss}": ["some command", "git add"]
},
"ignore": [
"**/dist/*.min.js"
]
},
"ignore": ["**/dist/*.min.js"]
}
}
```

Expand Down Expand Up @@ -346,3 +341,16 @@ If you wish to use `lint-staged` in a multi package monorepo, it is recommended
[`lerna`](https://github.com/lerna/lerna) can be used to execute the `precommit` script in all sub-packages.

Example repo: [sudo-suhas/lint-staged-multi-pkg](https://github.com/sudo-suhas/lint-staged-multi-pkg).

### Can I lint files outside of the current project folder?

tl;dr: Yes, but the pattern should start with `../`.

By default, `lint-staged` executes linters only on the files present inside the project folder(where `lint-staged` is installed and run from).
So this question is relevant _only_ when the project folder is a child folder inside the git repo.
In certain project setups, it might be desirable to bypass this restriction. See [#425](https://github.com/okonet/lint-staged/issues/425), [#487](https://github.com/okonet/lint-staged/issues/487) for more context.

`lint-staged` provides an escape hatch for the same(`>= v7.3.0`). For patterns that start with `../`, all the staged files are allowed to match against the pattern.
Note that patterns like `*.js`, `**/*.js` will still only match the project files and not any of the files in parent or sibling directories.

Example repo: [sudo-suhas/lint-staged-django-react-demo](https://github.com/sudo-suhas/lint-staged-django-react-demo).
12 changes: 7 additions & 5 deletions src/generateTasks.js
Expand Up @@ -8,7 +8,7 @@ const resolveGitDir = require('./resolveGitDir')

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

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

const normalizedConfig = getConfig(config) // Ensure we have a normalized config
Expand All @@ -17,16 +17,18 @@ module.exports = function generateTasks(config, relFiles) {

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

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

const fileList = micromatch(
files
// Only worry about children of the CWD
.filter(file => pathIsInside(file, cwd))
stagedFiles
// Only worry about children of the CWD unless the pattern explicitly
// specifies that it concerns a parent directory.
.filter(file => isParentDirPattern || pathIsInside(file, cwd))
// Make the paths relative to CWD for filtering
.map(file => path.relative(cwd, file)),
patterns,
Expand Down
29 changes: 28 additions & 1 deletion test/generateTasks.spec.js
Expand Up @@ -87,7 +87,7 @@ describe('generateTasks', () => {
})

it('should not match non-children files', () => {
const relPath = path.resolve(path.join(process.cwd(), '..'))
const relPath = path.join(process.cwd(), '..')
resolveGitDir.mockReturnValueOnce(relPath)
const result = generateTasks(Object.assign({}, config), files)
const linter = result.find(item => item.pattern === '*.js')
Expand Down Expand Up @@ -222,4 +222,31 @@ describe('generateTasks', () => {
fileList: [`${workDir}/cool/js.js`].map(path.normalize)
})
})

it('should not filter files for pattern which begins with `..`', () => {
jest.spyOn(process, 'cwd').mockReturnValueOnce(path.join(workDir, 'prj'))
const result = generateTasks(
{
linters: {
'*.js': 'my-cmd',
'../outside/*.js': 'my-cmd'
}
},
['root.js', 'prj/test.js', 'outside/test.js', 'outside/test2.js']
)

const prjTask = result.find(item => item.pattern === '*.js')
expect(prjTask).toEqual({
pattern: '*.js',
commands: 'my-cmd',
fileList: [`${workDir}/prj/test.js`].map(path.normalize)
})

const parentFolderTask = result.find(item => item.pattern === '../outside/*.js')
expect(parentFolderTask).toEqual({
pattern: '../outside/*.js',
commands: 'my-cmd',
fileList: [`${workDir}/outside/test.js`, `${workDir}/outside/test2.js`].map(path.normalize)
})
})
})

0 comments on commit d386c80

Please sign in to comment.