Skip to content

Commit 73f565d

Browse files
committed
Allow blocking of '.jsx' extensions with require()
Created a new rule, 'require-extension', which inspects the required module ID and warns if the file extension is .jsx. The particular disallowed file extensions are configurable, defaulting to just '.jsx'. If you wanted to also disallow '.js', the rule can be configured as: "rules": { "react/require-extension": [1, { "extensions": [".js", ".jsx"] }], } Note: this rule does not prevent required files from containing forbidden extensions, it merely prevents the extension from being included in the `require()` statement. This rule is inspired by the Airbnb react style guide: https://github.com/airbnb/javascript/tree/master/react#naming
1 parent c0ed5fc commit 73f565d

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)