Repository: yannickcr/eslint-plugin-react
Pull request #1013 (2017-01-05 04:33:31)

react/jsx-no-undef allowGlobals option
This should allow for the use case outlined in #922.

With the `disallowGlobals` option enabled, the global scope will be ignored
when checking for defined components. Without this option enabled,
Pascal case global variables will be allowed when checking for defined
components.


Patch:
diff --git a/docs/rules/jsx-no-undef.md b/docs/rules/jsx-no-undef.md
index 00cae519..63e927e6 100644
--- a/docs/rules/jsx-no-undef.md
+++ b/docs/rules/jsx-no-undef.md
@@ -10,6 +10,17 @@ The following patterns are considered warnings:
 <Hello name="John" />;
 ```
 
+```jsx
+// will ignore Text in the global scope and warn
+var Hello = React.createClass({
+  render: function() {
+    return <Text>Hello</Text>;
+  }
+});
+module.exports = Hello;
+```
+
+
 The following patterns are not considered warnings:
 
 ```jsx
@@ -18,6 +29,30 @@ var Hello = require('./Hello');
 <Hello name="John" />;
 ```
 
+## Rule Options
+
+```js
+...
+"react/jsx-no-undef": [<enabled>, { "allowGlobals": <boolean> }]
+...
+```
+
+### `allowGlobals`
+
+When `true` the rule will consider the global scope when checking for defined Components.
+
+The following patterns are considered okay and do not cause warnings:
+
+```jsx
+var Text = require('./Text');
+var Hello = React.createClass({
+  render: function() {
+    return <Text>Hello</Text>;
+  }
+});
+module.exports = Hello;
+```
+
 ## When Not To Use It
 
 If you are not using JSX then you can disable this rule.
diff --git a/lib/rules/jsx-no-undef.js b/lib/rules/jsx-no-undef.js
index d4960814..4192c51b 100644
--- a/lib/rules/jsx-no-undef.js
+++ b/lib/rules/jsx-no-undef.js
@@ -26,11 +26,22 @@ module.exports = {
       category: 'Possible Errors',
       recommended: true
     },
-    schema: []
+    schema: [{
+      type: 'object',
+      properties: {
+        allowGlobals: {
+          type: 'boolean'
+        }
+      },
+      additionalProperties: false
+    }]
   },
 
   create: function(context) {
 
+    var config = context.options[0] || {};
+    var allowGlobals = config.allowGlobals || false;
+
     /**
      * Compare an identifier with the variables declared in the scope
      * @param {ASTNode} node - Identifier or JSXIdentifier node
@@ -38,7 +49,10 @@ module.exports = {
      */
     function checkIdentifierInJSX(node) {
       var scope = context.getScope();
+      var sourceCode = context.getSourceCode();
+      var sourceType = sourceCode.ast.sourceType;
       var variables = scope.variables;
+      var scopeType = 'global';
       var i;
       var len;
 
@@ -47,7 +61,11 @@ module.exports = {
         return;
       }
 
-      while (scope.type !== 'global') {
+      if (!allowGlobals && sourceType === 'module') {
+        scopeType = 'module';
+      }
+
+      while (scope.type !== scopeType) {
         scope = scope.upper;
         variables = scope.variables.concat(variables);
       }


Test patch:
diff --git a/tests/lib/rules/jsx-no-undef.js b/tests/lib/rules/jsx-no-undef.js
index 1fa3732a..e5d5c848 100644
--- a/tests/lib/rules/jsx-no-undef.js
+++ b/tests/lib/rules/jsx-no-undef.js
@@ -56,6 +56,26 @@ ruleTester.run('jsx-no-undef', rule, {
       '}'
     ].join('\n'),
     parserOptions: parserOptions
+  }, {
+    code: 'var React; React.render(<Text />);',
+    parserOptions: parserOptions,
+    globals: {
+      Text: true
+    }
+  }, {
+    code: [
+      'import Text from "cool-module";',
+      'const TextWrapper = function (props) {',
+      '  return (',
+      '    <Text />',
+      '  );',
+      '};'
+    ].join('\n'),
+    options: [{
+      allowGlobals: false
+    }],
+    parser: 'babel-eslint',
+    parserOptions: parserOptions
   }],
   invalid: [{
     code: '/*eslint no-undef:1*/ var React; React.render(<App />);',
@@ -87,5 +107,31 @@ ruleTester.run('jsx-no-undef', rule, {
       message: '\'appp\' is not defined.'
     }],
     parserOptions: parserOptions
+  }, {
+    code: [
+      'const TextWrapper = function (props) {',
+      '  return (',
+      '    <Text />',
+      '  );',
+      '};',
+      'export default TextWrapper;'
+    ].join('\n'),
+    errors: [{
+      message: '\'Text\' is not defined.'
+    }],
+    options: [{
+      allowGlobals: false
+    }],
+    parser: 'babel-eslint',
+    parserOptions: parserOptions,
+    globals: {
+      Text: true
+    }
+  }, {
+    code: '/*eslint no-undef:1*/ var React; React.render(<Foo />);',
+    errors: [{
+      message: '\'Foo\' is not defined.'
+    }],
+    parserOptions: parserOptions
   }]
 });