From 106202594c4984f48aab966ce788ab26bdea041d Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Sun, 22 Mar 2015 20:22:32 +0000 Subject: [PATCH] Add @jsx pragma support (fixes #23) --- docs/rules/jsx-uses-react.md | 20 ++++++++++++++++++-- docs/rules/react-in-jsx-scope.md | 19 ++++++++++++++++++- lib/rules/jsx-uses-react.js | 15 ++++++++++++++- lib/rules/react-in-jsx-scope.js | 20 +++++++++++++++++--- tests/lib/rules/jsx-uses-react.js | 5 ++++- tests/lib/rules/react-in-jsx-scope.js | 10 ++++++++-- 6 files changed, 79 insertions(+), 10 deletions(-) diff --git a/docs/rules/jsx-uses-react.md b/docs/rules/jsx-uses-react.md index d373700100..e6f2f518c0 100644 --- a/docs/rules/jsx-uses-react.md +++ b/docs/rules/jsx-uses-react.md @@ -3,6 +3,8 @@ 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 @@ -10,17 +12,31 @@ This rule has no effect if the `no-unused-vars` rule is not enabled. 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 =
Hello {this.props.name}
; +``` + The following patterns are not considered warnings: ```js var React = require('react'); -var elem =
Some Stuff
; +var Hello =
Hello {this.props.name}
; +``` + +```js +/** @jsx Foo */ +var Foo = require('foo'); + +var Hello =
Hello {this.props.name}
; ``` ## When Not To Use It diff --git a/docs/rules/react-in-jsx-scope.md b/docs/rules/react-in-jsx-scope.md index 7575164f8c..de3ebff8eb 100644 --- a/docs/rules/react-in-jsx-scope.md +++ b/docs/rules/react-in-jsx-scope.md @@ -3,6 +3,8 @@ When using JSX, `` 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: @@ -11,10 +13,25 @@ The following patterns are considered warnings: var Hello =
Hello {this.props.name}
; ``` +```js +/** @jsx Foo.bar */ +var React = require('react'); + +var Hello =
Hello {this.props.name}
; +``` + The following patterns are not considered warnings: ```js -var React = require('react'); // or equivalent import +var React = require('react'); + +var Hello =
Hello {this.props.name}
; +``` + +```js +/** @jsx Foo.bar */ +var Foo = require('foo'); + var Hello =
Hello {this.props.name}
; ``` diff --git a/lib/rules/jsx-uses-react.js b/lib/rules/jsx-uses-react.js index 44ba0d147f..3bf700203a 100644 --- a/lib/rules/jsx-uses-react.js +++ b/lib/rules/jsx-uses-react.js @@ -8,8 +8,12 @@ // Rule Definition // ------------------------------------------------------------------------------ +var JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/; + module.exports = function(context) { + var id = 'React'; + // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- @@ -17,8 +21,17 @@ module.exports = function(context) { 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]; } + }; }; diff --git a/lib/rules/react-in-jsx-scope.js b/lib/rules/react-in-jsx-scope.js index 8370cdd754..081ffc7c7e 100644 --- a/lib/rules/react-in-jsx-scope.js +++ b/lib/rules/react-in-jsx-scope.js @@ -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; @@ -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]; } }; diff --git a/tests/lib/rules/jsx-uses-react.js b/tests/lib/rules/jsx-uses-react.js index 12e38c6b01..acdb17453a 100644 --- a/tests/lib/rules/jsx-uses-react.js +++ b/tests/lib/rules/jsx-uses-react.js @@ -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;
;', ecmaFeatures: {jsx: true}}, - {code: '/*eslint jsx-uses-react:1*/ var React; (function () {
})();', ecmaFeatures: {jsx: true}} + {code: '/*eslint jsx-uses-react:1*/ var React; (function () {
})();', ecmaFeatures: {jsx: true}}, + {code: '/*eslint jsx-uses-react:1*/ /** @jsx Foo */ var Foo;
;', 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;
;', errors: [{message: 'React is defined but never used'}], ecmaFeatures: {jsx: true}} ] }); diff --git a/tests/lib/rules/react-in-jsx-scope.js b/tests/lib/rules/react-in-jsx-scope.js index 81bc86e122..994bb130c1 100644 --- a/tests/lib/rules/react-in-jsx-scope.js +++ b/tests/lib/rules/react-in-jsx-scope.js @@ -24,7 +24,9 @@ eslintTester.addRuleTest('lib/rules/react-in-jsx-scope', { {code: 'var React; ;', args: [1, {vars: 'all'}], ecmaFeatures: {jsx: true}}, {code: 'var React, App, a=1; ;', ecmaFeatures: {jsx: true}}, {code: 'var React, App, a=1; function elem() { return ; }', ecmaFeatures: {jsx: true}}, - {code: 'var React, App; ;', args: [1, {vars: 'all'}], ecmaFeatures: {globalReturn: true, jsx: true}} + {code: 'var React, App; ;', args: [1, {vars: 'all'}], ecmaFeatures: {globalReturn: true, jsx: true}}, + {code: '/** @jsx Foo */ var Foo, App; ;', args: [1, {vars: 'all'}], ecmaFeatures: {jsx: true}}, + {code: '/** @jsx Foo.Bar */ var Foo, App; ;', args: [1, {vars: 'all'}], ecmaFeatures: {jsx: true}} ], invalid: [ {code: 'var App, a = ;', @@ -32,6 +34,10 @@ eslintTester.addRuleTest('lib/rules/react-in-jsx-scope', { {code: 'var a = ;', errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}}, {code: 'var a = ;', - 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 = ;', + errors: [{message: '\'React\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}}, + {code: '/** @jsx Foo.bar */ var React, a = ;', + errors: [{message: '\'Foo\' must be in scope when using JSX'}], ecmaFeatures: {jsx: true}} ] });