Open walleXD opened 6 years ago
I am looking for the same solution and came across here that poses a solution and then someone suggests (at the end) HtmlWebpackPlugin.
I tried using the htmlwebpackplugin but there are 2 main problems:
electron-webpack
builds includes all js files and so that means if I have another renderer entry point called background.js
that file will also be included with the main If, there's another entry exposed, just like the main thread's, then electron-webpack
can use the existing build pipe line for the extra entries
Extra renderer points are not supported yet. Help wanted (or you can https://webpack.electron.build/modifying-webpack-configurations, but as you pointed, " it would take some time to dig in deep to get things like module imports to work properly").
I tried to get it working but the second renderer file(s) messes up the main renderer since the htmlwebpackplugin
is setup to include all chunks. TBH completely reworking the rendering build pipeline would take out current ease associated with using electron-webpack
during development
@develar any plans on adding extra renderer entries?
If it any help, for my situation (and now with a little more experience) I am not finding it a major problem. The design I had to work with has four types of renders: some are web workers, others straight windows. I don't actually think you need to complicate the design (personally) and thus wouldn't encourage the ability to do multiple renderers. Instead, I would encourage simplicity which in this case would have forced better design!
Regardless, for what it is worth and may help others, my kludge used electron-window
to help switching renderers at runtime (that was my best shot and if there are better ways I would like to know). I'll include some code here because it does require getting it "just right" for it to work on the webpack dev server and a packaged version (ie get the slash in the right direction). Note: my file structure is the default from electron-webpack using src/main, src/renderer and I have a src/common/window file.
I it helps someone find a much better solution or realise not to design your application like this. Now that we are in control of the existing application we plan to remove all of this. Have one window and run the web workers in the background. Previously separate windows will have parent/child relationships.
common\window.js
/*global process __dirname */
import window from 'electron-window';
import { IsDevelopmentEnvironment } from '../../constants';
import log from 'electron-log';
/**
* There are four windows that make up the application. Each need their own renderer.
* @enum {string}
*/
export const windowName = {
application: 'main',
detectionDialog: 'alert',
backgroundTasks: 'tasks',
thirdPartyIntegrationConfiguration: 'config'
};
/**
* Makes the correct uri or path to locate the auto-generated index.html file from electron-webpack.
*
* In development `electron-webpack dev` requires serving on http://:
* - makes 'dist/renderer/index.html'
* - starts webpack development server
* - servers up this file on '/'
*
* In production `Auror.exe` requires a path and `electron-window` requires NO 'file://'
* - makes 'dist/win-unpacked/resources/app/index.html'
* - that is bundled up into the package
* - this is run as a local 'file://' that under windows is backslash '\' delimited
* - NOTE: `path.resolve` is not available here in the render for resolution
*
* WARNING: production builds are brittle. To test, make and run an unpacked version:
*
* 1. `electron-webpack`
* 2. `electron-builder --dir`
* 3. `dist\uwnin-unpacked\Auror.exe`
* 4. Check that windows load
* 5. Check no errors on console (Control-Shift-I)
*
* NOTE: loading issues can also be related `electron-windows` uri parsing with params
*
* @return {string} uriOrPath
*/
export function makeUrlToHtmlFile () {
return IsDevelopmentEnvironment
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}/`
: `${__dirname}\\index.html`; \\ MUST be backslashes for packaged version
}
/**
* @class WindowOptions
* @property {RendererWindowOptions} data params passed through to the renderer (to cross process boundaries)
* @property {BrowserWindowConstructorOptions} windowOptions options passed through to the {@link BrowserWindow}
*/
/**
* @class RendererWindowOptions
* @property {string} windowName type of window to load {@link windowName}
* @property {WindowLoadCallback) callback act on the electron window post showURL which 'shows' by default and ignores hide
*/
/**
* @class WindowLoadCallback
* @param {Electron.Window} window
*/
/**
* Open a window from the renderer toggling the load based on the options. The window is opened via the
* 'electron-window' mechanism that shares data via the url params.
* @param {WindowOptions} options
* @return {Electron.BrowserWindow}
*/
export function makeRendererWindow (options = {}) {
// ... stripped out default options for sample
let aWindow = window.createWindow(options);
const uriOrPath = makeUrlToHtmlFile();
log.debug(`Rendering using: '${uriOrPath}'`);
aWindow.showURL(uriOrPath, options.data, () => {
// most times you don't need a post-construction hook
// but electron-window is NOT equivalent to wdinow.loadURL
if (typeof options.callback === 'function') {
options.callback(aWindow);
}
log.info(`Window '${options.data.windowName}' opened`);
});
return aWindow;
}
src\main.js
import { makeRendererWindow, windowName } from '../common/window';
// ... standard stuff
// Create main BrowserWindow when electron is ready
app.on('ready', () => {
log.debug('Starting app window');
/**
* This is the main window that should only be showing.
*/
mainWindow = makeRendererWindow(
{
data: {
windowName: windowName.application
},
windowOptions: {
width: 1110,
minWidth: 1110 // ... and some more
}
});
otherWindow = makeRenderWindow() // ... now into psuedo code
});
src\renderer.js
import { windowName } from '../common/window';
// here's the actual javascript code to run
import { default as runMain } from '../app'; // this is angular
import { default as runTasks } from '../background'; // these are web workers
import { default as runDetection } from '../detection'; // these are angular
import { default as runConfig } from '../config'; // this is angular
import log from 'electron-log';
/**
* This renderer is called via 'electron-window' which passes through an object via params and
* attaches on `window.__args__`
*
* @type {RendererWindowOptions}
*/
const options = window.__args__;
if (options) {
const toRun = options.windowName;
log.info('In renderer', options.windowName);
// load up the correct javascript for the window. This gets around the multiple renders problem.
switch (toRun) {
case windowName.application:
runMain();
break;
case windowName.detectionDialog:
runDetection();
break;
case windowName.thirdPartyIntegrationConfiguration:
runConfig();
break;
case windowName.backgroundTasks:
runTasks();
break;
default:
log.error(`Nothing to run, found '${toRun}'`);
}
} else {
log.info('Renderer has been called without using \'electron-window\' see https://github.com/jprichardson/electron-window#usage');
}
@toddb holy guacamole, this is a great solution. I am already using electron-window
. My index.js
for renderer
import { parseArgs } from 'electron-window'
parseArgs()
const { name } = window.__args__
const initRenderer = async name => {
switch (name) {
case 'main': return import('./ui')
case 'test': return import('./test')
default:
console.error('Nothing to run')
}
}
initRenderer(name)
Using dynamic import
here makes sure the renderer
process only loads up the code it will be using base on the switch
cases
& my createWindow.js
import isDev from 'electron-is-dev'
import { createWindow, windows } from 'electron-window'
import windowStateKeeper from 'electron-window-state'
export default (
name,
{ devURL, prodURL },
windowStateOpts = {},
browserWindowOpts = {}
) => {
const windowState = windowStateKeeper({
defaultWidth: 1000,
defaultHeight: 800,
...windowStateOpts
})
const window = createWindow({
x: windowState.x,
y: windowState.y,
width: windowState.width,
height: windowState.height,
minWidth: 900,
minHeight: 620,
preload: true,
...browserWindowOpts
})
windowState.manage(window)
const url = isDev ? devURL : prodURL
window.showURL(url, { name })
return window
}
export { windows }
The only issue that this brings up is how do we keep the other windows hidden? By default, electron-window
load and shows the window it creates. So, I used _loadURLWithArgs
function on the window
object. The only thing is that it requires a callback to work, even if you aren't using it to do anything
@walleXD
import
(require). I really dislike inline but can see benefits here. Ah, I've just seen that you sneaked async
in there too!callback
in the RendererWindowOptions:options
. In practice, the post-load hook is actually needed for wiring up code that lives on the parent scope. But, electron-windows probably needs a little more love that we don't need to do this (I haven't looked at why they do this).
Hey all,
It's possible to create multiple main entries. Is it possible to do the same for the renderer process? I am trying to create a separate hidden browserWindow, with separate entry point, to run background tasks.
Thanks a bunch in advance