@atlaskit/eslint-plugin-design-system
Version:
The essential plugin for use with the Atlassian Design System.
96 lines (95 loc) • 4.54 kB
JavaScript
import { isNodeOfType } from 'eslint-codemod-utils';
import { getScope } from '@atlaskit/eslint-utils/context-compat';
import { createLintRule } from '../utils/create-lint-rule';
import { getImportName } from '../utils/get-import-name';
import { getUniqueButtonItemName } from './get-unique-button-item-name';
import { hasImportOfName } from './has-import-of-name';
import { insertButtonItemDefaultImport } from './insert-button-item-default-import';
import { insertButtonItemImport } from './insert-button-item-import';
import { hrefHasInvalidValue } from './utils';
export var hrefRequiredSuggestionText = 'Convert LinkItem to ButtonItem';
var rule = createLintRule({
meta: {
name: 'use-href-in-link-item',
type: 'suggestion',
fixable: 'code',
hasSuggestions: true,
docs: {
description: 'Inform developers of eventual requirement of `href` prop in `LinkItem` component. Elements with a `link` role require an `href` attribute for users to properly navigate, particularly those using assistive technologies. If no valid `href` is required for your use case, consider using a `ButtonItem` instead.',
recommended: true,
severity: 'warn'
},
messages: {
hrefRequired: 'A valid `href` will be required in future releases on LinkItem. This may be able to be replaced by a ButtonItem.'
}
},
create: function create(context) {
var menuNode = null;
var buttonItemDefaultImportNode = null;
var linkItemDefaultImportNode = null;
var customDefaultLinkItemSpecifier = null;
var allImportDeclarations = [];
return {
ImportDeclaration: function ImportDeclaration(node) {
if (node.source.value === '@atlaskit/menu') {
menuNode = node;
} else if (node.source.value === '@atlaskit/menu/link-item') {
linkItemDefaultImportNode = node;
customDefaultLinkItemSpecifier = node.specifiers[0].local.name;
} else if (node.source.value === '@atlaskit/menu/button-item') {
buttonItemDefaultImportNode = node;
}
allImportDeclarations.push(node);
},
JSXElement: function JSXElement(node) {
if (!isNodeOfType(node, 'JSXElement')) {
return;
}
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
return;
}
// Get the name of the LinkItem import
var linkItemImportName = customDefaultLinkItemSpecifier || getImportName(getScope(context, node), '@atlaskit/menu', 'LinkItem');
if (node.openingElement.name.name === linkItemImportName) {
// and if href prop does not exist
var linkProps = node.openingElement.attributes.filter(function (attr) {
return isNodeOfType(attr, 'JSXAttribute');
}).filter(function (attr) {
return attr.name.type === 'JSXIdentifier';
});
var href = linkProps.find(function (attr) {
return attr.name.name === 'href';
});
if (hrefHasInvalidValue(getScope(context, node), href)) {
context.report({
node: node,
messageId: 'hrefRequired',
suggest: [{
desc: hrefRequiredSuggestionText,
fix: function fix(fixer) {
var importFix = null;
var uniqueButtonItemName = getUniqueButtonItemName(menuNode, allImportDeclarations);
if (
// Default link item import but no button item imports
linkItemDefaultImportNode && !buttonItemDefaultImportNode && (!menuNode || menuNode && !hasImportOfName(menuNode, 'ButtonItem'))) {
importFix = insertButtonItemDefaultImport(fixer, linkItemDefaultImportNode);
} else if (
// No button item imports of any kind
menuNode && !hasImportOfName(menuNode, 'ButtonItem') && !buttonItemDefaultImportNode) {
importFix = insertButtonItemImport(fixer, menuNode, uniqueButtonItemName);
}
var fixes = (importFix ? [importFix] : []).concat(href ? [fixer.remove(href)] : [], [fixer.replaceText(node.openingElement.name, uniqueButtonItemName)]);
if (node.closingElement) {
fixes.push(fixer.replaceText(node.closingElement.name, uniqueButtonItemName));
}
return fixes;
}
}]
});
}
}
}
};
}
});
export default rule;