babel-plugin-transform-react-remove-prop-types
Advanced tools
Comparing version
@@ -10,4 +10,6 @@ module.exports = { | ||
}, | ||
'plugins': [ | ||
plugins: [ | ||
'react', | ||
'mocha', | ||
'flowtype', | ||
], | ||
@@ -19,3 +21,2 @@ ecmaFeatures: { | ||
rules: { | ||
// Errors | ||
'array-bracket-spacing': ['error', 'never'], | ||
@@ -41,2 +42,3 @@ 'arrow-spacing': 'error', | ||
'new-cap': ['error', {capIsNew: true, newIsCap: true}], | ||
'no-console': 'error', | ||
'no-unused-expressions': 'error', | ||
@@ -59,2 +61,5 @@ 'no-unused-vars': 'error', | ||
'no-unneeded-ternary': 'error', | ||
'no-useless-computed-key': 'error', | ||
'no-unsafe-finally': 'error', | ||
'no-process-exit': 'error', | ||
'no-var': 'error', | ||
@@ -77,13 +82,2 @@ 'object-curly-spacing': ['error', 'never'], | ||
'yoda': 'error', | ||
// Disabled | ||
'no-case-declarations': 'off', | ||
'strict': 'off', | ||
'no-console': 'off', | ||
'no-magic-numbers': 'off', | ||
'camelcase': 'off', | ||
'no-underscore-dangle': 'off', | ||
'handle-callback-err': 'off', | ||
// React errors | ||
'react/display-name': 'error', | ||
@@ -94,2 +88,5 @@ 'react/jsx-boolean-value': ['error', 'always'], | ||
'react/jsx-equals-spacing': 'error', | ||
'react/jsx-first-prop-new-line': ['error', 'multiline'], | ||
'react/jsx-filename-extension': ['error', {extensions: ['.js']}], | ||
'react/jsx-key': 'error', | ||
'react/jsx-handler-names': 'error', | ||
@@ -106,3 +103,5 @@ 'react/jsx-indent-props': ['error', 2], | ||
'react/jsx-uses-vars': 'error', | ||
'react/no-comment-textnodes': 'error', | ||
'react/no-danger': 'error', | ||
'react/no-deprecated': 'error', | ||
'react/no-did-mount-set-state': 'error', | ||
@@ -112,2 +111,3 @@ 'react/no-did-update-set-state': 'error', | ||
'react/no-multi-comp': 'error', | ||
'react/no-render-return-value': 'error', | ||
'react/no-is-mounted': 'error', | ||
@@ -119,2 +119,3 @@ 'react/no-unknown-property': 'error', | ||
'react/require-extension': 'error', | ||
'react/require-render-return': 'error', | ||
'react/self-closing-comp': 'error', | ||
@@ -124,8 +125,33 @@ 'react/sort-comp': 'error', | ||
'react/wrap-multilines': 'error', | ||
'mocha/handle-done-callback': 'error', | ||
'mocha/no-exclusive-tests': 'error', | ||
'mocha/no-global-tests': 'error', | ||
'mocha/no-pending-tests': 'error', | ||
'mocha/no-skipped-tests': 'error', | ||
'flowtype/require-valid-file-annotation': ['error', 'always'], | ||
// Disabled | ||
'no-case-declarations': 'off', | ||
'strict': 'off', | ||
'no-magic-numbers': 'off', | ||
'camelcase': 'off', | ||
'no-underscore-dangle': 'off', | ||
'handle-callback-err': 'off', | ||
'mocha/no-synchronous-tests': 'off', | ||
'mocha/valid-suite-description': 'off', | ||
'mocha/valid-test-description': 'off', | ||
'mocha/no-mocha-arrows': 'off', | ||
'react/jsx-no-bind': 'off', | ||
'react/jsx-no-target-blank': 'off', | ||
'react/jsx-sort-props': 'off', | ||
'react/no-set-state': 'off', | ||
} | ||
'react/no-string-refs': 'off', | ||
'react/forbid-prop-types': 'off', | ||
'react/prefer-stateless-function': 'off', | ||
'react/require-optimization': 'off', | ||
'flowtype/require-parameter-type': 'off', | ||
'flowtype/require-return-type': 'off', | ||
'flowtype/space-after-type-colon': 'off', | ||
'flowtype/space-before-type-colon': 'off', | ||
'flowtype/type-id-match': 'off', | ||
}, | ||
}; |
101
lib/index.js
@@ -7,6 +7,15 @@ 'use strict'; | ||
exports.default = function () { | ||
exports.default = function (_ref) { | ||
var template = _ref.template; | ||
var types = _ref.types; | ||
var wrapperIfTemplate = template('\n if (process.env.NODE_ENV !== "production") {\n NODE;\n }\n '); | ||
var VISITED_KEY = 'transform-react-remove-prop-types' + Date.now(); | ||
return { | ||
visitor: { | ||
Program: function Program(programPath) { | ||
Program: function Program(programPath, state) { | ||
var mode = state.opts.mode || 'remove'; | ||
// On program start, do an explicit traversal up front for this plugin. | ||
@@ -31,6 +40,12 @@ programPath.traverse({ | ||
if (parent) { | ||
path.remove(); | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'createClass' | ||
}); | ||
} | ||
} | ||
}, | ||
// Here to support stage-1 transform-class-properties. | ||
ClassProperty: function ClassProperty(path) { | ||
@@ -42,6 +57,13 @@ var node = path.node; | ||
if (node.key.name === 'propTypes') { | ||
var superClass = scope.path.get('superClass'); | ||
var pathClassDeclaration = scope.path; | ||
if (isReactClass(superClass, scope)) { | ||
path.remove(); | ||
if (isReactClass(pathClassDeclaration.get('superClass'), scope)) { | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'class static', | ||
types: types, | ||
pathClassDeclaration: pathClassDeclaration | ||
}); | ||
} | ||
@@ -70,6 +92,16 @@ } | ||
if (isReactClass(superClass, scope)) { | ||
path.remove(); | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'class assign' | ||
}); | ||
} | ||
} else if ((0, _isStatelessComponent2.default)(binding.path)) { | ||
path.remove(); | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'stateless' | ||
}); | ||
} | ||
@@ -106,2 +138,55 @@ } | ||
return answer; | ||
} // weak | ||
function remove(path, options) { | ||
var visitedKey = options.visitedKey; | ||
var wrapperIfTemplate = options.wrapperIfTemplate; | ||
var mode = options.mode; | ||
var type = options.type; | ||
var types = options.types; | ||
var pathClassDeclaration = options.pathClassDeclaration; | ||
if (mode === 'remove') { | ||
path.remove(); | ||
} else if (mode === 'wrap') { | ||
// Prevent infinity loop. | ||
if (path.node[visitedKey]) { | ||
return; | ||
} | ||
path.node[visitedKey] = true; | ||
switch (type) { | ||
// This is legacy, we do not optimize it. | ||
case 'createClass': | ||
break; | ||
// Inspired from babel-plugin-transform-class-properties. | ||
case 'class static': | ||
var ref = void 0; | ||
if (!pathClassDeclaration.isClassExpression() && pathClassDeclaration.node.id) { | ||
ref = pathClassDeclaration.node.id; | ||
} else { | ||
// Class without name not supported | ||
return; | ||
} | ||
var node = types.expressionStatement(types.assignmentExpression('=', types.memberExpression(ref, path.node.key), path.node.value)); | ||
pathClassDeclaration.insertAfter(node); | ||
path.remove(); | ||
break; | ||
case 'class assign': | ||
case 'stateless': | ||
path.replaceWith(wrapperIfTemplate({ | ||
NODE: path.node | ||
})); | ||
break; | ||
} | ||
} else { | ||
throw new Error('transform-react-remove-prop-type: unsupported mode ' + mode + '.'); | ||
} | ||
} |
@@ -7,2 +7,4 @@ 'use strict'; | ||
exports.default = isStatelessComponent; | ||
// weak | ||
function isJSXElementOrReactCreateElement(node) { | ||
@@ -9,0 +11,0 @@ var type = node.type; |
{ | ||
"name": "babel-plugin-transform-react-remove-prop-types", | ||
"version": "0.2.7", | ||
"version": "0.2.8", | ||
"description": "Remove unnecessary React propTypes from the production build", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"lint": "eslint .", | ||
"test:unit": "istanbul test _mocha", | ||
"test": "npm run lint && npm run test:unit", | ||
"lint": "eslint src test", | ||
"test:unit": "babel-node test/index.js", | ||
"test:unit:watch": "babel-node test/watch.js", | ||
"test": "npm run lint && npm run test:unit && npm run flow", | ||
"prebuild": "rm -rf lib/", | ||
"build": "babel src --out-dir lib", | ||
"flow": "flow", | ||
"prepublish": "npm run build" | ||
@@ -32,13 +34,20 @@ }, | ||
"devDependencies": { | ||
"babel-core": "^6.3.26", | ||
"babel-cli": "^6.7.7", | ||
"babel-eslint": "^6.0.3", | ||
"babel-cli": "^6.11.4", | ||
"babel-core": "^6.11.4", | ||
"babel-eslint": "^6.1.2", | ||
"babel-preset-es2015": "^6.6.0", | ||
"babel-preset-react": "^6.3.13", | ||
"babel-preset-react": "^6.11.1", | ||
"babel-preset-stage-0": "^6.3.13", | ||
"eslint": "^2.8.0", | ||
"eslint": "^3.1.1", | ||
"eslint-plugin-flowtype": "^2.4.0", | ||
"eslint-plugin-mocha": "^4.2.0", | ||
"eslint-plugin-react": "^5.0.1", | ||
"flow": "^0.2.3", | ||
"flow-bin": "^0.29.0", | ||
"glob": "^7.0.5", | ||
"istanbul": "^0.4.2", | ||
"mocha": "^2.2.5" | ||
"minimist": "^1.2.0", | ||
"mocha": "^2.2.5", | ||
"nodemon": "^1.10.0" | ||
} | ||
} |
@@ -1,2 +0,5 @@ | ||
# Babel Plugin for removing React propTypes | ||
# babel-plugin-transform-react-remove-prop-types | ||
> Remove unnecessary React propTypes from the production build. | ||
[](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types) | ||
@@ -9,3 +12,12 @@ [](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types) | ||
## Installation | ||
```sh | ||
npm install --save-dev babel-plugin-transform-react-remove-prop-types | ||
``` | ||
## The problem solved | ||
Remove unnecessary React `propTypes` from the production build. | ||
You can **save bandwidth** by removing them. | ||
@@ -32,8 +44,2 @@ ## Example | ||
## Installation | ||
```sh | ||
npm install --save-dev babel-plugin-transform-react-remove-prop-types | ||
``` | ||
## Usage | ||
@@ -45,2 +51,3 @@ | ||
without options: | ||
```json | ||
@@ -56,2 +63,13 @@ { | ||
with options: | ||
```json | ||
{ | ||
"env": { | ||
"production": { | ||
"plugins": ["transform-react-remove-prop-types", {"mode": "wrap"}] | ||
} | ||
} | ||
} | ||
``` | ||
### Via CLI | ||
@@ -65,10 +83,43 @@ | ||
without options: | ||
```js | ||
require("babel-core").transform("code", { | ||
plugins: ["transform-react-remove-prop-types"] | ||
require('babel-core').transform('code', { | ||
plugins: [ | ||
'transform-react-remove-prop-types', | ||
], | ||
}); | ||
``` | ||
#License | ||
with options: | ||
```js | ||
require('babel-core').transform('code', { | ||
plugins: [ | ||
[ | ||
'transform-react-remove-prop-types', | ||
{mode: 'wrap'}, | ||
], | ||
], | ||
}); | ||
``` | ||
### Options | ||
- `mode` two modes are available | ||
- `remove` (**default**): | ||
the `propTypes` definitions are removed from the source code. | ||
- `wrap`: | ||
the `propTypes` definitions are wrapped with the following code: | ||
```js | ||
if (process.env.NODE_ENV !== "production") { | ||
Foo.propTypes = { | ||
bar: React.PropTypes.string, | ||
}; | ||
} | ||
``` | ||
This mode is quite useful for lib authors. | ||
However, we do not support the old `React.createClass` syntax nor the | ||
name less classes yet. | ||
## License | ||
MIT |
111
src/index.js
@@ -0,1 +1,3 @@ | ||
// @flow weak | ||
import isStatelessComponent from './isStatelessComponent'; | ||
@@ -7,3 +9,3 @@ | ||
if (superClass.matchesPattern('React.Component') || | ||
superClass.node.name === 'Component') { | ||
(superClass.node.name === 'Component')) { | ||
answer = true; | ||
@@ -24,6 +26,74 @@ } else if (superClass.node.name) { // Check for inheritance | ||
export default function() { | ||
function remove(path, options) { | ||
const { | ||
visitedKey, | ||
wrapperIfTemplate, | ||
mode, | ||
type, | ||
types, | ||
pathClassDeclaration, | ||
} = options; | ||
if (mode === 'remove') { | ||
path.remove(); | ||
} else if (mode === 'wrap') { | ||
// Prevent infinity loop. | ||
if (path.node[visitedKey]) { | ||
return; | ||
} | ||
path.node[visitedKey] = true; | ||
switch (type) { | ||
// This is legacy, we do not optimize it. | ||
case 'createClass': | ||
break; | ||
// Inspired from babel-plugin-transform-class-properties. | ||
case 'class static': | ||
let ref; | ||
if (!pathClassDeclaration.isClassExpression() && pathClassDeclaration.node.id) { | ||
ref = pathClassDeclaration.node.id; | ||
} else { | ||
// Class without name not supported | ||
return; | ||
} | ||
const node = types.expressionStatement( | ||
types.assignmentExpression('=', types.memberExpression(ref, path.node.key), path.node.value) | ||
); | ||
pathClassDeclaration.insertAfter(node); | ||
path.remove(); | ||
break; | ||
case 'class assign': | ||
case 'stateless': | ||
path.replaceWith(wrapperIfTemplate( | ||
{ | ||
NODE: path.node, | ||
} | ||
)); | ||
break; | ||
} | ||
} else { | ||
throw new Error(`transform-react-remove-prop-type: unsupported mode ${mode}.`); | ||
} | ||
} | ||
export default function({template, types}) { | ||
const wrapperIfTemplate = template(` | ||
if (process.env.NODE_ENV !== "production") { | ||
NODE; | ||
} | ||
`); | ||
const VISITED_KEY = `transform-react-remove-prop-types${Date.now()}`; | ||
return { | ||
visitor: { | ||
Program(programPath) { | ||
Program(programPath, state) { | ||
const mode = state.opts.mode || 'remove'; | ||
// On program start, do an explicit traversal up front for this plugin. | ||
@@ -48,6 +118,12 @@ programPath.traverse({ | ||
if (parent) { | ||
path.remove(); | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'createClass', | ||
}); | ||
} | ||
}, | ||
}, | ||
// Here to support stage-1 transform-class-properties. | ||
ClassProperty(path) { | ||
@@ -60,6 +136,13 @@ const { | ||
if (node.key.name === 'propTypes') { | ||
const superClass = scope.path.get('superClass'); | ||
const pathClassDeclaration = scope.path; | ||
if (isReactClass(superClass, scope)) { | ||
path.remove(); | ||
if (isReactClass(pathClassDeclaration.get('superClass'), scope)) { | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'class static', | ||
types: types, | ||
pathClassDeclaration: pathClassDeclaration, | ||
}); | ||
} | ||
@@ -89,6 +172,16 @@ } | ||
if (isReactClass(superClass, scope)) { | ||
path.remove(); | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'class assign', | ||
}); | ||
} | ||
} else if (isStatelessComponent(binding.path)) { | ||
path.remove(); | ||
remove(path, { | ||
visitedKey: VISITED_KEY, | ||
wrapperIfTemplate: wrapperIfTemplate, | ||
mode: mode, | ||
type: 'stateless', | ||
}); | ||
} | ||
@@ -95,0 +188,0 @@ }, |
@@ -0,1 +1,3 @@ | ||
// @flow weak | ||
function isJSXElementOrReactCreateElement(node) { | ||
@@ -2,0 +4,0 @@ const { |
Sorry, the diff of this file is not supported yet
25981
48.44%15
15.38%610
48.78%121
72.86%17
70%