You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.9 KiB
173 lines
4.9 KiB
/** |
|
* @fileoverview Enforce default props alphabetical sorting |
|
* @author Vladimir Kattsov |
|
*/ |
|
|
|
'use strict'; |
|
|
|
const variableUtil = require('../util/variable'); |
|
const docsUrl = require('../util/docsUrl'); |
|
const report = require('../util/report'); |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Rule Definition |
|
// ------------------------------------------------------------------------------ |
|
|
|
const messages = { |
|
propsNotSorted: 'Default prop types declarations should be sorted alphabetically', |
|
}; |
|
|
|
module.exports = { |
|
meta: { |
|
docs: { |
|
description: 'Enforce default props alphabetical sorting', |
|
category: 'Stylistic Issues', |
|
recommended: false, |
|
url: docsUrl('jsx-sort-default-props'), |
|
}, |
|
// fixable: 'code', |
|
|
|
messages, |
|
|
|
schema: [{ |
|
type: 'object', |
|
properties: { |
|
ignoreCase: { |
|
type: 'boolean', |
|
}, |
|
}, |
|
additionalProperties: false, |
|
}], |
|
}, |
|
|
|
create(context) { |
|
const configuration = context.options[0] || {}; |
|
const ignoreCase = configuration.ignoreCase || false; |
|
|
|
/** |
|
* Get properties name |
|
* @param {Object} node - Property. |
|
* @returns {String} Property name. |
|
*/ |
|
function getPropertyName(node) { |
|
if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) { |
|
return node.key.name; |
|
} |
|
if (node.type === 'MemberExpression') { |
|
return node.property.name; |
|
// Special case for class properties |
|
// (babel-eslint@5 does not expose property name so we have to rely on tokens) |
|
} |
|
if (node.type === 'ClassProperty') { |
|
const tokens = context.getSourceCode().getFirstTokens(node, 2); |
|
return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value; |
|
} |
|
return ''; |
|
} |
|
|
|
/** |
|
* Checks if the Identifier node passed in looks like a defaultProps declaration. |
|
* @param {ASTNode} node The node to check. Must be an Identifier node. |
|
* @returns {Boolean} `true` if the node is a defaultProps declaration, `false` if not |
|
*/ |
|
function isDefaultPropsDeclaration(node) { |
|
const propName = getPropertyName(node); |
|
return (propName === 'defaultProps' || propName === 'getDefaultProps'); |
|
} |
|
|
|
function getKey(node) { |
|
return context.getSourceCode().getText(node.key || node.argument); |
|
} |
|
|
|
/** |
|
* Find a variable by name in the current scope. |
|
* @param {string} name Name of the variable to look for. |
|
* @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. |
|
*/ |
|
function findVariableByName(name) { |
|
const variable = variableUtil.variablesInScope(context).find((item) => item.name === name); |
|
|
|
if (!variable || !variable.defs[0] || !variable.defs[0].node) { |
|
return null; |
|
} |
|
|
|
if (variable.defs[0].node.type === 'TypeAlias') { |
|
return variable.defs[0].node.right; |
|
} |
|
|
|
return variable.defs[0].node.init; |
|
} |
|
|
|
/** |
|
* Checks if defaultProps declarations are sorted |
|
* @param {Array} declarations The array of AST nodes being checked. |
|
* @returns {void} |
|
*/ |
|
function checkSorted(declarations) { |
|
// function fix(fixer) { |
|
// return propTypesSortUtil.fixPropTypesSort(fixer, context, declarations, ignoreCase); |
|
// } |
|
|
|
declarations.reduce((prev, curr, idx, decls) => { |
|
if (/Spread(?:Property|Element)$/.test(curr.type)) { |
|
return decls[idx + 1]; |
|
} |
|
|
|
let prevPropName = getKey(prev); |
|
let currentPropName = getKey(curr); |
|
|
|
if (ignoreCase) { |
|
prevPropName = prevPropName.toLowerCase(); |
|
currentPropName = currentPropName.toLowerCase(); |
|
} |
|
|
|
if (currentPropName < prevPropName) { |
|
report(context, messages.propsNotSorted, 'propsNotSorted', { |
|
node: curr, |
|
// fix |
|
}); |
|
|
|
return prev; |
|
} |
|
|
|
return curr; |
|
}, declarations[0]); |
|
} |
|
|
|
function checkNode(node) { |
|
if (!node) { |
|
return; |
|
} |
|
if (node.type === 'ObjectExpression') { |
|
checkSorted(node.properties); |
|
} else if (node.type === 'Identifier') { |
|
const propTypesObject = findVariableByName(node.name); |
|
if (propTypesObject && propTypesObject.properties) { |
|
checkSorted(propTypesObject.properties); |
|
} |
|
} |
|
} |
|
|
|
// -------------------------------------------------------------------------- |
|
// Public API |
|
// -------------------------------------------------------------------------- |
|
|
|
return { |
|
'ClassProperty, PropertyDefinition'(node) { |
|
if (!isDefaultPropsDeclaration(node)) { |
|
return; |
|
} |
|
|
|
checkNode(node.value); |
|
}, |
|
|
|
MemberExpression(node) { |
|
if (!isDefaultPropsDeclaration(node)) { |
|
return; |
|
} |
|
|
|
checkNode(node.parent.right); |
|
}, |
|
}; |
|
}, |
|
};
|
|
|