1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const has = require('has');
|
9 | const docsUrl = require('../util/docsUrl');
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | module.exports = {
|
16 | meta: {
|
17 | docs: {
|
18 | description: 'Forbid certain elements',
|
19 | category: 'Best Practices',
|
20 | recommended: false,
|
21 | url: docsUrl('forbid-elements')
|
22 | },
|
23 |
|
24 | schema: [{
|
25 | type: 'object',
|
26 | properties: {
|
27 | forbid: {
|
28 | type: 'array',
|
29 | items: {
|
30 | anyOf: [
|
31 | {type: 'string'},
|
32 | {
|
33 | type: 'object',
|
34 | properties: {
|
35 | element: {type: 'string'},
|
36 | message: {type: 'string'}
|
37 | },
|
38 | required: ['element'],
|
39 | additionalProperties: false
|
40 | }
|
41 | ]
|
42 | }
|
43 | }
|
44 | },
|
45 | additionalProperties: false
|
46 | }]
|
47 | },
|
48 |
|
49 | create(context) {
|
50 | const configuration = context.options[0] || {};
|
51 | const forbidConfiguration = configuration.forbid || [];
|
52 |
|
53 | const indexedForbidConfigs = {};
|
54 |
|
55 | forbidConfiguration.forEach((item) => {
|
56 | if (typeof item === 'string') {
|
57 | indexedForbidConfigs[item] = {element: item};
|
58 | } else {
|
59 | indexedForbidConfigs[item.element] = item;
|
60 | }
|
61 | });
|
62 |
|
63 | function errorMessageForElement(name) {
|
64 | const message = `<${name}> is forbidden`;
|
65 | const additionalMessage = indexedForbidConfigs[name].message;
|
66 |
|
67 | if (additionalMessage) {
|
68 | return `${message}, ${additionalMessage}`;
|
69 | }
|
70 |
|
71 | return message;
|
72 | }
|
73 |
|
74 | function isValidCreateElement(node) {
|
75 | return node.callee
|
76 | && node.callee.type === 'MemberExpression'
|
77 | && node.callee.object.name === 'React'
|
78 | && node.callee.property.name === 'createElement'
|
79 | && node.arguments.length > 0;
|
80 | }
|
81 |
|
82 | function reportIfForbidden(element, node) {
|
83 | if (has(indexedForbidConfigs, element)) {
|
84 | context.report({
|
85 | node,
|
86 | message: errorMessageForElement(element)
|
87 | });
|
88 | }
|
89 | }
|
90 |
|
91 | return {
|
92 | JSXOpeningElement(node) {
|
93 | reportIfForbidden(context.getSourceCode().getText(node.name), node.name);
|
94 | },
|
95 |
|
96 | CallExpression(node) {
|
97 | if (!isValidCreateElement(node)) {
|
98 | return;
|
99 | }
|
100 |
|
101 | const argument = node.arguments[0];
|
102 | const argType = argument.type;
|
103 |
|
104 | if (argType === 'Identifier' && /^[A-Z_]/.test(argument.name)) {
|
105 | reportIfForbidden(argument.name, argument);
|
106 | } else if (argType === 'Literal' && /^[a-z][^.]*$/.test(argument.value)) {
|
107 | reportIfForbidden(argument.value, argument);
|
108 | } else if (argType === 'MemberExpression') {
|
109 | reportIfForbidden(context.getSourceCode().getText(argument), argument);
|
110 | }
|
111 | }
|
112 | };
|
113 | }
|
114 | };
|