From d386c802b42f8e374090b1f9c58e4b60531d30ff Mon Sep 17 00:00:00 2001 From: Suhas Karanth Date: Thu, 20 Sep 2018 12:57:29 +0530 Subject: [PATCH] feat: Allow linting files outside of project folder (#495) Only when pattern starts with `../`. --- README.md | 24 ++++++++++++++++-------- src/generateTasks.js | 12 +++++++----- test/generateTasks.spec.js | 29 ++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3fa678524..557e0cd64 100644 --- a/README.md +++ b/README.md @@ -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"] + } } ``` @@ -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). diff --git a/src/generateTasks.js b/src/generateTasks.js index d4bdd80f7..14bd319f2 100644 --- a/src/generateTasks.js +++ b/src/generateTasks.js @@ -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 @@ -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, diff --git a/test/generateTasks.spec.js b/test/generateTasks.spec.js index f3e146b37..8be57b574 100644 --- a/test/generateTasks.spec.js +++ b/test/generateTasks.spec.js @@ -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') @@ -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) + }) + }) })