Skip to content

Commit

Permalink
Add acceptTranspilerName option to display-name rule (fixes #75)
Browse files Browse the repository at this point in the history
  • Loading branch information
yannickcr committed Jun 22, 2015
1 parent 3a0be29 commit 9b54b90
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 2 deletions.
60 changes: 58 additions & 2 deletions docs/rules/display-name.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,62 @@ var Hello = React.createClass({
});
```

## When Not To Use It
## Rule Options

If you are using JSX this value is already automatically set and it is safe for you to disable this rule.
```js
...
"display-name": [<enabled>, { "acceptTranspilerName": <boolean> }]
...
```

### `acceptTranspilerName`

When `true` the rule will accept the name set by the transpiler and does not require a `displayName` property in this case.

The following patterns are considered okay and do not cause warnings:

```js
var Hello = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
module.exports = Hello;
```

```js
export default class Hello extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
```

With the following patterns the transpiler can not assign a name for the component and therefore it will still cause warnings:

```js
module.exports = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
```

```js
export default class extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
```

```js
function HelloComponent() {
return React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
}
module.exports = HelloComponent();
```
47 changes: 47 additions & 0 deletions lib/rules/display-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ var ComponentList = componentUtil.List;

module.exports = function(context) {

var config = context.options[0] || {};
var acceptTranspilerName = config.acceptTranspilerName || false;

var componentList = new ComponentList();

var MISSING_MESSAGE = 'Component definition is missing display name';
Expand Down Expand Up @@ -80,6 +83,39 @@ module.exports = function(context) {
);
}

/**
* Checks if the component have a name set by the transpiler
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True ifcomponent have a name, false if not.
*/
function hasTranspilerName(node) {
var namedAssignment = (
node.type === 'ObjectExpression' &&
node.parent &&
node.parent.parent &&
node.parent.parent.type === 'AssignmentExpression' && (
!node.parent.parent.left.object ||
node.parent.parent.left.object.name !== 'module' ||
node.parent.parent.left.property.name !== 'exports'
)
);
var namedDeclaration = (
node.type === 'ObjectExpression' &&
node.parent &&
node.parent.parent &&
node.parent.parent.type === 'VariableDeclarator'
);
var namedClass = (
node.type === 'ClassDeclaration' &&
node.id && node.id.name
);

if (namedAssignment || namedDeclaration || namedClass) {
return true;
}
return false;
}

// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -112,6 +148,13 @@ module.exports = function(context) {
markDisplayNameAsDeclared(node);
},

ClassDeclaration: function(node) {
if (!acceptTranspilerName || !hasTranspilerName(node)) {
return;
}
markDisplayNameAsDeclared(node);
},

ObjectExpression: function(node) {
// Search for the displayName declaration
node.properties.forEach(function(property) {
Expand All @@ -120,6 +163,10 @@ module.exports = function(context) {
}
markDisplayNameAsDeclared(node);
});
// Has transpiler name
if (acceptTranspilerName && hasTranspilerName(node)) {
markDisplayNameAsDeclared(node);
}

if (componentUtil.isComponentDefinition(node)) {
componentList.set(context, node, {
Expand Down
90 changes: 90 additions & 0 deletions tests/lib/rules/display-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,60 @@ eslintTester.addRuleTest('lib/rules/display-name', {
classes: true,
jsx: true
}
}, {
code: [
'var Hello = React.createClass({',
' render: function() {',
' return <div>Hello {this.props.name}</div>;',
' }',
'});'
].join('\n'),
args: [1, {
acceptTranspilerName: true
}],
ecmaFeatures: {
classes: true,
jsx: true
}
}, {
code: [
'class Hello extends React.Component {',
' render() {',
' return <div>Hello {this.props.name}</div>;',
' }',
'}'
].join('\n'),
parser: 'babel-eslint',
args: [1, {
acceptTranspilerName: true
}]
}, {
code: [
'export default class Hello {',
' render() {',
' return <div>Hello {this.props.name}</div>;',
' }',
'}'
].join('\n'),
parser: 'babel-eslint',
args: [1, {
acceptTranspilerName: true
}]
}, {
code: [
'var Hello;',
'Hello = React.createClass({',
' render: function() {',
' return <div>Hello {this.props.name}</div>;',
' }',
'});'
].join('\n'),
args: [1, {
acceptTranspilerName: true
}],
ecmaFeatures: {
jsx: true
}
}],

invalid: [{
Expand Down Expand Up @@ -142,5 +196,41 @@ eslintTester.addRuleTest('lib/rules/display-name', {
errors: [{
message: 'Hello component definition is missing display name'
}]
}, {
code: [
'function HelloComponent() {',
' return React.createClass({',
' render: function() {',
' return <div>Hello {this.props.name}</div>;',
' }',
' });',
'}',
'module.exports = HelloComponent();'
].join('\n'),
args: [1, {
acceptTranspilerName: true
}],
ecmaFeatures: {
classes: true,
jsx: true
},
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: [
'export default class {',
' render() {',
' return <div>Hello {this.props.name}</div>;',
' }',
'}'
].join('\n'),
parser: 'babel-eslint',
args: [1, {
acceptTranspilerName: true
}],
errors: [{
message: 'Component definition is missing display name'
}]
}
]});

0 comments on commit 9b54b90

Please sign in to comment.