@atlaskit/eslint-plugin-design-system
Version:
The essential plugin for use with the Atlassian Design System.
238 lines (233 loc) • 9.51 kB
JavaScript
;
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);
}
}
}
}