Skip to content

Conversation

EdwardDrapkin
Copy link

I wanted to forbid some prop types on DOM nodes as well as Components, so I added the option here. Let me know if there's anything that needs to be done here to get this approved. Thanks in advance!

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way, this will need tests.


An array of strings, with the names of props that are forbidden. The default value of this option is `['className', 'style']`.

### `includeDOM`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like something that will vary per prop name. Perhaps the enhancement should be that forbid can have strings or objects, and if an object, it can be something like { "property": "foo", "includeDOM": true }?

@EdwardDrapkin
Copy link
Author

EdwardDrapkin commented Sep 18, 2016

How about forbid and forbidOnDOM? forbidOnDOM would be an array of strings just like forbid except that it's empty by default. I can implement a second, clearer error message this way, but I'd have to rework the way the documentation is written a fair bit. Is that alright with you?

If that API is okay, I'll get this PR updated with new documentation, the new API, and tests tomorrow.

@ljharb
Copy link
Member

ljharb commented Sep 19, 2016

I think that allowing string-or-object in the array gives far greater flexibility - that would also let me potentially forbid a prop on one list of components, but not on others.

@EdwardDrapkin
Copy link
Author

EdwardDrapkin commented Sep 19, 2016

I think the flexibility is the same:

 {
  "forbid":  ["className", "style"], 
  "forbidOnDOM": ["style"] 
 }  

would achieve the same thing, no?

I think that's easier for users to manage than: `

   {
     "forbid": [ "style", { "className": { "forbidOnDOM": true } } ]
   }

Or:

   {
     "forbid": [ "style", { "property": "className", "forbidOnDOM": true } ]
   }

(or any other permutation of nesting an object inside the array of string that I can imagine)

It's a completely subjective decision and ultimately it's your code, so I'll do what you think is best, but IMO the first example is the easiest to read if I'm a hypothetical engineer and I'm reading someone else's .eslintrc and I'm not necessarily familiar with this particular plugin.

@ljharb
Copy link
Member

ljharb commented Sep 19, 2016

@EdwardDrapkin imagine a hypothetical future option where you could forbid "foo" on a Foo component, but allow "foo" on anything else - or where you want to forbid "style" on both React and DOM components, but simultaneously forbid "className" only on React components.

The latter certainly can be achieved with your suggestion, but that won't allow for the former, and that also requires that property names be duplicated in both lists, which can be hard to maintain.

@EdwardDrapkin
Copy link
Author

EdwardDrapkin commented Sep 19, 2016

Ahhhhh, I wasn't even considering that! You make excellent points.

Perhaps we could also take this opportunity to allow people the flexibility you imagine? I have two ideas.

First, we could allow passing custom validator functions like:

  {
    "property": "style",
    "validator": "no-dom-components"|"only-dom-components"|"all-components"|function 
  }

where the function is just (componentName:string) => boolean that the plugin would use to choose whether or not to apply the filters to the component? Alternatively, I could do (componentName:string, property:string) => boolean and expose the validation step itself to the user, but I'm having trouble imagining a case where that more useful than the former.

This would be pretty simple to implement, and is probably the most flexible approach, but not exactly user friendly because it's too complex for what I believe is likely to be the common use case...

So maybe, instead we just allow a regular expression to be passed?

  {
    "property": "style",
    "isValid": "^[A-Z]" 
  }

This is probably the easiest for users to use and covers like 99% of the cases people would want, but would require documentation to tell people to use something like isValid: "." to match all components' names, or a special case.

What do you think? I'm not sure what to name the config options in either case to be the most user friendly.

@ljharb
Copy link
Member

ljharb commented Sep 19, 2016

eslint configs are defined in JSON or yaml, so it'd be impossible to provide a function.

I think adding regex support is unneeded complexity - being able to configure a boolean for DOM and another for React, along with simple component names per-property that can override the booleans, seems like it would cover all the current and potential use cases pretty well.

@EdwardDrapkin
Copy link
Author

Can't you use a JS file as an .eslintrc.js? It's irrelevant anyway...

How does this look?

 {
   "property": <string>,
   "include": "React"|"DOM"|"Both"|<array> 
 }

Is include a good param name?

@ljharb
Copy link
Member

ljharb commented Sep 19, 2016

I think I'd prefer:

{
  "property": <string>,
  "allowOnComponents": <boolean>, // default false
  "allowOnDOM": <boolean>, // default false
  "forbid": <array of component name strings>, // overrides previous two booleans
}

where passing a simple string "foo" as the property name is equivalent to:

{
    "property": "foo",
    "allowOnComponents": false,
    "allowOnDOM": true, // simply so it's not a breaking change
    "forbid": [],
}

@EdwardDrapkin
Copy link
Author

Perfect. I'll get this PR updated sometime in the next 24-48 hours with these changes!

