Skip to content

Commit

Permalink
Add @jsx pragma support (fixes #23)
Browse files Browse the repository at this point in the history
  • Loading branch information
yannickcr committed Mar 22, 2015
1 parent 00d5b85 commit 1062025
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 10 deletions.
20 changes: 18 additions & 2 deletions docs/rules/jsx-uses-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,40 @@
JSX expands to a call to `React.createElement`, a file which includes `React`
but only uses JSX should consider the `React` variable as used.

If you are using the @jsx pragma this rule will mark the designated variable and not the `React` one.

This rule has no effect if the `no-unused-vars` rule is not enabled.

## Rule Details

The following patterns are considered warnings:

```js
var React = require('react'); // and other equivalent imports
var React = require('react');

// nothing to do with React
```

```js
/** @jsx Foo */
var React = require('react');

var Hello = <div>Hello {this.props.name}</div>;
```

The following patterns are not considered warnings:

```js
var React = require('react');

var elem = <div>Some Stuff</div>;
var Hello = <div>Hello {this.props.name}</div>;
```

```js
/** @jsx Foo */
var Foo = require('foo');

var Hello = <div>Hello {this.props.name}</div>;
```

## When Not To Use It
Expand Down
19 changes: 18 additions & 1 deletion docs/rules/react-in-jsx-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
When using JSX, `<a />` expands to `React.createElement("a")`. Therefore the
`React` variable must be in scope.

If you are using the @jsx pragma this rule will check the designated variable and not the `React` one.

## Rule Details

The following patterns are considered warnings:
Expand All @@ -11,10 +13,25 @@ The following patterns are considered warnings:
var Hello = <div>Hello {this.props.name}</div>;
```

```js
/** @jsx Foo.bar */
var React = require('react');

var Hello = <div>Hello {this.props.name}</div>;
```

The following patterns are not considered warnings:

```js
var React = require('react'); // or equivalent import
var React = require('react');

var Hello = <div>Hello {this.props.name}</div>;
```

```js
/** @jsx Foo.bar */
var Foo = require('foo');

var Hello = <div>Hello {this.props.name}</div>;
```

Expand Down
15 changes: 14 additions & 1 deletion lib/rules/jsx-uses-react.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,30 @@
// Rule Definition
// ------------------------------------------------------------------------------

var JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/;

module.exports = function(context) {

var id = 'React';

// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------

return {

JSXOpeningElement: function() {
context.markVariableAsUsed('React');
context.markVariableAsUsed(id);
},

BlockComment: function(node) {
var matches = JSX_ANNOTATION_REGEX.exec(node.value);
if (!matches) {
return;
}
id = matches[1].split('.')[0];
}

};

};
20 changes: 17 additions & 3 deletions lib/rules/react-in-jsx-scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
// Rule Definition
// -----------------------------------------------------------------------------

var JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/;

module.exports = function(context) {

var NOT_DEFINED_MESSAGE = '\'React\' must be in scope when using JSX';
var id = 'React';
var NOT_DEFINED_MESSAGE = '\'{{name}}\' must be in scope when using JSX';

function findVariable(variables, name) {
var i, len;
Expand Down Expand Up @@ -41,9 +44,20 @@ module.exports = function(context) {

JSXOpeningElement: function(node) {
var variables = variablesInScope();
if (!findVariable(variables, 'React')) {
context.report(node, NOT_DEFINED_MESSAGE);
if (findVariable(variables, id)) {
return;
}
context.report(node, NOT_DEFINED_MESSAGE, {
name: id
});
},

BlockComment: function(node) {
var matches = JSX_ANNOTATION_REGEX.exec(node.value);
if (!matches) {
return;
}
id = matches[1].split('.')[0];
}

};
Expand Down
5 changes: 4 additions & 1 deletion tests/lib/rules/jsx-uses-react.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ eslint.defineRule('jsx-uses-react', require('../../../lib/rules/jsx-uses-react')
eslintTester.addRuleTest('node_modules/eslint/lib/rules/no-unused-vars', {
valid: [
{code: '/*eslint jsx-uses-react:1*/ var React; <div />;', ecmaFeatures: {jsx: true}},
{code: '/*eslint jsx-uses-react:1*/ var React; (function () { <div /> })();', ecmaFeatures: {jsx: true}}
{code: '/*eslint jsx-uses-react:1*/ var React; (function () { <div /> })();', ecmaFeatures: {jsx: true}},
{code: '/*eslint jsx-uses-react:1*/ /** @jsx Foo */ var Foo; <div />;', ecmaFeatures: {jsx: true}}
],
invalid: [
{code: '/*eslint jsx-uses-react:1*/ var React;',
errors: [{message: 'React is defined but never used'}], ecmaFeatures: {jsx: true}},
{code: '/*eslint jsx-uses-react:1*/ /** @jsx Foo */ var React; <div />;',
errors: [{message: 'React is defined but never used'}], ecmaFeatures: {jsx: true}}
]
});
10 changes: 8 additions & 2 deletions tests/lib/rules/react-in-jsx-scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,20 @@ eslintTester.addRuleTest('lib/rules/react-in-jsx-scope', {
{code: 'var React; <x-gif />;', args: [1, {vars: 'all'}], ecmaFeatures: {jsx: true}},
{code: 'var React, App, a=1; <App attr={a} />;', ecmaFeatures: {jsx: true}},
{code: 'var React, App, a=1; function elem() { return <App attr={a} />; }', ecmaFeatures: {jsx: true}},
{code: 'var React, App; <App />;', args: [1, {vars: 'all'}], ecmaFeatures: {globalReturn: true, jsx: true}}
{code: 'var React, App; <App />;', args: [1, {vars: 'all'}], ecmaFeatures: {globalReturn: true, jsx: true}},
{code: '/** @jsx Foo */ var Foo, App; <App />;', args: [1, {vars: 'all'}], ecmaFeatures: {jsx: true}},
{code: '/** @jsx Foo.Bar */ var Foo, App; <App />;', args: [1, {vars: 'all'}], ecmaFeatures: {jsx: true}}
],
invalid: [
{code: 'var App, a = <App />;',
errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}},
{code: 'var a = <App />;',
errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}},
{code: 'var a = <img />;',
errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}}
errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}},
{code: '/** @jsx React.DOM */ var a = <img />;',
errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}},
{code: '/** @jsx Foo.bar */ var React, a = <img />;',
errors: [{message: '\'Foo\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}}
]
});

0 comments on commit 1062025

Please sign in to comment.