├── .babelrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── dist └── index.js ├── package.json ├── src └── index.js └── test ├── fixtures ├── should-inject-require-when-other-requires-and-imports-are-present │ ├── actual.js │ └── expected.js ├── should-inject-require │ ├── actual.js │ └── expected.js ├── should-not-duplicate-import │ ├── actual.js │ └── expected.js ├── should-not-duplicate-require │ ├── actual.js │ └── expected.js ├── should-not-inject-when-no-jsx-element │ ├── actual.js │ └── expected.js └── should-respect-options-override │ ├── actual.js │ ├── expected.js │ └── options.json └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | src/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "5" 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transform-react-require [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] 2 | 3 | Automatically require React (or other implementations) when using JSX. 4 | This babel plugin inserts CommonJS style require where it detects 5 | JSX and React isn't already required (or imported using ES2015 syntax). 6 | 7 | If you're using JSX in your React (or similar) components, you often 8 | don't need the `React` module to anything else, and it will seem as 9 | if you have an unused require-statement. When using something like 10 | [`eslint`](http://eslint.org/) you'd have to add a rule saying 11 | unused `React` should be ignored, but that's not always true. 12 | 13 | With this module you'll no longer need to use this anti-pattern: 14 | 15 | ```jsx 16 | let React = require('react'); // free, seemingly unused 17 | 18 | module.exports = function MyComponent () { 19 | return ( 20 |

Hello World!

21 | ); 22 | }; 23 | ``` 24 | 25 | You can instead let it be an implementation detail and write: 26 | 27 | ```jsx 28 | module.exports = function MyComponent () { 29 | return ( 30 |

Hello World!

31 | ); 32 | }; 33 | ``` 34 | 35 | 36 | ## Installation 37 | 38 | ```sh 39 | $ npm install --save babel-plugin-transform-react-require 40 | ``` 41 | 42 | ## Usage 43 | 44 | ### Via `.babelrc` (Recommended) 45 | 46 | **.babelrc** 47 | 48 | ```json 49 | { 50 | "plugins": ["transform-react-require"] 51 | } 52 | ``` 53 | 54 | #### Options 55 | 56 | Not using React with JSX? You can define module name and module identifier 57 | as options. For instance, if you are using `dom('div', {})` instead of 58 | `React.createElement('div', {})`, you might want to require `dom`, instead 59 | of `React`. In that case, you can override defaults by doing: 60 | 61 | ```json 62 | { 63 | "plugins": [ 64 | ["transform-react-require", { 65 | "identifier": "dom", 66 | "moduleName": "my-dom-library" 67 | }] 68 | ] 69 | } 70 | ``` 71 | 72 | This would cause the plugin to automatically require `my-dom-library`, 73 | when JSX syntax is detected, and store it in the identifier `dom`. 74 | 75 | ### Via CLI 76 | 77 | ```sh 78 | $ babel --plugins transform-react-require script.js 79 | ``` 80 | 81 | ### Via Node API 82 | 83 | ```javascript 84 | require('babel-core').transform('code', { 85 | plugins: ['transform-react-require'] 86 | }); 87 | ``` 88 | 89 | #### Options 90 | 91 | Same as through the `.babelrc`, you can override defaults: 92 | 93 | 94 | ```js 95 | require('babel-core').transform('code', { 96 | plugins: ['transform-react-require', { 97 | 'identifier': 'dom', 98 | 'moduleName': 'my-dom-library' 99 | }] 100 | }); 101 | ``` 102 | 103 | 104 | [npm-url]: https://npmjs.org/package/babel-plugin-transform-react-require 105 | [npm-image]: http://img.shields.io/npm/v/babel-plugin-transform-react-require.svg?style=flat 106 | [npm-downloads]: http://img.shields.io/npm/dm/babel-plugin-transform-react-require.svg?style=flat 107 | 108 | [travis-url]: http://travis-ci.org/mikaelbr/babel-plugin-transform-react-require 109 | [travis-image]: http://img.shields.io/travis/mikaelbr/babel-plugin-transform-react-require.svg?style=flat 110 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | exports.default = function (_ref) { 8 | var t = _ref.types; 9 | 10 | return { 11 | visitor: { 12 | JSXElement: function JSXElement() { 13 | this.hasJsx = true; 14 | }, 15 | ImportDeclaration: function ImportDeclaration(_ref2, _ref3) { 16 | var node = _ref2.node; 17 | var _ref3$opts$moduleName = _ref3.opts.moduleName; 18 | var moduleName = _ref3$opts$moduleName === undefined ? 'react' : _ref3$opts$moduleName; 19 | 20 | if (!node.source || !node.source.value) return; 21 | if (node.source.value !== moduleName) return; 22 | this.hasRequired = true; 23 | }, 24 | CallExpression: function CallExpression(_ref4, _ref5) { 25 | var node = _ref4.node; 26 | var _ref5$opts$moduleName = _ref5.opts.moduleName; 27 | var moduleName = _ref5$opts$moduleName === undefined ? 'react' : _ref5$opts$moduleName; 28 | 29 | if (node.callee.name !== 'require') return; 30 | if (!node.arguments || !node.arguments[0]) return; 31 | if (node.arguments[0].value !== moduleName) return; 32 | this.hasRequired = true; 33 | }, 34 | 35 | Program: { 36 | enter: function enter() { 37 | this.hasJsx = false; 38 | this.hasRequired = false; 39 | }, 40 | exit: function exit(path, _ref6) { 41 | var _ref6$opts = _ref6.opts; 42 | var _ref6$opts$identifier = _ref6$opts.identifier; 43 | var identifier = _ref6$opts$identifier === undefined ? 'React' : _ref6$opts$identifier; 44 | var _ref6$opts$moduleName = _ref6$opts.moduleName; 45 | var moduleName = _ref6$opts$moduleName === undefined ? 'react' : _ref6$opts$moduleName; 46 | 47 | if (this.hasJsx && !this.hasRequired) { 48 | var ref = t.identifier(identifier); 49 | path.unshiftContainer('body', [t.variableDeclaration('var', [t.variableDeclarator(ref, buildRequire(t.stringLiteral(moduleName)).expression)])]); 50 | } 51 | } 52 | } 53 | } 54 | }; 55 | }; 56 | 57 | var template = require('babel-template'); 58 | 59 | var buildRequire = template('\n require($0);\n'); 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-react-require", 3 | "version": "1.0.1", 4 | "description": "Transform files using JSX to implicitly require React (or other implementations)", 5 | "repository": "https://github.com/mikaelbr/babel-plugin-transform-react-require", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "mocha --compilers js:babel-core/register test/ --timeout 10000", 9 | "prepublish": "npm run build", 10 | "build": "mkdir -p dist; babel src/index.js --out-file dist/index.js" 11 | }, 12 | "main": "dist/index.js", 13 | "keywords": [ 14 | "babel-plugin" 15 | ], 16 | "dependencies": { 17 | "babel-template": "^6.1.18" 18 | }, 19 | "devDependencies": { 20 | "babel-cli": "^6.1.18", 21 | "babel-core": "^6.1.21", 22 | "babel-plugin-transform-es2015-modules-commonjs": "^6.1.20", 23 | "babel-plugin-transform-react-jsx": "^6.1.18", 24 | "babel-preset-es2015": "^6.1.18", 25 | "mocha": "^2.3.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var template = require('babel-template'); 4 | 5 | var buildRequire = template(` 6 | require($0); 7 | `); 8 | 9 | export default function ({ types: t }) { 10 | return { 11 | visitor: { 12 | JSXElement() { 13 | this.hasJsx = true; 14 | }, 15 | 16 | ImportDeclaration ({ node }, { opts: { moduleName = 'react' } }) { 17 | if (!node.source || !node.source.value) return; 18 | if (node.source.value !== moduleName) return; 19 | this.hasRequired = true; 20 | }, 21 | 22 | CallExpression ({ node }, { opts: { moduleName = 'react' } }) { 23 | if (node.callee.name !== 'require') return; 24 | if (!node.arguments || !node.arguments[0]) return; 25 | if (node.arguments[0].value !== moduleName) return; 26 | this.hasRequired = true; 27 | }, 28 | 29 | Program: { 30 | enter() { 31 | this.hasJsx = false; 32 | this.hasRequired = false; 33 | }, 34 | 35 | exit(path, { opts: { 36 | identifier = 'React', 37 | moduleName = 'react' 38 | }}) { 39 | if (this.hasJsx && !this.hasRequired) { 40 | let ref = t.identifier(identifier); 41 | path.unshiftContainer('body', [ 42 | t.variableDeclaration('var', [ 43 | t.variableDeclarator(ref, 44 | buildRequire(t.stringLiteral(moduleName)).expression) 45 | ]) 46 | ]); 47 | } 48 | 49 | } 50 | } 51 | } 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /test/fixtures/should-inject-require-when-other-requires-and-imports-are-present/actual.js: -------------------------------------------------------------------------------- 1 | var Foo = require('foo'); 2 | import Bar from 'bar'; 3 | ; 4 | -------------------------------------------------------------------------------- /test/fixtures/should-inject-require-when-other-requires-and-imports-are-present/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _bar = require('bar'); 4 | 5 | var _bar2 = _interopRequireDefault(_bar); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8 | 9 | var React = require('react'); 10 | 11 | var Foo = require('foo'); 12 | 13 | React.createElement( 14 | 'button', 15 | { 'data-value': 'a value' }, 16 | 'Button' 17 | ); 18 | -------------------------------------------------------------------------------- /test/fixtures/should-inject-require/actual.js: -------------------------------------------------------------------------------- 1 | ; 2 | -------------------------------------------------------------------------------- /test/fixtures/should-inject-require/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | React.createElement( 6 | 'button', 7 | { 'data-value': 'a value' }, 8 | 'Button' 9 | ); 10 | -------------------------------------------------------------------------------- /test/fixtures/should-not-duplicate-import/actual.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | ; 3 | -------------------------------------------------------------------------------- /test/fixtures/should-not-duplicate-import/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _react = require('react'); 4 | 5 | var _react2 = _interopRequireDefault(_react); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8 | 9 | _react2.default.createElement( 10 | 'button', 11 | { 'data-value': 'a value' }, 12 | 'Button' 13 | ); 14 | -------------------------------------------------------------------------------- /test/fixtures/should-not-duplicate-require/actual.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | ; 3 | -------------------------------------------------------------------------------- /test/fixtures/should-not-duplicate-require/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | React.createElement( 5 | 'button', 6 | { 'data-value': 'a value' }, 7 | 'Button' 8 | ); 9 | -------------------------------------------------------------------------------- /test/fixtures/should-not-inject-when-no-jsx-element/actual.js: -------------------------------------------------------------------------------- 1 | var hello = 'No JSX in here'; 2 | function fooBar() { 3 | console.log('this is some code', hello); 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/should-not-inject-when-no-jsx-element/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var hello = 'No JSX in here'; 4 | function fooBar() { 5 | console.log('this is some code', hello); 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/should-respect-options-override/actual.js: -------------------------------------------------------------------------------- 1 | ; 2 | -------------------------------------------------------------------------------- /test/fixtures/should-respect-options-override/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dom = require('my-custom-dom'); 4 | 5 | dom( 6 | 'button', 7 | { 'data-value': 'a value' }, 8 | 'Button' 9 | ); 10 | -------------------------------------------------------------------------------- /test/fixtures/should-respect-options-override/options.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "identifier": "dom", 4 | "moduleName": "my-custom-dom" 5 | }, 6 | { "pragma": "dom" } 7 | ] 8 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var babel = require('babel-core'); 2 | var assert = require('assert'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var pluginPath = path.join(__dirname, '..', 'src', 'index'); 7 | 8 | var options = { 9 | 'plugins': [ 10 | pluginPath, 11 | 'transform-es2015-modules-commonjs', 12 | 'transform-react-jsx' 13 | ] 14 | }; 15 | 16 | describe('transform-react-require', function () { 17 | var folders = fs.readdirSync(path.join(__dirname, 'fixtures')); 18 | folders.forEach(function (folder) { 19 | if (folder.substring(0, 1) === '.') return; 20 | it(folder.replace(/-/g, ' '), function () { 21 | var base = path.join(__dirname, 'fixtures', folder); 22 | var actual = fs.readFileSync(path.join(base, 'actual.js')).toString('utf-8'); 23 | var expected = fs.readFileSync(path.join(base, 'expected.js')).toString('utf-8'); 24 | 25 | var possibleOptions, result; 26 | 27 | try { 28 | possibleOptions = fs.readFileSync(path.join(base, 'options.json')).toString('utf-8'); 29 | } catch (e) { 30 | possibleOptions = void 0; 31 | } 32 | 33 | if (!possibleOptions) { 34 | result = babel.transform(actual, options); 35 | } else { 36 | var extraOpts = JSON.parse(possibleOptions); 37 | let myOpts = Object.assign({}, options, { 38 | 'plugins': [ 39 | [options.plugins[0]].concat(extraOpts[0]), 40 | options.plugins[1], 41 | [options.plugins[2]].concat(extraOpts[1]), 42 | ] 43 | }); 44 | result = babel.transform(actual, myOpts); 45 | } 46 | 47 | // console.log(actual, expected); 48 | // console.log(result.code); 49 | assert.equal(result.code.trim(), expected.trim()); 50 | }); 51 | }); 52 | }); 53 | --------------------------------------------------------------------------------