```js
...
"forbid-component-props": [<enabled>, { "forbid": [<string>] }]
"forbid-component-props": [<enabled>, { "forbid": [<string>|<object>], "defaultAllowOnComponents": <bool>, "defaultAllowOnDOM": <bool>}]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the two "default" booleans need to be on the object, not on the outer config object. not sure how to denote this tho.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unless you're suggesting that they'd be top-level settings as well? if so, i do think i like that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, there's an outer config option for specifying the default behavior and an option for overloading the default, but i wasn't quite sure how to make that clear in the documentation other than in the examples...

@EdwardDrapkin
Copy link
Author

Okay, the football sucked, so I went ahead and got to work on this.

I did some extra work that we hadn't discussed, but I was adding examples to the documentation and surfaced some cases I wanted to address.

I added defaultAllowOnComponents and defaultAllowOnDOM as top level config options to do exactly what they say. I also allowed a white allow alongside the blacklist forbid you asked for, because I imagined a case where someone was okay with font icon i tags having classNames but everything else going through something like css modules.

I updated the documentation and I think it's okay, but please do read and update it if you feel it's lacking. Or tell me and I gladly will!

I also added more test cases so Istanbul reports 100% coverage for me.

Let me know if there's any issues this time.

Thanks so much for your time helping me get this working (and for the rule in the first place)!

@ljharb
Copy link
Member

ljharb commented Sep 19, 2016

I think this is looking much better, and I like the additions you've made! I think we'll want to add a very large quantity of test cases, that cover as many interesting combinations of rules as possible.

@EdwardDrapkin
Copy link
Author

I added some more test cases, up to 21 now. I'm having trouble coming up with cases that aren't already tested.

{
type: 'object',
properties: {
defaultAllowOnDOM: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the schema is missing defaultAllowOnComponents?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops is all i got

additionalProperties: true
}]
},
additionalProperties: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can maybe set this to false, so nobody accidentally passes an invalid config option?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I can change it... I just didn't wanna mess with what you already had

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's a good point, it might constitute a breaking change. probably best to leave it true for now.

defaultAllowOnDOM: {
type: 'boolean'
},
forbid: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the schema also seems to be missing allow, which i think should probably share a schema with forbid? (ie, reuse the array schema in both)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay i was a little excited


var forbid = configuration.forbid || DEFAULTS;
return forbid.indexOf(prop) >= 0;
for (var i = 0; i < input.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use input.forEach here, instead of a for loop?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, done


function getError(prop, tag) {
if (rules[prop].forbid.indexOf(tag) > -1) {
return 'Prop `' + prop + '` is specifically forbidden on tags named ' + tag;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'… on ' + tag + ' components'?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It swaps between "$tag Components" and "$tag DOM nodes" now.

]
}, {
code: [
'var First = React.createClass({',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for each of the tests you're using for React.createClass, let's please also add a test case for an SFC, and a class-based component.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only added one total test case for each. I figure if one test case works for all three, all test cases should, because we're only getting at the JSX tags inside the (render) function body, right? I can add more if you think they're necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's another thing - we should check for React.createElement calls too (ie, non-JSX) :-)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want me to, that's cool and I'm down to learn how, but I don't know... so can you point me in the right direction if there's another plugin I might be able to read? If not, I'll just RTFM I guess.

And do you have any problem if I do it in a separate pull request? It seems outside the scope of this one and I might not have time to get to it until this upcoming weekend.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you may have opened a can of worms, at first glance there are several other plugins that do things with props that rely on JSXExpression too... no-duplicate-props, no-unknown-property, sort-props, maybe more....

What is the best way to go forward if this is an issue that affects multiple rules? Open a new issue and then read each rule one by one and make sure they're not affected?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a number of rules that address both - however, I'm fine if this rule covers jsx right now, but is expanded to non-JSX in a future PR.

defaultAllowOnComponents: {
type: 'boolean'
},
forbid: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there not a top-level allow as well?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that mean the entire plugin would have to switch into whitelist mode? Would that be useful to anyone?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yeah i guess if i wanted to override a forbid i'd now just modify the object inside the array. This is fine as-is.

@EdwardDrapkin
Copy link
Author

I think I addressed everything from your notes - and I'll make a separate pull request to add React.createElement support - but is there anything else that needs to be done now?

@burabure
Copy link
Contributor

burabure commented Nov 2, 2016

this might clash with #936

@ljharb
Copy link
Member

ljharb commented Nov 3, 2016

@EdwardDrapkin would you be alright with @burabure building off your commits to achieve both this PR and #936?

@burabure
Copy link
Contributor

@EdwardDrapkin ping

@burabure
Copy link
Contributor

@ljharb I don't think @EdwardDrapkin is going to reply. Should I go ahead?

@ljharb
Copy link
Member

ljharb commented Nov 16, 2016

Sure, go for it.

@EdwardDrapkin
Copy link
Author

I'm sorry, I've been really sick lately fighting off MRSA (yikes!) and I won't have time to get to this. You should definitely go ahead.

@ljharb
Copy link
Member

ljharb commented Nov 18, 2016

Thanks for confirming - @burabure, do your thing :-)

@burabure
Copy link
Contributor

burabure commented Nov 18, 2016

@EdwardDrapkin sorry to hear that, I have a friend that had MRSA and I know it really sucks. Hope you feel better soon!

@burabure
Copy link
Contributor

I don't have time to see this through, if anyone is up to it please do =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants