style-prop-object.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * @fileoverview Enforce style prop value is an object
  3. * @author David Petersen
  4. */
  5. 'use strict';
  6. const variableUtil = require('../util/variable');
  7. const docsUrl = require('../util/docsUrl');
  8. const isCreateElement = require('../util/isCreateElement');
  9. const report = require('../util/report');
  10. // ------------------------------------------------------------------------------
  11. // Rule Definition
  12. // ------------------------------------------------------------------------------
  13. const messages = {
  14. stylePropNotObject: 'Style prop value must be an object',
  15. };
  16. /** @type {import('eslint').Rule.RuleModule} */
  17. module.exports = {
  18. meta: {
  19. docs: {
  20. description: 'Enforce style prop value is an object',
  21. category: 'Possible Errors',
  22. recommended: false,
  23. url: docsUrl('style-prop-object'),
  24. },
  25. messages,
  26. schema: [
  27. {
  28. type: 'object',
  29. properties: {
  30. allow: {
  31. type: 'array',
  32. items: {
  33. type: 'string',
  34. },
  35. additionalItems: false,
  36. uniqueItems: true,
  37. },
  38. },
  39. },
  40. ],
  41. },
  42. create(context) {
  43. const allowed = new Set(((context.options.length > 0) && context.options[0].allow) || []);
  44. /**
  45. * @param {ASTNode} expression An Identifier node
  46. * @returns {boolean}
  47. */
  48. function isNonNullaryLiteral(expression) {
  49. return expression.type === 'Literal' && expression.value !== null;
  50. }
  51. /**
  52. * @param {object} node A Identifier node
  53. */
  54. function checkIdentifiers(node) {
  55. const variable = variableUtil.getVariableFromContext(context, node, node.name);
  56. if (!variable || !variable.defs[0] || !variable.defs[0].node.init) {
  57. return;
  58. }
  59. if (isNonNullaryLiteral(variable.defs[0].node.init)) {
  60. report(context, messages.stylePropNotObject, 'stylePropNotObject', {
  61. node,
  62. });
  63. }
  64. }
  65. return {
  66. CallExpression(node) {
  67. if (
  68. isCreateElement(context, node)
  69. && node.arguments.length > 1
  70. ) {
  71. if ('name' in node.arguments[0] && node.arguments[0].name) {
  72. // store name of component
  73. const componentName = node.arguments[0].name;
  74. // allowed list contains the name
  75. if (allowed.has(componentName)) {
  76. // abort operation
  77. return;
  78. }
  79. }
  80. if (node.arguments[1].type === 'ObjectExpression') {
  81. const style = node.arguments[1].properties.find((property) => (
  82. 'key' in property
  83. && property.key
  84. && 'name' in property.key
  85. && property.key.name === 'style'
  86. && !property.computed
  87. ));
  88. if (style && 'value' in style) {
  89. if (style.value.type === 'Identifier') {
  90. checkIdentifiers(style.value);
  91. } else if (isNonNullaryLiteral(style.value)) {
  92. report(context, messages.stylePropNotObject, 'stylePropNotObject', {
  93. node: style.value,
  94. });
  95. }
  96. }
  97. }
  98. }
  99. },
  100. JSXAttribute(node) {
  101. if (!node.value || node.name.name !== 'style') {
  102. return;
  103. }
  104. // store parent element
  105. const parentElement = node.parent;
  106. // parent element is a JSXOpeningElement
  107. if (parentElement && parentElement.type === 'JSXOpeningElement') {
  108. // get the name of the JSX element
  109. const name = parentElement.name && parentElement.name.name;
  110. // allowed list contains the name
  111. if (allowed.has(name)) {
  112. // abort operation
  113. return;
  114. }
  115. }
  116. if (node.value.type !== 'JSXExpressionContainer' || isNonNullaryLiteral(node.value.expression)) {
  117. report(context, messages.stylePropNotObject, 'stylePropNotObject', {
  118. node,
  119. });
  120. } else if (node.value.expression.type === 'Identifier') {
  121. checkIdentifiers(node.value.expression);
  122. }
  123. },
  124. };
  125. },
  126. };