Repository: yannickcr/eslint-plugin-react (js)

Issue:
Does jsx-pascal-case break <script crossorigin src="..."></script>?
Using prettier-eslint with react/recommended.
Trying to set script tags within JSX.
[React recommends](https://reactjs.org/docs/cross-origin-errors.html) adding `crossorigin` attribute.
Prettier keeps pascal-casing the attribute to `crossOrigin`.

Possible bug?

Pull request:
[Fix] crossOrigin is wrong; and crossorigin is only valid on script/img/video
Fixes #1642

Patch:
diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js
index e4315872..70b5772d 100644
--- a/lib/rules/no-unknown-property.js
+++ b/lib/rules/no-unknown-property.js
@@ -15,12 +15,18 @@ const DEFAULTS = {
 };
 
 const UNKNOWN_MESSAGE = 'Unknown property \'{{name}}\' found, use \'{{standardName}}\' instead';
+const WRONG_TAG_MESSAGE = 'Invalid property \'{{name}}\' found on tag \'{{tagName}}\', but it is only allowed on: {{allowedTags}}';
 
 const DOM_ATTRIBUTE_NAMES = {
   'accept-charset': 'acceptCharset',
   class: 'className',
   for: 'htmlFor',
-  'http-equiv': 'httpEquiv'
+  'http-equiv': 'httpEquiv',
+  crossOrigin: 'crossorigin'
+};
+
+const ATTRIBUTE_TAGS_MAP = {
+  crossorigin: ['script', 'img', 'video']
 };
 
 const SVGDOM_ATTRIBUTE_NAMES = {
@@ -112,7 +118,7 @@ const DOM_PROPERTY_NAMES = [
   // Standard
   'acceptCharset', 'accessKey', 'allowFullScreen', 'allowTransparency', 'autoComplete', 'autoFocus', 'autoPlay',
   'cellPadding', 'cellSpacing', 'charSet', 'classID', 'className', 'colSpan', 'contentEditable', 'contextMenu',
-  'crossOrigin', 'dateTime', 'encType', 'formAction', 'formEncType', 'formMethod', 'formNoValidate', 'formTarget',
+  'dateTime', 'encType', 'formAction', 'formEncType', 'formMethod', 'formNoValidate', 'formTarget',
   'frameBorder', 'hrefLang', 'htmlFor', 'httpEquiv', 'inputMode', 'keyParams', 'keyType', 'marginHeight', 'marginWidth',
   'maxLength', 'mediaGroup', 'minLength', 'noValidate', 'onAnimationEnd', 'onAnimationIteration', 'onAnimationStart',
   'onBlur', 'onChange', 'onClick', 'onContextMenu', 'onCopy', 'onCompositionEnd', 'onCompositionStart',
@@ -149,6 +155,18 @@ function isTagName(node) {
   return false;
 }
 
+/**
+ * Extracts the tag name for the JSXAttribute
+ * @param {Object} node - JSXAttribute being tested.
+ * @returns {String} tag name
+ */
+function getTagName(node) {
+  if (node && node.parent && node.parent.name && node.parent.name) {
+    return node.parent.name.name;
+  }
+  return null;
+}
+
 /**
  * Get the standard name of the attribute.
  * @param {String} name - Name of the attribute.
@@ -209,8 +227,26 @@ module.exports = {
       JSXAttribute: function(node) {
         const ignoreNames = getIgnoreConfig();
         const name = sourceCode.getText(node.name);
+        if (ignoreNames.indexOf(name) >= 0) {
+          return;
+        }
+
+        const tagName = getTagName(node);
+        const allowedTags = ATTRIBUTE_TAGS_MAP[name];
+        if (tagName && allowedTags && /[^A-Z]/.test(tagName.charAt(0)) && allowedTags.indexOf(tagName) === -1) {
+          context.report({
+            node: node,
+            message: WRONG_TAG_MESSAGE,
+            data: {
+              name: name,
+              tagName: tagName,
+              allowedTags: allowedTags.join(', ')
+            }
+          });
+        }
+
         const standardName = getStandardName(name);
-        if (!isTagName(node) || !standardName || ignoreNames.indexOf(name) >= 0) {
+        if (!isTagName(node) || !standardName) {
           return;
         }
         context.report({


Test patch:
diff --git a/tests/lib/rules/no-unknown-property.js b/tests/lib/rules/no-unknown-property.js
index eb079fd6..5a730b9c 100644
--- a/tests/lib/rules/no-unknown-property.js
+++ b/tests/lib/rules/no-unknown-property.js
@@ -41,7 +41,8 @@ ruleTester.run('no-unknown-property', rule, {
     {code: '<atom-panel class="foo"></atom-panel>;'}, {
       code: '<div class="bar"></div>;',
       options: [{ignore: ['class']}]
-    }
+    },
+    {code: '<script crossorigin />'}
   ],
   invalid: [{
     code: '<div class="bar"></div>;',
@@ -79,5 +80,14 @@ ruleTester.run('no-unknown-property', rule, {
     code: '<rect clip-path="bar" />;',
     output: '<rect clipPath="bar" />;',
     errors: [{message: 'Unknown property \'clip-path\' found, use \'clipPath\' instead'}]
+  }, {
+    code: '<script crossOrigin />',
+    errors: [{message: 'Unknown property \'crossOrigin\' found, use \'crossorigin\' instead'}]
+  }, {
+    code: '<div crossOrigin />',
+    errors: [{message: 'Unknown property \'crossOrigin\' found, use \'crossorigin\' instead'}]
+  }, {
+    code: '<div crossorigin />',
+    errors: [{message: 'Invalid property \'crossorigin\' found on tag \'div\', but it is only allowed on: script, img, video'}]
   }]
 });