Skip to content

Commit

Permalink
Merge pull request #390 from syedadeel2/LiveReload
Browse files Browse the repository at this point in the history
Live reload
  • Loading branch information
GregorBiswanger authored May 1, 2020
2 parents 71955b9 + 01d938f commit 80d160a
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 66 deletions.
2 changes: 1 addition & 1 deletion ElectronNET.API/ElectronNET.API.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>..\artifacts</PackageOutputPath>
<PackageId>ElectronNET.API</PackageId>
Expand Down
20 changes: 16 additions & 4 deletions ElectronNET.API/WebHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Hosting;
using System;
using System.IO;

namespace ElectronNET.API
{
Expand All @@ -22,16 +23,27 @@ public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[]
{
BridgeSettings.SocketPort = argument.ToUpper().Replace("/ELECTRONPORT=", "");
Console.WriteLine("Use Electron Port: " + BridgeSettings.SocketPort);
} else if(argument.ToUpper().Contains("ELECTRONWEBPORT"))
}
else if (argument.ToUpper().Contains("ELECTRONWEBPORT"))
{
BridgeSettings.WebPort = argument.ToUpper().Replace("/ELECTRONWEBPORT=", "");
}
}

if(HybridSupport.IsElectronActive)
if (HybridSupport.IsElectronActive)
{
builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
.UseUrls("http://127.0.0.1:" + BridgeSettings.WebPort);
// check for the content folder if its exists in base director otherwise no need to include
// It was used before because we are publishing the project which copies everything to bin folder and contentroot wwwroot was folder there.
// now we have implemented the live reload if app is run using /watch then we need to use the default project path.
if (Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}\\wwwroot"))
{
builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
.UseUrls("http://localhost:" + BridgeSettings.WebPort);
}
else
{
builder.UseUrls("http://localhost:" + BridgeSettings.WebPort);
}
}

return builder;
Expand Down
13 changes: 12 additions & 1 deletion ElectronNET.CLI/Commands/StartElectronCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ public Task<bool> ExecuteAsync()
var platformInfo = GetTargetPlatformInformation.Do(string.Empty, string.Empty);
string tempBinPath = Path.Combine(tempPath, "bin");
var resultCode = ProcessHelper.CmdExecute($"dotnet publish -r {platformInfo.NetCorePublishRid} --output \"{tempBinPath}\" /p:PublishReadyToRun=true --no-self-contained", aspCoreProjectPath);
var resultCode = 0;
if (parser != null && !parser.Arguments.ContainsKey("watch"))
{
resultCode = ProcessHelper.CmdExecute($"dotnet publish -r {platformInfo.NetCorePublishRid} --output \"{tempBinPath}\" /p:PublishReadyToRun=true --no-self-contained", aspCoreProjectPath);
}
if (resultCode != 0)
{
Expand Down Expand Up @@ -110,13 +115,19 @@ public Task<bool> ExecuteAsync()
arguments += " --clear-cache=true";
}
if (parser.Arguments.ContainsKey("watch"))
{
arguments += " --watch=true";
}
string path = Path.Combine(tempPath, "node_modules", ".bin");
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWindows)
{
Console.WriteLine("Invoke electron.cmd - in dir: " + path);
ProcessHelper.CmdExecute(@"electron.cmd ""..\..\main.js"" " + arguments, path);
}
else
{
Expand Down
9 changes: 7 additions & 2 deletions ElectronNET.CLI/ElectronNET.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>

<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>dotnet-electronize</AssemblyName>
<ToolCommandName>electronize</ToolCommandName>

Expand All @@ -29,6 +29,11 @@
<PackageReleaseNotes>Changelog: https://github.com/ElectronNET/Electron.NET/blob/master/Changelog.md</PackageReleaseNotes>
<PackageIcon>PackageIcon.png</PackageIcon>
<PackAsTool>true</PackAsTool>
<StartupObject></StartupObject>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>

<ItemGroup>
Expand All @@ -37,7 +42,7 @@
</ItemGroup>

<ItemGroup>
<None Include="PackageIcon.png" Pack="true" PackagePath="\"/>
<None Include="PackageIcon.png" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion ElectronNET.CLI/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"ElectronNET.CLI": {
"commandName": "Project",
"commandLineArgs": "build \"C:\\Users\\Gregor\\Documents\\Visual Studio 2017\\Projects\\ElectronNET\\ElectronNET.WebApp\""
"commandLineArgs": "start /project-path \"C:\\Users\\Rizvi\\source\\repos\\Electron.NET\\ElectronNET.WebApp\" /watch"
}
}
}
17 changes: 16 additions & 1 deletion ElectronNET.Host/api/browserWindows.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 18 additions & 2 deletions ElectronNET.Host/api/browserWindows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const path = require('path');
const windows: Electron.BrowserWindow[] = [];
let readyToShowWindowsIds: number[] = [];
let window, lastOptions, electronSocket;

