Skip to content

Commit ade0b59

Browse files
committedMar 28, 2020
✨ add no-restricted-import
1 parent 578110e commit ade0b59

File tree

4 files changed

+466
-0
lines changed

4 files changed

+466
-0
lines changed
 

‎docs/rules/no-restricted-import.md

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# node/no-restricted-import
2+
> disallow specified modules when loaded by `require`
3+
4+
## 📖 Rule Details
5+
6+
This rule allows you to specify modules that you don’t want to use in your application.
7+
8+
### Options
9+
10+
The rule takes an array as options: the names of restricted modules.
11+
12+
```json
13+
{
14+
"no-restricted-import": ["error", [
15+
"foo-module",
16+
"bar-module"
17+
]]
18+
}
19+
```
20+
21+
You may also specify a custom message for each module you want to restrict as follows:
22+
23+
```json
24+
{
25+
"no-restricted-import": ["error", [
26+
{
27+
"name": "foo-module",
28+
"message": "Please use foo-module2 instead."
29+
},
30+
{
31+
"name": "bar-module",
32+
"message": "Please use bar-module2 instead."
33+
}
34+
]]
35+
}
36+
```
37+
38+
And you can use glob patterns in the `name` property.
39+
40+
```json
41+
{
42+
"no-restricted-import": ["error", [
43+
{
44+
"name": "lodash/*",
45+
"message": "Please use xyz-module instead."
46+
},
47+
{
48+
"name": ["foo-module/private/*", "bar-module/*", "!baz-module/good"],
49+
"message": "Please use xyz-module instead."
50+
}
51+
]]
52+
}
53+
```
54+
55+
And you can use absolute paths in the `name` property.
56+
57+
```js
58+
module.exports = {
59+
overrides: [
60+
{
61+
files: "client/**",
62+
rules: {
63+
"no-restricted-import": ["error", [
64+
{
65+
name: path.resolve(__dirname, "server/**"),
66+
message: "Don't use server code from client code."
67+
}
68+
]]
69+
}
70+
},
71+
{
72+
files: "server/**",
73+
rules: {
74+
"no-restricted-import": ["error", [
75+
{
76+
name: path.resolve(__dirname, "client/**"),
77+
message: "Don't use client code from server code."
78+
}
79+
]]
80+
}
81+
}
82+
]
83+
}
84+
```
85+
86+
### Examples
87+
88+
Examples of **incorrect** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
89+
90+
```js
91+
/*eslint no-restricted-import: ["error", ["fs", "cluster", "lodash/*"]]*/
92+
93+
import fs from 'fs';
94+
import cluster from 'cluster';
95+
import pick from 'lodash/pick';
96+
```
97+
98+
Examples of **correct** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
99+
100+
```js
101+
/*eslint no-restricted-import: ["error", ["fs", "cluster", "lodash/*"]]*/
102+
103+
import crypto from 'crypto';
104+
import _ from 'lodash';
105+
```
106+
107+
```js
108+
/*eslint no-restricted-import: ["error", ["fs", "cluster", { "name": ["lodash/*", "!lodash/pick"] }]]*/
109+
110+
import pick from 'lodash/pick';
111+
```
112+
113+
## 🔎 Implementation
114+
115+
- [Rule source](../../lib/rules/no-restricted-import.js)
116+
- [Test source](../../tests/lib/rules/no-restricted-import.js)

