├── .gitignore ├── .npmignore ├── .babelrc ├── .travis.yml ├── test ├── fixtures │ ├── should-inject-require │ │ ├── actual.js │ │ └── expected.js │ ├── should-respect-options-override │ │ ├── actual.js │ │ ├── options.json │ │ └── 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-inject-require-when-other-requires-and-imports-are-present │ │ ├── actual.js │ │ └── expected.js └── index.js ├── package.json ├── src └── index.js ├── dist └── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | src/ 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "5" 5 | -------------------------------------------------------------------------------- /test/fixtures/should-inject-require/actual.js: -------------------------------------------------------------------------------- 1 | ; 2 | -------------------------------------------------------------------------------- /test/fixtures/should-respect-options-override/actual.js: -------------------------------------------------------------------------------- 1 | ; 2 | -------------------------------------------------------------------------------- /test/fixtures/should-not-duplicate-import/actual.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | ; 3 | -------------------------------------------------------------------------------- /test/fixtures/should-not-duplicate-require/actual.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | ; 3 | -------------------------------------------------------------------------------- /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-respect-options-override/options.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "identifier": "dom", 4 | "moduleName": "my-custom-dom" 5 | }, 6 | { "pragma": "dom" } 7 | ] 8 | -------------------------------------------------------------------------------- /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-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-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-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-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-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-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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 |