Skip to content

Commit 578110e

Browse files
committed
⚒ simplify the options of no-restricted-require
1 parent 8788a11 commit 578110e

File tree

4 files changed

+323
-305
lines changed

4 files changed

+323
-305
lines changed

docs/rules/no-restricted-require.md

Lines changed: 69 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,96 +14,107 @@ This rule allows you to specify modules that you don’t want to use in your app
1414

1515
### Options
1616

17-
The rule takes one or more strings as options: the names of restricted modules.
17+
The rule takes an array as options: the names of restricted modules.
1818

1919
```json
20-
"no-restricted-require": ["error", "foo-module", "bar-module"]
21-
```
22-
23-
It can also take an object with lists of `paths` and gitignore-style `patterns` strings.
24-
25-
```json
26-
"no-restricted-require": ["error", { "paths": ["foo-module", "bar-module"] }]
27-
```
28-
29-
```json
30-
"no-restricted-require": ["error", {
31-
"paths": ["foo-module", "bar-module"],
32-
"patterns": ["foo-module/private/*", "bar-module/*","!baz-module/good"]
33-
}]
34-
```
35-
36-
You may also specify a custom message for any paths you want to restrict as follows:
37-
38-
```json
39-
"no-restricted-require": ["error", {
40-
"name": "foo-module",
41-
"message": "Please use bar-module instead."
42-
}
43-
]
20+
{
21+
"no-restricted-require": ["error", [
22+
"foo-module",
23+
"bar-module"
24+
]]
25+
}
4426
```
4527

46-
or like this:
28+
You may also specify a custom message for each module you want to restrict as follows:
4729

4830
```json
49-
"no-restricted-require": ["error",{
50-
"paths":[{
51-
"name": "foo-module",
52-
"message": "Please use bar-module instead."
53-
}]
54-
}]
31+
{
32+
"no-restricted-require": ["error", [
33+
{
34+
"name": "foo-module",
35+
"message": "Please use foo-module2 instead."
36+
},
37+
{
38+
"name": "bar-module",
39+
"message": "Please use bar-module2 instead."
40+
}
41+
]]
42+
}
5543
```
5644

