Angular 16 and NeutralinoJS 9.7.0

Erik Hegen
6 min readOct 2, 2023

--

A development guide to archive Frontend Lib development, Hot Reloading and Debugging with Neutralino and its window mode

NeutralinoJS Logo

Preface

When you read this article, I assume that you already know what NeutralinoJS and Angular are, so we will skip the part explaining both in detail.

I am using npm as package manager but of course you can also use yarn or any other package manager of your liking.

Installation

If you haven’t already, install angular according to the guide here.

We start by creating a new Angular Application via ng new and give it a name (in this case, “NeutralinoTest”)

Now, you have a fully functional boilerplate that can be served via ng serve on a port of our liking (default 4200). Don’t forget to navigate to the folder before doing so ;)

Next, we want to install NeutralinoJS.

npm install -g @neutralinojs/neu

I would recommend to install the dependency not global but local so that your coworkers or collaborators are also going to install the dependency (and more importantly: the same version) when they first checkout the repository and use npm i :

npm i @neutralinojs/neu --save-dev

Next we are going to setup a new neutralinoJS application by using the following command. We can place the neutralino application into the angular root folder, that’s not a problem.

neu create neutralino

You can choose any other name than neutralino if you want. The application name can be also be changed later.

Back to Angular, we want to also include the Neutralino JS SDK so that we can use Neutralino’s Native API.

npm i @neutralinojs/lib

Configuration

Neutralino

First of all, we need to configure the NeutralinoJS by editing the neutralino.config.json file.

We set the property port to 5006. This is the port of the websocket connection of the frontend end SDK and the neutralino backend process. You can also choose another port number if you want to.

We add the following to cli property:

"cli": {
...
"frontendLibrary": {
"patchFile": "../src/index.html",
"devUrl": "http://localhost:4200"
}
...
}

This defines the parameters for when we later use Neutralino’s frontend-dev mode. Please use the same port number you plan to use when serving your angular application!

Afterward, you can safely remove this part:

"clientLibrary": "/resources/js/neutralino.js",

Nearly done! The only thing left for the Neutralino part is the start command. And this is the part where it gets a little bit dirty…

To run the Neutralino application with a frontend framework we usually using the following command:

neu run --frontend-lib-dev

But we also want to be able to attach the debugger process of our IDE to Neutralino so that we can make use of remote-debugging and not only rely on the developer tools of the web view.

Unfortunately this is not yet supported by NeutralinoJS. At least for the window mode. Thankfully, WebView2 provides another way to configure the process: via Environment Variables.

Via WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS we can inject some helpful options, like e.g.

--remote-debugging-port=9222

So let’s define a npm script for running the Neutralino application with these parameters:

"scripts": [
"start:neutralino": "cd /<AppName> && set WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=\"--remote-debugging-port=9222\" && neu run --frontend-lib-dev"
]

Please substitute <AppName> with the name you have chosen for your Neutralino application (in our case neutralino ).

Angular

Next we need some more steps for our frontend process to work with Neutralino.

Please go to angular.json projects.<angularAppName>.architect.build.options.assets and add the following glob:

{
"input": "./node_modules/@neutralinojs/lib/dist",
"glob": "neutralino.js",
"output": "/assets/"
}

This copies the sdk js file to the asset folder so that we can load it directly into our index.html file later. This bypasses webpacks tree shaking bundle process and let’s

Next add the following lines to your index.html <head> section:

<script src="http://localhost:5006/__neutralino_globals.js"></script>
<script src="/assets/neutralino.js"></script>

Neutralino generates the file __neutralino_globals.js on the fly and provides it to the frontend process. Via this file the JS SDK receives the parameters for the frontend authentication, websocket port etc.

Last but not least: we’d like to initialize neutralino in the fronend process. Meaning: connecting to the backend process and authenticating.

Therefore we are going to the main.ts and change it to the following:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import * as neutralino from "@neutralinojs/lib";

// Tell typescript that there is a global variable called "Neutralino"
declare global {
const Neutralino: typeof neutralino;
}

async function initNeutralino() {
return new Promise<void>(async resolve => {
console.log('Initializing NeutralinoJS');
await Neutralino.events.on('ready', () => {
console.log('NeutralinoJS is ready to go!');
resolve();
});

Neutralino.init();
});
}

if (document.readyState === 'complete') {
// HMR update: only initialize angular
platformBrowserDynamic().bootstrapModule(AppModule).then();
} else {
document.addEventListener('DOMContentLoaded', async () => {
// Initialize neutralino before initializing angular
await initNeutralino();
await platformBrowserDynamic().bootstrapModule(AppModule);
});
}

We did two importing things:

  1. Initializing the Neutralino SDK before Angular → this way you can already use Neutralino Functions in e.g. APP_INITIALIZER and you don’t need to wait for Neutralino’s initialization
  2. Added support for HMR mode (optional)

Start developing

That’s it! Congrats, you made it to this section without going completely insane about the whole configuration process…

God thing first: you are ready to go and start developing!

Lets start with serving the application. Open two terminals (or alternatively use “concurrently” etc) and enter the start commands:

ng serve
npm run start:neutralino

You can also use the Hot Module Reload mode of angular by running ng serve --hmr instead. But that’s another topic.

Congratulations, you may now enjoy your boilerplate application in a neutralino window.

Now let’s try to use the neutralino SDK. For this purpose we change the whole app.component.html to the following:

<h1>My OS Information</h1>
<div>{{architecture}}</div>
<div>
<pre>{{cpuInfo | json}}</pre>
</div>

And app.component.ts :

import { Component, OnInit } from '@angular/core';
import {computer} from "@neutralinojs/lib";

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {


architecture?: string;
cpuInfo?: computer.CPUInfo;

async ngOnInit() {
this.architecture = await Neutralino.computer.getArch();
this.cpuInfo = await Neutralino.computer.getCPUInfo();
}
}

You will now see an error in the console:

core.mjs:8400  ERROR Error: Uncaught (in promise): Object: {"code":"NE_RT_INVTOKN","message":"Invalid or expired NL_TOKEN value from client"}

That’s because the commands Neutralino.computer.getArch() and Neutralino.computer.getCPUInfo() are not whitelisted, meaning that you are not allowed to run these operations. This is a security mechanism to prevent malicious code injected into your application to run native API calls.

Go and add these commands to the allowList by editing neutralino.config.json accordingly:

"nativeAllowList": [
"app.*",
"os.*",
"computer.*",
"debug.log"
],

You can also write

"nativeAllowList": [
"app.*",
"os.*",
"computer.getArch",
"computer.getCPUInfo",
"debug.log"
],

Debugging

We should now be able to attach to the remove debugging port and set some breakpoints in our app.

For Visual Code edit the launch.json

{
...
"configurations": [
{
"name": "Attach to Chrome",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}"
}
]
...
}

For Webstorm / Rider Jetbrains IDE add a new Debug Configuration Attach to NodeJS/Chrome

Configuration screen of Jetbrains Rider IDE for debugging the neutralino frontend process

You should now be able to add a breakpoint e.g. in the ngOnInit method app.component.ts . Refresh the page in neutralino via F5 and voilá!

Conclusion

NeutralinoJS is a wonderful and very interesting project worth to keep an eye on.

It’s very lightweight and fast, even though the configuration part is still a little bit sloppy.

I hope this article helped someone out there!

Cheers,
Erik

--

--