UNPKG

@atlaskit/eslint-plugin-ui-styling-standard

Version:

The eslint plugin to enforce and educate on Atlassian's UI Styling Standard

89 lines (88 loc) • 3.27 kB
import { getSourceCode } from '@atlaskit/eslint-utils/context-compat'; const styleFunctionNames = new Set(['css', 'cssMap', 'keyframes', 'styled', 'xcss']); const defaultStyledImportSources = new Set(['@emotion/styled', 'styled' + '-components']); const styleCallsBySourceCode = new WeakMap(); /** * Visit imported style calls without resolving every CallExpression against its current scope. * * ESLint has already linked import bindings to all of their references. Building this index once * per source file makes each styling rule proportional to the number of style calls instead of the * number of all calls in the file. */ export function getStyleCalls(context) { const sourceCode = getSourceCode(context); let styleCalls = styleCallsBySourceCode.get(sourceCode); if (!styleCalls) { styleCalls = buildStyleCallIndex(sourceCode); styleCallsBySourceCode.set(sourceCode, styleCalls); } return styleCalls; } function buildStyleCallIndex(sourceCode) { const styleCalls = []; const seenCalls = new Set(); const { scopeManager } = sourceCode; if (!scopeManager) { return styleCalls; } for (const statement of sourceCode.ast.body) { if (statement.type !== 'ImportDeclaration' || typeof statement.source.value !== 'string') { continue; } const importSource = statement.source.value; for (const specifier of statement.specifiers) { const styleFunction = getStyleFunctionFromSpecifier(specifier, importSource); if (!styleFunction) { continue; } const [variable] = scopeManager.getDeclaredVariables(specifier); if (!variable) { continue; } for (const reference of variable.references) { const call = getStyleCall(reference.identifier, styleFunction); if (!call || seenCalls.has(call)) { continue; } seenCalls.add(call); styleCalls.push({ importSource, node: call, styleFunction }); } } } return styleCalls; } function getStyleFunctionFromSpecifier(specifier, importSource) { if (specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier' && styleFunctionNames.has(specifier.imported.name)) { return specifier.imported.name; } if (specifier.type !== 'ImportDefaultSpecifier') { return null; } if (styleFunctionNames.has(specifier.local.name)) { return specifier.local.name; } return defaultStyledImportSources.has(importSource) ? 'styled' : null; } function getStyleCall(identifier, styleFunction) { var _parent2, _parent$parent; let callee = identifier; let parent = callee.parent; while (((_parent = parent) === null || _parent === void 0 ? void 0 : _parent.type) === 'MemberExpression' && parent.object === callee) { var _parent; callee = parent; parent = callee.parent; } if (((_parent2 = parent) === null || _parent2 === void 0 ? void 0 : _parent2.type) !== 'CallExpression' || parent.callee !== callee) { return null; } if (styleFunction === 'styled' && ((_parent$parent = parent.parent) === null || _parent$parent === void 0 ? void 0 : _parent$parent.type) === 'CallExpression' && parent.parent.callee === parent) { return parent.parent; } return parent; }