Skip to content

Commit 80d160a

Browse files
Merge pull request #390 from syedadeel2/LiveReload
Live reload
2 parents 71955b9 + 01d938f commit 80d160a

File tree

12 files changed

+315
-66
lines changed

12 files changed

+315
-66
lines changed

ElectronNET.API/ElectronNET.API.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.0</TargetFramework>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
66
<PackageOutputPath>..\artifacts</PackageOutputPath>
77
<PackageId>ElectronNET.API</PackageId>

ElectronNET.API/WebHostBuilderExtensions.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.AspNetCore.Hosting;
22
using System;
3+
using System.IO;
34

45
namespace ElectronNET.API
56
{
@@ -22,16 +23,27 @@ public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[]
2223
{
2324
BridgeSettings.SocketPort = argument.ToUpper().Replace("/ELECTRONPORT=", "");
2425
Console.WriteLine("Use Electron Port: " + BridgeSettings.SocketPort);
25-
} else if(argument.ToUpper().Contains("ELECTRONWEBPORT"))
26+
}
27+
else if (argument.ToUpper().Contains("ELECTRONWEBPORT"))
2628
{
2729
BridgeSettings.WebPort = argument.ToUpper().Replace("/ELECTRONWEBPORT=", "");
2830
}
2931
}
3032

31-
if(HybridSupport.IsElectronActive)
33+
if (HybridSupport.IsElectronActive)
3234
{
33-
builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
34-
.UseUrls("http://127.0.0.1:" + BridgeSettings.WebPort);
35+
// check for the content folder if its exists in base director otherwise no need to include
36+
// It was used before because we are publishing the project which copies everything to bin folder and contentroot wwwroot was folder there.
37+
// now we have implemented the live reload if app is run using /watch then we need to use the default project path.
38+
if (Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}\\wwwroot"))
39+
{
40+
builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
41+
.UseUrls("http://localhost:" + BridgeSettings.WebPort);
42+
}
43+
else
44+
{
45+
builder.UseUrls("http://localhost:" + BridgeSettings.WebPort);
46+
}
3547
}
3648

3749
return builder;

ElectronNET.CLI/Commands/StartElectronCommand.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ public Task<bool> ExecuteAsync()
6060
var platformInfo = GetTargetPlatformInformation.Do(string.Empty, string.Empty);
6161

6262
string tempBinPath = Path.Combine(tempPath, "bin");
63-
var resultCode = ProcessHelper.CmdExecute($"dotnet publish -r {platformInfo.NetCorePublishRid} --output \"{tempBinPath}\" /p:PublishReadyToRun=true --no-self-contained", aspCoreProjectPath);
63+
var resultCode = 0;
64+
65+
if (parser != null && !parser.Arguments.ContainsKey("watch"))
66+
{
67+
resultCode = ProcessHelper.CmdExecute($"dotnet publish -r {platformInfo.NetCorePublishRid} --output \"{tempBinPath}\" /p:PublishReadyToRun=true --no-self-contained", aspCoreProjectPath);
68+
}
6469

6570
if (resultCode != 0)
6671
{
@@ -110,13 +115,19 @@ public Task<bool> ExecuteAsync()
110115
arguments += " --clear-cache=true";
111116
}
112117

118+
if (parser.Arguments.ContainsKey("watch"))
119+
{
120+
arguments += " --watch=true";
121+
}
122+
113123
string path = Path.Combine(tempPath, "node_modules", ".bin");
114124
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
115125

116126
if (isWindows)
117127
{
118128
Console.WriteLine("Invoke electron.cmd - in dir: " + path);
119129
ProcessHelper.CmdExecute(@"electron.cmd ""..\..\main.js"" " + arguments, path);
130+
120131
}
121132
else
122133
{

ElectronNET.CLI/ElectronNET.CLI.csproj

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
55

6-
<TargetFramework>netcoreapp3.0</TargetFramework>
6+
<TargetFramework>netcoreapp3.1</TargetFramework>
77
<AssemblyName>dotnet-electronize</AssemblyName>
88
<ToolCommandName>electronize</ToolCommandName>
99

@@ -29,6 +29,11 @@
2929
<PackageReleaseNotes>Changelog: https://github.com/ElectronNET/Electron.NET/blob/master/Changelog.md</PackageReleaseNotes>
3030
<PackageIcon>PackageIcon.png</PackageIcon>
3131
<PackAsTool>true</PackAsTool>
32+
<StartupObject></StartupObject>
33+
</PropertyGroup>
34+
35+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
36+
<PlatformTarget>AnyCPU</PlatformTarget>
3237
</PropertyGroup>
3338

3439
<ItemGroup>
@@ -37,7 +42,7 @@
3742
</ItemGroup>
3843

3944
<ItemGroup>
40-
<None Include="PackageIcon.png" Pack="true" PackagePath="\"/>
45+
<None Include="PackageIcon.png" Pack="true" PackagePath="\" />
4146
</ItemGroup>
4247

4348
<ItemGroup>

ElectronNET.CLI/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"ElectronNET.CLI": {
44
"commandName": "Project",
5-
"commandLineArgs": "build \"C:\\Users\\Gregor\\Documents\\Visual Studio 2017\\Projects\\ElectronNET\\ElectronNET.WebApp\""
5+
"commandLineArgs": "start /project-path \"C:\\Users\\Rizvi\\source\\repos\\Electron.NET\\ElectronNET.WebApp\" /watch"
66
}
77
}
88
}

