@@ -15,12 +15,18 @@ const DEFAULTS = {
1515} ;
1616
1717const UNKNOWN_MESSAGE = 'Unknown property \'{{name}}\' found, use \'{{standardName}}\' instead' ;
18+ const WRONG_TAG_MESSAGE = 'Invalid property \'{{name}}\' found on tag \'{{tagName}}\', but it is only allowed on: {{allowedTags}}' ;
1819
1920const DOM_ATTRIBUTE_NAMES = {
2021 'accept-charset' : 'acceptCharset' ,
2122 class : 'className' ,
2223 for : 'htmlFor' ,
23- 'http-equiv' : 'httpEquiv'
24+ 'http-equiv' : 'httpEquiv' ,
25+ crossOrigin : 'crossorigin'
26+ } ;
27+
28+ const ATTRIBUTE_TAGS_MAP = {
29+ crossorigin : [ 'script' , 'img' , 'video' ]
2430} ;
2531
2632const SVGDOM_ATTRIBUTE_NAMES = {
@@ -112,7 +118,7 @@ const DOM_PROPERTY_NAMES = [
112118 // Standard
113119 'acceptCharset' , 'accessKey' , 'allowFullScreen' , 'allowTransparency' , 'autoComplete' , 'autoFocus' , 'autoPlay' ,
114120 'cellPadding' , 'cellSpacing' , 'charSet' , 'classID' , 'className' , 'colSpan' , 'contentEditable' , 'contextMenu' ,
115- 'crossOrigin' , ' dateTime', 'encType' , 'formAction' , 'formEncType' , 'formMethod' , 'formNoValidate' , 'formTarget' ,
121+ 'dateTime' , 'encType' , 'formAction' , 'formEncType' , 'formMethod' , 'formNoValidate' , 'formTarget' ,
116122 'frameBorder' , 'hrefLang' , 'htmlFor' , 'httpEquiv' , 'inputMode' , 'keyParams' , 'keyType' , 'marginHeight' , 'marginWidth' ,
117123 'maxLength' , 'mediaGroup' , 'minLength' , 'noValidate' , 'onAnimationEnd' , 'onAnimationIteration' , 'onAnimationStart' ,
118124 'onBlur' , 'onChange' , 'onClick' , 'onContextMenu' , 'onCopy' , 'onCompositionEnd' , 'onCompositionStart' ,
@@ -149,6 +155,18 @@ function isTagName(node) {
149155 return false ;
150156}
151157
158+ /**
159+ * Extracts the tag name for the JSXAttribute
160+ * @param {Object } node - JSXAttribute being tested.
161+ * @returns {String } tag name
162+ */
163+ function getTagName ( node ) {
164+ if ( node && node . parent && node . parent . name && node . parent . name ) {
165+ return node . parent . name . name ;
166+ }
167+ return null ;
168+ }
169+
152170/**
153171 * Get the standard name of the attribute.
154172 * @param {String } name - Name of the attribute.
@@ -209,8 +227,26 @@ module.exports = {
209227 JSXAttribute : function ( node ) {
210228 const ignoreNames = getIgnoreConfig ( ) ;
211229 const name = sourceCode . getText ( node . name ) ;
230+ if ( ignoreNames . indexOf ( name ) >= 0 ) {
231+ return ;
232+ }
233+
234+ const tagName = getTagName ( node ) ;
235+ const allowedTags = ATTRIBUTE_TAGS_MAP [ name ] ;
236+ if ( tagName && allowedTags && / [ ^ A - Z ] / . test ( tagName . charAt ( 0 ) ) && allowedTags . indexOf ( tagName ) === - 1 ) {
237+ context . report ( {
238+ node : node ,
239+ message : WRONG_TAG_MESSAGE ,
240+ data : {
241+ name : name ,
242+ tagName : tagName ,
243+ allowedTags : allowedTags . join ( ', ' )
244+ }
245+ } ) ;
246+ }
247+
212248 const standardName = getStandardName ( name ) ;
213- if ( ! isTagName ( node ) || ! standardName || ignoreNames . indexOf ( name ) >= 0 ) {
249+ if ( ! isTagName ( node ) || ! standardName ) {
214250 return ;
215251 }
216252 context . report ( {
0 commit comments