1. Install Angular 9 CLI

node -v
v12.16.1

npm -v
6.13.4

npm install -g @angular/cli
ng version
Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.900.7
@angular-devkit/core         9.0.7
@angular-devkit/schematics   9.0.7
@schematics/angular          9.0.7
@schematics/update           0.900.7
rxjs                         6.5.3

2. Create an empty Angular project

ng new ng-elk   # use scss
ng serve        # this runs on http://localhost:4200 

3. Install Electron package

npm install --save-dev electron
npm install --save-dev electron-builder
npm install --save-dev electron-reload
npm install --save-dev wait-on
npm install --save-dev npm-run-all

4. Fix some setting in angular.json

a). Change outputPath from "dist/ng-elk" to "dist"

b). prepare some more icon files for more flexibility.

c). disable test spec files generation.

{
  "projects": {
    "ng-elk": {
      "schematics": {
        "@schematics/angular:class": {
          "skipTests": true
        },
        "@schematics/angular:component": {
          "style": "scss",
          "skipTests": true
        },
        "@schematics/angular:directive": {
          "skipTests": true
        },
        "@schematics/angular:guard": {
          "skipTests": true
        },
        "@schematics/angular:interceptor": {
          "skipTests": true
        },
        "@schematics/angular:module": {
          "skipTests": true
        },
        "@schematics/angular:pipe": {
          "skipTests": true
        },
        "@schematics/angular:service": {
          "skipTests": true
        }
      },
      "architect": {
        "build": {
          "options": {
            "outputPath": "dist",
            "assets": [
              "src/assets",
              "src/favicon.ico",
              "src/favicon.png",
              "src/favicon.icns",
              "src/favicon.256x256.png",
              "src/favicon.512x512.png"
            ],

5. Add keyword "main" to package.json

{
  "name": "ng-elk",
  "version": "1.0.0",
  "description": "Angular with Electron",
  "author": "abc.com",
  "icon": "favicon.png",
  "main": "main.js",
}

6. Add a electron cmd in package.json

  "scripts": {
    "postinstall": "electron-builder install-app-deps",
    "start": "npm-run-all -p elk:serve ng:serve",
    "ng": "ng",
    "ng:serve": "ng serve",
    "elk:serve": "wait-on http-get://localhost:4200/ && electron . --serve",
    "build:win": "ng build --prod && electron-builder build --windows",
    "build:lnx": "ng build --prod && electron-builder build --linux",
    "build:mac": "ng build --prod && electron-builder build --mac",

7. Fix base href of index.html

  <base href="./">		<!-- add a dot before slash -->

8. Create a main.js file

const { app, BrowserWindow, screen } = require('electron')
const url = require("url");
const path = require("path");

const args = process.argv.slice(1),
    serve = args.some(val => val === '--serve');

let mainWindow

function createWindow() {
    const electronScreen = screen;
    const size = electronScreen.getPrimaryDisplay().workAreaSize;

    mainWindow = new BrowserWindow({
        x: 0,
        y: 0,
        width: size.width,
        height: size.height,
        webPreferences: {
            nodeIntegration: true,
            allowRunningInsecureContent: (serve) ? true : false,
        },
    });

    if (serve) {
        require('electron-reload')(__dirname, {
            electron: require(`${__dirname}/node_modules/electron`)
        });
        mainWindow.loadURL('http://localhost:4200');
    } else {
        mainWindow.loadURL(url.format({
            pathname: path.join(__dirname, 'dist/index.html'),
            protocol: 'file:',
            slashes: true
        }));
    }
    // Open the DevTools.
    // mainWindow.webContents.openDevTools()

    mainWindow.on('closed', function () {
        mainWindow = null
    })
}
app.allowRendererProcessReuse = true;
app.on('ready', createWindow)

app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') app.quit()
})

app.on('activate', function () {
    if (mainWindow === null) createWindow()
})

9. create a electron-builder.json file

{
    "productName": "ng-elk",
    "directories": {
        "output": "release/"
    },
    "files": [
        "**/*",
        "!**/*.ts",
        "!*.code-workspace",
        "!LICENSE.md",
        "!package.json",
        "!package-lock.json",
        "!src/",
        "!e2e/",
        "!hooks/",
        "!angular.json",
        "!_config.yml",
        "!karma.conf.js",
        "!tsconfig.json",
        "!tslint.json"
    ],
    "win": {
        "icon": "src/favicon.png",
        "target": [
            "portable"
        ]
    },
    "mac": {
        "icon": "src/favicon.png",
        "target": [
            "dmg"
        ]
    },
    "linux": {
        "icon": "src/favicon.png",
        "target": [
            "AppImage"
        ],
        "category": "Utility"
    }
}

10. Run as an Electron desktop app

npm start                   # dev mode
npm run build:win           # build binary

PS. Fix some issues

a. change permission for chrome-sandbox

#for npm serve on linux
sudo chown root:root node_modules/electron/dist/chrome-sandbox
sudo chmod 4755 node_modules/electron/dist/chrome-sandbox

#for npm run build:lnx on linux
./release/ng-elk-1.0.0.AppImage --no-sandbox

b. fix max file reached issue

# Error: ENOSPC: System limit for number of file watchers reached
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p