1 | /**
|
---|
2 | * @fileoverview Rule to enforce concise object methods and properties.
|
---|
3 | * @author Jamund Ferguson
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | const OPTIONS = {
|
---|
9 | always: "always",
|
---|
10 | never: "never",
|
---|
11 | methods: "methods",
|
---|
12 | properties: "properties",
|
---|
13 | consistent: "consistent",
|
---|
14 | consistentAsNeeded: "consistent-as-needed"
|
---|
15 | };
|
---|
16 |
|
---|
17 | //------------------------------------------------------------------------------
|
---|
18 | // Requirements
|
---|
19 | //------------------------------------------------------------------------------
|
---|
20 | const astUtils = require("./utils/ast-utils");
|
---|
21 |
|
---|
22 | //------------------------------------------------------------------------------
|
---|
23 | // Rule Definition
|
---|
24 | //------------------------------------------------------------------------------
|
---|
25 | /** @type {import('../shared/types').Rule} */
|
---|
26 | module.exports = {
|
---|
27 | meta: {
|
---|
28 | type: "suggestion",
|
---|
29 |
|
---|
30 | docs: {
|
---|
31 | description: "Require or disallow method and property shorthand syntax for object literals",
|
---|
32 | recommended: false,
|
---|
33 | url: "https://eslint.org/docs/latest/rules/object-shorthand"
|
---|
34 | },
|
---|
35 |
|
---|
36 | fixable: "code",
|
---|
37 |
|
---|
38 | schema: {
|
---|
39 | anyOf: [
|
---|
40 | {
|
---|
41 | type: "array",
|
---|
42 | items: [
|
---|
43 | {
|
---|
44 | enum: ["always", "methods", "properties", "never", "consistent", "consistent-as-needed"]
|
---|
45 | }
|
---|
46 | ],
|
---|
47 | minItems: 0,
|
---|
48 | maxItems: 1
|
---|
49 | },
|
---|
50 | {
|
---|
51 | type: "array",
|
---|
52 | items: [
|
---|
53 | {
|
---|
54 | enum: ["always", "methods", "properties"]
|
---|
55 | },
|
---|
56 | {
|
---|
57 | type: "object",
|
---|
58 | properties: {
|
---|
59 | avoidQuotes: {
|
---|
60 | type: "boolean"
|
---|
61 | }
|
---|
62 | },
|
---|
63 | additionalProperties: false
|
---|
64 | }
|
---|
65 | ],
|
---|
66 | minItems: 0,
|
---|
67 | maxItems: 2
|
---|
68 | },
|
---|
69 | {
|
---|
70 | type: "array",
|
---|
71 | items: [
|
---|
72 | {
|
---|
73 | enum: ["always", "methods"]
|
---|
74 | },
|
---|
75 | {
|
---|
76 | type: "object",
|
---|
77 | properties: {
|
---|
78 | ignoreConstructors: {
|
---|
79 | type: "boolean"
|
---|
80 | },
|
---|
81 | methodsIgnorePattern: {
|
---|
82 | type: "string"
|
---|
83 | },
|
---|
84 | avoidQuotes: {
|
---|
85 | type: "boolean"
|
---|
86 | },
|
---|
87 | avoidExplicitReturnArrows: {
|
---|
88 | type: "boolean"
|
---|
89 | }
|
---|
90 | },
|
---|
91 | additionalProperties: false
|
---|
92 | }
|
---|
93 | ],
|
---|
94 | minItems: 0,
|
---|
95 | maxItems: 2
|
---|
96 | }
|
---|
97 | ]
|
---|
98 | },
|
---|
99 |
|
---|
100 | messages: {
|
---|
101 | expectedAllPropertiesShorthanded: "Expected shorthand for all properties.",
|
---|
102 | expectedLiteralMethodLongform: "Expected longform method syntax for string literal keys.",
|
---|
103 | expectedPropertyShorthand: "Expected property shorthand.",
|
---|
104 | expectedPropertyLongform: "Expected longform property syntax.",
|
---|
105 | expectedMethodShorthand: "Expected method shorthand.",
|
---|
106 | expectedMethodLongform: "Expected longform method syntax.",
|
---|
107 | unexpectedMix: "Unexpected mix of shorthand and non-shorthand properties."
|
---|
108 | }
|
---|
109 | },
|
---|
110 |
|
---|
111 | create(context) {
|
---|
112 | const APPLY = context.options[0] || OPTIONS.always;
|
---|
113 | const APPLY_TO_METHODS = APPLY === OPTIONS.methods || APPLY === OPTIONS.always;
|
---|
114 | const APPLY_TO_PROPS = APPLY === OPTIONS.properties || APPLY === OPTIONS.always;
|
---|
115 | const APPLY_NEVER = APPLY === OPTIONS.never;
|
---|
116 | const APPLY_CONSISTENT = APPLY === OPTIONS.consistent;
|
---|
117 | const APPLY_CONSISTENT_AS_NEEDED = APPLY === OPTIONS.consistentAsNeeded;
|
---|
118 |
|
---|
119 | const PARAMS = context.options[1] || {};
|
---|
120 | const IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors;
|
---|
121 | const METHODS_IGNORE_PATTERN = PARAMS.methodsIgnorePattern
|
---|
122 | ? new RegExp(PARAMS.methodsIgnorePattern, "u")
|
---|
123 | : null;
|
---|
124 | const AVOID_QUOTES = PARAMS.avoidQuotes;
|
---|
125 | const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
|
---|
126 | const sourceCode = context.sourceCode;
|
---|
127 |
|
---|
128 | //--------------------------------------------------------------------------
|
---|
129 | // Helpers
|
---|
130 | //--------------------------------------------------------------------------
|
---|
131 |
|
---|
132 | const CTOR_PREFIX_REGEX = /[^_$0-9]/u;
|
---|
133 |
|
---|
134 | /**
|
---|
135 | * Determines if the first character of the name is a capital letter.
|
---|
136 | * @param {string} name The name of the node to evaluate.
|
---|
137 | * @returns {boolean} True if the first character of the property name is a capital letter, false if not.
|
---|
138 | * @private
|
---|
139 | */
|
---|
140 | function isConstructor(name) {
|
---|
141 | const match = CTOR_PREFIX_REGEX.exec(name);
|
---|
142 |
|
---|
143 | // Not a constructor if name has no characters apart from '_', '$' and digits e.g. '_', '$$', '_8'
|
---|
144 | if (!match) {
|
---|
145 | return false;
|
---|
146 | }
|
---|
147 |
|
---|
148 | const firstChar = name.charAt(match.index);
|
---|
149 |
|
---|
150 | return firstChar === firstChar.toUpperCase();
|
---|
151 | }
|
---|
152 |
|
---|
153 | /**
|
---|
154 | * Determines if the property can have a shorthand form.
|
---|
155 | * @param {ASTNode} property Property AST node
|
---|
156 | * @returns {boolean} True if the property can have a shorthand form
|
---|
157 | * @private
|
---|
158 | */
|
---|
159 | function canHaveShorthand(property) {
|
---|
160 | return (property.kind !== "set" && property.kind !== "get" && property.type !== "SpreadElement" && property.type !== "SpreadProperty" && property.type !== "ExperimentalSpreadProperty");
|
---|
161 | }
|
---|
162 |
|
---|
163 | /**
|
---|
164 | * Checks whether a node is a string literal.
|
---|
165 | * @param {ASTNode} node Any AST node.
|
---|
166 | * @returns {boolean} `true` if it is a string literal.
|
---|
167 | */
|
---|
168 | function isStringLiteral(node) {
|
---|
169 | return node.type === "Literal" && typeof node.value === "string";
|
---|
170 | }
|
---|
171 |
|
---|
172 | /**
|
---|
173 | * Determines if the property is a shorthand or not.
|
---|
174 | * @param {ASTNode} property Property AST node
|
---|
175 | * @returns {boolean} True if the property is considered shorthand, false if not.
|
---|
176 | * @private
|
---|
177 | */
|
---|
178 | function isShorthand(property) {
|
---|
179 |
|
---|
180 | // property.method is true when `{a(){}}`.
|
---|
181 | return (property.shorthand || property.method);
|
---|
182 | }
|
---|
183 |
|
---|
184 | /**
|
---|
185 | * Determines if the property's key and method or value are named equally.
|
---|
186 | * @param {ASTNode} property Property AST node
|
---|
187 | * @returns {boolean} True if the key and value are named equally, false if not.
|
---|
188 | * @private
|
---|
189 | */
|
---|
190 | function isRedundant(property) {
|
---|
191 | const value = property.value;
|
---|
192 |
|
---|
193 | if (value.type === "FunctionExpression") {
|
---|
194 | return !value.id; // Only anonymous should be shorthand method.
|
---|
195 | }
|
---|
196 | if (value.type === "Identifier") {
|
---|
197 | return astUtils.getStaticPropertyName(property) === value.name;
|
---|
198 | }
|
---|
199 |
|
---|
200 | return false;
|
---|
201 | }
|
---|
202 |
|
---|
203 | /**
|
---|
204 | * Ensures that an object's properties are consistently shorthand, or not shorthand at all.
|
---|
205 | * @param {ASTNode} node Property AST node
|
---|
206 | * @param {boolean} checkRedundancy Whether to check longform redundancy
|
---|
207 | * @returns {void}
|
---|
208 | */
|
---|
209 | function checkConsistency(node, checkRedundancy) {
|
---|
210 |
|
---|
211 | // We are excluding getters/setters and spread properties as they are considered neither longform nor shorthand.
|
---|
212 | const properties = node.properties.filter(canHaveShorthand);
|
---|
213 |
|
---|
214 | // Do we still have properties left after filtering the getters and setters?
|
---|
215 | if (properties.length > 0) {
|
---|
216 | const shorthandProperties = properties.filter(isShorthand);
|
---|
217 |
|
---|
218 | /*
|
---|
219 | * If we do not have an equal number of longform properties as
|
---|
220 | * shorthand properties, we are using the annotations inconsistently
|
---|
221 | */
|
---|
222 | if (shorthandProperties.length !== properties.length) {
|
---|
223 |
|
---|
224 | // We have at least 1 shorthand property
|
---|
225 | if (shorthandProperties.length > 0) {
|
---|
226 | context.report({ node, messageId: "unexpectedMix" });
|
---|
227 | } else if (checkRedundancy) {
|
---|
228 |
|
---|
229 | /*
|
---|
230 | * If all properties of the object contain a method or value with a name matching it's key,
|
---|
231 | * all the keys are redundant.
|
---|
232 | */
|
---|
233 | const canAlwaysUseShorthand = properties.every(isRedundant);
|
---|
234 |
|
---|
235 | if (canAlwaysUseShorthand) {
|
---|
236 | context.report({ node, messageId: "expectedAllPropertiesShorthanded" });
|
---|
237 | }
|
---|
238 | }
|
---|
239 | }
|
---|
240 | }
|
---|
241 | }
|
---|
242 |
|
---|
243 | /**
|
---|
244 | * Fixes a FunctionExpression node by making it into a shorthand property.
|
---|
245 | * @param {SourceCodeFixer} fixer The fixer object
|
---|
246 | * @param {ASTNode} node A `Property` node that has a `FunctionExpression` or `ArrowFunctionExpression` as its value
|
---|
247 | * @returns {Object} A fix for this node
|
---|
248 | */
|
---|
249 | function makeFunctionShorthand(fixer, node) {
|
---|
250 | const firstKeyToken = node.computed
|
---|
251 | ? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken)
|
---|
252 | : sourceCode.getFirstToken(node.key);
|
---|
253 | const lastKeyToken = node.computed
|
---|
254 | ? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken)
|
---|
255 | : sourceCode.getLastToken(node.key);
|
---|
256 | const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
|
---|
257 | let keyPrefix = "";
|
---|
258 |
|
---|
259 | // key: /* */ () => {}
|
---|
260 | if (sourceCode.commentsExistBetween(lastKeyToken, node.value)) {
|
---|
261 | return null;
|
---|
262 | }
|
---|
263 |
|
---|
264 | if (node.value.async) {
|
---|
265 | keyPrefix += "async ";
|
---|
266 | }
|
---|
267 | if (node.value.generator) {
|
---|
268 | keyPrefix += "*";
|
---|
269 | }
|
---|
270 |
|
---|
271 | const fixRange = [firstKeyToken.range[0], node.range[1]];
|
---|
272 | const methodPrefix = keyPrefix + keyText;
|
---|
273 |
|
---|
274 | if (node.value.type === "FunctionExpression") {
|
---|
275 | const functionToken = sourceCode.getTokens(node.value).find(token => token.type === "Keyword" && token.value === "function");
|
---|
276 | const tokenBeforeParams = node.value.generator ? sourceCode.getTokenAfter(functionToken) : functionToken;
|
---|
277 |
|
---|
278 | return fixer.replaceTextRange(
|
---|
279 | fixRange,
|
---|
280 | methodPrefix + sourceCode.text.slice(tokenBeforeParams.range[1], node.value.range[1])
|
---|
281 | );
|
---|
282 | }
|
---|
283 |
|
---|
284 | const arrowToken = sourceCode.getTokenBefore(node.value.body, astUtils.isArrowToken);
|
---|
285 | const fnBody = sourceCode.text.slice(arrowToken.range[1], node.value.range[1]);
|
---|
286 |
|
---|
287 | let shouldAddParensAroundParameters = false;
|
---|
288 | let tokenBeforeParams;
|
---|
289 |
|
---|
290 | if (node.value.params.length === 0) {
|
---|
291 | tokenBeforeParams = sourceCode.getFirstToken(node.value, astUtils.isOpeningParenToken);
|
---|
292 | } else {
|
---|
293 | tokenBeforeParams = sourceCode.getTokenBefore(node.value.params[0]);
|
---|
294 | }
|
---|
295 |
|
---|
296 | if (node.value.params.length === 1) {
|
---|
297 | const hasParen = astUtils.isOpeningParenToken(tokenBeforeParams);
|
---|
298 | const isTokenOutsideNode = tokenBeforeParams.range[0] < node.range[0];
|
---|
299 |
|
---|
300 | shouldAddParensAroundParameters = !hasParen || isTokenOutsideNode;
|
---|
301 | }
|
---|
302 |
|
---|
303 | const sliceStart = shouldAddParensAroundParameters
|
---|
304 | ? node.value.params[0].range[0]
|
---|
305 | : tokenBeforeParams.range[0];
|
---|
306 | const sliceEnd = sourceCode.getTokenBefore(arrowToken).range[1];
|
---|
307 |
|
---|
308 | const oldParamText = sourceCode.text.slice(sliceStart, sliceEnd);
|
---|
309 | const newParamText = shouldAddParensAroundParameters ? `(${oldParamText})` : oldParamText;
|
---|
310 |
|
---|
311 | return fixer.replaceTextRange(
|
---|
312 | fixRange,
|
---|
313 | methodPrefix + newParamText + fnBody
|
---|
314 | );
|
---|
315 |
|
---|
316 | }
|
---|
317 |
|
---|
318 | /**
|
---|
319 | * Fixes a FunctionExpression node by making it into a longform property.
|
---|
320 | * @param {SourceCodeFixer} fixer The fixer object
|
---|
321 | * @param {ASTNode} node A `Property` node that has a `FunctionExpression` as its value
|
---|
322 | * @returns {Object} A fix for this node
|
---|
323 | */
|
---|
324 | function makeFunctionLongform(fixer, node) {
|
---|
325 | const firstKeyToken = node.computed ? sourceCode.getTokens(node).find(token => token.value === "[") : sourceCode.getFirstToken(node.key);
|
---|
326 | const lastKeyToken = node.computed ? sourceCode.getTokensBetween(node.key, node.value).find(token => token.value === "]") : sourceCode.getLastToken(node.key);
|
---|
327 | const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
|
---|
328 | let functionHeader = "function";
|
---|
329 |
|
---|
330 | if (node.value.async) {
|
---|
331 | functionHeader = `async ${functionHeader}`;
|
---|
332 | }
|
---|
333 | if (node.value.generator) {
|
---|
334 | functionHeader = `${functionHeader}*`;
|
---|
335 | }
|
---|
336 |
|
---|
337 | return fixer.replaceTextRange([node.range[0], lastKeyToken.range[1]], `${keyText}: ${functionHeader}`);
|
---|
338 | }
|
---|
339 |
|
---|
340 | /*
|
---|
341 | * To determine whether a given arrow function has a lexical identifier (`this`, `arguments`, `super`, or `new.target`),
|
---|
342 | * create a stack of functions that define these identifiers (i.e. all functions except arrow functions) as the AST is
|
---|
343 | * traversed. Whenever a new function is encountered, create a new entry on the stack (corresponding to a different lexical
|
---|
344 | * scope of `this`), and whenever a function is exited, pop that entry off the stack. When an arrow function is entered,
|
---|
345 | * keep a reference to it on the current stack entry, and remove that reference when the arrow function is exited.
|
---|
346 | * When a lexical identifier is encountered, mark all the arrow functions on the current stack entry by adding them
|
---|
347 | * to an `arrowsWithLexicalIdentifiers` set. Any arrow function in that set will not be reported by this rule,
|
---|
348 | * because converting it into a method would change the value of one of the lexical identifiers.
|
---|
349 | */
|
---|
350 | const lexicalScopeStack = [];
|
---|
351 | const arrowsWithLexicalIdentifiers = new WeakSet();
|
---|
352 | const argumentsIdentifiers = new WeakSet();
|
---|
353 |
|
---|
354 | /**
|
---|
355 | * Enters a function. This creates a new lexical identifier scope, so a new Set of arrow functions is pushed onto the stack.
|
---|
356 | * Also, this marks all `arguments` identifiers so that they can be detected later.
|
---|
357 | * @param {ASTNode} node The node representing the function.
|
---|
358 | * @returns {void}
|
---|
359 | */
|
---|
360 | function enterFunction(node) {
|
---|
361 | lexicalScopeStack.unshift(new Set());
|
---|
362 | sourceCode.getScope(node).variables.filter(variable => variable.name === "arguments").forEach(variable => {
|
---|
363 | variable.references.map(ref => ref.identifier).forEach(identifier => argumentsIdentifiers.add(identifier));
|
---|
364 | });
|
---|
365 | }
|
---|
366 |
|
---|
367 | /**
|
---|
368 | * Exits a function. This pops the current set of arrow functions off the lexical scope stack.
|
---|
369 | * @returns {void}
|
---|
370 | */
|
---|
371 | function exitFunction() {
|
---|
372 | lexicalScopeStack.shift();
|
---|
373 | }
|
---|
374 |
|
---|
375 | /**
|
---|
376 | * Marks the current function as having a lexical keyword. This implies that all arrow functions
|
---|
377 | * in the current lexical scope contain a reference to this lexical keyword.
|
---|
378 | * @returns {void}
|
---|
379 | */
|
---|
380 | function reportLexicalIdentifier() {
|
---|
381 | lexicalScopeStack[0].forEach(arrowFunction => arrowsWithLexicalIdentifiers.add(arrowFunction));
|
---|
382 | }
|
---|
383 |
|
---|
384 | //--------------------------------------------------------------------------
|
---|
385 | // Public
|
---|
386 | //--------------------------------------------------------------------------
|
---|
387 |
|
---|
388 | return {
|
---|
389 | Program: enterFunction,
|
---|
390 | FunctionDeclaration: enterFunction,
|
---|
391 | FunctionExpression: enterFunction,
|
---|
392 | "Program:exit": exitFunction,
|
---|
393 | "FunctionDeclaration:exit": exitFunction,
|
---|
394 | "FunctionExpression:exit": exitFunction,
|
---|
395 |
|
---|
396 | ArrowFunctionExpression(node) {
|
---|
397 | lexicalScopeStack[0].add(node);
|
---|
398 | },
|
---|
399 | "ArrowFunctionExpression:exit"(node) {
|
---|
400 | lexicalScopeStack[0].delete(node);
|
---|
401 | },
|
---|
402 |
|
---|
403 | ThisExpression: reportLexicalIdentifier,
|
---|
404 | Super: reportLexicalIdentifier,
|
---|
405 | MetaProperty(node) {
|
---|
406 | if (node.meta.name === "new" && node.property.name === "target") {
|
---|
407 | reportLexicalIdentifier();
|
---|
408 | }
|
---|
409 | },
|
---|
410 | Identifier(node) {
|
---|
411 | if (argumentsIdentifiers.has(node)) {
|
---|
412 | reportLexicalIdentifier();
|
---|
413 | }
|
---|
414 | },
|
---|
415 |
|
---|
416 | ObjectExpression(node) {
|
---|
417 | if (APPLY_CONSISTENT) {
|
---|
418 | checkConsistency(node, false);
|
---|
419 | } else if (APPLY_CONSISTENT_AS_NEEDED) {
|
---|
420 | checkConsistency(node, true);
|
---|
421 | }
|
---|
422 | },
|
---|
423 |
|
---|
424 | "Property:exit"(node) {
|
---|
425 | const isConciseProperty = node.method || node.shorthand;
|
---|
426 |
|
---|
427 | // Ignore destructuring assignment
|
---|
428 | if (node.parent.type === "ObjectPattern") {
|
---|
429 | return;
|
---|
430 | }
|
---|
431 |
|
---|
432 | // getters and setters are ignored
|
---|
433 | if (node.kind === "get" || node.kind === "set") {
|
---|
434 | return;
|
---|
435 | }
|
---|
436 |
|
---|
437 | // only computed methods can fail the following checks
|
---|
438 | if (node.computed && node.value.type !== "FunctionExpression" && node.value.type !== "ArrowFunctionExpression") {
|
---|
439 | return;
|
---|
440 | }
|
---|
441 |
|
---|
442 | //--------------------------------------------------------------
|
---|
443 | // Checks for property/method shorthand.
|
---|
444 | if (isConciseProperty) {
|
---|
445 | if (node.method && (APPLY_NEVER || AVOID_QUOTES && isStringLiteral(node.key))) {
|
---|
446 | const messageId = APPLY_NEVER ? "expectedMethodLongform" : "expectedLiteralMethodLongform";
|
---|
447 |
|
---|
448 | // { x() {} } should be written as { x: function() {} }
|
---|
449 | context.report({
|
---|
450 | node,
|
---|
451 | messageId,
|
---|
452 | fix: fixer => makeFunctionLongform(fixer, node)
|
---|
453 | });
|
---|
454 | } else if (APPLY_NEVER) {
|
---|
455 |
|
---|
456 | // { x } should be written as { x: x }
|
---|
457 | context.report({
|
---|
458 | node,
|
---|
459 | messageId: "expectedPropertyLongform",
|
---|
460 | fix: fixer => fixer.insertTextAfter(node.key, `: ${node.key.name}`)
|
---|
461 | });
|
---|
462 | }
|
---|
463 | } else if (APPLY_TO_METHODS && !node.value.id && (node.value.type === "FunctionExpression" || node.value.type === "ArrowFunctionExpression")) {
|
---|
464 | if (IGNORE_CONSTRUCTORS && node.key.type === "Identifier" && isConstructor(node.key.name)) {
|
---|
465 | return;
|
---|
466 | }
|
---|
467 |
|
---|
468 | if (METHODS_IGNORE_PATTERN) {
|
---|
469 | const propertyName = astUtils.getStaticPropertyName(node);
|
---|
470 |
|
---|
471 | if (propertyName !== null && METHODS_IGNORE_PATTERN.test(propertyName)) {
|
---|
472 | return;
|
---|
473 | }
|
---|
474 | }
|
---|
475 |
|
---|
476 | if (AVOID_QUOTES && isStringLiteral(node.key)) {
|
---|
477 | return;
|
---|
478 | }
|
---|
479 |
|
---|
480 | // {[x]: function(){}} should be written as {[x]() {}}
|
---|
481 | if (node.value.type === "FunctionExpression" ||
|
---|
482 | node.value.type === "ArrowFunctionExpression" &&
|
---|
483 | node.value.body.type === "BlockStatement" &&
|
---|
484 | AVOID_EXPLICIT_RETURN_ARROWS &&
|
---|
485 | !arrowsWithLexicalIdentifiers.has(node.value)
|
---|
486 | ) {
|
---|
487 | context.report({
|
---|
488 | node,
|
---|
489 | messageId: "expectedMethodShorthand",
|
---|
490 | fix: fixer => makeFunctionShorthand(fixer, node)
|
---|
491 | });
|
---|
492 | }
|
---|
493 | } else if (node.value.type === "Identifier" && node.key.name === node.value.name && APPLY_TO_PROPS) {
|
---|
494 |
|
---|
495 | // {x: x} should be written as {x}
|
---|
496 | context.report({
|
---|
497 | node,
|
---|
498 | messageId: "expectedPropertyShorthand",
|
---|
499 | fix(fixer) {
|
---|
500 | return fixer.replaceText(node, node.value.name);
|
---|
501 | }
|
---|
502 | });
|
---|
503 | } else if (node.value.type === "Identifier" && node.key.type === "Literal" && node.key.value === node.value.name && APPLY_TO_PROPS) {
|
---|
504 | if (AVOID_QUOTES) {
|
---|
505 | return;
|
---|
506 | }
|
---|
507 |
|
---|
508 | // {"x": x} should be written as {x}
|
---|
509 | context.report({
|
---|
510 | node,
|
---|
511 | messageId: "expectedPropertyShorthand",
|
---|
512 | fix(fixer) {
|
---|
513 | return fixer.replaceText(node, node.value.name);
|
---|
514 | }
|
---|
515 | });
|
---|
516 | }
|
---|
517 | }
|
---|
518 | };
|
---|
519 | }
|
---|
520 | };
|
---|