Skip to content

Commit dfe9346

Browse files
authored
Merge branch 'main' into patch-1
2 parents 2f20877 + 7784948 commit dfe9346

File tree

10 files changed

+207
-27
lines changed

10 files changed

+207
-27
lines changed

.eslintrc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
"eqeqeq": [2, "allow-null"],
2626
"func-call-spacing": 2,
2727
"indent": [2, 2],
28+
"keyword-spacing": ["error", {
29+
before: true,
30+
after: true,
31+
overrides: {
32+
return: { after: true },
33+
throw: { after: true },
34+
case: { after: true }
35+
}
36+
}],
2837
"max-len": [1, 99, 2],
2938
"no-cond-assign": [2, "always"],
3039
"no-return-assign": [2, "always"],
@@ -47,7 +56,7 @@
4756
"named": "never",
4857
"asyncArrow": "always",
4958
}],
50-
59+
5160
"eslint-plugin/consistent-output": [
5261
"error",
5362
"always",

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
88

99
### Added
1010
- [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser])
11+
- [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho])
1112

1213
## [2.24.2] - 2021-08-24
1314

@@ -903,6 +904,7 @@ for info on changes for earlier releases.
903904

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

907+
[#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219
906908
[#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196
907909
[#2194]: https://github.com/import-js/eslint-plugin-import/pull/2194
908910
[#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184

docs/rules/no-restricted-paths.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@ In order to prevent such scenarios this rule allows you to define restricted zon
99

1010
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.
1111
The default value for `basePath` is the current working directory.
12-
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.
13-
You may also specify an optional `message` for a zone, which will be displayed in case of the rule violation.
12+
13+
Each zone consists of the `target` path, a `from` path, and an optional `except` and `message` attribute.
14+
- `target` is the path where the restricted imports should be applied. It can be expressed by
15+
- directory string path that matches all its containing files
16+
- glob pattern matching all the targeted files
17+
- `from` path defines the folder that is not allowed to be used in an import. It can be expressed by
18+
- directory string path that matches all its containing files
19+
- glob pattern matching all the files restricted to be imported
20+
- `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that it does not alter the behaviour of `target` in any way.
21+
- in case `from` is a glob pattern, `except` must be an array of glob patterns as well
22+
- in case `from` is a directory path, `except` is relative to `from` and cannot backtrack to a parent directory.
23+
- `message` - will be displayed in case of the rule violation.
1424

1525
### Examples
1626

@@ -77,4 +87,40 @@ The following pattern is not considered a problem:
7787

7888
```js
7989
import b from './b'
90+
91+
```
92+
93+
---------------
94+
95+
Given the following folder structure:
96+
97+
```
98+
my-project
99+
├── client
100+
└── foo.js
101+
└── sub-module
102+
└── bar.js
103+
└── baz.js
104+
105+
```
106+
107+
and the current configuration is set to:
108+
109+
```
110+
{ "zones": [ {
111+
"target": "./tests/files/restricted-paths/client/!(sub-module)/**/*",
112+
"from": "./tests/files/restricted-paths/client/sub-module/**/*",
113+
} ] }
114+
```
115+
116+
The following import is considered a problem in `my-project/client/foo.js`:
117+
118+
```js
119+
import a from './sub-module/baz'
120+
```
121+
122+
The following import is not considered a problem in `my-project/client/sub-module/bar.js`:
123+
124+
```js
125+
import b from './baz'
80126
```

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
"find-up": "^2.0.0",
108108
"has": "^1.0.3",
109109
"is-core-module": "^2.6.0",
110+
"is-glob": "^4.0.1",
110111
"minimatch": "^3.0.4",
111112
"object.values": "^1.1.4",
112113
"pkg-up": "^2.0.0",

resolvers/webpack/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ exports.resolve = function (source, file, settings) {
8484
if (configPath) {
8585
try {
8686
webpackConfig = require(configPath);
87-
} catch(e) {
87+
} catch (e) {
8888
console.log('Error resolving webpackConfig', e);
8989
throw e;
9090
}

src/core/importType.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function isScopedMain(name) {
7575
}
7676

7777
function isRelativeToParent(name) {
78-
return/^\.\.$|^\.\.[\\/]/.test(name);
78+
return /^\.\.$|^\.\.[\\/]/.test(name);
7979
}
8080

8181
const indexFiles = ['.', './', './index', './index.js'];

src/rules/no-restricted-paths.js

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import path from 'path';
22

33
import resolve from 'eslint-module-utils/resolve';
44
import moduleVisitor from 'eslint-module-utils/moduleVisitor';
5+
import isGlob from 'is-glob';
6+
import { Minimatch, default as minimatch } from 'minimatch';
57
import docsUrl from '../docsUrl';
68
import importType from '../core/importType';
79

@@ -56,6 +58,10 @@ module.exports = {
5658
const matchingZones = restrictedPaths.filter((zone) => {
5759
const targetPath = path.resolve(basePath, zone.target);
5860

61+
if (isGlob(targetPath)) {
62+
return minimatch(currentFilename, targetPath);
63+
}
64+
5965
return containsPath(currentFilename, targetPath);
6066
});
6167

@@ -72,18 +78,59 @@ module.exports = {
7278
});
7379
}
7480

75-
const zoneExceptions = matchingZones.map((zone) => {
76-
const exceptionPaths = zone.except || [];
77-
const absoluteFrom = path.resolve(basePath, zone.from);
78-
const absoluteExceptionPaths = exceptionPaths.map((exceptionPath) => path.resolve(absoluteFrom, exceptionPath));
79-
const hasValidExceptionPaths = absoluteExceptionPaths
80-
.every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath));
81+
function reportInvalidExceptionGlob(node) {
82+
context.report({
83+
node,
84+
message: 'Restricted path exceptions must be glob patterns when`from` is a glob pattern',
85+
});
86+
}
87+
88+
const makePathValidator = (zoneFrom, zoneExcept = []) => {
89+
const absoluteFrom = path.resolve(basePath, zoneFrom);
90+
const isGlobPattern = isGlob(zoneFrom);
91+
let isPathRestricted;
92+
let hasValidExceptions;
93+
let isPathException;
94+
let reportInvalidException;
95+
96+
if (isGlobPattern) {
97+
const mm = new Minimatch(absoluteFrom);
98+
isPathRestricted = (absoluteImportPath) => mm.match(absoluteImportPath);
99+
100+
hasValidExceptions = zoneExcept.every(isGlob);
101+
102+
if (hasValidExceptions) {
103+
const exceptionsMm = zoneExcept.map((except) => new Minimatch(except));
104+
isPathException = (absoluteImportPath) => exceptionsMm.some((mm) => mm.match(absoluteImportPath));
105+
}
106+
107+
reportInvalidException = reportInvalidExceptionGlob;
108+
} else {
109+
isPathRestricted = (absoluteImportPath) => containsPath(absoluteImportPath, absoluteFrom);
110+
111+
const absoluteExceptionPaths = zoneExcept
112+
.map((exceptionPath) => path.resolve(absoluteFrom, exceptionPath));
113+
hasValidExceptions = absoluteExceptionPaths
114+
.every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath));
115+
116+
if (hasValidExceptions) {
117+
isPathException = (absoluteImportPath) => absoluteExceptionPaths.some(
118+
(absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath),
119+
);
120+
}
121+
122+
reportInvalidException = reportInvalidExceptionPath;
123+
}
81124

82125
return {
83-
absoluteExceptionPaths,
84-
hasValidExceptionPaths,
126+
isPathRestricted,
127+
hasValidExceptions,
128+
isPathException,
129+
reportInvalidException,
85130
};
86-
});
131+
};
132+
133+
const validators = [];
87134

88135
function checkForRestrictedImportPath(importPath, node) {
89136
const absoluteImportPath = resolve(importPath, context);
@@ -93,22 +140,27 @@ module.exports = {
93140
}
94141

95142
matchingZones.forEach((zone, index) => {
96-
const absoluteFrom = path.resolve(basePath, zone.from);
97-
98-
if (!containsPath(absoluteImportPath, absoluteFrom)) {
99-
return;
143+
if (!validators[index]) {
144+
validators[index] = makePathValidator(zone.from, zone.except);
100145
}
101146

102-
const { hasValidExceptionPaths, absoluteExceptionPaths } = zoneExceptions[index];
147+
const {
148+
isPathRestricted,
149+
hasValidExceptions,
150+
isPathException,
151+
reportInvalidException,
152+
} = validators[index];
103153

104-
if (!hasValidExceptionPaths) {
105-
reportInvalidExceptionPath(node);
154+
if (!isPathRestricted(absoluteImportPath)) {
106155
return;
107156
}
108157

109-
const pathIsExcepted = absoluteExceptionPaths
110-
.some((absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath));
158+
if (!hasValidExceptions) {
159+
reportInvalidException(node);
160+
return;
161+
}
111162

163+
const pathIsExcepted = isPathException(absoluteImportPath);
112164
if (pathIsExcepted) {
113165
return;
114166
}

tests/src/rules/no-restricted-paths.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ ruleTester.run('no-restricted-paths', rule, {
1414
zones: [ { target: './tests/files/restricted-paths/server', from: './tests/files/restricted-paths/other' } ],
1515
} ],
1616
}),
17+
test({
18+
code: 'import a from "../client/a.js"',
19+
filename: testFilePath('./restricted-paths/server/b.js'),
20+
options: [ {
21+
zones: [ { target: '**/*', from: './tests/files/restricted-paths/other' } ],
22+
} ],
23+
}),
24+
test({
25+
code: 'import a from "../client/a.js"',
26+
filename: testFilePath('./restricted-paths/client/b.js'),
27+
options: [ {
28+
zones: [ {
29+
target: './tests/files/restricted-paths/!(client)/**/*',
30+
from: './tests/files/restricted-paths/client/**/*',
31+
} ],
32+
} ],
33+
}),
1734
test({
1835
code: 'const a = require("../client/a.js")',
1936
filename: testFilePath('./restricted-paths/server/b.js'),
@@ -61,7 +78,17 @@ ruleTester.run('no-restricted-paths', rule, {
6178
} ],
6279
} ],
6380
}),
64-
81+
test({
82+
code: 'import A from "../two/a.js"',
83+
filename: testFilePath('./restricted-paths/server/one/a.js'),
84+
options: [ {
85+
zones: [ {
86+
target: '**/*',
87+
from: './tests/files/restricted-paths/server/**/*',
88+
except: ['**/a.js'],
89+
} ],
90+
} ],
91+
}),
6592

6693
// irrelevant function calls
6794
test({ code: 'notrequire("../server/b.js")' }),
@@ -93,6 +120,18 @@ ruleTester.run('no-restricted-paths', rule, {
93120
column: 15,
94121
} ],
95122
}),
123+
test({
124+
code: 'import b from "../server/b.js"',
125+
filename: testFilePath('./restricted-paths/client/a.js'),
126+
options: [ {
127+
zones: [ { target: './tests/files/restricted-paths/client/**/*', from: './tests/files/restricted-paths/server' } ],
128+
} ],
129+
errors: [ {
130+
message: 'Unexpected path "../server/b.js" imported in restricted zone.',
131+
line: 1,
132+
column: 15,
133+
} ],
134+
}),
96135
test({
97136
code: 'import a from "../client/a"\nimport c from "./c"',
98137
filename: testFilePath('./restricted-paths/server/b.js'),
@@ -190,5 +229,36 @@ ruleTester.run('no-restricted-paths', rule, {
190229
column: 15,
191230
} ],
192231
}),
232+
test({
233+
code: 'import A from "../two/a.js"',
234+
filename: testFilePath('./restricted-paths/server/one/a.js'),
235+
options: [ {
236+
zones: [ {
237+
target: '**/*',
238+
from: './tests/files/restricted-paths/server/**/*',
239+
} ],
240+
} ],
241+
errors: [ {
242+
message: 'Unexpected path "../two/a.js" imported in restricted zone.',
243+
line: 1,
244+
column: 15,
245+
} ],
246+
}),
247+
test({
248+
code: 'import A from "../two/a.js"',
249+
filename: testFilePath('./restricted-paths/server/one/a.js'),
250+
options: [ {
251+
zones: [ {
252+
target: '**/*',
253+
from: './tests/files/restricted-paths/server/**/*',
254+
except: ['a.js'],
255+
} ],
256+
} ],
257+
errors: [ {
258+
message: 'Restricted path exceptions must be glob patterns when`from` is a glob pattern',
259+
line: 1,
260+
column: 15,
261+
} ],
262+
}),
193263
],
194264
});

utils/module-require.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ exports.default = function moduleRequire(p) {
1818
const eslintPath = require.resolve('eslint');
1919
const eslintModule = createModule(eslintPath);
2020
return require(Module._resolveFilename(p, eslintModule));
21-
} catch(err) { /* ignore */ }
21+
} catch (err) { /* ignore */ }
2222

2323
try {
2424
// try relative to entry point
2525
return require.main.require(p);
26-
} catch(err) { /* ignore */ }
26+
} catch (err) { /* ignore */ }
2727

2828
// finally, try from here
2929
return require(p);

utils/resolve.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function tryRequire(target, sourceFile) {
4242
} else {
4343
resolved = require.resolve(target);
4444
}
45-
} catch(e) {
45+
} catch (e) {
4646
// If the target does not exist then just return undefined
4747
return undefined;
4848
}

0 commit comments

Comments
 (0)