├── .gitignore ├── CODE_OF_CONDUCT.md ├── README.md ├── package.json └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | *.log 4 | DS_Store 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | >## This Project Is Deprecated 2 | 3 | >React Hot Loader 3 is [on the horizon](https://github.com/gaearon/react-hot-loader/pull/240), and you can try it today ([boilerplate branch](https://github.com/gaearon/react-hot-boilerplate/pull/61), [upgrade example](https://github.com/gaearon/redux-devtools/commit/64f58b7010a1b2a71ad16716eb37ac1031f93915)). It fixes some [long-standing issues](https://twitter.com/dan_abramov/status/722040946075045888) with both React Hot Loader and React Transform, and is intended as a replacement for both. The docs are not there yet, but they will be added before the final release. For now, [this commit](https://github.com/gaearon/redux-devtools/commit/64f58b7010a1b2a71ad16716eb37ac1031f93915) is a good reference. 4 | 5 | 6 | # react-transform-hmr 7 | 8 | [![react-transform channel on discord](https://img.shields.io/badge/discord-react--transform%40reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com) 9 | 10 | 11 | A [React Transform](https://github.com/gaearon/babel-plugin-react-transform) that enables hot reloading React classes using Hot Module Replacement API. Hot module replacement is [supported natively by Webpack](http://webpack.github.io/docs/hot-module-replacement-with-webpack.html) and available in Browserify with [browserify-hmr](https://github.com/AgentME/browserify-hmr). 12 | 13 | ## 🚧🚧🚧🚧🚧 14 | 15 | This is **highly experimental tech**. If you’re enthusiastic about hot reloading, by all means, give it a try, but don’t bet your project on it. Either of the technologies it relies upon may change drastically or get deprecated any day. You’ve been warned 😉 . 16 | 17 | **This technology exists to prototype next-generation React developer experience**. Please don’t use it blindly if you don’t know the underlying technologies well. Otherwise you are likely to get disillusioned with JavaScript tooling. 18 | 19 | **No effort went into making this user-friendly yet. The goal is to eventually kill this technology** in favor of less hacky technologies baked into React. These projects are not long term. 20 | 21 | ## Installation 22 | 23 | First, install the [Babel plugin](https://github.com/gaearon/babel-plugin-react-transform): 24 | 25 | ``` 26 | npm install --save-dev babel-plugin-react-transform 27 | ``` 28 | 29 | Then, install the transform: 30 | 31 | ``` 32 | npm install --save-dev react-transform-hmr 33 | ``` 34 | 35 | ### React 36 | 37 | Edit your `.babelrc` to include a plugin configuration for `react-transform`. It contains array of the transforms you want to use: 38 | 39 | ```js 40 | { 41 | "presets": ["es2015", "stage-0"], 42 | "env": { 43 | // only enable it when process.env.NODE_ENV is 'development' or undefined 44 | "development": { 45 | "plugins": [["react-transform", { 46 | "transforms": [{ 47 | "transform": "react-transform-hmr", 48 | // if you use React Native, pass "react-native" instead: 49 | "imports": ["react"], 50 | // this is important for Webpack HMR: 51 | "locals": ["module"] 52 | }] 53 | // note: you can put more transforms into array 54 | // this is just one of them! 55 | }]] 56 | } 57 | } 58 | } 59 | ``` 60 | 61 | Make sure you process files with `babel-loader`, and that you *don’t* use React Hot Loader (it’s not needed with this transform). 62 | 63 | **It is up to you to ensure that the transform is not enabled when you compile the app in production mode.** The easiest way to do this is to put React Transform configuration inside `env.development` in `.babelrc` and ensure you’re calling `babel` with `NODE_ENV=production`. See [babelrc documentation](https://babeljs.io/docs/usage/babelrc/#env-option) for more details about using `env` option. 64 | 65 | **Warning!** This doesn't currently work for stateless functional components that were introduced in [React 0.14](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components)! 66 | 67 | ### React Native 68 | 69 | This transform enables hot reloading when used together with [React Native Webpack Server](https://github.com/mjohnston/react-native-webpack-server). **However note that you should not use `.babelrc` to configure it with React Native.** Otherwise you’ll get [`Uncaught SyntaxError: Unexpected reserved word` in `ActivityIndicatorIOS.ios.js`](https://github.com/mjohnston/react-native-webpack-server/issues/57#issuecomment-141487449). 70 | 71 | There are two problems why `.babelrc` doesn’t work well in React Native: 72 | 73 | * Changes in it [aren’t picked up by packager’s aggressive caching](https://github.com/mjohnston/react-native-webpack-server/issues/63). 74 | * Another unknown problem causes `import` generated by `babel-plugin-react-transform` to not be compiled into a `require` call. 75 | 76 | Until we have better `.babelrc` support in React Native, **you should configure React Transform together with `babel-loader`**: 77 | 78 | ```js 79 | var fs = require('fs'); 80 | var path = require('path'); 81 | var webpack = require('webpack'); 82 | 83 | var config = { 84 | debug: true, 85 | 86 | devtool: 'source-map', 87 | 88 | entry: { 89 | 'index.ios': ['./src/main.js'], 90 | }, 91 | 92 | output: { 93 | path: path.resolve(__dirname, 'build'), 94 | filename: '[name].js', 95 | }, 96 | 97 | module: { 98 | loaders: [{ 99 | test: /\.js$/, 100 | exclude: /node_modules/, 101 | loader: 'babel', 102 | query: { 103 | stage: 0, 104 | plugins: [] 105 | } 106 | }] 107 | }, 108 | 109 | plugins: [] 110 | }; 111 | 112 | // Hot mode 113 | if (process.env.HOT) { 114 | config.devtool = 'eval'; 115 | config.entry['index.ios'].unshift('react-native-webpack-server/hot/entry'); 116 | config.entry['index.ios'].unshift('webpack/hot/only-dev-server'); 117 | config.entry['index.ios'].unshift('webpack-dev-server/client?http://localhost:8082'); 118 | config.output.publicPath = 'http://localhost:8082/'; 119 | config.plugins.unshift(new webpack.HotModuleReplacementPlugin()); 120 | 121 | // Note: enabling React Transform and React Transform HMR: 122 | config.module.loaders[0].query.plugins.push([ 123 | 'react-transform', { 124 | transforms: [{ 125 | transform : 'react-transform-hmr', 126 | imports : ['react'], 127 | locals : ['module'] 128 | }] 129 | } 130 | ]); 131 | } 132 | 133 | if (process.env.NODE_ENV === 'production') { 134 | config.plugins.push(new webpack.optimize.OccurrenceOrderPlugin()); 135 | config.plugins.push(new webpack.optimize.UglifyJsPlugin()); 136 | } 137 | 138 | module.exports = config; 139 | ``` 140 | 141 | See [React Native Webpack Server examples](https://github.com/mjohnston/react-native-webpack-server/tree/master/Examples/) for details. 142 | 143 | 144 | ## License 145 | 146 | MIT 147 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-transform-hmr", 3 | "version": "1.0.4", 4 | "description": "A React Transform that enables hot reloading React classes using Hot Module Replacement API", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib", 8 | "src" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/gaearon/react-transform-hmr.git" 13 | }, 14 | "author": "Dan Abramov ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/gaearon/react-transform-hmr/issues" 18 | }, 19 | "homepage": "https://github.com/gaearon/react-transform-hmr#readme", 20 | "scripts": { 21 | "clean": "rimraf lib", 22 | "build": "babel src --out-dir lib", 23 | "prepublish": "npm run clean && npm run build" 24 | }, 25 | "keywords": [ 26 | "react-transform", 27 | "react", 28 | "reactjs", 29 | "webpack", 30 | "hmr", 31 | "live", 32 | "edit", 33 | "hot", 34 | "reload", 35 | "rhl", 36 | "dx" 37 | ], 38 | "dependencies": { 39 | "global": "^4.3.0", 40 | "react-proxy": "^1.1.7" 41 | }, 42 | "devDependencies": { 43 | "babel": "^5.8.23", 44 | "rimraf": "^2.4.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { getForceUpdate, createProxy } from 'react-proxy'; 2 | import window from 'global/window'; 3 | 4 | let componentProxies; 5 | if (window.__reactComponentProxies) { 6 | componentProxies = window.__reactComponentProxies; 7 | } else { 8 | componentProxies = {}; 9 | Object.defineProperty(window, '__reactComponentProxies', { 10 | configurable: true, 11 | enumerable: false, 12 | writable: false, 13 | value: componentProxies 14 | }); 15 | } 16 | 17 | export default function proxyReactComponents({ filename, components, imports, locals }) { 18 | const [React] = imports; 19 | const [{ hot }] = locals; 20 | 21 | if (!React.Component) { 22 | throw new Error( 23 | 'imports[0] for react-transform-hmr does not look like React.' 24 | ); 25 | } 26 | 27 | if (!hot || typeof hot.accept !== 'function') { 28 | throw new Error( 29 | 'locals[0] does not appear to be a `module` object with Hot Module ' + 30 | 'replacement API enabled. You should disable react-transform-hmr in ' + 31 | 'production by using `env` section in Babel configuration. See the ' + 32 | 'example in README: https://github.com/gaearon/react-transform-hmr' 33 | ); 34 | } 35 | 36 | if (Object.keys(components).some(key => !components[key].isInFunction)) { 37 | hot.accept(err => { 38 | if (err) { 39 | console.warn(`[React Transform HMR] There was an error updating ${filename}:`); 40 | console.error(err); 41 | } 42 | }); 43 | } 44 | 45 | const forceUpdate = getForceUpdate(React); 46 | 47 | return function wrapWithProxy(ReactClass, uniqueId) { 48 | const { 49 | isInFunction = false, 50 | displayName = uniqueId 51 | } = components[uniqueId]; 52 | 53 | if (isInFunction) { 54 | return ReactClass; 55 | } 56 | 57 | const globalUniqueId = filename + '$' + uniqueId; 58 | if (componentProxies[globalUniqueId]) { 59 | console.info('[React Transform HMR] Patching ' + displayName); 60 | const instances = componentProxies[globalUniqueId].update(ReactClass); 61 | setTimeout(() => instances.forEach(forceUpdate)); 62 | } else { 63 | componentProxies[globalUniqueId] = createProxy(ReactClass); 64 | } 65 | 66 | return componentProxies[globalUniqueId].get(); 67 | }; 68 | } 69 | --------------------------------------------------------------------------------