├── .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 |
--------------------------------------------------------------------------------