1 | const {docsUrl} = require('../utilities');
|
2 |
|
3 | module.exports = {
|
4 | meta: {
|
5 | docs: {
|
6 | description: 'Prevent importing the entirety of a package.',
|
7 | category: 'Best Practices',
|
8 | recommended: false,
|
9 | uri: docsUrl('restrict-full-import'),
|
10 | },
|
11 | schema: [
|
12 | {
|
13 | restricted: {
|
14 | type: 'array',
|
15 | items: {
|
16 | type: 'string',
|
17 | },
|
18 | uniqueItems: true,
|
19 | },
|
20 | },
|
21 | ],
|
22 | },
|
23 |
|
24 | create(context) {
|
25 | const restricted = context.options[0] || [];
|
26 |
|
27 | function isRestrictedModule(mod) {
|
28 | return restricted.indexOf(mod) >= 0;
|
29 | }
|
30 |
|
31 | function isPotentiallyProblematicLeft(left) {
|
32 | return (
|
33 | left.type === 'Identifier' ||
|
34 | (left.type === 'ObjectPattern' &&
|
35 | left.properties.some((prop) => {
|
36 | return (
|
37 | prop.type === 'SpreadProperty' ||
|
38 | prop.type === 'ExperimentalRestProperty' ||
|
39 | prop.type === 'RestElement'
|
40 | );
|
41 | })) ||
|
42 | (left.type === 'ArrayPattern' &&
|
43 | left.elements.some((element) => {
|
44 | return element != null && element.type === 'RestElement';
|
45 | }))
|
46 | );
|
47 | }
|
48 |
|
49 | function isPotentiallyProblematicRight(right) {
|
50 | return (
|
51 | right &&
|
52 | right.type === 'CallExpression' &&
|
53 | right.callee.type === 'Identifier' &&
|
54 | right.callee.name === 'require' &&
|
55 | isRestrictedModule(right.arguments[0].value)
|
56 | );
|
57 | }
|
58 |
|
59 | function isFullImportSpecifier(specifier) {
|
60 | return (
|
61 | specifier.type === 'ImportDefaultSpecifier' ||
|
62 | specifier.type === 'ImportNamespaceSpecifier' ||
|
63 | (specifier.type === 'ImportSpecifier' &&
|
64 | specifier.imported.name === 'default')
|
65 | );
|
66 | }
|
67 |
|
68 | function hasFullImport(specifiers) {
|
69 | return specifiers.some(isFullImportSpecifier);
|
70 | }
|
71 |
|
72 | function checkImportDeclaration(node) {
|
73 | const specifiers = node.specifiers;
|
74 |
|
75 | if (isRestrictedModule(node.source.value) && hasFullImport(specifiers)) {
|
76 | context.report({
|
77 | node:
|
78 | specifiers.length > 1
|
79 | ? specifiers.find(isFullImportSpecifier)
|
80 | : node,
|
81 | message: `Unexpected full import of restricted module '${node.source.value}'.`,
|
82 | });
|
83 | }
|
84 | }
|
85 |
|
86 | function checkRequire(node, left, right) {
|
87 | if (
|
88 | isPotentiallyProblematicLeft(left) &&
|
89 | isPotentiallyProblematicRight(right)
|
90 | ) {
|
91 | context.report({
|
92 | node,
|
93 | message: `Unexpected full import of restricted module '${right.arguments[0].value}'.`,
|
94 | });
|
95 | }
|
96 | }
|
97 |
|
98 | function checkVariableDeclarator(node) {
|
99 | checkRequire(node, node.id, node.init);
|
100 | }
|
101 |
|
102 | function checkAssignmentExpression(node) {
|
103 | checkRequire(node, node.left, node.right);
|
104 | }
|
105 |
|
106 | return {
|
107 | ImportDeclaration: checkImportDeclaration,
|
108 | VariableDeclarator: checkVariableDeclarator,
|
109 | AssignmentExpression: checkAssignmentExpression,
|
110 | };
|
111 | },
|
112 | };
|