Skip to content

Commit 9a4a7cc

Browse files
committed
Fix #529: Add rule no-unassigned-import
1 parent 1bd0650 commit 9a4a7cc

File tree

6 files changed

+128
-0
lines changed

6 files changed

+128
-0
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
55

66
## [Unreleased]
7+
### Added
8+
- Added [`no-unassigned-import`] rule ([#529])
79

810
## 2.0.0 - WIP
911
### Added
@@ -339,6 +341,7 @@ for info on changes for earlier releases.
339341
[`no-internal-modules`]: ./docs/rules/no-internal-modules.md
340342
[`no-dynamic-require`]: ./docs/rules/no-dynamic-require.md
341343
[`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md
344+
[`no-unassigned-import`]: ./docs/rules/no-unassigned-import.md
342345

343346
[#586]: https://github.com/benmosher/eslint-plugin-import/pull/586
344347
[#578]: https://github.com/benmosher/eslint-plugin-import/pull/578
@@ -394,6 +397,7 @@ for info on changes for earlier releases.
394397
[#566]: https://github.com/benmosher/eslint-plugin-import/issues/566
395398
[#545]: https://github.com/benmosher/eslint-plugin-import/issues/545
396399
[#530]: https://github.com/benmosher/eslint-plugin-import/issues/530
400+
[#529]: https://github.com/benmosher/eslint-plugin-import/issues/529
397401
[#519]: https://github.com/benmosher/eslint-plugin-import/issues/519
398402
[#507]: https://github.com/benmosher/eslint-plugin-import/issues/507
399403
[#478]: https://github.com/benmosher/eslint-plugin-import/issues/478

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a
7474
* Enforce a newline after import statements ([`newline-after-import`])
7575
* Prefer a default export if module exports a single name ([`prefer-default-export`])
7676
* Limit the maximum number of dependencies a module can have. ([`max-dependencies`])
77+
* Forbid unassigned imports. ([`no-unassigned-import`])
7778

7879
[`first`]: ./docs/rules/first.md
7980
[`no-duplicates`]: ./docs/rules/no-duplicates.md
@@ -83,6 +84,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a
8384
[`newline-after-import`]: ./docs/rules/newline-after-import.md
8485
[`prefer-default-export`]: ./docs/rules/prefer-default-export.md
8586
[`max-dependencies`]: ./docs/rules/max-dependencies.md
87+
[`no-unassigned-import`]: ./docs/rules/no-unassigned-import.md
8688

8789
## Installation
8890

docs/rules/no-unassigned-import.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Forbid unassigned imports
2+
3+
With both CommonJS' `require` and the ES6 modules' `import` syntax, it is possible to import a module but not to use its result. This can be done explicitly by not assigning the module to as variable. Doing so can mean either of the following things:
4+
- The module is imported but not used
5+
- The module has side-effects (like [`should`](https://www.npmjs.com/package/should)). Having side-effects, makes it hard to know whether the module is actually used or can be removed. It can also make it harder to test or mock parts of your application.
6+
7+
This rule aims to remove modules with side-effects by reporting when a module is imported but not assigned.
8+
9+
## Fail
10+
11+
```js
12+
import 'should'
13+
require('should')
14+
```
15+
16+
17+
## Pass
18+
19+
```js
20+
import _ from 'foo'
21+
import _, {foo} from 'foo'
22+
import _, {foo as bar} from 'foo'
23+
import {foo as bar} from 'foo'
24+
import * as _ from 'foo'
25+
26+
const _ = require('foo')
27+
const {foo} = require('foo')
28+
const {foo: bar} = require('foo')
29+
const [a, b] = require('foo')
30+
const _ = require('foo')
31+
32+
// Module is not assigned, but it is used
33+
bar(require('foo'))
34+
require('foo').bar
35+
require('foo').bar()
36+
require('foo')()
37+
```

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const rules = {
2727
'prefer-default-export': require('./rules/prefer-default-export'),
2828
'no-dynamic-require': require('./rules/no-dynamic-require'),
2929
'unambiguous': require('./rules/unambiguous'),
30+
'no-unassigned-import': require('./rules/no-unassigned-import'),
3031

3132
// metadata-based
3233
'no-deprecated': require('./rules/no-deprecated'),

src/rules/no-unassigned-import.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import isStaticRequire from '../core/staticRequire'
2+
3+
function report(context, node) {
4+
context.report({
5+
node,
6+
message: 'Imported module should be assigned',
7+
})
8+
}
9+
10+
function create(context) {
11+
return {
12+
ImportDeclaration(node) {
13+
if (node.specifiers.length === 0) {
14+
report(context, node)
15+
}
16+
},
17+
ExpressionStatement(node) {
18+
if (node.expression.type === 'CallExpression' && isStaticRequire(node.expression)) {
19+
report(context, node.expression)
20+
}
21+
},
22+
}
23+
}
24+
25+
module.exports = {
26+
create,
27+
meta: {
28+
docs: {},
29+
schema: [
30+
{
31+
'type': 'object',
32+
'properties': {
33+
'devDependencies': { 'type': ['boolean', 'array'] },
34+
'optionalDependencies': { 'type': ['boolean', 'array'] },
35+
'peerDependencies': { 'type': ['boolean', 'array'] },
36+
},
37+
'additionalProperties': false,
38+
},
39+
],
40+
},
41+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { test } from '../utils'
2+
import * as path from 'path'
3+
4+
import { RuleTester } from 'eslint'
5+
6+
const ruleTester = new RuleTester()
7+
, rule = require('rules/no-unassigned-import')
8+
9+
const error = {
10+
ruleId: 'no-unassigned-import',
11+
message: 'Imported module should be assigned'
12+
}
13+
14+
ruleTester.run('no-unassigned-import', rule, {
15+
valid: [
16+
test({ code: 'import _ from "lodash"'}),
17+
test({ code: 'import _, {foo} from "lodash"'}),
18+
test({ code: 'import _, {foo as bar} from "lodash"'}),
19+
test({ code: 'import {foo as bar} from "lodash"'}),
20+
test({ code: 'import * as _ from "lodash"'}),
21+
test({ code: 'import _ from "./"'}),
22+
test({ code: 'const _ = require("lodash")'}),
23+
test({ code: 'const {foo} = require("lodash")'}),
24+
test({ code: 'const {foo: bar} = require("lodash")'}),
25+
test({ code: 'const [a, b] = require("lodash")'}),
26+
test({ code: 'const _ = require("lodash")'}),
27+
test({ code: 'const _ = require("./")'}),
28+
test({ code: 'foo(require("lodash"))'}),
29+
test({ code: 'require("lodash").foo'}),
30+
test({ code: 'require("lodash").foo()'}),
31+
test({ code: 'require("lodash")()'}),
32+
],
33+
invalid: [
34+
test({
35+
code: 'import "lodash"',
36+
errors: [error],
37+
}),
38+
test({
39+
code: 'require("lodash")',
40+
errors: [error],
41+
}),
42+
],
43+
})

0 commit comments

Comments
 (0)