Closed flexzuu closed 7 years ago
Auto refresh is pretty trivial:
https://www.npmjs.com/package/electron-reload
The real magic is when we can refresh whilst maintaining state.
Does electron-reload works with electron-compile though?
Just did a quick test, yeah it works.
I'm think about having a crack at this in the next few weeks when I get some time off. First gotta fix the 'unaware if dependencies change' problem.
This is On My List, specifically for React.
Couldn't sleep, decided to fix the 'unaware of dependencies thing' ahah, I'll make a PRs against electron-compile and electron-compilers ~tomorrow~ now here and here. mnquintana is a massive champion having done almost all the work already.
So far got ES6, SASS, SCSS, Stylus, and LESS all working, which leaves TypeScript and CoffeeScript. If I get more free time I'll probably have a crack at a hot reload solution instead of doing the other languages, then if I get even more free time, I'll tackle those languages.
I've got a prototype of hot-reloading going, should this be put into a separate module for the same reasoning as electron-compilers?
@Mike-Dax Should probably defer to @paulcbetts but I think an electron-compile
HMR tool should be separate to electron-compile
itself so that it isn't pulled into the production dependencies.
Something like electron-compile-hmr
would be a good fit possibly.
Also depends on how big the code is IMO, if it's not much code then modularizing it simply increases complexity. :man_shrugging: Do what you think works and we can discuss it đ
Seems to be running out of electron-forge
well.
Going to try and work out how react-proxy works so this can be useful in a stateful application instead of being just a party trick.
Things that compile down to css all seem to work well.
I think I'll go the route of electron-compile checking to see if the package is installed, and if so bootstrap it up so the user needs as little boilerplate as possible. I think no matter what though I'll need a reference to the root node used by React in order to force a reload.
@Mike-Dax This looks super cool, if you need a reference to the root node you probably need to follow the pattern from react HMR AppContainer
https://github.com/gaearon/redux-devtools/blob/master/examples/todomvc/index.js#L11
@paulcbetts
So the way I've been doing hmr in my prototype is to inject into the end of the index.html that Electron uses to boot, much like the 'magic' electron-compile file.
I would then scrape from the dom the list of script and link elements, extract the details of the files in there.
For each of those files I would build a dependency tree using the functions created earlier (in electron-compilers).
I would then attach an fs.watch, much like you have, but to the files individually.
From my testing in the current implementation in 6.0.0, it doesn't have the functionality to detect nested changes with the compilers that I put my fingers in, however it works with babel? (I don't know if I'm understanding the babel implementation correctly, does that work because of the 'globalish' file watcher + the fact that babel will traverse those dependencies implicitly? If so why doesn't that work with everything else) If you've got the time I'd love an explanation from your perspective.
If one of these watchers detects a change within one of the individual trees, it would traverse up the tree deleting the node cache then re-require the root node, or reset the dom link in the case of an scss file.
For a react component I'd just re-render the root node, throwing away state. I'm still trying to parse how all the react-hmr implementations work (and how they keep state) so I can do one for this properly, with all the fun that @gaearon does in React Hot Loader 3.
Rough pre-react earlier code snippet available here, does it as a flat list instead of a tree:
https://github.com/Mike-Dax/electron-compile-hmr/blob/master/simplesnippet.js
Just wanted to be transparent in what I was working on, this is the first real contribution I've done to an OSS project.
@MarshallOfSound So if we can land #183, I've got code 80% of the way there for css based hot reloading. It's just a bit messy at the moment, I've discovered rxjs and it's changed my life.
@Mike-Dax Sounds awesome đ đ
@Mike-Dax, @MarshallOfSound. I'am new in electron, plz get me a work prototype or boilerplate, to get work electron and react-hot-reload.
@mavajee we'll write up documentation after its landed and the api settles, this will probably find its way into an electron-forge template for react specifically.
@Mike-Dax, all template I have saw, very hard for me, and I don't get find electron compile with react-hot-reload.
@Mike-Dax Over the weekend @MarshallOfSound was able to get this working and I published a new electron-compile version. Check out https://github.com/electron/electron-compile#react-hot-module-loading and see if you can get it working for your app
@Mike-Dax Thanks a ton for the work you put into this, this was a huge contribution!
Hey guys, thanks so much for doing this! I wasn't able to get it working unfortunately... Here's my index.js (which is called from index.html).
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import routes from 'app/routes';
import Root from 'containers/Root';
const appEl = document.getElementById('app');
ReactDOM.render(
<AppContainer>
<Root routes={routes} />
</AppContainer>,
appEl
);
// Hot Module Replacement API
console.log(module);
if (module.hot) {
module.hot.accept([
'./containers/Root.js',
'./routes.js',
], () => {
// TODO: HMR+Epics? https://redux-observable.js.org/docs/recipes/HotModuleReplacement.html /*
console.log('module.hot.accept');
const nextRoutes = require('app/routes').default;
const hotReloadRoutes = require('utils/hotReloadRoutes').default;
hotReloadRoutes(routes, nextRoutes);
ReactDOM.render(
<AppContainer>
<Root routes={routes} />
</AppContainer>,
appEl
);
});
}
Note this setup works for hot reloading through webpack on our web build. The problem is that module.hot
is undefined. Tbh I don't really know where that property would get added. Note I did try using the code specified in the docs (with the render
function) and was unable to get that to work (it also broke my web hot loading âšī¸ ).
index.html
<!DOCTYPE html>
<!--[if lt IE 7 ]> <html lang="en" class="ie6" > <![endif]-->
<!--[if IE 7 ]> <html lang="en" class="ie7" > <![endif]-->
<!--[if IE 8 ]> <html lang="en" class="ie8" > <![endif]-->
<!--[if IE 9 ]> <html lang="en" class="ie9" > <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="" > <!--<![endif]-->
<head>
<title>React Demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type='text/sass' href="../styles/main.scss">
</head>
<body>
<div id="app"></div>
<script type="application/javascript">
require('../index.js');
</script>
</body>
</html>
main-process.js (which is the entry to electron-forge
const { app, BrowserWindow } = require('electron');
const path = require('path');
const enableLiveReload = require('electron-compile').enableLiveReload;
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
// Hot reloading
const isDevMode = process.execPath.match(/[\\/]electron/);
if(isDevMode) enableLiveReload({ strategy: 'react-hmr' });
function addDevTools() {
// Open the DevTools.
mainWindow.webContents.openDevTools();
require('devtron').install();
require('electron-debug')();
const installer = require('electron-devtools-installer');
const extensions = [
'REACT_DEVELOPER_TOOLS',
'REDUX_DEVTOOLS',
'REACT_PERF',
];
for(const name of extensions) {
installer.default(installer[name], true)
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log(`An error occurred: ${err}`));
}
}
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow();
mainWindow.maximize();
// and load the index.html of the app.
mainWindow.loadURL(`file://${__dirname}/index.html`);
if (process.env.NODE_ENV === 'development') {
addDevTools();
}
// Emitted when the window is closed.
mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Any insights or help would be great! I'm taking over the electron stuff from a team member who left and I'm pretty new to it.
EDIT: Note I also just installed a new electron-forge app with electron-forge init test --template=react
and then ran it and HMR is not working for it either. Same issue, where module.hot
is undefined.
@JakeDluhy Woops, looks like a bug got introduced at some point with enable hot reloading đ
Fix incoming đ
@JakeDluhy PR up #216 đ
Worked, thanks!
In the talk about this package on the Github Universe Conference, it was mentioned that you are thinking about adding "Hot-Reloading". I like the idea of this package, but without "Hot-Reloading" other Build-Pipelines are simply more productive. I am interested of spending some time adding "Hot-Reloading" or even first only just Auto-Refresh to this package. Can you give me some information how to start? My first idea was so simply call something like browserWindow.refresh() on every rebuilding or file change detection. This would not add Hot-Reloading but "Auto-refresh".