eslint-plugin-react-native-unistyles
Version:
React Native Unistyles rules for ESLint
281 lines (280 loc) • 12.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.stylesASTHelpers = exports.StyleSheets = void 0;
const utils_1 = require("@typescript-eslint/utils");
const generic_1 = require("./generic");
/**
* StyleSheets represents the StyleSheets found in the source code.
*/
class StyleSheets {
styleSheets = {};
/**
* Add a StyleSheet to our StyleSheets collections.
*/
add(styleSheetName, properties) {
this.styleSheets[styleSheetName] = properties;
}
/**
* MarkAsUsed marks a rule as used in our source code by removing it from the
* specified StyleSheet rules.
*
* @param fullyQualifiedName - The fully qualified name of the rule (e.g., 'styles.text')
*/
markAsUsed(fullyQualifiedName) {
const nameSplit = fullyQualifiedName.split('.');
const styleSheetName = nameSplit[0];
const styleSheetProperty = nameSplit[1];
if (styleSheetName && this.styleSheets[styleSheetName]) {
this.styleSheets[styleSheetName] = this.styleSheets[styleSheetName].filter((property) => {
if (property.key.type === utils_1.AST_NODE_TYPES.Identifier) {
return property.key.name !== styleSheetProperty;
}
if (property.key.type === utils_1.AST_NODE_TYPES.Literal) {
return property.key.value?.toString() !== styleSheetProperty;
}
if (property.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
const keyString = generic_1.genericUtils.getStringFromTemplateLiteral(property.key);
return keyString !== styleSheetProperty;
}
return true;
});
}
}
/**
* GetUnusedReferences returns all collected StyleSheets and their unmarked rules.
*/
getUnusedReferences() {
return this.styleSheets;
}
}
exports.StyleSheets = StyleSheets;
exports.stylesASTHelpers = {
containsStyleSheetObject(node, objectNames) {
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
const callee = node.callee;
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
const object = callee.object;
if (object.type === utils_1.AST_NODE_TYPES.Identifier) {
return objectNames.includes(object.name);
}
}
}
return false;
},
containsCreateCall(node) {
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
const callee = node.callee;
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
// StyleSheet.create
const property = callee.property;
if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
return property.name === 'create';
}
// StyleSheet["create"]
if (property.type === utils_1.AST_NODE_TYPES.Literal) {
return property.value === 'create';
}
// StyleSheet[`create`]
if (property.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
const propertyName = generic_1.genericUtils.getStringFromTemplateLiteral(property);
return propertyName === 'create';
}
}
}
return false;
},
isStyleSheetDeclaration(node) {
const objectNames = ['StyleSheet'];
// has StylesSheet indetifier
const hasStyleSheetObject = exports.stylesASTHelpers.containsStyleSheetObject(node, objectNames);
// calls create function StyleSheet.create
const hasCreateCall = exports.stylesASTHelpers.containsCreateCall(node);
return hasStyleSheetObject && hasCreateCall;
},
getStyleSheetName(node) {
const parent = node.parent;
if (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
if (parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
return parent.id.name;
}
}
return undefined;
},
getStyleDeclarations(node) {
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
const firstArgument = node.arguments[0];
if (firstArgument?.type === utils_1.AST_NODE_TYPES.ObjectExpression
|| firstArgument?.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
const properties = firstArgument.properties;
return properties.filter((property) => property.type === utils_1.AST_NODE_TYPES.Property);
}
if (firstArgument?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
const body = firstArgument.body;
if (body?.type === utils_1.AST_NODE_TYPES.ObjectExpression || body?.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
const properties = body.properties;
return properties.filter((property) => property.type === utils_1.AST_NODE_TYPES.Property);
}
if (body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
const statements = body.body;
const returnStatement = statements.find((body) => body.type === utils_1.AST_NODE_TYPES.ReturnStatement);
const argument = returnStatement?.argument;
if (argument?.type === utils_1.AST_NODE_TYPES.ObjectExpression
|| argument?.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
const properties = argument.properties;
return properties.filter((property) => property.type === utils_1.AST_NODE_TYPES.Property);
}
}
}
if (firstArgument?.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
const body = firstArgument.body;
const statements = body.body;
const returnStatement = statements.find((body) => body.type === utils_1.AST_NODE_TYPES.ReturnStatement);
const argument = returnStatement?.argument;
if (argument?.type === utils_1.AST_NODE_TYPES.ObjectExpression
|| argument?.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
const properties = argument.properties;
return properties.filter((property) => property.type === utils_1.AST_NODE_TYPES.Property);
}
}
}
return [];
},
getStyleDeclarationsChunks(node) {
const getChunks = (properties) => {
const result = [];
let chunk = [];
for (const property of properties) {
if (property.type === utils_1.AST_NODE_TYPES.Property) {
chunk.push(property);
}
else if (chunk.length) {
result.push(chunk);
chunk = [];
}
}
if (chunk.length) {
result.push(chunk);
}
return result;
};
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
const firstArgument = node.arguments[0];
// example StyleSheet.create( { ... } )
if (firstArgument?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
const properties = firstArgument.properties;
return getChunks(properties);
}
// example StyleSheet.create(() => ( { ... } ))
// example StyleSheet.create(() => { return { ... } })
if (firstArgument?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
const body = firstArgument.body;
// example StyleSheet.create(() => ( { ... } ))
if (body?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
const properties = body.properties;
return getChunks(properties);
}
// example StyleSheet.create(() => { return { ... } })
if (body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
const statements = body.body;
const returnStatement = statements.find((body) => body.type === utils_1.AST_NODE_TYPES.ReturnStatement);
const argument = returnStatement?.argument;
if (argument?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
const properties = argument.properties;
return getChunks(properties);
}
}
}
// example StyleSheet.create(function () { ... })
// example2 StyleSheet.create(function namedfunc() { ... })
if (firstArgument?.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
const body = firstArgument.body;
const statements = body.body;
const returnStatement = statements.find((body) => body.type === utils_1.AST_NODE_TYPES.ReturnStatement);
const argument = returnStatement?.argument;
if (argument?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
const properties = argument.properties;
return getChunks(properties);
}
}
}
return [];
},
getPropertiesChunks(properties) {
const result = [];
let chunk = [];
for (const property of properties) {
if (property.type === utils_1.AST_NODE_TYPES.Property) {
chunk.push(property);
}
else if (chunk.length) {
result.push(chunk);
chunk = [];
}
}
if (chunk.length) {
result.push(chunk);
}
return result;
},
getExpressionIdentifier(node) {
if (node) {
switch (node.type) {
case 'Identifier':
return node.name;
case 'Literal':
return node.value?.toString() ?? '';
case 'TemplateLiteral':
return generic_1.genericUtils.getStringFromTemplateLiteral(node) ?? '';
default:
return '';
}
}
return '';
},
getStylePropertyIdentifier(node) {
if (node.key) {
return exports.stylesASTHelpers.getExpressionIdentifier(node.key);
}
return undefined;
},
// example: styles.text & styles['text'] & styles[`text`]
getPotentialStyleReferenceFromMemberExpression(node) {
if (node.parent.type === utils_1.AST_NODE_TYPES.MemberExpression)
return undefined;
let objectName;
if (node.object.type === utils_1.AST_NODE_TYPES.Identifier) {
objectName = node.object.name;
}
if (node.object.type === utils_1.AST_NODE_TYPES.Literal) {
objectName = node.object.value?.toString();
}
if (node.object.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
objectName = generic_1.genericUtils.getStringFromTemplateLiteral(node.object);
}
let propertyName;
if (node.property.type === utils_1.AST_NODE_TYPES.Identifier) {
propertyName = node.property.name;
}
if (node.property.type === utils_1.AST_NODE_TYPES.Literal) {
propertyName = node.property.value?.toString();
}
if (node.property.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
propertyName = generic_1.genericUtils.getStringFromTemplateLiteral(node.property);
}
if (objectName && propertyName) {
return `${objectName}.${propertyName}`;
}
return undefined;
},
// example: margin vs marginTop
isEitherShortHand(property1, property2) {
const shorthands = ['margin', 'padding', 'border', 'flex'];
if (shorthands.includes(property1)) {
return property2.startsWith(property1);
}
if (shorthands.includes(property2)) {
return property1.startsWith(property2);
}
return false;
},
};