diff --git a/README.md b/README.md
index efb2ed9..856e4d5 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,7 @@ Rule | Meaning
`components/*/utils` | ✅ The directory `components` is accepted.
✅ Any *first level* nested directory is accepted.
✅ The *second level* nested directory `utils` is accepted.
❌ Any other *second level* nested directory is not accepted.
`legacy/**` | ✅ The directory `legacy` is accepted.
✅ Any nested directory on *any level* is accepted.
`components/*/legacy/**` | ✅ The directory `components` is accepted.
✅ Any *first level* nested directory is accepted.
✅ The *second level* nested directory `legacy` is accepted.
❌ Any other *second level* nested directory is not accepted.
✅ Any nested directory on *any level* inside of *legacy* directory is accepted.
+`!pages/*` | ❌ Any nested directory inside `pages` is not accepted.
⚠️ A rule like `components/*/utils` automatically make the `components` and `components/*` rules work. So, no need to specify a rule for every level directory. You need to specify the deepest path.
diff --git a/__tests__/runLinter.js b/__tests__/runLinter.js
index 9f7d448..fa4f055 100644
--- a/__tests__/runLinter.js
+++ b/__tests__/runLinter.js
@@ -196,3 +196,20 @@ test('should not accept files with * and ** in the same rule', () => {
'folderslint: 2 error(s) found'
)
})
+
+test('should not accept files with ! (negation) in the beginning of the rule ', () => {
+ parseConfig.mockReturnValue({
+ root: 'src',
+ rules: ['!pages/*'],
+ })
+
+ runLinter(['cwd/src/pages/components'])
+
+ expect(process.exit).toHaveBeenLastCalledWith(1)
+
+ expect(console.log).toHaveBeenNthCalledWith(
+ 1,
+ chalk.underline('cwd/src/pages/components')
+ )
+ expect(console.log).toHaveBeenNthCalledWith(2, ERROR_MESSAGE)
+})
diff --git a/src/parseConfig.js b/src/parseConfig.js
index 660bbc3..7a5790e 100644
--- a/src/parseConfig.js
+++ b/src/parseConfig.js
@@ -19,6 +19,16 @@ const validateParsedConfig = (config) => {
console.error('A rule can have ** only at the end')
process.exit(1)
}
+
+ if (
+ rule.includes('!') &&
+ rule.startsWith('!') &&
+ rule.split('!').length > 2
+ ) {
+ console.error(`Invalid rule: ${rule}`)
+ console.error('A rule can have at most one ! at the beginning')
+ process.exit(1)
+ }
})
return config
}
diff --git a/src/rules.js b/src/rules.js
index 06ce788..0975264 100644
--- a/src/rules.js
+++ b/src/rules.js
@@ -29,7 +29,9 @@ const getExtendedRules = (root = '', rules) => {
const isPathMatchRule = (path, rule) => {
const splittedPath = path.split('/')
- const splittedRule = rule.split('/')
+
+ const isNegation = rule.startsWith('!')
+ const splittedRule = (isNegation ? rule.substring(1) : rule).split('/')
const isValid = splittedPath.reduce((acc, pathPart, i) => {
const rulePart = splittedRule[i]
@@ -41,14 +43,14 @@ const isPathMatchRule = (path, rule) => {
}, true)
if (!isValid) {
- return false
+ return isNegation
}
if (!rule.includes('**') && splittedPath.length > splittedRule.length) {
- return false
+ return isNegation
}
- return true
+ return !isNegation
}
const checkPath = (path, rules) => {