123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- 'use strict';
- /**
- * StyleSheets represents the StyleSheets found in the source code.
- * @constructor
- */
- function StyleSheets() {
- this.styleSheets = {};
- }
- /**
- * Add adds a StyleSheet to our StyleSheets collections.
- *
- * @param {string} styleSheetName - The name of the StyleSheet.
- * @param {object} properties - The collection of rules in the styleSheet.
- */
- StyleSheets.prototype.add = function (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 {string} fullyQualifiedName - The fully qualified name of the rule.
- * for example 'styles.text'
- */
- StyleSheets.prototype.markAsUsed = function (fullyQualifiedName) {
- const nameSplit = fullyQualifiedName.split('.');
- const styleSheetName = nameSplit[0];
- const styleSheetProperty = nameSplit[1];
- if (this.styleSheets[styleSheetName]) {
- this.styleSheets[styleSheetName] = this
- .styleSheets[styleSheetName]
- .filter((property) => property.key.name !== styleSheetProperty);
- }
- };
- /**
- * GetUnusedReferences returns all collected StyleSheets and their
- * unmarked rules.
- */
- StyleSheets.prototype.getUnusedReferences = function () {
- return this.styleSheets;
- };
- /**
- * AddColorLiterals adds an array of expressions that contain color literals
- * to the ColorLiterals collection
- * @param {array} expressions - an array of expressions containing color literals
- */
- StyleSheets.prototype.addColorLiterals = function (expressions) {
- if (!this.colorLiterals) {
- this.colorLiterals = [];
- }
- this.colorLiterals = this.colorLiterals.concat(expressions);
- };
- /**
- * GetColorLiterals returns an array of collected color literals expressions
- * @returns {Array}
- */
- StyleSheets.prototype.getColorLiterals = function () {
- return this.colorLiterals;
- };
- /**
- * AddObjectexpressions adds an array of expressions to the ObjectExpressions collection
- * @param {Array} expressions - an array of expressions containing ObjectExpressions in
- * inline styles
- */
- StyleSheets.prototype.addObjectExpressions = function (expressions) {
- if (!this.objectExpressions) {
- this.objectExpressions = [];
- }
- this.objectExpressions = this.objectExpressions.concat(expressions);
- };
- /**
- * GetObjectExpressions returns an array of collected object expressiosn used in inline styles
- * @returns {Array}
- */
- StyleSheets.prototype.getObjectExpressions = function () {
- return this.objectExpressions;
- };
- let currentContent;
- const getSourceCode = (node) => currentContent
- .getSourceCode(node)
- .getText(node);
- const getStyleSheetObjectNames = (settings) => settings['react-native/style-sheet-object-names'] || ['StyleSheet'];
- const astHelpers = {
- containsStyleSheetObject: function (node, objectNames) {
- return Boolean(
- node
- && node.type === 'CallExpression'
- && node.callee
- && node.callee.object
- && node.callee.object.name
- && objectNames.includes(node.callee.object.name)
- );
- },
- containsCreateCall: function (node) {
- return Boolean(
- node
- && node.callee
- && node.callee.property
- && node.callee.property.name === 'create'
- );
- },
- isStyleSheetDeclaration: function (node, settings) {
- const objectNames = getStyleSheetObjectNames(settings);
- return Boolean(
- astHelpers.containsStyleSheetObject(node, objectNames)
- && astHelpers.containsCreateCall(node)
- );
- },
- getStyleSheetName: function (node) {
- if (node && node.parent && node.parent.id) {
- return node.parent.id.name;
- }
- },
- getStyleDeclarations: function (node) {
- if (
- node
- && node.type === 'CallExpression'
- && node.arguments
- && node.arguments[0]
- && node.arguments[0].properties
- ) {
- return node.arguments[0].properties.filter((property) => property.type === 'Property');
- }
- return [];
- },
- getStyleDeclarationsChunks: function (node) {
- if (
- node
- && node.type === 'CallExpression'
- && node.arguments
- && node.arguments[0]
- && node.arguments[0].properties
- ) {
- const { properties } = node.arguments[0];
- const result = [];
- let chunk = [];
- for (let i = 0; i < properties.length; i += 1) {
- const property = properties[i];
- if (property.type === 'Property') {
- chunk.push(property);
- } else if (chunk.length) {
- result.push(chunk);
- chunk = [];
- }
- }
- if (chunk.length) {
- result.push(chunk);
- }
- return result;
- }
- return [];
- },
- getPropertiesChunks: function (properties) {
- const result = [];
- let chunk = [];
- for (let i = 0; i < properties.length; i += 1) {
- const property = properties[i];
- if (property.type === 'Property') {
- chunk.push(property);
- } else if (chunk.length) {
- result.push(chunk);
- chunk = [];
- }
- }
- if (chunk.length) {
- result.push(chunk);
- }
- return result;
- },
- getExpressionIdentifier: function (node) {
- if (node) {
- switch (node.type) {
- case 'Identifier':
- return node.name;
- case 'Literal':
- return node.value;
- case 'TemplateLiteral':
- return node.quasis.reduce(
- (result, quasi, index) => result
- + quasi.value.cooked
- + astHelpers.getExpressionIdentifier(node.expressions[index]),
- ''
- );
- default:
- return '';
- }
- }
- return '';
- },
- getStylePropertyIdentifier: function (node) {
- if (
- node
- && node.key
- ) {
- return astHelpers.getExpressionIdentifier(node.key);
- }
- },
- isStyleAttribute: function (node) {
- return Boolean(
- node.type === 'JSXAttribute'
- && node.name
- && node.name.name
- && node.name.name.toLowerCase().includes('style')
- );
- },
- collectStyleObjectExpressions: function (node, context) {
- currentContent = context;
- if (astHelpers.hasArrayOfStyleReferences(node)) {
- const styleReferenceContainers = node
- .expression
- .elements;
- return astHelpers.collectStyleObjectExpressionFromContainers(
- styleReferenceContainers
- );
- } if (node && node.expression) {
- return astHelpers.getStyleObjectExpressionFromNode(node.expression);
- }
- return [];
- },
- collectColorLiterals: function (node, context) {
- if (!node) {
- return [];
- }
- currentContent = context;
- if (astHelpers.hasArrayOfStyleReferences(node)) {
- const styleReferenceContainers = node
- .expression
- .elements;
- return astHelpers.collectColorLiteralsFromContainers(
- styleReferenceContainers
- );
- }
- if (node.type === 'ObjectExpression') {
- return astHelpers.getColorLiteralsFromNode(node);
- }
- return astHelpers.getColorLiteralsFromNode(node.expression);
- },
- collectStyleObjectExpressionFromContainers: function (nodes) {
- let objectExpressions = [];
- nodes.forEach((node) => {
- objectExpressions = objectExpressions
- .concat(astHelpers.getStyleObjectExpressionFromNode(node));
- });
- return objectExpressions;
- },
- collectColorLiteralsFromContainers: function (nodes) {
- let colorLiterals = [];
- nodes.forEach((node) => {
- colorLiterals = colorLiterals
- .concat(astHelpers.getColorLiteralsFromNode(node));
- });
- return colorLiterals;
- },
- getStyleReferenceFromNode: function (node) {
- let styleReference;
- let leftStyleReferences;
- let rightStyleReferences;
- if (!node) {
- return [];
- }
- switch (node.type) {
- case 'MemberExpression':
- styleReference = astHelpers.getStyleReferenceFromExpression(node);
- return [styleReference];
- case 'LogicalExpression':
- leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.left);
- rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.right);
- return [].concat(leftStyleReferences).concat(rightStyleReferences);
- case 'ConditionalExpression':
- leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.consequent);
- rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.alternate);
- return [].concat(leftStyleReferences).concat(rightStyleReferences);
- default:
- return [];
- }
- },
- getStyleObjectExpressionFromNode: function (node) {
- let leftStyleObjectExpression;
- let rightStyleObjectExpression;
- if (!node) {
- return [];
- }
- if (node.type === 'ObjectExpression') {
- return [astHelpers.getStyleObjectFromExpression(node)];
- }
- switch (node.type) {
- case 'LogicalExpression':
- leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.left);
- rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.right);
- return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression);
- case 'ConditionalExpression':
- leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.consequent);
- rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.alternate);
- return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression);
- default:
- return [];
- }
- },
- getColorLiteralsFromNode: function (node) {
- let leftColorLiterals;
- let rightColorLiterals;
- if (!node) {
- return [];
- }
- if (node.type === 'ObjectExpression') {
- return [astHelpers.getColorLiteralsFromExpression(node)];
- }
- switch (node.type) {
- case 'LogicalExpression':
- leftColorLiterals = astHelpers.getColorLiteralsFromNode(node.left);
- rightColorLiterals = astHelpers.getColorLiteralsFromNode(node.right);
- return [].concat(leftColorLiterals).concat(rightColorLiterals);
- case 'ConditionalExpression':
- leftColorLiterals = astHelpers.getColorLiteralsFromNode(node.consequent);
- rightColorLiterals = astHelpers.getColorLiteralsFromNode(node.alternate);
- return [].concat(leftColorLiterals).concat(rightColorLiterals);
- default:
- return [];
- }
- },
- hasArrayOfStyleReferences: function (node) {
- return node && Boolean(
- node.type === 'JSXExpressionContainer'
- && node.expression
- && node.expression.type === 'ArrayExpression'
- );
- },
- getStyleReferenceFromExpression: function (node) {
- const result = [];
- const name = astHelpers.getObjectName(node);
- if (name) {
- result.push(name);
- }
- const property = astHelpers.getPropertyName(node);
- if (property) {
- result.push(property);
- }
- return result.join('.');
- },
- getStyleObjectFromExpression: function (node) {
- const obj = {};
- let invalid = false;
- if (node.properties && node.properties.length) {
- node.properties.forEach((p) => {
- if (!p.value || !p.key) {
- return;
- }
- if (p.value.type === 'Literal') {
- invalid = true;
- obj[p.key.name] = p.value.value;
- } else if (p.value.type === 'ConditionalExpression') {
- const innerNode = p.value;
- if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') {
- invalid = true;
- obj[p.key.name] = getSourceCode(innerNode);
- }
- } else if (p.value.type === 'UnaryExpression' && p.value.operator === '-' && p.value.argument.type === 'Literal') {
- invalid = true;
- obj[p.key.name] = -1 * p.value.argument.value;
- } else if (p.value.type === 'UnaryExpression' && p.value.operator === '+' && p.value.argument.type === 'Literal') {
- invalid = true;
- obj[p.key.name] = p.value.argument.value;
- }
- });
- }
- return invalid ? { expression: obj, node: node } : undefined;
- },
- getColorLiteralsFromExpression: function (node) {
- const obj = {};
- let invalid = false;
- if (node.properties && node.properties.length) {
- node.properties.forEach((p) => {
- if (p.key && p.key.name && p.key.name.toLowerCase().indexOf('color') !== -1) {
- if (p.value.type === 'Literal') {
- invalid = true;
- obj[p.key.name] = p.value.value;
- } else if (p.value.type === 'ConditionalExpression') {
- const innerNode = p.value;
- if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') {
- invalid = true;
- obj[p.key.name] = getSourceCode(innerNode);
- }
- }
- }
- });
- }
- return invalid ? { expression: obj, node: node } : undefined;
- },
- getObjectName: function (node) {
- if (
- node
- && node.object
- && node.object.name
- ) {
- return node.object.name;
- }
- },
- getPropertyName: function (node) {
- if (
- node
- && node.property
- && node.property.name
- ) {
- return node.property.name;
- }
- },
- getPotentialStyleReferenceFromMemberExpression: function (node) {
- if (
- node
- && node.object
- && node.object.type === 'Identifier'
- && node.object.name
- && node.property
- && node.property.type === 'Identifier'
- && node.property.name
- && node.parent.type !== 'MemberExpression'
- ) {
- return [node.object.name, node.property.name].join('.');
- }
- },
- isEitherShortHand: function (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;
- },
- };
- module.exports.astHelpers = astHelpers;
- module.exports.StyleSheets = StyleSheets;
|