ElectronNET.Host/api/browserWindows.js

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ElectronNET.Host/api/browserWindows.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const path = require('path');
33
const windows: Electron.BrowserWindow[] = [];
44
let readyToShowWindowsIds: number[] = [];
55
let window, lastOptions, electronSocket;
6-
6+
let mainWindowURL;
77
export = (socket: SocketIO.Socket, app: Electron.App) => {
88
electronSocket = socket;
99
socket.on('register-browserWindow-ready-to-show', (id) => {
@@ -199,7 +199,16 @@ export = (socket: SocketIO.Socket, app: Electron.App) => {
199199
options = { ...options, webPreferences: { nodeIntegration: true } };
200200
}
201201

202-
window = new BrowserWindow(options);
202+
// we dont want to recreate the window when watch is ready.
203+
if (app.commandLine.hasSwitch('watch') && app['mainWindowURL'] === loadUrl) {
204+
window = app['mainWindow'];
205+
if (window) {
206+
window.reload();
207+
}
208+
} else {
209+
window = new BrowserWindow(options);
210+
}
211+
203212
window.on('ready-to-show', () => {
204213
if (readyToShowWindowsIds.includes(window.id)) {
205214
readyToShowWindowsIds = readyToShowWindowsIds.filter(value => value !== window.id);
@@ -245,6 +254,12 @@ export = (socket: SocketIO.Socket, app: Electron.App) => {
245254
console.log('auto clear-cache active for new window.');
246255
}
247256

257+
// set main window url
258+
if (app['mainWindowURL'] == undefined || app['mainWindowURL'] == "") {
259+
app['mainWindowURL'] = loadUrl;
260+
app['mainWindow'] = window;
261+
}
262+
248263
windows.push(window);
249264
electronSocket.emit('BrowserWindowCreated', window.id);
250265
});
@@ -744,6 +759,7 @@ export = (socket: SocketIO.Socket, app: Electron.App) => {
744759
getWindowById(id).setBrowserView(browserView);
745760
});
746761

762+
747763
function getWindowById(id: number): Electron.BrowserWindow {
748764
for (let index = 0; index < windows.length; index++) {
749765
const element = windows[index];

ElectronNET.Host/main.js

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
const { app } = require('electron');
22
const { BrowserWindow } = require('electron');
33
const path = require('path');
4-
const process = require('child_process').spawn;
4+
const cProcess = require('child_process').spawn;
55
const portscanner = require('portscanner');
66
const imageSize = require('image-size');
7+
const chalk = require('chalk');
78
let io, server, browserWindows, ipc, apiProcess, loadURL;
89
let appApi, menu, dialogApi, notification, tray, webContents;
910
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
1011
let commandLine, browserView;
1112
let splashScreen, hostHook;
13+
let mainWindowId;
1214

1315
let manifestJsonFileName = 'electron.manifest.json';
14-
if(app.commandLine.hasSwitch('manifest')) {
16+
let watchable = false;
17+
if (app.commandLine.hasSwitch('manifest')) {
1518
manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
1619
};
1720

18-
const currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
19-
const manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
21+
if (app.commandLine.hasSwitch('watch')) {
22+
watchable = true;
23+
};
24+
25+
let currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
26+
let manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
27+
28+
// if watch is enabled lets change the path
29+
if (watchable) {
30+
currentBinPath = path.join(__dirname, '../../'); // go to project directory
31+
manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
32+
}
33+
2034
const manifestJsonFile = require(manifestJsonFilePath);
2135
if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
2236
const mainInstance = app.requestSingleInstanceLock();
@@ -42,7 +56,7 @@ app.on('ready', () => {
4256

4357
// hostname needs to belocalhost, otherwise Windows Firewall will be triggered.
4458
portscanner.findAPortNotInUse(8000, 65535, 'localhost', function (error, port) {
45-
console.log('Electron Socket IO Port: ' + port);
59+
console.log(chalk.blue('Electron Socket IO Port: ' + port));
4660
startSocketApiBridge(port);
4761
});
4862

@@ -62,8 +76,8 @@ function startSplashScreen() {
6276
let imageFile = path.join(currentBinPath, manifestJsonFile.splashscreen.imageFile);
6377
imageSize(imageFile, (error, dimensions) => {
6478
if (error) {
65-
console.log(`load splashscreen error:`);
66-
console.log(error);
79+
console.log(chalk.bold.red(`load splashscreen error:`));
80+
console.log(chalk.bold.red(error));
6781

6882
throw new Error(error.message);
6983
}
@@ -104,15 +118,46 @@ function startSocketApiBridge(port) {
104118

105119
server.listen(port, 'localhost');
106120
server.on('listening', function () {
107-
console.log('Electron Socket started on port %s at %s', server.address().port, server.address().address);
121+
console.log(chalk.bgGreenBright('Electron Socket started on port %s at %s', server.address().port, server.address().address));
108122
// Now that socket connection is established, we can guarantee port will not be open for portscanner
109-
startAspCoreBackend(port);
123+
if (watchable) {
124+
startAspCoreBackendWithWatch(port);
125+
} else {
126+
startAspCoreBackend(port);
127+
}
110128
});
111129

130+
// prototype
131+
app['mainWindowURL'] = "";
132+
app['mainWindow'] = null;
133+
112134
io.on('connection', (socket) => {
135+
136+
// we need to remove previously cache instances
137+
// otherwise it will fire the same event multiple depends how many time
138+
// live reload watch happen.
139+
socket.on('disconnect', function () {
140+
console.log(chalk.bold.red('Got disconnect!'));
141+
delete require.cache[require.resolve('./api/app')];
142+
delete require.cache[require.resolve('./api/browserWindows')];
143+
delete require.cache[require.resolve('./api/commandLine')];
144+
delete require.cache[require.resolve('./api/autoUpdater')];
145+
delete require.cache[require.resolve('./api/ipc')];
146+
delete require.cache[require.resolve('./api/menu')];
147+
delete require.cache[require.resolve('./api/dialog')];
148+
delete require.cache[require.resolve('./api/notification')];
149+
delete require.cache[require.resolve('./api/tray')];
150+
delete require.cache[require.resolve('./api/webContents')];
151+
delete require.cache[require.resolve('./api/globalShortcut')];
152+
delete require.cache[require.resolve('./api/shell')];
153+
delete require.cache[require.resolve('./api/screen')];
154+
delete require.cache[require.resolve('./api/clipboard')];
155+
delete require.cache[require.resolve('./api/browserView')];
156+
});
157+
113158
global['electronsocket'] = socket;
114159
global['electronsocket'].setMaxListeners(0);
115-
console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());
160+
console.log(chalk.bold.bgCyan('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date()));
116161

117162
appApi = require('./api/app')(socket, app);
118163
browserWindows = require('./api/browserWindows')(socket, app);
@@ -130,6 +175,8 @@ function startSocketApiBridge(port) {
130175
clipboard = require('./api/clipboard')(socket);
131176
browserView = require('./api/browserView')(socket);
132177

178+
179+
133180
try {
134181
const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');
135182

@@ -139,7 +186,7 @@ function startSocketApiBridge(port) {
139186
hostHook.onHostReady();
140187
}
141188
} catch (error) {
142-
console.log(error.message);
189+
console.log(chalk.bold.red(error.message));
143190
}
144191
});
145192
}
@@ -153,7 +200,7 @@ function isModuleAvailable(name) {
153200
}
154201

155202
function startAspCoreBackend(electronPort) {
156-
if(manifestJsonFile.aspCoreBackendPort) {
203+
if (manifestJsonFile.aspCoreBackendPort) {
157204
startBackend(manifestJsonFile.aspCoreBackendPort)
158205
} else {
159206
// hostname needs to be localhost, otherwise Windows Firewall will be triggered.
@@ -175,10 +222,37 @@ function startAspCoreBackend(electronPort) {
175222

176223
let binFilePath = path.join(currentBinPath, binaryFile);
177224
var options = { cwd: currentBinPath };
178-
apiProcess = process(binFilePath, parameters, options);
225+
apiProcess = cProcess(binFilePath, parameters, options);
179226

180227
apiProcess.stdout.on('data', (data) => {
181228
console.log(`stdout: ${data.toString()}`);
182229
});
183230
}
184231
}
232+
233+
function startAspCoreBackendWithWatch(electronPort) {
234+
if (manifestJsonFile.aspCoreBackendPort) {
235+
startBackend(manifestJsonFile.aspCoreBackendPort)
236+
} else {
237+
// hostname needs to be localhost, otherwise Windows Firewall will be triggered.
238+
portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
239+
startBackend(electronWebPort);
240+
});
241+
}
242+
243+
function startBackend(aspCoreBackendPort) {
244+
console.log('ASP.NET Core Watch Port: ' + aspCoreBackendPort);
245+
loadURL = `http://localhost:${aspCoreBackendPort}`;
246+
const parameters = ['watch', 'run', `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`];
247+
248+
var options = {
249+
cwd: currentBinPath,
250+
env: process.env,
251+
};
252+
apiProcess = cProcess('dotnet', parameters, options);
253+
254+
apiProcess.stdout.on('data', (data) => {
255+
console.log(chalk.bold.blue(`${data.toString()}`));
256+
});
257+
}
258+
}

0 commit comments

Comments
 (0)