cycle-hmr
v0.3.1
Published
Cycle.js hot module replacement (HMR).
Downloads
3
Readme
cycle-hmr
:fire: Hot reloading of cycle.js dataflows without need of restarting your app.
Demo (editing cyclic component's DOM vtree)
How does it work?
cycle-hmr utilizes "standard" HMR approach - replacing internals of existing instances with newly updated versions on the fly. It is achieved by proxying cycle components (like it is done for example in React Hot Reloader). In cycle.js application components are pure functions that output sink streams, that makes it quite straightforward to transparently and safely extend them. When updated version of module with cyclic functions arrives (using some hot reload technique) we replace components while their runtime keeping the rest application parts not touched (though of course injection of updated components potentially my cause some "unexpected" effects).
What do you need to use it?
- Well first you need to use cycle.js. Always. If you still don't.
- Have a great desire to be more efficient with your development workflow, HMR is about quick feedback loop while dev process, you should use it.
- Use webpack or broserify for modules loading/bundling. Other loaders (e.g System.js) are not tested and not supported yet.
- Use ES2015 compatible modules, only ES6's
export
s are currently suported.CJS
modules will be ignored. - Use babel transpiler. If you do't use babel
for ES2015 => ES5 transformation you can still use it, just for
cycle-hmr
plugin. - Understand that it is experimental feature and bugs are possible.
Supported stream libraries
cycle-hmr
is stream library agnostic. So you can use it with any library supported by cyclejs
(rx4,
rx5,
most, xstream) -
it will detect and use needed stream adapter, but you should
have it installed in your dependencies, if cycle-hmr will not find valid adapter
it will just not proxy your streams.
Usage
1) Install from npm
npm install cycle-hmr --save-dev
Also my need to install adapters, but usually they are installed with your cycle run
modules (like @cycle/xstream-run
)
npm install @cycle/rxjs-adapter @cycle/xstream-adapter --save
2) Configure babel plugin and point to dataflow files
cycle-hmr
comes with babel plugin (as dependency).
You should include the babel plugin (for example in .babelrc
) and
point where files with cyclic dataflows are located (you may also exclude
option to point files that should not be processed):
.babelrc:
{
"env": {
"development": {
"plugins": [
["cycle-hmr", {
"include": "**/cycles/**.js"
}]
]
}
}
}
You can also use only specific stream library plugin: "cycle-hmr/xstream"
"development": {
"plugins": [
["cycle-hmr/xstream", {
"include": "*"
}]
]
}
Note that include/exclude
is a glob matchers -
not relative paths, or regexps. If you don't use include/exclude
options, no modules will be processed by default.
But you can mark files individually with comment on the top:
/* @cycle-hmr */
For each processed module cycle-hmr
babel plugin wraps all the exports
with (safe) HMR proxy and adds also hot.accept()
(webpack/browserify HMR API),
so no dependants of the module will be reloaded when module changes.
Note: if it proxies something that it should not, well, unlikely that it will break anything - HMR proxy wrapper is transparent for non-cyclic exports.
NB! if you have non cycle exports in processed modules, and use them somewhere, changes to those exports will not have any effect - so it is recommended to have only cyclic exports in processed modules .
Also you may filter exports names which will be proxied with babel plugin
parameter testExportName
, for example such config:
{
"plugins": [
["cycle-hmr", {
"testExportName": "^[A-Z]"
"include": "*"
}]
]
}
will process all the files, but will proxy only named exports starting with capital letter
(not it will not include default
exports in this case, to include them you would use
^([A-Z]|default)
regExp expression).
3) Configure bundler/loader for hot-reloading
It is easy to use cycle-hmr with webpack or browserify.
Webpack
Setup you hot reloading workflow with webpack-dev-server
and babel-loader
using this need parts of config:
...
entry: [
'webpack-dev-server/client?http://localhost:' + process.env.PORT,
'webpack/hot/only-dev-server',
'./app.js' // your app's entry
]
module: {
loaders: [{
test: /\.js$/,
loader: 'babel' // .babelrc should plug in `cycle-hmr`
}]
},
plugins: [
new webpack.NoErrorsPlugin(), // use it
new webpack.HotModuleReplacementPlugin(),
new webpack.IgnorePlugin(/most-adapter/) // for adaperts not installed
],
devServer: {
hot: true,
...
NB! To have less problems when dealing with compile and runtime errors,
because of existing issues with webpack-dev-server
recommendation is to run
it using node API, instead of cli (especially avoid --hot
and --inline
options).
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
new WebpackDevServer(webpack(config), config.devServer)
.listen(process.env.PORT);
Browserify
Use browserify-hmr
plugin and babelify
. Use --ingnore-missing
option to ignore missing dependencies.
For example launch with budo server:
budo entry.js -- -t babelify --ignore-missing -p browserify-hmr
4) Turn on debug output if needed
If there is something wrong and you don't understand what (for example HMR does not work), you may want to turn on the debug output: It will show what happens to components that are proxied.
Fo this add proxy.debug
option to cycle-hmr
babel plugin:
"plugins": [
["cycle-hmr", {
"include": "**/cycles/**.js",
"proxy": {
"debug": "info"
}
}]
]
This will turn on debug output for all modules, but you can turn on it individually using the comment on the top of the module:
/* @cycle-hmr-debug */
Licence
MIT.