UNPKG

@atlaskit/eslint-plugin-design-system

Version:

The essential plugin for use with the Atlassian Design System.

96 lines (95 loc) 4.54 kB
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;