[d565449] | 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 | };
|
---|