Skip to content

Commit a00bc6e

Browse files
committed
Merge pull request #117 from scothis/require-ext
Add require-extension rule
2 parents c0ed5fc + 73f565d commit a00bc6e

File tree

4 files changed

+209
-2
lines changed

4 files changed

+209
-2
lines changed

docs/rules/require-extenion.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Restrict file extensions that may be required (require-extension)
2+
3+
`require()` statements should generally not include a file extension as there is a well defined mechanism for resolving a module ID to a specific file. This rule inspects the module ID being required and creates a warning if the ID contains a '.jsx' file extension.
4+
5+
Note: this rule does not prevent required files from containing these extensions, it merely prevents the extension from being included in the `require()` statement.
6+
7+
## Rule Details
8+
9+
The following patterns are considered warnings:
10+
11+
```js
12+
var index = require('./index.jsx');
13+
14+
// When [1, {extensions: ['.js']}]
15+
var index = require('./index.js');
16+
```
17+
18+
The following patterns are not considered warnings:
19+
20+
```js
21+
var index = require('./index');
22+
23+
var eslint = require('eslint');
24+
```
25+
26+
## Rule Options
27+
28+
The set of forbidden extensions is configurable. By default '.jsx' is blocked. If you wanted to forbid both '.jsx' and '.js', the configuration would be:
29+
30+
```js
31+
"rules": {
32+
"react/require-extension": [1, { "extensions": [".js", ".jsx"] }],
33+
}
34+
```
35+
36+
## When Not To Use It
37+
38+
If you have file in your project with a '.jsx' file extension and do not have `require()` configured to automatically resolve '.jsx' files.

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ module.exports = {
1818
'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
1919
'jsx-sort-prop-types': require('./lib/rules/jsx-sort-prop-types'),
2020
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
21-
'sort-comp': require('./lib/rules/sort-comp')
21+
'sort-comp': require('./lib/rules/sort-comp'),
22+
'require-extension': require('./lib/rules/require-extension')
2223
},
2324
rulesConfig: {
2425
'jsx-uses-react': 0,
@@ -37,6 +38,7 @@ module.exports = {
3738
'jsx-sort-props': 0,
3839
'jsx-sort-prop-types': 0,
3940
'jsx-boolean-value': 0,
40-
'sort-comp': 0
41+
'sort-comp': 0,
42+
'require-extension': 0
4143
}
4244
};

lib/rules/require-extension.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @fileoverview Restrict file extensions that may be required
3+
* @author Scott Andrews
4+
*/
5+
'use strict';
6+
7+
var path = require('path');
8+
9+
// ------------------------------------------------------------------------------
10+
// Constants
11+
// ------------------------------------------------------------------------------
12+
13+
var DEFAULTS = {
14+
extentions: ['.jsx']
15+
};
16+
17+
// ------------------------------------------------------------------------------
18+
// Rule Definition
19+
// ------------------------------------------------------------------------------
20+
21+
module.exports = function(context) {
22+
23+
function isRequire(expression) {
24+
return expression.callee.name === 'require';
25+
}
26+
27+
function getId(expression) {
28+
return expression.arguments[0] && expression.arguments[0].value;
29+
}
30+
31+
function getExtension(id) {
32+
return path.extname(id || '');
33+
}
34+
35+
function getExtentionsConfig() {
36+
return context.options[0] && context.options[0].extensions || DEFAULTS.extentions;
37+
}
38+
39+
var forbiddenExtensions = getExtentionsConfig().reduce(function (extensions, extension) {
40+
extensions[extension] = true;
41+
return extensions;
42+
}, Object.create(null));
43+
44+
function isForbiddenExtension(ext) {
45+
return ext in forbiddenExtensions;
46+
}
47+
48+
// --------------------------------------------------------------------------
49+
// Public
50+
// --------------------------------------------------------------------------
51+
52+
return {
53+
54+
CallExpression: function(node) {
55+
if (isRequire(node)) {
56+
var ext = getExtension(getId(node));
57+
if (isForbiddenExtension(ext)) {
58+
context.report(node, 'Unable to require module with extension \'' + ext + '\'');
59+
}
60+
}
61+
}
62+
63+
};
64+
65+
};
66+
67+
module.exports.schema = [{
68+
type: 'object',
69+
properties: {
70+
extensions: {type: 'array'}
71+
}
72+
}];

tests/lib/rules/require-extension.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* @fileoverview Restrict file extensions that may be required
3+
* @author Scott Andrews
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var eslint = require('eslint').linter;
12+
var ESLintTester = require('eslint-tester');
13+
14+
// ------------------------------------------------------------------------------
15+
// Code Snippets
16+
// ------------------------------------------------------------------------------
17+
18+
var REQUIRE_PACKAGE = 'require(\'eslint\')';
19+
20+
var REQUIRE_JS = 'require(\'./index.js\')';
21+
22+
var REQUIRE_JSX = 'require(\'./index.jsx\')';
23+
24+
var REQUIRE_JSON = 'require(\'./index.json\')';
25+
26+
var REQUIRE_EMPTY = 'require()';
27+
28+
var REQUIRE_OBJECT = 'require({})';
29+
30+
// ------------------------------------------------------------------------------
31+
// Tests
32+
// ------------------------------------------------------------------------------
33+
34+
var eslintTester = new ESLintTester(eslint);
35+
eslintTester.addRuleTest('lib/rules/require-extension', {
36+
37+
valid: [
38+
{
39+
code: REQUIRE_PACKAGE
40+
}, {
41+
code: REQUIRE_JS
42+
}, {
43+
code: REQUIRE_JSON
44+
}, {
45+
code: REQUIRE_EMPTY
46+
}, {
47+
code: REQUIRE_OBJECT
48+
}, {
49+
code: REQUIRE_PACKAGE,
50+
args: [1]
51+
}, {
52+
code: REQUIRE_JS,
53+
args: [1]
54+
}, {
55+
code: REQUIRE_JSON,
56+
args: [1]
57+
}, {
58+
code: REQUIRE_EMPTY,
59+
args: [1]
60+
}, {
61+
code: REQUIRE_OBJECT,
62+
args: [1]
63+
}, {
64+
code: REQUIRE_JSON,
65+
args: [1, {extensions: ['.js']}]
66+
}, {
67+
code: REQUIRE_JSX,
68+
args: [1, {extensions: ['.js']}]
69+
}
70+
],
71+
72+
invalid: [
73+
{
74+
code: REQUIRE_JSX,
75+
errors: [{message: 'Unable to require module with extension \'.jsx\''}]
76+
}, {
77+
code: REQUIRE_JSX,
78+
args: [1],
79+
errors: [{message: 'Unable to require module with extension \'.jsx\''}]
80+
}, {
81+
code: REQUIRE_JS,
82+
args: [1, {extensions: ['.js']}],
83+
errors: [{message: 'Unable to require module with extension \'.js\''}]
84+
}, {
85+
code: REQUIRE_JS,
86+
args: [1, {extensions: ['.js', '.jsx']}],
87+
errors: [{message: 'Unable to require module with extension \'.js\''}]
88+
}, {
89+
code: REQUIRE_JSX,
90+
args: [1, {extensions: ['.js', '.jsx']}],
91+
errors: [{message: 'Unable to require module with extension \'.jsx\''}]
92+
}
93+
]
94+
95+
});

0 commit comments

Comments
 (0)