source: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.9 KB
Line 
1/**
2 * @fileoverview Enforce shorthand or standard form for React fragments.
3 * @author Alex Zherdev
4 */
5
6'use strict';
7
8const elementType = require('jsx-ast-utils/elementType');
9const pragmaUtil = require('../util/pragma');
10const variableUtil = require('../util/variable');
11const testReactVersion = require('../util/version').testReactVersion;
12const docsUrl = require('../util/docsUrl');
13const report = require('../util/report');
14const getText = require('../util/eslint').getText;
15
16// ------------------------------------------------------------------------------
17// Rule Definition
18// ------------------------------------------------------------------------------
19
20function replaceNode(source, node, text) {
21 return `${source.slice(0, node.range[0])}${text}${source.slice(node.range[1])}`;
22}
23
24const messages = {
25 fragmentsNotSupported: 'Fragments are only supported starting from React v16.2. Please disable the `react/jsx-fragments` rule in `eslint` settings or upgrade your version of React.',
26 preferPragma: 'Prefer {{react}}.{{fragment}} over fragment shorthand',
27 preferFragment: 'Prefer fragment shorthand over {{react}}.{{fragment}}',
28};
29
30/** @type {import('eslint').Rule.RuleModule} */
31module.exports = {
32 meta: {
33 docs: {
34 description: 'Enforce shorthand or standard form for React fragments',
35 category: 'Stylistic Issues',
36 recommended: false,
37 url: docsUrl('jsx-fragments'),
38 },
39 fixable: 'code',
40
41 messages,
42
43 schema: [{
44 enum: ['syntax', 'element'],
45 }],
46 },
47
48 create(context) {
49 const configuration = context.options[0] || 'syntax';
50 const reactPragma = pragmaUtil.getFromContext(context);
51 const fragmentPragma = pragmaUtil.getFragmentFromContext(context);
52 const openFragShort = '<>';
53 const closeFragShort = '</>';
54 const openFragLong = `<${reactPragma}.${fragmentPragma}>`;
55 const closeFragLong = `</${reactPragma}.${fragmentPragma}>`;
56
57 function reportOnReactVersion(node) {
58 if (!testReactVersion(context, '>= 16.2.0')) {
59 report(context, messages.fragmentsNotSupported, 'fragmentsNotSupported', {
60 node,
61 });
62 return true;
63 }
64
65 return false;
66 }
67
68 function getFixerToLong(jsxFragment) {
69 if (!jsxFragment.closingFragment || !jsxFragment.openingFragment) {
70 // the old TS parser crashes here
71 // TODO: FIXME: can we fake these two descriptors?
72 return null;
73 }
74 return function fix(fixer) {
75 let source = getText(context);
76 source = replaceNode(source, jsxFragment.closingFragment, closeFragLong);
77 source = replaceNode(source, jsxFragment.openingFragment, openFragLong);
78 const lengthDiff = openFragLong.length - getText(context, jsxFragment.openingFragment).length
79 + closeFragLong.length - getText(context, jsxFragment.closingFragment).length;
80 const range = jsxFragment.range;
81 return fixer.replaceTextRange(range, source.slice(range[0], range[1] + lengthDiff));
82 };
83 }
84
85 function getFixerToShort(jsxElement) {
86 return function fix(fixer) {
87 let source = getText(context);
88 let lengthDiff;
89 if (jsxElement.closingElement) {
90 source = replaceNode(source, jsxElement.closingElement, closeFragShort);
91 source = replaceNode(source, jsxElement.openingElement, openFragShort);
92 lengthDiff = getText(context, jsxElement.openingElement).length - openFragShort.length
93 + getText(context, jsxElement.closingElement).length - closeFragShort.length;
94 } else {
95 source = replaceNode(source, jsxElement.openingElement, `${openFragShort}${closeFragShort}`);
96 lengthDiff = getText(context, jsxElement.openingElement).length - openFragShort.length
97 - closeFragShort.length;
98 }
99
100 const range = jsxElement.range;
101 return fixer.replaceTextRange(range, source.slice(range[0], range[1] - lengthDiff));
102 };
103 }
104
105 function refersToReactFragment(node, name) {
106 const variableInit = variableUtil.findVariableByName(context, node, name);
107 if (!variableInit) {
108 return false;
109 }
110
111 // const { Fragment } = React;
112 if (variableInit.type === 'Identifier' && variableInit.name === reactPragma) {
113 return true;
114 }
115
116 // const Fragment = React.Fragment;
117 if (
118 variableInit.type === 'MemberExpression'
119 && variableInit.object.type === 'Identifier'
120 && variableInit.object.name === reactPragma
121 && variableInit.property.type === 'Identifier'
122 && variableInit.property.name === fragmentPragma
123 ) {
124 return true;
125 }
126
127 // const { Fragment } = require('react');
128 if (
129 variableInit.callee
130 && variableInit.callee.name === 'require'
131 && variableInit.arguments
132 && variableInit.arguments[0]
133 && variableInit.arguments[0].value === 'react'
134 ) {
135 return true;
136 }
137
138 return false;
139 }
140
141 const jsxElements = [];
142 const fragmentNames = new Set([`${reactPragma}.${fragmentPragma}`]);
143
144 // --------------------------------------------------------------------------
145 // Public
146 // --------------------------------------------------------------------------
147
148 return {
149 JSXElement(node) {
150 jsxElements.push(node);
151 },
152
153 JSXFragment(node) {
154 if (reportOnReactVersion(node)) {
155 return;
156 }
157
158 if (configuration === 'element') {
159 report(context, messages.preferPragma, 'preferPragma', {
160 node,
161 data: {
162 react: reactPragma,
163 fragment: fragmentPragma,
164 },
165 fix: getFixerToLong(node),
166 });
167 }
168 },
169
170 ImportDeclaration(node) {
171 if (node.source && node.source.value === 'react') {
172 node.specifiers.forEach((spec) => {
173 if (
174 'imported' in spec
175 && spec.imported
176 && 'name' in spec.imported
177 && spec.imported.name === fragmentPragma
178 ) {
179 if (spec.local) {
180 fragmentNames.add(spec.local.name);
181 }
182 }
183 });
184 }
185 },
186
187 'Program:exit'() {
188 jsxElements.forEach((node) => {
189 const openingEl = node.openingElement;
190 const elName = elementType(openingEl);
191
192 if (fragmentNames.has(elName) || refersToReactFragment(node, elName)) {
193 if (reportOnReactVersion(node)) {
194 return;
195 }
196
197 const attrs = openingEl.attributes;
198 if (configuration === 'syntax' && !(attrs && attrs.length > 0)) {
199 report(context, messages.preferFragment, 'preferFragment', {
200 node,
201 data: {
202 react: reactPragma,
203 fragment: fragmentPragma,
204 },
205 fix: getFixerToShort(node),
206 });
207 }
208 }
209 });
210 },
211 };
212 },
213};
Note: See TracBrowser for help on using the repository browser.