UNPKG

@atlaskit/eslint-plugin-design-system

Version:

The essential plugin for use with the Atlassian Design System.

238 lines (233 loc) 9.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkStylesObject = checkStylesObject; var _eslintCodemodUtils = require("eslint-codemod-utils"); var getScope = function getScope(context, node) { if ('getScope' in context.sourceCode) { return context.sourceCode.getScope(node); } // this is needed for jira's eslint update, as otherwise there is type errors only in jira typechecking return context.getScope(node); }; var unsupportedSelectors = [':', // pseudo-classes/elements '[', // attribute selectors '>', // child combinator '+', // adjacent sibling combinator '~', // general sibling combinator ' ', // descendant combinator '*', // universal selector '#', // ID selector '.', // class selector '@', // at-rules '&', // parent selector '|', // namespace separator '^', // starts with '$', // ends with '=' // equals ]; function checkForPseudoClasses(node, objectExpression, context) { if (!(0, _eslintCodemodUtils.isNodeOfType)(objectExpression, 'ObjectExpression')) { return; } objectExpression.properties.forEach(function (prop) { if ((0, _eslintCodemodUtils.isNodeOfType)(prop, 'Property')) { // Check if property key is a pseudo-class var keyValue = null; if ((0, _eslintCodemodUtils.isNodeOfType)(prop.key, 'Literal')) { keyValue = prop.key.value; } else if ((0, _eslintCodemodUtils.isNodeOfType)(prop.key, 'Identifier')) { keyValue = prop.key.name; } if (keyValue && typeof keyValue === 'string' && unsupportedSelectors.some(function (selector) { return keyValue.includes(selector); })) { context.report({ node: prop.key, messageId: 'noPseudoClass', data: { pseudo: keyValue } }); } // Recursively check nested objects (for function return values) if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'ArrowFunctionExpression') || (0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'FunctionExpression')) { // Check the function body for returned object expressions var body = prop.value.body; if ((0, _eslintCodemodUtils.isNodeOfType)(body, 'ObjectExpression')) { checkForPseudoClasses(node, body, context); } else if ((0, _eslintCodemodUtils.isNodeOfType)(body, 'BlockStatement')) { // Look for return statements body.body.forEach(function (stmt) { if ((0, _eslintCodemodUtils.isNodeOfType)(stmt, 'ReturnStatement') && stmt.argument && (0, _eslintCodemodUtils.isNodeOfType)(stmt.argument, 'ObjectExpression')) { checkForPseudoClasses(node, stmt.argument, context); } }); } } else if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'ObjectExpression')) { checkForPseudoClasses(node, prop.value, context); } } else if ((0, _eslintCodemodUtils.isNodeOfType)(prop, 'SpreadElement')) { // Handle spread elements like ...styles or ...conditionalStyles checkSpreadElement(node, prop, context); } }); } function checkSpreadElement(node, spreadElement, context) { if (!(0, _eslintCodemodUtils.isNodeOfType)(spreadElement, 'SpreadElement')) { return; } var argument = spreadElement.argument; // Handle direct identifier (e.g., ...styles) if ((0, _eslintCodemodUtils.isNodeOfType)(argument, 'Identifier')) { var scope = getScope(context, argument); var variable = null; var currentScope = scope; // Search through scope chain while (currentScope && !variable) { variable = currentScope.variables.find(function (v) { return v.name === argument.name; }); currentScope = currentScope.upper; } if (variable && variable.defs.length > 0) { var def = variable.defs[0]; if ((0, _eslintCodemodUtils.isNodeOfType)(def.node, 'VariableDeclarator') && def.node.init) { if ((0, _eslintCodemodUtils.isNodeOfType)(def.node.init, 'ObjectExpression')) { checkForPseudoClasses(node, def.node.init, context); } } } } // Handle conditional expressions (e.g., ...(condition ? { ':hover': ... } : undefined)) else if ((0, _eslintCodemodUtils.isNodeOfType)(argument, 'ConditionalExpression')) { // Check both consequent and alternate if ((0, _eslintCodemodUtils.isNodeOfType)(argument.consequent, 'ObjectExpression')) { checkForPseudoClasses(node, argument.consequent, context); } if (argument.alternate && (0, _eslintCodemodUtils.isNodeOfType)(argument.alternate, 'ObjectExpression')) { checkForPseudoClasses(node, argument.alternate, context); } } // Handle logical expressions (e.g., ...condition && { ':hover': ... }) else if ((0, _eslintCodemodUtils.isNodeOfType)(argument, 'LogicalExpression')) { if ((0, _eslintCodemodUtils.isNodeOfType)(argument.right, 'ObjectExpression')) { checkForPseudoClasses(node, argument.right, context); } if ((0, _eslintCodemodUtils.isNodeOfType)(argument.left, 'ObjectExpression')) { checkForPseudoClasses(node, argument.left, context); } } // Handle direct object expressions (e.g., ...{ ':hover': ... }) else if ((0, _eslintCodemodUtils.isNodeOfType)(argument, 'ObjectExpression')) { checkForPseudoClasses(node, argument, context); } } function checkStylesObject(node, stylesValue, context) { if (!stylesValue) { return; } if ((0, _eslintCodemodUtils.isNodeOfType)(stylesValue, 'ObjectExpression')) { stylesValue.properties.forEach(function (prop) { if (!(0, _eslintCodemodUtils.isNodeOfType)(prop, 'Property') || !prop.value || !(0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'ArrowFunctionExpression') && !(0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'FunctionExpression')) { return; } var body = prop.value.body; if ((0, _eslintCodemodUtils.isNodeOfType)(body, 'ObjectExpression')) { checkForPseudoClasses(node, body, context); } else if ((0, _eslintCodemodUtils.isNodeOfType)(body, 'BlockStatement')) { var visitor = { ReturnStatement: function ReturnStatement(returnStmt) { if (returnStmt.argument && (0, _eslintCodemodUtils.isNodeOfType)(returnStmt.argument, 'ObjectExpression')) { checkForPseudoClasses(node, returnStmt.argument, context); } }, AssignmentExpression: function AssignmentExpression(assignExpr) { // Handle cases like styles[':hover'] = { ... } var left = assignExpr.left; if (!(0, _eslintCodemodUtils.isNodeOfType)(left, 'MemberExpression')) { return; } var property = left.property; if (!(0, _eslintCodemodUtils.isNodeOfType)(property, 'Literal')) { return; } var value = property.value; if (typeof value !== 'string') { return; } if (unsupportedSelectors.some(function (selector) { return value.includes(selector); })) { context.report({ node: property, messageId: 'noPseudoClass', data: { pseudo: value } }); } } }; body.body.forEach(function (stmt) { if ((0, _eslintCodemodUtils.isNodeOfType)(stmt, 'ReturnStatement')) { visitor.ReturnStatement(stmt); } else if ((0, _eslintCodemodUtils.isNodeOfType)(stmt, 'ExpressionStatement') && (0, _eslintCodemodUtils.isNodeOfType)(stmt.expression, 'AssignmentExpression')) { visitor.AssignmentExpression(stmt.expression); } else if ((0, _eslintCodemodUtils.isNodeOfType)(stmt, 'IfStatement')) { var checkBlock = function checkBlock(block) { if ((0, _eslintCodemodUtils.isNodeOfType)(block, 'BlockStatement')) { block.body.forEach(function (innerStmt) { if ((0, _eslintCodemodUtils.isNodeOfType)(innerStmt, 'ExpressionStatement') && (0, _eslintCodemodUtils.isNodeOfType)(innerStmt.expression, 'AssignmentExpression')) { visitor.AssignmentExpression(innerStmt.expression); } }); } else if ((0, _eslintCodemodUtils.isNodeOfType)(block, 'ExpressionStatement') && (0, _eslintCodemodUtils.isNodeOfType)(block.expression, 'AssignmentExpression')) { visitor.AssignmentExpression(block.expression); } }; if (stmt.consequent) { checkBlock(stmt.consequent); } if (stmt.alternate) { checkBlock(stmt.alternate); } } }); } }); } else if ((0, _eslintCodemodUtils.isNodeOfType)(stylesValue, 'Identifier')) { // track the variable var scope = getScope(context, stylesValue); var variable = null; var currentScope = scope; // Search through scope chain while (currentScope && !variable) { variable = currentScope.variables.find(function (v) { return v.name === stylesValue.name; }); currentScope = currentScope.upper; } if (variable && variable.defs.length > 0) { var def = variable.defs[0]; if ((0, _eslintCodemodUtils.isNodeOfType)(def.node, 'VariableDeclarator') && def.node.init) { checkStylesObject(node, def.node.init, context); } } } }