Skip to content

Commit

Permalink
Merge remote-tracking branch 'tizmagik/feature/max-dependencies' (#489)
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
  • Loading branch information
benmosher committed Sep 1, 2016
2 parents 79f3f83 + 159034c commit 61b1765
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 1 deletion.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
### Added
- Added an `allow` option to [`no-nodejs-modules`] to allow exceptions ([#452], [#509]).
- Added [`no-absolute-path`] rule ([#530], [#538])
- [`max-dependencies`] for specifying the maximum number of dependencies (both `import` and `require`) a module can have. (see [#489], thanks [@tizmagik])

### Fixed
- [`no-named-as-default-member`] Allow default import to have a property named "default" ([#507]+[#508], thanks [@jquense] for both!)
Expand Down Expand Up @@ -294,13 +295,18 @@ for info on changes for earlier releases.
[`no-mutable-exports`]: ./docs/rules/no-mutable-exports.md
[`prefer-default-export`]: ./docs/rules/prefer-default-export.md
[`no-restricted-paths`]: ./docs/rules/no-restricted-paths.md
<<<<<<< HEAD
[`no-absolute-path`]: ./docs/rules/no-absolute-path.md
=======
[`max-dependencies`]: ./docs/rules/max-dependencies.md
>>>>>>> tizmagik/feature/max-dependencies
[#538]: https://github.com/benmosher/eslint-plugin-import/pull/538
[#509]: https://github.com/benmosher/eslint-plugin-import/pull/509
[#508]: https://github.com/benmosher/eslint-plugin-import/pull/508
[#503]: https://github.com/benmosher/eslint-plugin-import/pull/503
[#499]: https://github.com/benmosher/eslint-plugin-import/pull/499
[#489]: https://github.com/benmosher/eslint-plugin-import/pull/489
[#461]: https://github.com/benmosher/eslint-plugin-import/pull/461
[#444]: https://github.com/benmosher/eslint-plugin-import/pull/444
[#428]: https://github.com/benmosher/eslint-plugin-import/pull/428
Expand Down Expand Up @@ -434,3 +440,4 @@ for info on changes for earlier releases.
[@zloirock]: https://github.com/zloirock
[@rhys-vdw]: https://github.com/rhys-vdw
[@wKich]: https://github.com/wKich
[@tizmagik]: https://github.com/tizmagik
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a
[`no-amd`]: ./docs/rules/no-amd.md
[`no-nodejs-modules`]: ./docs/rules/no-nodejs-modules.md


**Style guide:**

* Ensure all imports appear before other statements ([`imports-first`])
Expand All @@ -63,6 +64,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a
* Enforce a convention in module import order ([`order`])
* Enforce a newline after import statements ([`newline-after-import`])
* Prefer a default export if module exports a single name ([`prefer-default-export`])
* Limit the maximum number of dependencies a module can have. ([`max-dependencies`])

[`imports-first`]: ./docs/rules/imports-first.md
[`no-duplicates`]: ./docs/rules/no-duplicates.md
Expand All @@ -71,7 +73,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a
[`order`]: ./docs/rules/order.md
[`newline-after-import`]: ./docs/rules/newline-after-import.md
[`prefer-default-export`]: ./docs/rules/prefer-default-export.md

[`max-dependencies`]: ./docs/rules/max-dependencies.md

## Installation

Expand Down
44 changes: 44 additions & 0 deletions docs/rules/max-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# max-dependencies

Forbid modules to have too many dependencies (`import` or `require` statements).

This is a useful rule because a module with too many dependencies is a code smell, and usually indicates the module is doing too much and/or should be broken up into smaller modules.

Importing multiple named exports from a single module will only count once (e.g. `import {x, y, z} from './foo'` will only count as a single dependency).

### Options

This rule takes the following option:

`max`: The maximum number of dependencies allowed. Anything over will trigger the rule. **Default is 10** if the rule is enabled and no `max` is specified.

You can set the option like this:

```js
"import/max-dependencies": ["error", {"max": 10}]
```


## Example

Given a max value of `{"max": 2}`:

### Fail

```js
import a from './a'; // 1
const b = require('./b'); // 2
import c from './c'; // 3 - exceeds max!
```

### Pass

```js
import a from './a'; // 1
const anotherA = require('./a'); // still 1
import {x, y, z} from './foo'; // 2
```

## When Not To Use It

If you don't care how many dependencies a module has.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const rules = {
'no-amd': require('./rules/no-amd'),
'no-duplicates': require('./rules/no-duplicates'),
'imports-first': require('./rules/imports-first'),
'max-dependencies': require('./rules/max-dependencies'),
'no-extraneous-dependencies': require('./rules/no-extraneous-dependencies'),
'no-absolute-path': require('./rules/no-absolute-path'),
'no-nodejs-modules': require('./rules/no-nodejs-modules'),
Expand Down
49 changes: 49 additions & 0 deletions src/rules/max-dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Set from 'es6-set'
import isStaticRequire from '../core/staticRequire'

const DEFAULT_MAX = 10

const countDependencies = (dependencies, lastNode, context) => {
const {max} = context.options[0] || { max: DEFAULT_MAX }

if (dependencies.size > max) {
context.report(
lastNode,
`Maximum number of dependencies (${max}) exceeded.`
)
}
}

module.exports = context => {
const dependencies = new Set() // keep track of dependencies
let lastNode // keep track of the last node to report on

return {
ImportDeclaration(node) {
dependencies.add(node.source.value)
lastNode = node.source
},

CallExpression(node) {
if (isStaticRequire(node)) {
const [ requirePath ] = node.arguments
dependencies.add(requirePath.value)
lastNode = node
}
},

'Program:exit': function () {
countDependencies(dependencies, lastNode, context)
},
}
}

module.exports.schema = [
{
'type': 'object',
'properties': {
'max': { 'type': 'number' },
},
'additionalProperties': false,
},
]
78 changes: 78 additions & 0 deletions tests/src/rules/max-dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { test } from '../utils'

import { RuleTester } from 'eslint'

const ruleTester = new RuleTester()
, rule = require('rules/max-dependencies')

ruleTester.run('max-dependencies', rule, {
valid: [
test({ code: 'import "./foo.js"' }),

test({ code: 'import "./foo.js"; import "./bar.js";',
options: [{
max: 2,
}],
}),

test({ code: 'import "./foo.js"; import "./bar.js"; const a = require("./foo.js"); const b = require("./bar.js");',
options: [{
max: 2,
}],
}),

test({ code: 'import {x, y, z} from "./foo"'}),
],
invalid: [
test({
code: 'import { x } from \'./foo\'; import { y } from \'./foo\'; import {z} from \'./bar\';',
options: [{
max: 1,
}],
errors: [
'Maximum number of dependencies (1) exceeded.',
],
}),

test({
code: 'import { x } from \'./foo\'; import { y } from \'./bar\'; import { z } from \'./baz\';',
options: [{
max: 2,
}],
errors: [
'Maximum number of dependencies (2) exceeded.',
],
}),

test({
code: 'import { x } from \'./foo\'; require("./bar"); import { z } from \'./baz\';',
options: [{
max: 2,
}],
errors: [
'Maximum number of dependencies (2) exceeded.',
],
}),

test({
code: 'import { x } from \'./foo\'; import { z } from \'./foo\'; require("./bar"); const path = require("path");',
options: [{
max: 2,
}],
errors: [
'Maximum number of dependencies (2) exceeded.',
],
}),

test({
code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'',
parser: 'babel-eslint',
options: [{
max: 1,
}],
errors: [
'Maximum number of dependencies (1) exceeded.',
],
}),
],
})

0 comments on commit 61b1765

Please sign in to comment.