57-
The custom message will be appended to the default error message. Please note that you may not specify custom error messages for restricted patterns as a particular module may match more than one pattern.
58-
59-
60-
To restrict the use of all Node.js core modules (via https://github.com/nodejs/node/tree/master/lib):
45+
And you can use glob patterns in the `name` property.
6146

6247
```json
6348
{
64-
"no-restricted-require": ["error",
65-
"assert","buffer","child_process","cluster","crypto","dgram","dns","domain","events","freelist","fs","http","https","module","net","os","path","punycode","querystring","readline","repl","smalloc","stream","string_decoder","sys","timers","tls","tracing","tty","url","util","vm","zlib"
66-
]
49+
"no-restricted-require": ["error", [
50+
{
51+
"name": "lodash/*",
52+
"message": "Please use xyz-module instead."
53+
},
54+
{
55+
"name": ["foo-module/private/*", "bar-module/*", "!baz-module/good"],
56+
"message": "Please use xyz-module instead."
57+
}
58+
]]
6759
}
6860
```
6961

70-
Examples of **incorrect** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
62+
And you can use absolute paths in the `name` property.
7163

7264
```js
73-
/*eslint no-restricted-require: ["error", "fs", "cluster"]*/
74-
75-
var fs = require('fs');
76-
var cluster = require('cluster');
65+
module.exports = {
66+
overrides: [
67+
{
68+
files: "client/**",
69+
rules: {
70+
"no-restricted-require": ["error", [
71+
{
72+
name: path.resolve(__dirname, "server/**"),
73+
message: "Don't use server code from client code."
74+
}
75+
]]
76+
}
77+
},
78+
{
79+
files: "server/**",
80+
rules: {
81+
"no-restricted-require": ["error", [
82+
{
83+
name: path.resolve(__dirname, "client/**"),
84+
message: "Don't use client code from server code."
85+
}
86+
]]
87+
}
88+
}
89+
]
90+
}
7791
```
7892

79-
```js
80-
/*eslint no-restricted-require: ["error", {"paths": ["cluster"] }]*/
93+
### Examples
8194

82-
var cluster = require('cluster');
83-
```
95+
Examples of **incorrect** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
8496

8597
```js
86-
/*eslint no-restricted-require: ["error", { "patterns": ["lodash/*"] }]*/
98+
/*eslint no-restricted-require: ["error", ["fs", "cluster", "lodash/*"]]*/
8799

88-
var pick = require('lodash/pick');
100+
const fs = require('fs');
101+
const cluster = require('cluster');
102+
const pick = require('lodash/pick');
89103
```
90104

91105
Examples of **correct** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
92106

93107
```js
94-
/*eslint no-restricted-require: ["error", "fs", "cluster"]*/
108+
/*eslint no-restricted-require: ["error", ["fs", "cluster", "lodash/*"]]*/
95109

96-
var crypto = require('crypto');
110+
const crypto = require('crypto');
111+
const _ = require('lodash');
97112
```
98113

99114
```js
100-
/*eslint no-restricted-require: ["error", {
101-
"paths": ["fs", "cluster"],
102-
"patterns": ["lodash/*", "!lodash/pick"]
103-
}]*/
115+
/*eslint no-restricted-require: ["error", ["fs", "cluster", { "name": ["lodash/*", "!lodash/pick"] }]]*/
104116

105-
var crypto = require('crypto');
106-
var pick = require('lodash/pick');
117+
const pick = require('lodash/pick');
107118
```
108119

109120
## 🔎 Implementation

lib/rules/no-restricted-require.js

Lines changed: 34 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,12 @@
11
/**
22
* @author Christian Schulz
3+
* @author Toru Nagashima
34
* See LICENSE file in root directory for full license.
45
*/
56
"use strict"
67

7-
const ignore = require("ignore")
8-
9-
const arrayOfStrings = {
10-
type: "array",
11-
items: { type: "string" },
12-
uniqueItems: true,
13-
}
14-
15-
const arrayOfStringsOrObjects = {
16-
type: "array",
17-
items: {
18-
anyOf: [
19-
{ type: "string" },
20-
{
21-
type: "object",
22-
properties: {
23-
name: { type: "string" },
24-
message: {
25-
type: "string",
26-
minLength: 1,
27-
},
28-
},
29-
additionalProperties: false,
30-
required: ["name"],
31-
},
32-
],
33-
},
34-
uniqueItems: true,
35-
}
8+
const check = require("../util/check-restricted")
9+
const visit = require("../util/visit-require")
3610

3711
module.exports = {
3812
meta: {
@@ -45,148 +19,44 @@ module.exports = {
4519
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-restricted-require.md",
4620
},
4721
fixable: null,
48-
schema: {
49-
anyOf: [
50-
arrayOfStringsOrObjects,
51-
{
52-
type: "array",
53-
items: {
54-
type: "object",
55-
properties: {
56-
paths: arrayOfStringsOrObjects,
57-
patterns: arrayOfStrings,
22+
schema: [
23+
{
24+
type: "array",
25+
items: {
26+
anyOf: [
27+
{ type: "string" },
28+
{
29+
type: "object",
30+
properties: {
31+
name: {
32+
anyOf: [
33+
{ type: "string" },
34+
{
35+
type: "array",
36+
items: { type: "string" },
37+
additionalItems: false,
38+
},
39+
],
40+
},
41+
message: { type: "string" },
42+
},
43+
additionalProperties: false,
44+
required: ["name"],
5845
},
59-
additionalProperties: false,
60-
},
61-
additionalItems: false,
46+
],
6247
},
63-
],
64-
},
48+
additionalItems: false,
49+
},
50+
],
6551
messages: {
66-
defaultMessage: "'{{name}}' module is restricted from being used.",
67-
customMessage:
52+
restricted:
6853
// eslint-disable-next-line @mysticatea/eslint-plugin/report-message-format
69-
"'{{name}}' module is restricted from being used. {{customMessage}}",
70-
patternMessage:
71-
"'{{name}}' module is restricted from being used by a pattern.",
54+
"'{{name}}' module is restricted from being used.{{customMessage}}",
7255
},
7356
},
7457

7558
create(context) {
76-
const options = Array.isArray(context.options) ? context.options : []
77-
const isPathAndPatternsObject =
78-
typeof options[0] === "object" &&
79-
(Object.prototype.hasOwnProperty.call(options[0], "paths") ||
80-
Object.prototype.hasOwnProperty.call(options[0], "patterns"))
81-
82-
const restrictedPaths =
83-
(isPathAndPatternsObject ? options[0].paths : context.options) || []
84-
const restrictedPatterns =
85-
(isPathAndPatternsObject ? options[0].patterns : []) || []
86-
87-
const restrictedPathMessages = restrictedPaths.reduce(
88-
(memo, importName) => {
89-
if (typeof importName === "string") {
90-
memo[importName] = null
91-
} else {
92-
memo[importName.name] = importName.message
93-
}
94-
return memo
95-
},
96-
{}
97-
)
98-
99-
// if no imports are restricted we don"t need to check
100-
if (
101-
Object.keys(restrictedPaths).length === 0 &&
102-
restrictedPatterns.length === 0
103-
) {
104-
return {}
105-
}
106-
107-
const ig = ignore().add(restrictedPatterns)
108-
109-
/**
110-
* Function to check if a node is a string literal.
111-
* @param {ASTNode} node The node to check.
112-
* @returns {boolean} If the node is a string literal.
113-
*/
114-
function isString(node) {
115-
return (
116-
node &&
117-
node.type === "Literal" &&
118-
typeof node.value === "string"
119-
)
120-
}
121-
122-
/**
123-
* Function to check if a node is a require call.
124-
* @param {ASTNode} node The node to check.
125-
* @returns {boolean} If the node is a require call.
126-
*/
127-
function isRequireCall(node) {
128-
return (
129-
node.callee.type === "Identifier" &&
130-
node.callee.name === "require"
131-
)
132-
}
133-
134-
/**
135-
* Report a restricted path.
136-
* @param {node} node representing the restricted path reference
137-
* @returns {void}
138-
* @private
139-
*/
140-
function reportPath(node) {
141-
const name = node.arguments[0].value.trim()
142-
const customMessage = restrictedPathMessages[name]
143-
const messageId = customMessage ? "customMessage" : "defaultMessage"
144-
145-
context.report({
146-
node,
147-
messageId,
148-
data: {
149-
name,
150-
customMessage,
151-
},
152-
})
153-
}
154-
155-
/**
156-
* Check if the given name is a restricted path name
157-
* @param {string} name name of a variable
158-
* @returns {boolean} whether the variable is a restricted path or not
159-
* @private
160-
*/
161-
function isRestrictedPath(name) {
162-
return Object.prototype.hasOwnProperty.call(
163-
restrictedPathMessages,
164-
name
165-
)
166-
}
167-
168-
return {
169-
CallExpression(node) {
170-
if (isRequireCall(node)) {
171-
// node has arguments and first argument is string
172-
if (node.arguments.length && isString(node.arguments[0])) {
173-
const name = node.arguments[0].value.trim()
174-
175-
// check if argument value is in restricted modules array
176-
if (isRestrictedPath(name)) {
177-
reportPath(node)
178-
}
179-
180-
if (restrictedPatterns.length > 0 && ig.ignores(name)) {
181-
context.report({
182-
node,
183-
messageId: "patternMessage",
184-
data: { name },
185-
})
186-
}
187-
}
188-
}
189-
},
190-
}
59+
const opts = { includeCore: true }
60+
return visit(context, opts, targets => check(context, targets))
19161
},
19262
}

0 commit comments

Comments
 (0)