‎lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module.exports = {
2727
"no-path-concat": require("./rules/no-path-concat"),
2828
"no-process-env": require("./rules/no-process-env"),
2929
"no-process-exit": require("./rules/no-process-exit"),
30+
"no-restricted-import": require("./rules/no-restricted-import"),
3031
"no-restricted-require": require("./rules/no-restricted-require"),
3132
"no-sync": require("./rules/no-sync"),
3233
"no-unpublished-bin": require("./rules/no-unpublished-bin"),

‎lib/rules/no-restricted-import.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @author Toru Nagashima
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const check = require("../util/check-restricted")
8+
const visit = require("../util/visit-import")
9+
10+
module.exports = {
11+
meta: {
12+
type: "suggestion",
13+
docs: {
14+
description: "disallow specified modules when loaded by `require`",
15+
category: "Stylistic Issues",
16+
recommended: false,
17+
url:
18+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-restricted-import.md",
19+
},
20+
fixable: null,
21+
schema: [
22+
{
23+
type: "array",
24+
items: {
25+
anyOf: [
26+
{ type: "string" },
27+
{
28+
type: "object",
29+
properties: {
30+
name: {
31+
anyOf: [
32+
{ type: "string" },
33+
{
34+
type: "array",
35+
items: { type: "string" },
36+
additionalItems: false,
37+
},
38+
],
39+
},
40+
message: { type: "string" },
41+
},
42+
additionalProperties: false,
43+
required: ["name"],
44+
},
45+
],
46+
},
47+
additionalItems: false,
48+
},
49+
],
50+
messages: {
51+
restricted:
52+
// eslint-disable-next-line @mysticatea/eslint-plugin/report-message-format
53+
"'{{name}}' module is restricted from being used.{{customMessage}}",
54+
},
55+
},
56+
57+
create(context) {
58+
const opts = { includeCore: true }
59+
return visit(context, opts, targets => check(context, targets))
60+
},
61+
}
+288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/**
2+
* @author Christian Schulz
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const path = require("path")
8+
const { Linter, RuleTester } = require("eslint")
9+
const rule = require("../../../lib/rules/no-restricted-import")
10+
11+
const DynamicImportSupported = (() => {
12+
const config = { parserOptions: { ecmaVersion: 2020 } }
13+
const messages = new Linter().verify("import(s)", config)
14+
return messages.length === 0
15+
})()
16+
17+
if (!DynamicImportSupported) {
18+
//eslint-disable-next-line no-console
19+
console.warn(
20+
"[%s] Skip tests for 'import()'",
21+
path.basename(__filename, ".js")
22+
)
23+
}
24+
25+
new RuleTester({
26+
parserOptions: {
27+
ecmaVersion: 2015,
28+
sourceType: "module",
29+
},
30+
}).run("no-restricted-import", rule, {
31+
valid: [
32+
{ code: 'import "fs"', options: [["crypto"]] },
33+
{ code: 'import "path"', options: [["crypto", "stream", "os"]] },
34+
'import "fs "',
35+
{ code: 'import "foo/bar";', options: [["foo"]] },
36+
{
37+
code: 'import "foo/bar";',
38+
options: [[{ name: ["foo", "bar"] }]],
39+
},
40+
{
41+
code: 'import "foo/bar";',
42+
options: [[{ name: ["foo/c*"] }]],
43+
},
44+
{
45+
code: 'import "foo/bar";',
46+
options: [[{ name: ["foo"] }, { name: ["foo/c*"] }]],
47+
},
48+
{
49+
code: 'import "foo/bar";',
50+
options: [[{ name: ["foo"] }, { name: ["foo/*", "!foo/bar"] }]],
51+
},
52+
{
53+
code: 'import "os "',
54+
options: [["fs", "crypto ", "stream", "os"]],
55+
},
56+
{
57+
code: 'import "./foo"',
58+
options: [["foo"]],
59+
},
60+
{
61+
code: 'import "foo"',
62+
options: [["./foo"]],
63+
},
64+
{
65+
code: 'import "foo/bar";',
66+
options: [[{ name: "@foo/bar" }]],
67+
},
68+
{
69+
filename: path.resolve(__dirname, "lib/sub/test.js"),
70+
code: 'import "../foo";',
71+
options: [[{ name: path.resolve(__dirname, "foo") }]],
72+
},
73+
74+
// import()
75+
...(DynamicImportSupported
76+
? [
77+
{
78+
code: "import(fs)",
79+
options: [["fs"]],
80+
parserOptions: { ecmaVersion: 2020 },
81+
},
82+
]
83+
: []),
84+
],
85+
invalid: [
86+
{
87+
code: 'import "fs"',
88+
options: [["fs"]],
89+
errors: [
90+
{
91+
messageId: "restricted",
92+
data: { name: "fs", customMessage: "" },
93+
},
94+
],
95+
},
96+
{
97+
code: 'import fs from "fs"',
98+
options: [["fs"]],
99+
errors: [
100+
{
101+
messageId: "restricted",
102+
data: { name: "fs", customMessage: "" },
103+
},
104+
],
105+
},
106+
{
107+
code: 'import {} from "fs"',
108+
options: [["fs"]],
109+
errors: [
110+
{
111+
messageId: "restricted",
112+
data: { name: "fs", customMessage: "" },
113+
},
114+
],
115+
},
116+
{
117+
code: 'export * from "fs"',
118+
options: [["fs"]],
119+
errors: [
120+
{
121+
messageId: "restricted",
122+
data: { name: "fs", customMessage: "" },
123+
},
124+
],
125+
},
126+
{
127+
code: 'export {} from "fs"',
128+
options: [["fs"]],
129+
errors: [
130+
{
131+
messageId: "restricted",
132+
data: { name: "fs", customMessage: "" },
133+
},
134+
],
135+
},
136+
{
137+
code: 'import "foo/bar";',
138+
options: [["foo/bar"]],
139+
errors: [
140+
{
141+
messageId: "restricted",
142+
data: { name: "foo/bar", customMessage: "" },
143+
},
144+
],
145+
},
146+
{
147+
code: 'import "foo/bar";',
148+
options: [[{ name: ["foo/bar"] }]],
149+
errors: [
150+
{
151+
messageId: "restricted",
152+
data: { name: "foo/bar", customMessage: "" },
153+
},
154+
],
155+
},
156+
{
157+
code: 'import "foo/bar";',
158+
options: [[{ name: ["foo/*"] }]],
159+
errors: [
160+
{
161+
messageId: "restricted",
162+
data: { name: "foo/bar", customMessage: "" },
163+
},
164+
],
165+
},
166+
{
167+
code: 'import "foo/bar";',
168+
options: [[{ name: ["foo/*"] }, { name: ["foo"] }]],
169+
errors: [
170+
{
171+
messageId: "restricted",
172+
data: { name: "foo/bar", customMessage: "" },
173+
},
174+
],
175+
},
176+
{
177+
code: 'import "foo/bar";',
178+
options: [[{ name: ["foo/*", "!foo/baz"] }, { name: ["foo"] }]],
179+
errors: [
180+
{
181+
messageId: "restricted",
182+
data: { name: "foo/bar", customMessage: "" },
183+
},
184+
],
185+
},
186+
{
187+
code: 'import "foo";',
188+
options: [
189+
[
190+
{
191+
name: "foo",
192+
message: "Please use 'bar' module instead.",
193+
},
194+
],
195+
],
196+
errors: [
197+
{
198+
messageId: "restricted",
199+
data: {
200+
name: "foo",
201+
customMessage: " Please use 'bar' module instead.",
202+
},
203+
},
204+
],
205+
},
206+
{
207+
code: 'import "bar";',
208+
options: [
209+
[
210+
"foo",
211+
{
212+
name: "bar",
213+
message: "Please use 'baz' module instead.",
214+
},
215+
"baz",
216+
],
217+
],
218+
errors: [
219+
{
220+
messageId: "restricted",
221+
data: {
222+
name: "bar",
223+
customMessage: " Please use 'baz' module instead.",
224+
},
225+
},
226+
],
227+
},
228+
{
229+
code: 'import "@foo/bar";',
230+
options: [[{ name: "@foo/*" }]],
231+
errors: [
232+
{
233+
messageId: "restricted",
234+
data: { name: "@foo/bar", customMessage: "" },
235+
},
236+
],
237+
},
238+
{
239+
code: 'import "./foo/bar";',
240+
options: [[{ name: "./foo/*" }]],
241+
errors: [
242+
{
243+
messageId: "restricted",
244+
data: { name: "./foo/bar", customMessage: "" },
245+
},
246+
],
247+
},
248+
{
249+
filename: path.resolve(__dirname, "lib/test.js"),
250+
code: 'import "../foo";',
251+
options: [[{ name: path.resolve(__dirname, "foo") }]],
252+
errors: [
253+
{
254+
messageId: "restricted",
255+
data: { name: "../foo", customMessage: "" },
256+
},
257+
],
258+
},
259+
{
260+
filename: path.resolve(__dirname, "lib/sub/test.js"),
261+
code: 'import "../../foo";',
262+
options: [[{ name: path.resolve(__dirname, "foo") }]],
263+
errors: [
264+
{
265+
messageId: "restricted",
266+
data: { name: "../../foo", customMessage: "" },
267+
},
268+
],
269+
},
270+
271+
// import()
272+
...(DynamicImportSupported
273+
? [
274+
{
275+
code: 'import("fs")',
276+
options: [["fs"]],
277+
parserOptions: { ecmaVersion: 2020 },
278+
errors: [
279+
{
280+
messageId: "restricted",
281+
data: { name: "fs", customMessage: "" },
282+
},
283+
],
284+
},
285+
]
286+
: []),
287+
],
288+
})

0 commit comments

Comments
 (0)
Please sign in to comment.