let mainWindowURL;
export = (socket: SocketIO.Socket, app: Electron.App) => {
electronSocket = socket;
socket.on('register-browserWindow-ready-to-show', (id) => {
Expand Down Expand Up @@ -199,7 +199,16 @@ export = (socket: SocketIO.Socket, app: Electron.App) => {
options = { ...options, webPreferences: { nodeIntegration: true } };
}

window = new BrowserWindow(options);
// we dont want to recreate the window when watch is ready.
if (app.commandLine.hasSwitch('watch') && app['mainWindowURL'] === loadUrl) {
window = app['mainWindow'];
if (window) {
window.reload();
}
} else {
window = new BrowserWindow(options);
}

window.on('ready-to-show', () => {
if (readyToShowWindowsIds.includes(window.id)) {
readyToShowWindowsIds = readyToShowWindowsIds.filter(value => value !== window.id);
Expand Down Expand Up @@ -245,6 +254,12 @@ export = (socket: SocketIO.Socket, app: Electron.App) => {
console.log('auto clear-cache active for new window.');
}

// set main window url
if (app['mainWindowURL'] == undefined || app['mainWindowURL'] == "") {
app['mainWindowURL'] = loadUrl;
app['mainWindow'] = window;
}

windows.push(window);
electronSocket.emit('BrowserWindowCreated', window.id);
});
Expand Down Expand Up @@ -744,6 +759,7 @@ export = (socket: SocketIO.Socket, app: Electron.App) => {
getWindowById(id).setBrowserView(browserView);
});


function getWindowById(id: number): Electron.BrowserWindow {
for (let index = 0; index < windows.length; index++) {
const element = windows[index];
Expand Down
100 changes: 87 additions & 13 deletions ElectronNET.Host/main.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
const { app } = require('electron');
const { BrowserWindow } = require('electron');
const path = require('path');
const process = require('child_process').spawn;
const cProcess = require('child_process').spawn;
const portscanner = require('portscanner');
const imageSize = require('image-size');
const chalk = require('chalk');
let io, server, browserWindows, ipc, apiProcess, loadURL;
let appApi, menu, dialogApi, notification, tray, webContents;
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
let commandLine, browserView;
let splashScreen, hostHook;
let mainWindowId;

let manifestJsonFileName = 'electron.manifest.json';
if(app.commandLine.hasSwitch('manifest')) {
let watchable = false;
if (app.commandLine.hasSwitch('manifest')) {
manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
};

const currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
const manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
if (app.commandLine.hasSwitch('watch')) {
watchable = true;
};

let currentBinPath = path.join(__dirname.replace('app.asar', ''), 'bin');
let manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);

// if watch is enabled lets change the path
if (watchable) {
currentBinPath = path.join(__dirname, '../../'); // go to project directory
manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
}

const manifestJsonFile = require(manifestJsonFilePath);
if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
const mainInstance = app.requestSingleInstanceLock();
Expand All @@ -42,7 +56,7 @@ app.on('ready', () => {

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

Expand All @@ -62,8 +76,8 @@ function startSplashScreen() {
let imageFile = path.join(currentBinPath, manifestJsonFile.splashscreen.imageFile);
imageSize(imageFile, (error, dimensions) => {
if (error) {
console.log(`load splashscreen error:`);
console.log(error);
console.log(chalk.bold.red(`load splashscreen error:`));
console.log(chalk.bold.red(error));

throw new Error(error.message);
}
Expand Down Expand Up @@ -104,15 +118,46 @@ function startSocketApiBridge(port) {

server.listen(port, 'localhost');
server.on('listening', function () {
console.log('Electron Socket started on port %s at %s', server.address().port, server.address().address);
console.log(chalk.bgGreenBright('Electron Socket started on port %s at %s', server.address().port, server.address().address));
// Now that socket connection is established, we can guarantee port will not be open for portscanner
startAspCoreBackend(port);
if (watchable) {
startAspCoreBackendWithWatch(port);
} else {
startAspCoreBackend(port);
}
});

// prototype
app['mainWindowURL'] = "";
app['mainWindow'] = null;

io.on('connection', (socket) => {

// we need to remove previously cache instances
// otherwise it will fire the same event multiple depends how many time
// live reload watch happen.
socket.on('disconnect', function () {
console.log(chalk.bold.red('Got disconnect!'));
delete require.cache[require.resolve('./api/app')];
delete require.cache[require.resolve('./api/browserWindows')];
delete require.cache[require.resolve('./api/commandLine')];
delete require.cache[require.resolve('./api/autoUpdater')];
delete require.cache[require.resolve('./api/ipc')];
delete require.cache[require.resolve('./api/menu')];
delete require.cache[require.resolve('./api/dialog')];
delete require.cache[require.resolve('./api/notification')];
delete require.cache[require.resolve('./api/tray')];
delete require.cache[require.resolve('./api/webContents')];
delete require.cache[require.resolve('./api/globalShortcut')];
delete require.cache[require.resolve('./api/shell')];
delete require.cache[require.resolve('./api/screen')];
delete require.cache[require.resolve('./api/clipboard')];
delete require.cache[require.resolve('./api/browserView')];
});

global['electronsocket'] = socket;
global['electronsocket'].setMaxListeners(0);
console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());
console.log(chalk.bold.bgCyan('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date()));

appApi = require('./api/app')(socket, app);
browserWindows = require('./api/browserWindows')(socket, app);
Expand All @@ -130,6 +175,8 @@ function startSocketApiBridge(port) {
clipboard = require('./api/clipboard')(socket);
browserView = require('./api/browserView')(socket);



try {
const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');

Expand All @@ -139,7 +186,7 @@ function startSocketApiBridge(port) {
hostHook.onHostReady();
}
} catch (error) {
console.log(error.message);
console.log(chalk.bold.red(error.message));
}
});
}
Expand All @@ -153,7 +200,7 @@ function isModuleAvailable(name) {
}

function startAspCoreBackend(electronPort) {
if(manifestJsonFile.aspCoreBackendPort) {
if (manifestJsonFile.aspCoreBackendPort) {
startBackend(manifestJsonFile.aspCoreBackendPort)
} else {
// hostname needs to be localhost, otherwise Windows Firewall will be triggered.
Expand All @@ -175,10 +222,37 @@ function startAspCoreBackend(electronPort) {

let binFilePath = path.join(currentBinPath, binaryFile);
var options = { cwd: currentBinPath };
apiProcess = process(binFilePath, parameters, options);
apiProcess = cProcess(binFilePath, parameters, options);

apiProcess.stdout.on('data', (data) => {
console.log(`stdout: ${data.toString()}`);
});
}
}

function startAspCoreBackendWithWatch(electronPort) {
if (manifestJsonFile.aspCoreBackendPort) {
startBackend(manifestJsonFile.aspCoreBackendPort)
} else {
// hostname needs to be localhost, otherwise Windows Firewall will be triggered.
portscanner.findAPortNotInUse(electronPort + 1, 65535, 'localhost', function (error, electronWebPort) {
startBackend(electronWebPort);
});
}

function startBackend(aspCoreBackendPort) {
console.log('ASP.NET Core Watch Port: ' + aspCoreBackendPort);
loadURL = `http://localhost:${aspCoreBackendPort}`;
const parameters = ['watch', 'run', `/electronPort=${electronPort}`, `/electronWebPort=${aspCoreBackendPort}`];

var options = {
cwd: currentBinPath,
env: process.env,
};
apiProcess = cProcess('dotnet', parameters, options);

apiProcess.stdout.on('data', (data) => {
console.log(chalk.bold.blue(`${data.toString()}`));
});
}
}
Loading

0 comments on commit 80d160a

Please sign in to comment.