Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live reload #390

Merged
merged 5 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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