source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 6.6 KB
Line 
1/**
2 * @fileoverview Defines where React component static properties should be positioned.
3 * @author Daniel Mason
4 */
5
6'use strict';
7
8const fromEntries = require('object.fromentries');
9const Components = require('../util/Components');
10const docsUrl = require('../util/docsUrl');
11const astUtil = require('../util/ast');
12const componentUtil = require('../util/componentUtil');
13const propsUtil = require('../util/props');
14const report = require('../util/report');
15const getScope = require('../util/eslint').getScope;
16
17// ------------------------------------------------------------------------------
18// Positioning Options
19// ------------------------------------------------------------------------------
20const STATIC_PUBLIC_FIELD = 'static public field';
21const STATIC_GETTER = 'static getter';
22const PROPERTY_ASSIGNMENT = 'property assignment';
23const POSITION_SETTINGS = [STATIC_PUBLIC_FIELD, STATIC_GETTER, PROPERTY_ASSIGNMENT];
24
25// ------------------------------------------------------------------------------
26// Rule messages
27// ------------------------------------------------------------------------------
28const ERROR_MESSAGES = {
29 [STATIC_PUBLIC_FIELD]: 'notStaticClassProp',
30 [STATIC_GETTER]: 'notGetterClassFunc',
31 [PROPERTY_ASSIGNMENT]: 'declareOutsideClass',
32};
33
34// ------------------------------------------------------------------------------
35// Properties to check
36// ------------------------------------------------------------------------------
37const propertiesToCheck = {
38 propTypes: propsUtil.isPropTypesDeclaration,
39 defaultProps: propsUtil.isDefaultPropsDeclaration,
40 childContextTypes: propsUtil.isChildContextTypesDeclaration,
41 contextTypes: propsUtil.isContextTypesDeclaration,
42 contextType: propsUtil.isContextTypeDeclaration,
43 displayName: (node) => propsUtil.isDisplayNameDeclaration(astUtil.getPropertyNameNode(node)),
44};
45
46const classProperties = Object.keys(propertiesToCheck);
47const schemaProperties = fromEntries(classProperties.map((property) => [property, { enum: POSITION_SETTINGS }]));
48
49// ------------------------------------------------------------------------------
50// Rule Definition
51// ------------------------------------------------------------------------------
52
53const messages = {
54 notStaticClassProp: '\'{{name}}\' should be declared as a static class property.',
55 notGetterClassFunc: '\'{{name}}\' should be declared as a static getter class function.',
56 declareOutsideClass: '\'{{name}}\' should be declared outside the class body.',
57};
58
59/** @type {import('eslint').Rule.RuleModule} */
60module.exports = {
61 meta: {
62 docs: {
63 description: 'Enforces where React component static properties should be positioned.',
64 category: 'Stylistic Issues',
65 recommended: false,
66 url: docsUrl('static-property-placement'),
67 },
68 fixable: null, // or 'code' or 'whitespace'
69
70 messages,
71
72 schema: [
73 { enum: POSITION_SETTINGS },
74 {
75 type: 'object',
76 properties: schemaProperties,
77 additionalProperties: false,
78 },
79 ],
80 },
81
82 create: Components.detect((context, components, utils) => {
83 // variables should be defined here
84 const options = context.options;
85 const defaultCheckType = options[0] || STATIC_PUBLIC_FIELD;
86 const hasAdditionalConfig = options.length > 1;
87 const additionalConfig = hasAdditionalConfig ? options[1] : {};
88
89 // Set config
90 const config = fromEntries(classProperties.map((property) => [
91 property,
92 additionalConfig[property] || defaultCheckType,
93 ]));
94
95 // ----------------------------------------------------------------------
96 // Helpers
97 // ----------------------------------------------------------------------
98
99 /**
100 * Checks if we are declaring context in class
101 * @param {ASTNode} node
102 * @returns {Boolean} True if we are declaring context in class, false if not.
103 */
104 function isContextInClass(node) {
105 let blockNode;
106 let scope = getScope(context, node);
107 while (scope) {
108 blockNode = scope.block;
109 if (blockNode && blockNode.type === 'ClassDeclaration') {
110 return true;
111 }
112 scope = scope.upper;
113 }
114
115 return false;
116 }
117
118 /**
119 * Check if we should report this property node
120 * @param {ASTNode} node
121 * @param {string} expectedRule
122 */
123 function reportNodeIncorrectlyPositioned(node, expectedRule) {
124 // Detect if this node is an expected property declaration adn return the property name
125 const name = classProperties.find((propertyName) => {
126 if (propertiesToCheck[propertyName](node)) {
127 return !!propertyName;
128 }
129
130 return false;
131 });
132
133 // If name is set but the configured rule does not match expected then report error
134 if (
135 name
136 && (
137 config[name] !== expectedRule
138 || (!node.static && (config[name] === STATIC_PUBLIC_FIELD || config[name] === STATIC_GETTER))
139 )
140 ) {
141 const messageId = ERROR_MESSAGES[config[name]];
142 report(context, messages[messageId], messageId, {
143 node,
144 data: { name },
145 });
146 }
147 }
148
149 // ----------------------------------------------------------------------
150 // Public
151 // ----------------------------------------------------------------------
152 return {
153 'ClassProperty, PropertyDefinition'(node) {
154 if (!componentUtil.getParentES6Component(context, node)) {
155 return;
156 }
157
158 reportNodeIncorrectlyPositioned(node, STATIC_PUBLIC_FIELD);
159 },
160
161 MemberExpression(node) {
162 // If definition type is undefined then it must not be a defining expression or if the definition is inside a
163 // class body then skip this node.
164 const right = node.parent.right;
165 if (!right || right.type === 'undefined' || isContextInClass(node)) {
166 return;
167 }
168
169 // Get the related component
170 const relatedComponent = utils.getRelatedComponent(node);
171
172 // If the related component is not an ES6 component then skip this node
173 if (!relatedComponent || !componentUtil.isES6Component(relatedComponent.node, context)) {
174 return;
175 }
176
177 // Report if needed
178 reportNodeIncorrectlyPositioned(node, PROPERTY_ASSIGNMENT);
179 },
180
181 MethodDefinition(node) {
182 // If the function is inside a class and is static getter then check if correctly positioned
183 if (
184 componentUtil.getParentES6Component(context, node)
185 && node.static
186 && node.kind === 'get'
187 ) {
188 // Report error if needed
189 reportNodeIncorrectlyPositioned(node, STATIC_GETTER);
190 }
191 },
192 };
193 }),
194};
Note: See TracBrowser for help on using the repository browser.