Skip to content

Commit

Permalink
[New] Add forbid options for no-internal-modules
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaumewuip committed Jul 4, 2020
1 parent 843055c commit 345cfb0
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 9 deletions.
11 changes: 9 additions & 2 deletions docs/rules/no-internal-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ Use this rule to prevent importing the submodules of other modules.

## Rule Details

This rule has one option, `allow` which is an array of [minimatch/glob patterns](https://github.com/isaacs/node-glob#glob-primer) patterns that whitelist paths and import statements that can be imported with reaching.
This rule has two options that are arrays of [minimatch/glob patterns](https://github.com/isaacs/node-glob#glob-primer) patterns:

- `allow` that include paths and import statements that can be imported with reaching.
- `forbid` that exclude paths and import statements that can be imported with reaching.

### Examples

Expand All @@ -13,6 +16,8 @@ Given the following folder structure:
```
my-project
├── actions
│ └── special
│ └── getAdmin.js
│ └── getUser.js
│ └── updateUser.js
├── reducer
Expand All @@ -33,7 +38,8 @@ And the .eslintrc file:
...
"rules": {
"import/no-internal-modules": [ "error", {
"allow": [ "**/actions/*", "source-map-support/*" ]
"allow": [ "**/actions/*", "source-map-support/*" ],
"forbid": [ "**/actions/special/*" ],
} ]
}
}
Expand All @@ -49,6 +55,7 @@ The following patterns are considered problems:
import { settings } from './app/index'; // Reaching to "./app/index" is not allowed
import userReducer from './reducer/user'; // Reaching to "./reducer/user" is not allowed
import configureStore from './redux/configureStore'; // Reaching to "./redux/configureStore" is not allowed
import getAdmin from './actions/special/getAdmin'; // Reaching to "./actions/special/getAdmin" is not allowed

export { settings } from './app/index'; // Reaching to "./app/index" is not allowed
export * from './reducer/user'; // Reaching to "./reducer/user" is not allowed
Expand Down
36 changes: 29 additions & 7 deletions src/rules/no-internal-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ module.exports = {
type: 'string',
},
},
forbid: {
type: 'array',
items: {
type: 'string',
},
},
},
additionalProperties: false,
},
Expand All @@ -31,12 +37,18 @@ module.exports = {
create: function noReachingInside(context) {
const options = context.options[0] || {}
const allowRegexps = (options.allow || []).map(p => minimatch.makeRe(p))
const forbidRegexps = (options.forbid || []).map(p => minimatch.makeRe(p))

// test if reaching to this destination is allowed
function reachingAllowed(importPath) {
return allowRegexps.some(re => re.test(importPath))
}

// test if reaching to this destination is forbidden
function reachingForbidden(importPath) {
return forbidRegexps.some(re => re.test(importPath))
}

// minimatch patterns are expected to use / path separators, like import
// statements, so normalize paths to use the same
function normalizeSep(somePath) {
Expand All @@ -58,21 +70,31 @@ module.exports = {
}, [])

const nonScopeSteps = steps.filter(step => step.indexOf('@') !== 0)

if (nonScopeSteps.length <= 1) return false

// before trying to resolve, see if the raw import (with relative
// segments resolved) matches an allowed pattern
// segments resolved) matches an forbidden pattern
const justSteps = steps.join('/')
if (reachingAllowed(justSteps) || reachingAllowed(`/${justSteps}`)) return false

if (reachingForbidden(justSteps) || reachingForbidden(`/${justSteps}`)) return true

const resolved = resolve(importPath, context)

// if the import statement doesn't match directly, try to match the
// resolved path if the import is resolvable
const resolved = resolve(importPath, context)
if (!resolved || reachingAllowed(normalizeSep(resolved))) return false
if (resolved && reachingForbidden(normalizeSep(resolved))) return true

// path is not explicitly forbidden

// if path not explicitly allowed, consider it forbidden
if (
!reachingAllowed(justSteps)
&& !reachingAllowed(`/${justSteps}`)
&& (resolved && !reachingAllowed(normalizeSep(resolved)))
) return true

// this import was not allowed by the allowed paths, and reaches
// so it is a violation
return true
return false
}

function checkImportForReaching(importPath, node) {
Expand Down
47 changes: 47 additions & 0 deletions tests/src/rules/no-internal-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ ruleTester.run('no-internal-modules', rule, {
filename: testFilePath('./internal-modules/plugins/plugin.js'),
options: [],
}),
test({
code: 'import a from "./plugin2"',
filename: testFilePath('./internal-modules/plugins/plugin.js'),
options: [ {
forbid: [ '**/something/*' ],
} ],
}),
test({
code: 'const a = require("./plugin2")',
filename: testFilePath('./internal-modules/plugins/plugin.js'),
Expand Down Expand Up @@ -184,6 +191,46 @@ ruleTester.run('no-internal-modules', rule, {
},
],
}),
test({
code: 'import a from "./plugin2/index.js"',
filename: testFilePath('./internal-modules/plugins/plugin.js'),
options: [ {
forbid: [ '**/plugin2/*' ],
} ],
errors: [ {
message: 'Reaching to "./plugin2/index.js" is not allowed.',
line: 1,
column: 15,
} ],
}),
test({
code: 'export * from "jquery/dist/jquery"',
filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'),
options: [ {
allow: [ 'jquery/dist/*' ],
forbid: [ 'jquery/dist/jquery' ],
} ],
errors: [ {
message: 'Reaching to "jquery/dist/jquery" is not allowed.',
line: 1,
column: 15,
} ],
}),
test({
code: 'import a from "../api/service/index"',
filename: testFilePath('./internal-modules/plugins/plugin.js'),
options: [ {
allow: [ '**' ],
forbid: [ '**/api/**' ],
} ],
errors: [
{
message: 'Reaching to "../api/service/index" is not allowed.',
line: 1,
column: 15,
},
],
}),
// exports
test({
code: 'export * from "./plugin2/index.js";\nexport * from "./plugin2/app/index"',
Expand Down

0 comments on commit 345cfb0

Please sign in to comment.