@atlaskit/eslint-plugin-design-system
Version:
The essential plugin for use with the Atlassian Design System.
87 lines (86 loc) • 3.69 kB
JavaScript
import { isNodeOfType, jsxIdentifier } from 'eslint-codemod-utils';
import { JSXAttribute as HelperJSXAttribute } from './jsx-attribute';
export const JSXElementHelper = {
/**
* Names of JSXElements can be any of:
* `<Component></Component>` - (JSXIdentifier)
* `<MyComponents.Component></MyComponents.Component>` - `MyComponents` is a namespace (JSXNamespacedName)
* `<MyComponents.Component></MyComponents.Component>` - `MyComponents` is an object (JSXMemberExpression)
*
* getting the name of a JSXMemberExpression is difficult, because object can contain objects, which is recursively defined in the AST.
* e.g. Getting the name of `<MyComponents.PresentationLayer.LeftSideBar.Header />` would require `getName` to recursively resolve all parts of the name.
* `getName` does not currently have this functionality. Add it if you need it.
*/
getName(node) {
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
// TODO: We may want to log this
return '';
}
return node.openingElement.name.name;
},
updateName(node, newName, fixer) {
const isSelfClosing = JSXElementHelper.isSelfClosing(node);
const openingElementFix = fixer.replaceText(node.openingElement.name, jsxIdentifier(newName).toString());
if (isSelfClosing || !node.closingElement) {
return [openingElementFix];
}
const closingElementFix = fixer.replaceText(node.closingElement.name, jsxIdentifier(newName).toString());
return [openingElementFix, closingElementFix];
},
isSelfClosing(node) {
return node.openingElement.selfClosing;
},
getAttributes(node) {
return node.openingElement.attributes;
},
getAttributeByName(node, name) {
return node.openingElement.attributes.find(attr => {
// Ignore anything other than JSXAttribute
if (!isNodeOfType(attr, 'JSXAttribute')) {
return false;
}
return attr.name.name === name;
});
},
containsSpreadAttributes(node) {
return node.openingElement.attributes.some(attr => {
return isNodeOfType(attr, 'JSXSpreadAttribute');
});
},
addAttribute(node, name, value, fixer) {
const attributeString = ` ${name}='${value}'`;
const isSelfClosing = JSXElementHelper.isSelfClosing(node);
const start = node.openingElement.range ? node.openingElement.range[0] : 0;
const end = node.openingElement.range ? node.openingElement.range[1] - (isSelfClosing ? 3 : 1) : 0;
const range = [start, end];
const fix = fixer.insertTextAfterRange(range, attributeString);
return fix;
},
getChildren(node) {
const filteredChildren = node.children.filter(child => {
// Filter out text children with whitespace characters only as JSX removes whitespace used for intendation
if (isNodeOfType(child, 'JSXText')) {
const whiteSpaceChars = new RegExp('\\s', 'g');
return !whiteSpaceChars.test(child.value);
}
// Filter out empty JSX expressions, for example JSX expression containing comments only, including eslint ignore comments
if (isNodeOfType(child, 'JSXExpressionContainer')) {
return !isNodeOfType(child.expression, 'JSXEmptyExpression');
}
return true;
});
return filteredChildren;
},
hasAllowedAttrsOnly(node, allowedProps) {
const attrs = JSXElementHelper.getAttributes(node);
return attrs.every(attr => {
if (!isNodeOfType(attr, 'JSXAttribute')) {
return false;
}
const name = HelperJSXAttribute.getName(attr);
return allowedProps.includes(name);
});
}
};
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
export { JSXElementHelper as JSXElement };