Repository: brigand/babel-plugin-flow-react-proptypes (js)

Issue:
[bug] ImportDeclarations with importKind: "type" do not account for local specifiers
**input**

```js
import type { Scroll as GridScroll } from '../Grid';
import type { Scroll } from './types';
```

**output**

```js
import { bpfrpt_proptype_Scroll } from '../Grid';
import { bpfrpt_proptype_Scroll } from './types';
```

Which of course is a clash of identifiers and results in
> [!] (commonjs plugin) SyntaxError: Identifier 'bpfrpt_proptype_Scroll' has already been declared (238:9) in /Users/mateuszburzynski/Desktop/react-virtualized/source/List/List.js

**NOTE**
I'm reporting it but also preparing a fix right now.

Pull request:
Fixed case with imported types with different local name
This fixes #176. Got suddenly sleepy a lot and couldn't get my mind into the game so variable naming and code structure probably could be a little bit better, but unfortunately I have to get to other things right now. 

I suspect (didnt check) that this issue also affects other cases:
- cjs style - I havent touched that branch of the code
- `export type { Foo as Bar } from './foo'`

I can work on those, but certainly not before the weekend and also not sure how much time I will have then, so feel free to pick up this if you want from where I left - if you have any comments regarding changes in this PR I will find some time to adjust it based on your suggestions.

Patch:
diff --git a/src/index.js b/src/index.js
index 6250c6c..ecd7857 100644
--- a/src/index.js
+++ b/src/index.js
@@ -72,27 +72,34 @@ module.exports = function flowReactPropTypes(babel) {
 
   const impTemplates = {
     named: babel.template(`import { LOCAL } from 'change me'`, { sourceType: 'module' }),
+    renamed: babel.template(`import { SOURCE as LOCAL } from 'change me'`, { sourceType: 'module' }),
     default: babel.template(`import NAME from 'change me'`,  { sourceType: 'module' }),
     requireDefault: babel.template(`require(PATH)`),
     requireNamed: babel.template(`require(PATH).NAME`),
   };
-  function getFromModule(path, { type = 'default', name, location }) {
+  function getFromModule(path, { type = 'default', name, local = name, location }) {
     const tid = t.identifier;
     const tstr = t.stringLiteral;
     const key = `name:${location}:${name}`;
 
     if (shouldUseImport()) {
       if (!addedImports[key]) {
-        const local = name.replace(/[^a-zA-Z0-9]+/g, '_');
-        addedImports[key] = local;
+        const localName = local.replace(/[^a-zA-Z0-9]+/g, '_');
+        const sourceName = name.replace(/[^a-zA-Z0-9]+/g, '_');
+        addedImports[key] = localName;
 
         let toAdd = null;
 
         if (type === 'default') {
-          toAdd = impTemplates.default({ NAME: tid(local) });
+          toAdd = impTemplates.default({ NAME: tid(localName) });
         }
         else if (type === 'named') {
-          toAdd = impTemplates.named({ LOCAL: tid(local) });
+          if (localName === sourceName) {
+            toAdd = impTemplates.named({ LOCAL: tid(localName) });
+          }
+          else {
+            toAdd = impTemplates.renamed({ LOCAL: tid(localName), SOURCE: tid(sourceName) });
+          }
         }
         if (toAdd) {
           toAdd.source.value = location;
@@ -594,8 +601,9 @@ module.exports = function flowReactPropTypes(babel) {
         node.specifiers.forEach((specifier) => {
           if (node.importKind !== 'type' && specifier.importKind !== 'type') return;
 
-          const typeName = specifier.type === 'ImportDefaultSpecifier'
-            ? specifier.local.name
+          const typeName = specifier.local.name;
+          const originalTypeName = specifier.type === 'ImportDefaultSpecifier'
+            ? typeName
             : specifier.imported.name;
           // Store the name the type so we can use it later. We do
           // mark it as importedTypes because we do handle these
@@ -606,7 +614,7 @@ module.exports = function flowReactPropTypes(babel) {
           // Later, we will check importedTypes to determine if
           // we want to put this as a 'raw' type in our internal
           // representation
-          importedTypes[typeName] = { exportName: getExportNameForType(typeName), accessNode: null };
+          importedTypes[typeName] = { exportName: getExportNameForType(originalTypeName), accessNode: null };
 
           // https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/129
           if (node.source.value === 'react' && typeName === 'ComponentType') {
@@ -618,7 +626,8 @@ module.exports = function flowReactPropTypes(babel) {
 
           const accessNode = getFromModule(path, {
             type: 'named',
-            name: getExportNameForType(typeName),
+            name: getExportNameForType(originalTypeName),
+            local: getExportNameForType(typeName),
             location: node.source.value,
           });
 


Test patch:
diff --git a/src/__tests__/__snapshots__/import-imported-clash.js.snap b/src/__tests__/__snapshots__/import-imported-clash.js.snap
new file mode 100644
index 0000000..4697fc7
--- /dev/null
+++ b/src/__tests__/__snapshots__/import-imported-clash.js.snap
@@ -0,0 +1,43 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`import-imported-clash 1`] = `
+"'use strict';
+
+var _foo = require('./foo');
+
+var _bar = require('./bar');
+
+var _propTypes = require('prop-types');
+
+var _propTypes2 = _interopRequireDefault(_propTypes);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var C = function (_React$Component) {
+  _inherits(C, _React$Component);
+
+  function C() {
+    _classCallCheck(this, C);
+
+    return _possibleConstructorReturn(this, (C.__proto__ || Object.getPrototypeOf(C)).apply(this, arguments));
+  }
+
+  return C;
+}(React.Component);
+
+C.propTypes = {
+  foo: function foo() {
+    return (typeof _foo.bpfrpt_proptype_Foo === 'function' ? _foo.bpfrpt_proptype_Foo.isRequired ? _foo.bpfrpt_proptype_Foo.isRequired : _foo.bpfrpt_proptype_Foo : _propTypes2.default.shape(_foo.bpfrpt_proptype_Foo).isRequired).apply(this, arguments);
+  },
+  bar: function bar() {
+    return (typeof _bar.bpfrpt_proptype_Foo === 'function' ? _bar.bpfrpt_proptype_Foo.isRequired ? _bar.bpfrpt_proptype_Foo.isRequired : _bar.bpfrpt_proptype_Foo : _propTypes2.default.shape(_bar.bpfrpt_proptype_Foo).isRequired).apply(this, arguments);
+  }
+};
+;"
+`;
diff --git a/src/__tests__/import-imported-clash.js b/src/__tests__/import-imported-clash.js
new file mode 100644
index 0000000..1fbc0dd
--- /dev/null
+++ b/src/__tests__/import-imported-clash.js
@@ -0,0 +1,23 @@
+const babel = require('babel-core');
+const content = `
+import type {Foo} from './foo';
+import type {Foo as Bar} from './bar';
+
+type Props = {
+  foo: Foo,
+  bar: Bar,
+};
+
+class C extends React.Component {
+    props: Props
+};
+`;
+
+it('import-imported-clash', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});