Skip to content

Commit

Permalink
Merge a108a4a into 71ca88f
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinHerklotz committed Mar 29, 2020
2 parents 71ca88f + a108a4a commit 10fd3bd
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 27 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).

## [Unreleased]
### Added
- [`no-restricted-paths`]: Now you can also add glob patterns to the `except` option ([#1701], thanks [@KevinHerklotz])

## [2.20.2] - 2020-03-28
### Fixed
Expand Down Expand Up @@ -660,6 +662,7 @@ for info on changes for earlier releases.

[`memo-parser`]: ./memo-parser/README.md

[#1701]: https://github.com/benmosher/eslint-plugin-import/issues/1701
[#1666]: https://github.com/benmosher/eslint-plugin-import/pull/1666
[#1664]: https://github.com/benmosher/eslint-plugin-import/pull/1664
[#1658]: https://github.com/benmosher/eslint-plugin-import/pull/1658
Expand Down Expand Up @@ -1117,6 +1120,7 @@ for info on changes for earlier releases.
[@bmish]: https://github.com/bmish
[@redbugz]: https://github.com/redbugz
[@kentcdodds]: https://github.com/kentcdodds
[@KevinHerklotz]: https://github.com/KevinHerklotz
[@IvanGoncharov]: https://github.com/IvanGoncharov
[@wschurman]: https://github.com/wschurman
[@fisker]: https://github.com/fisker
Expand Down
63 changes: 39 additions & 24 deletions docs/rules/no-restricted-paths.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,71 +9,86 @@ In order to prevent such scenarios this rule allows you to define restricted zon

This rule has one option. The option is an object containing the definition of all restricted `zones` and the optional `basePath` which is used to resolve relative paths within.
The default value for `basePath` is the current working directory.
Each zone consists of the `target` path and a `from` path. The `target` is the path where the restricted imports should be applied. The `from` path defines the folder that is not allowed to be used in an import. An optional `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that `except` is relative to `from` and cannot backtrack to a parent directory.
Each zone consists of the `target` path and a `from` path. The `target` is the path where the restricted imports should be applied. The `from` path defines the folder that is not allowed to be used in an import. An optional `except` may be defined for a zone, allowing exception paths or glob patterns that would otherwise violate the related `from`. Note that `except` is relative to `from` and cannot backtrack to a parent directory.

### Examples
## Examples

### Basic example

Given the following folder structure:

```
my-project
├── client
│ └── foo.js
│ └── baz.js
│ └── homepage.js
│ └── about.js
└── server
└── bar.js
└── api.js
```

and the current file being linted is `my-project/client/foo.js`.
and the current file being linted is `my-project/client/homepage.js`.

The following patterns are considered problems when configuration set to `{ "zones": [ { "target": "./client", "from": "./server" } ] }`:
and the configuration set to:

```js
import bar from '../server/bar';
{ "zones": [ {
"target": "./client",
"from": "./server"
} ] }
```

The following patterns are not considered problems when configuration set to `{ "zones": [ { "target": "./client", "from": "./server" } ] }`:
#### Fail:

```js
import baz from '../client/baz';
import api from '../server/api';
```

---------------
#### Pass:

```js
import about from './about';
```

### Example with exceptions

Given the following folder structure:

```
my-project
├── client
│ └── foo.js
│ └── baz.js
│ └── homepage.js
│ └── about.js
└── server
├── one
├── api
│ └── a.js
│ └── b.js
└── two
│ └── types.js
└── helper
└── a.js
└── b.js
```

and the current file being linted is `my-project/server/one/a.js`.
and the current file being linted is `my-project/client/homepage.js`.

and the current configuration is set to:

```
```js
{ "zones": [ {
"target": "./tests/files/restricted-paths/server/one",
"from": "./tests/files/restricted-paths/server",
"except": ["./one"]
"target": "./client",
"from": "./server",
"except": ["./helper", "**/types.js"] // relative from "from" path
} ] }
```

The following pattern is considered a problem:
#### Fail:

```js
import a from '../two/a'
import a from '../server/api/a'
```

The following pattern is not considered a problem:
#### Pass:

```js
import b from './b'
import { MyType } from '../server/api/types'
import a from '../server/helpers/a'
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.4.1",
"has": "^1.0.3",
"is-glob": "^4.0.1",
"minimatch": "^3.0.4",
"object.values": "^1.1.0",
"read-pkg-up": "^2.0.0",
Expand Down
30 changes: 27 additions & 3 deletions src/rules/no-restricted-paths.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import containsPath from 'contains-path'
import path from 'path'
import minimatch from 'minimatch'
import isGlob from 'is-glob'

import resolve from 'eslint-module-utils/resolve'
import isStaticRequire from '../core/staticRequire'
Expand Down Expand Up @@ -75,13 +77,35 @@ module.exports = {
}

matchingZones.forEach((zone) => {
const exceptionPaths = zone.except || []
const exceptions = zone.except || []
const absoluteFrom = path.resolve(basePath, zone.from)
let exceptionPaths = []
let exceptionGlobs = []

exceptions.forEach(exception => {
if (isGlob(exception)) {
exceptionGlobs.push(exception)
} else {
exceptionPaths.push(exception)
}
})


if (!containsPath(absoluteImportPath, absoluteFrom)) {
return
}

// exception globs
const relativeImportFromFrom = absoluteImportPath.replace(absoluteFrom, '')
const pathIsExceptedByExceptionGlob = exceptionGlobs.some((exceptionGlob) =>
minimatch(relativeImportFromFrom, exceptionGlob)
)

if (pathIsExceptedByExceptionGlob) {
return
}

// exception paths
const absoluteExceptionPaths = exceptionPaths.map((exceptionPath) =>
path.resolve(absoluteFrom, exceptionPath)
)
Expand All @@ -93,10 +117,10 @@ module.exports = {
return
}

const pathIsExcepted = absoluteExceptionPaths
const pathIsExceptedByExceptionPath = absoluteExceptionPaths
.some((absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath))

if (pathIsExcepted) {
if (pathIsExceptedByExceptionPath) {
return
}

Expand Down
52 changes: 52 additions & 0 deletions tests/src/rules/no-restricted-paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ ruleTester.run('no-restricted-paths', rule, {
zones: [ { target: './tests/files/restricted-paths/client', from: './tests/files/restricted-paths/other' } ],
} ],
}),

// except (using path)
test({
code: 'import a from "./a.js"',
filename: testFilePath('./restricted-paths/server/one/a.js'),
Expand All @@ -50,7 +52,41 @@ ruleTester.run('no-restricted-paths', rule, {
} ],
} ],
}),
test({
code: 'import a from "../server/two/a.js"',
filename: testFilePath('./restricted-paths/client/a.js'),
options: [ {
zones: [ {
target: './tests/files/restricted-paths/client',
from: './tests/files/restricted-paths/server',
except: ['../server/two'],
} ],
} ],
}),

// except (using glob pattern)
test({
code: 'import a from "../server/one/a.js"',
filename: testFilePath('./restricted-paths/client/a.js'),
options: [ {
zones: [ {
target: './tests/files/restricted-paths/client',
from: './tests/files/restricted-paths/server',
except: ['**/one/**'],
} ],
} ],
}),
test({
code: 'import a from "../server/one/a"',
filename: testFilePath('./restricted-paths/client/a.js'),
options: [ {
zones: [ {
target: './tests/files/restricted-paths/client',
from: './tests/files/restricted-paths/server',
except: ['**/a.js'],
} ],
} ],
}),

// irrelevant function calls
test({ code: 'notrequire("../server/b.js")' }),
Expand Down Expand Up @@ -145,6 +181,22 @@ ruleTester.run('no-restricted-paths', rule, {
column: 15,
} ],
}),
test({
code: 'import b from "../two/a.js"',
filename: testFilePath('./restricted-paths/server/one/a.js'),
options: [ {
zones: [ {
target: './tests/files/restricted-paths/server/one',
from: './tests/files/restricted-paths/server',
except: ['**/server/**'],
} ],
} ],
errors: [ {
message: 'Unexpected path "../two/a.js" imported in restricted zone.',
line: 1,
column: 15,
} ],
}),
test({
code: 'import b from "../two/a.js"',
filename: testFilePath('./restricted-paths/server/one/a.js'),
Expand Down

0 comments on commit 10fd3bd

Please sign in to comment.