├── .gitignore ├── package.json ├── bower.json ├── LICENSE ├── jsx.js └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | .idea 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "requirejs-react-jsx", 3 | "version": "1.0.2", 4 | "description": "A RequireJS plugin for loading jsx in require.js and r.js", 5 | "main": "jsx.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/podio/requirejs-react-jsx.git" 12 | }, 13 | "keywords": [ 14 | "requirejs", 15 | "rjs", 16 | "react", 17 | "jsx" 18 | ], 19 | "author": "Søren Brokær ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/podio/requirejs-react-jsx/issues" 23 | }, 24 | "homepage": "https://github.com/podio/requirejs-react-jsx" 25 | } 26 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "requirejs-react-jsx", 3 | "homepage": "https://github.com/podio/requirejs-react-jsx", 4 | "authors": [ 5 | "Søren Brokær " 6 | ], 7 | "description": "A RequireJS plugin for compiling React JSX files. Will use react-tools when compiling using r.js, and will use JSXTransformer when running in the browser in development.", 8 | "main": "jsx.js", 9 | "keywords": [ 10 | "react", 11 | "jsx", 12 | "requirejs", 13 | "r.js", 14 | "require.js", 15 | "react-jsx", 16 | "requirejs-react-jsx" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests" 25 | ], 26 | "dependencies": { 27 | "react": ">=0.11.2", 28 | "requirejs-text": ">=2.0.12" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Podio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /jsx.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var isNode = typeof process !== "undefined" && 5 | process.versions && 6 | !!process.versions.node; 7 | 8 | var buildMap = {}; 9 | 10 | function getSourceMapConfig(config) { 11 | var fallback = config.isBuild ? false : 'inline'; 12 | 13 | if (config && config.babel && 'sourceMaps' in config.babel) { 14 | return config.babel.sourceMaps; 15 | } else { 16 | return fallback; 17 | } 18 | } 19 | 20 | function babelSync(name, parentRequire, onLoadNative, config) { 21 | var babel = require.nodeRequire('babel'); 22 | var path = parentRequire.toUrl(ensureJSXFileExtension(name, config)); 23 | 24 | try { 25 | var result = babel.transformFileSync(path, { 26 | sourceFileName: config.baseUrl + name, 27 | sourceMaps: getSourceMapConfig(config), 28 | filename: config.baseUrl + name 29 | }); 30 | 31 | var compiled = result.code; 32 | 33 | if (process.env.running_under_istanbul) { 34 | var istanbul = require.nodeRequire('istanbul'); 35 | var coverageVariable = Object.keys(global).filter(function (key) { return key.indexOf('$$cov_') === 0 })[0]; 36 | var instrumenter = new istanbul.Instrumenter({ 37 | coverageVariable: coverageVariable, 38 | noCompact: true, 39 | embedSource: true 40 | }); 41 | 42 | compiled = instrumenter.instrumentSync(compiled, path); 43 | } 44 | 45 | buildMap[name] = compiled; 46 | 47 | onLoadNative.fromText(compiled); 48 | } catch (err) { 49 | onLoadNative.error(err); 50 | } 51 | } 52 | 53 | function babelAsync(name, parentRequire, onLoadNative, config) { 54 | name = ensureJSXFileExtension(name, config); 55 | 56 | parentRequire(['babel', 'text'], function (babel, text) { 57 | text.load(name, parentRequire, function (content) { 58 | 59 | try { 60 | var result = babel.transform(content, { 61 | sourceFileName: config.baseUrl + name, 62 | sourceMaps: getSourceMapConfig(config), 63 | filename: config.baseUrl + name 64 | }); 65 | 66 | onLoadNative.fromText(result.code); 67 | } catch (err) { 68 | onLoadNative.error(err); 69 | } 70 | }); 71 | }); 72 | } 73 | 74 | function ensureJSXFileExtension(name, config) { 75 | var fileExtension = config && config.jsx && config.jsx.fileExtension || '.jsx'; 76 | 77 | if (name.indexOf(fileExtension) === -1) { 78 | name = name + fileExtension; 79 | } 80 | 81 | return name; 82 | } 83 | 84 | var jsx = { 85 | load: isNode ? babelSync : babelAsync, 86 | 87 | write: function (pluginName, name, write) { 88 | if (typeof buildMap[name] === 'string') { 89 | var text = buildMap[name]; 90 | 91 | write.asModule(pluginName + "!" + name, text); 92 | } else { 93 | throw new Error('Module not found in build map: ' + name); 94 | } 95 | } 96 | }; 97 | 98 | return jsx; 99 | }); 100 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # requirejs-react-jsx 2 | 3 | [![NPM version](https://badge.fury.io/js/requirejs-react-jsx.svg)](http://badge.fury.io/js/requirejs-react-jsx) 4 | [![Dependency Status](http://img.shields.io/gemnasium/podio/requirejs-react-jsx.svg?style=flat-square)](https://gemnasium.com/podio/requirejs-react-jsx) 5 | 6 | A RequireJS plugin for compiling React JSX files. Will use [react-tools](https://www.npmjs.org/package/react-tools) when compiling using `r.js`, and will use `JSXTransformer` or Babel when running in the browser in development. This allows us to support multiple bundles in `r.js` and exclude the `JSXTransformer` from all of them since we're requiring it dynamically and not explicitly. This also means that we can get `1:1` Source Maps in both development and production. 7 | 8 | # Example 9 | 10 | ![http://i.imgur.com/upv8B0g.png](http://i.imgur.com/upv8B0g.png) 11 | 12 | # Install 13 | 14 | ```sh 15 | $ bower install requirejs-react-jsx --save 16 | ``` 17 | 18 | If you're not using [bower](http://bower.io/search/) to manage your dependencies (you should), you can just download the [jsx.js](jsx.js) file manually. 19 | 20 | Since we're also using [react-tools](https://www.npmjs.org/package/react-tools) for the build step while running in a node process, and not in the browser, you will need to install that also: 21 | 22 | ```sh 23 | $ npm install react-tools --save 24 | ``` 25 | 26 | # Usage 27 | 28 | ### Setup 29 | 30 | `app.jsx` 31 | 32 | ```js 33 | define(function(require){ 34 | 35 | var React = require('react'); 36 | 37 | function App() { 38 | this.AppView = React.createClass({ 39 | render: function () { 40 | return ( 41 |
42 |

Hello, React!

43 |
44 | ); 45 | } 46 | }); 47 | } 48 | 49 | App.prototype.init = function () { 50 | React.render(, document.body); 51 | }; 52 | 53 | return App; 54 | 55 | }); 56 | ``` 57 | 58 | `main.js` 59 | 60 | ```js 61 | require.config({ 62 | paths: { 63 | "react": "bower_components/react/react-with-addons", 64 | "babel": "bower_components/requirejs-react-jsx/babel-5.8.34.min", 65 | "jsx": "bower_components/requirejs-react-jsx/jsx", 66 | "text": "bower_components/requirejs-text/text" 67 | }, 68 | 69 | shim : { 70 | "react": { 71 | "exports": "React" 72 | } 73 | }, 74 | 75 | config: { 76 | babel: { 77 | sourceMaps: "inline", // One of [false, 'inline', 'both']. See https://babeljs.io/docs/usage/options/ 78 | fileExtension: ".jsx" // Can be set to anything, like .es6 or .js. Defaults to .jsx 79 | } 80 | } 81 | }); 82 | 83 | require(['jsx!app'], function(App){ 84 | 85 | var app = new App(); 86 | app.init(); 87 | 88 | }); 89 | ``` 90 | 91 | ### Building 92 | 93 | Call with `$ node bower_components/r.js/dist/r.js -o build.js` 94 | 95 | In your [r.js](https://github.com/jrburke/r.js/) `build.js` config: 96 | 97 | ``` 98 | // add `optimize=none` to skip script optimization (useful during debugging). 99 | 100 | ({ 101 | appDir: "./", 102 | baseUrl: "./", 103 | dir: "./compiled", 104 | mainConfigFile: "./main.js", 105 | 106 | optimize: "uglify2", 107 | skipDirOptimize: true, 108 | generateSourceMaps: true, 109 | findNestedDependencies: true, 110 | preserveLicenseComments: false, 111 | 112 | onBuildWrite: function (moduleName, path, singleContents) { 113 | return singleContents.replace(/jsx!/g, ''); 114 | }, 115 | 116 | modules: [ 117 | { 118 | name: "main", 119 | exclude: ['jsx'] 120 | } 121 | ] 122 | }) 123 | ``` 124 | 125 | # Istanbul Code Coverage 126 | 127 | If you want code coverage with [Istanbul](https://github.com/gotwarlost/istanbul) you will have to do a little extra work. Istanbul only instruments code required by nodes `require` function by default. However, you can make Istanbul also instrument RequireJS loaded dependencies in a node environment by adding the `--hook-run-in-context` switch. 128 | 129 | requirejs-react-jsx will automatically detect that it is being run in an Istanbul enabled environment and 130 | 131 | The `--hook-run-in-context` only makes Istanbul pick up normally loaded RequireJS files though, and not the ones transformed by RequireJS plugins. So requirejs-react-jsx will automatically detect that it is being run in an Istanbul enabled environment and manually instrument the transpiled code so Istanbul can collect coverage. 132 | 133 | A full example of a coverage script in `package.json` could look like this: 134 | 135 | ```json 136 | { 137 | "scripts": { 138 | "test": "mocha", 139 | "coverage": "istanbul cover --hook-run-in-context _mocha" 140 | } 141 | } 142 | ``` 143 | 144 | # Changelog 145 | 146 | **1.0** - Eliminated all other transformer options than Babel. Switched config variable from `jsx` to `babel`. Added browser compatible babel 5.x build to repository to use for in-browser compilations 147 | 148 | # License 149 | 150 | [MIT](LICENSE) 151 | --------------------------------------------------------------------------------