Repository: oliviertassinari/babel-plugin-transform-react-remove-prop-types (js)

Issue:
"unsafe-wrap" mode crashes when a type is referenced from a variable
[Here](https://github.com/billyjanitsch/babel-rm-proptypes-crash)'s a minimal repro.

The code below causes the plugin to crash when `mode` is set to `"unsafe-wrap"`:

```js
import * as React from 'react'
import PropTypes from 'prop-types'

const sharedPropType = PropTypes.number

export default class Foo extends React.Component {
  static propTypes = {
    bar: sharedPropType,
  }
}
```

Here's the stack trace:

```
TypeError: @babel/template placeholder "NODE": Property expression of ExpressionStatement expected node to be of a type ["Expression"] but instead got "VariableDeclarator"
    at Object.validate (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/types/lib/definitions/utils.js:128:13)
    at validate (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/types/lib/validators/validate.js:17:9)
    at builder (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/types/lib/builders/builder.js:46:27)
    at Object.ExpressionStatement (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/types/lib/builders/generated/index.js:293:31)
    at applyReplacement (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/template/lib/populate.js:92:29)
    at metadata.placeholders.slice.reverse.forEach.placeholder (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/template/lib/populate.js:42:7)
    at Array.forEach (<anonymous>)
    at populatePlaceholders (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/template/lib/populate.js:40:43)
    at arg (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/template/lib/string.js:22:51)
    at arg (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/template/lib/builder.js:77:14)
    =============
    at PluginPass.Program (/Users/billy/work/babel-rm-proptypes-crash/node_modules/babel-plugin-transform-react-remove-prop-types/lib/index.js:160:31)
    at newFn (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/visitors.js:193:21)
    at NodePath._call (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/path/context.js:53:20)
    at NodePath.call (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/path/context.js:40:17)
    at NodePath.visit (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/path/context.js:88:12)
    at TraversalContext.visitQueue (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/context.js:118:16)
    at TraversalContext.visitSingle (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/context.js:90:19)
    at TraversalContext.visit (/Users/billy/work/babel-rm-proptypes-crash/node_modules/@babel/traverse/lib/context.js:146:19)
```

Pull request description:
Fix removing declarator type for unsafe-wrap mode. Fixes #175
This is a fix for https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types/issues/175. @lencioni and I figured out that when removing newly unused identifiers in `unsafe-wrap` mode, we should just use the wrapTemplate instead of the unwrapTemplate.

For example, `const sharedPropType = PropTypes.number;` would be transformed into:

unwrapTemplate:
```
if (process.env.NODE_ENV !== "production") {
  const sharedPropType = PropTypes.number; // NOTE the scope issue here! 
}
```

wrapTemplate:
```
const sharedPropType = process.env.NODE_ENV !== "production" ? PropTypes.number : {};
```

Closes #175

Patch:
diff --git a/src/remove.js b/src/remove.js
index c3be561..8a44eb0 100644
--- a/src/remove.js
+++ b/src/remove.js
@@ -105,23 +105,15 @@ export default function remove(path, globalOptions, options) {
         break
 
       case 'declarator':
-        if (mode === 'unsafe-wrap') {
-          path.replaceWith(
-            unsafeWrapTemplate({
-              NODE: path.node,
-            })
+        path.replaceWith(
+          wrapTemplate(
+            {
+              LEFT: path.node.id,
+              RIGHT: path.node.init,
+            },
+            { as: 'variableDeclarator' }
           )
-        } else {
-          path.replaceWith(
-            wrapTemplate(
-              {
-                LEFT: path.node.id,
-                RIGHT: path.node.init,
-              },
-              { as: 'variableDeclarator' }
-            )
-          )
-        }
+        )
         path.node[visitedKey] = true
         break
 


Test patch:
diff --git a/test/fixtures.test.js b/test/fixtures.test.js
index 39e5602..e3f4485 100644
--- a/test/fixtures.test.js
+++ b/test/fixtures.test.js
@@ -13,7 +13,7 @@ import babelPluginProposalObjectRestSpread from '@babel/plugin-proposal-object-r
 import babelPluginTransformReactRemovePropTypes from '../src/index'
 import { trim } from './utils'
 
-const modes = ['options', 'remove-es5', 'wrap-es5', 'remove-es6', 'wrap-es6']
+const modes = ['options', 'remove-es5', 'wrap-es5', 'remove-es6', 'wrap-es6', 'unsafe-wrap-es6']
 
 describe('fixtures', () => {
   const fixturesDir = path.join(__dirname, 'fixtures')
@@ -123,6 +123,25 @@ describe('fixtures', () => {
               }
               break
 
+            case 'unsafe-wrap-es6':
+              babelConfig = {
+                babelrc: false,
+                plugins: [
+                  babelPluginExternalHelpers,
+                  babelPluginSyntaxJsx,
+                  babelPluginTransformClassProperties,
+                  babelPluginProposalObjectRestSpread,
+                  [
+                    babelPluginTransformReactRemovePropTypes,
+                    {
+                      ...options,
+                      mode: 'unsafe-wrap',
+                    },
+                  ],
+                ],
+              }
+              break
+
             default:
               babelConfig = {
                 plugins: [
diff --git a/test/fixtures/bugfix-175/actual.js b/test/fixtures/bugfix-175/actual.js
new file mode 100644
index 0000000..e1b7f24
--- /dev/null
+++ b/test/fixtures/bugfix-175/actual.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const sharedPropType = PropTypes.number;
+
+export default class Foo extends React.Component {
+  static propTypes = {
+    bar: sharedPropType,
+  }
+}
diff --git a/test/fixtures/bugfix-175/expected-unsafe-wrap-es6.js b/test/fixtures/bugfix-175/expected-unsafe-wrap-es6.js
new file mode 100644
index 0000000..3bd858d
--- /dev/null
+++ b/test/fixtures/bugfix-175/expected-unsafe-wrap-es6.js
@@ -0,0 +1,7 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+const sharedPropType = process.env.NODE_ENV !== "production" ? PropTypes.number : {};
+export default class Foo extends React.Component {}
+process.env.NODE_ENV !== "production" ? Foo.propTypes = {
+  bar: sharedPropType
+} : void 0;