====== Guida operativa per creare applicazioni npm desktop con Electron, Vite ed electron-builder ======
Base di partenza: configurazione `package.json` in stile LibrePM
===== 1. Scopo della guida =====
Questa guida spiega come usare una configurazione npm + Electron + Vite + electron-builder simile a quella del progetto LibrePM per creare nuove applicazioni desktop che:
* si sviluppano con tooling npm moderno
* usano una UI web locale renderizzata in Electron
* generano build desktop multipiattaforma
* producono anche pacchetti **.deb** installabili su Debian/Ubuntu e derivate in modo ordinato
* possono includere anche risorse esterne, ad esempio un backend `.jar`, binari, template, file statici o altri asset di runtime
L’obiettivo non è solo “far partire Electron”, ma usare una struttura **replicabile per nuovi progetti** con una pipeline di build pulita, comprensibile e pronta per crescere.
===== 2. Cosa fa questa configurazione e perché è una buona base =====
Il `package.json` di LibrePM implementa una pipeline molto concreta:
* Vite costruisce il frontend
* Electron avvia l’app desktop
* electron-builder impacchetta l’app per macOS, Windows e Linux
* la build desktop include anche risorse extra esterne al bundle frontend
* su Linux sono esplicitati i target `AppImage`, `snap` e `deb` (è possibile il `flatpak` ma non è molto stabile)
* per Linux viene personalizzato anche il blocco `desktop`, utile per il file `.desktop` così da permettere l'inserimento di icone e altri oggetti utili alla build
* in LibrePM la build completa lancia prima il backend Java con `gradlew bootJar`, poi il frontend Vite, poi il packaging Electron
* NOTA BENE: il backend Java può essere sostituito con uno Python/Go/altri linguaggi a scelta, purché si espliciti nel pacchetto e si includa poi tutto il necessario
In pratica è una soluzione ideale quando vuoi costruire una **desktop app npm-based** ma con capacità più enterprise rispetto a una semplice web app.
===== 3. Anatomia della configurazione =====
Di seguito i punti più importanti della configurazione di partenza e il loro significato operativo
==== 3.1 Metadati di base ====
```json
{
"name": "nometuaapp-desktop",
"version": "0.1.0",
"description": "NomeTuaApp - Descrizione",
"type": "module",
"main": "electron/main.cjs"
}
```
==== Perché conta ====
* `name` influenza il nome tecnico del pacchetto
* `version` viene usata anche negli artefatti prodotti
* `description` aiuta packaging e identificazione
* `type: "module"` abilita l’uso di ESM nel progetto Node lato tooling
* `main` indica l’entrypoint Electron principale
==== Nota importante ====
In questa configurazione il progetto usa ESM a livello package ma il processo principale Electron è in CommonJS con file `.cjs`
È una scelta pratica e sensata quando vuoi:
* tooling moderno lato frontend
* meno attrito nel bootstrap Electron
* compatibilità elevata con dipendenze e script di runtime
===== 4. Gli script che rendono la pipeline riusabile =====
**Script presenti**
```json
"scripts": {
"dev": "concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"",
"dev:renderer": "vite",
"dev:electron": "wait-on http://localhost:5173 && electron .",
"build": "vite build",
"dist": "cd .. && ./gradlew bootJar && cd desktop && vite build && electron-builder",
"preview": "vite preview"
}
```
**Logica operativa**
* `dev` avvia frontend Vite ed Electron insieme
* `wait-on` evita che Electron parta prima che il renderer sia disponibile
* `dist` esegue la pipeline completa con:
- build del backend
- build del frontend
- packaging desktop
**Perché questa impostazione è utile anche per nuovi progetti**
Per nuove app puoi mantenere lo stesso schema anche se:
* non hai backend Java
* hai un backend Node separato
* vuoi solo una desktop app frontend-only
Ti basta sostituire la parte centrale della pipeline
Ad esempio:
```json
"dist": "vite build && electron-builder"
```
oppure
```json
"dist": "npm run build:backend && vite build && electron-builder"
```
===== 5. Quando usare questa architettura =====
Usa questa struttura quando vuoi realizzare:
* gestionali desktop
* dashboard locali con database locale o backend embedded
* software offline-first
* tool professionali distribuiti come installer
* applicazioni che devono installarsi come programma “vero” su Linux, Windows e macOS
Non è invece la scelta più leggera se vuoi solo:
* una SPA da deployare su hosting web
* una utility CLI
* una libreria npm
===== 6. Struttura progetto consigliata =====
Una struttura chiara per replicare questa configurazione è la seguente
```text
my-app/
├─ desktop/
│ ├─ electron/
│ │ ├─ main.cjs
│ │ └─ preload.cjs
│ ├─ src/
│ │ ├─ assets/
│ │ │ ├─ icon.png
│ │ │ ├─ icon.ico
│ │ │ └─ icon.icns
│ │ ├─ App.jsx
│ │ └─ main.jsx
│ ├─ dist/
│ ├─ dist-electron/
│ ├─ index.html
│ ├─ package.json
│ └─ vite.config.js
├─ backend/
│ └─ ... eventuale backend
└─ build/
└─ libs/
└─ my-backend.jar
```
**Osservazione importante**
Nel caso di LibrePM, la cartella `desktop/` vive dentro un progetto più grande e la build desktop pesca il backend da:
```json
"from": "../build/libs"
```
Questa è una soluzione ottima per applicazioni ibride in cui il pacchetto desktop deve incorporare un backend compilato altrove.
===== 7. Dipendenze principali e loro ruolo =====
**Dipendenze runtime**
Nel progetto di partenza sono presenti React, i18n, Bootstrap, charting, drag and drop, datepicker, gantt e altri componenti di UI
Per nuove app non devi copiarle tutte: devi selezionare solo ciò che serve.
**Dev dependencies essenziali per replicare la pipeline**
```bash
npm install -D electron electron-builder vite @vitejs/plugin-react concurrently wait-on
npm install react react-dom
```
Aggiungi poi solo i pacchetti necessari per la tua UI.
===== 8. File minimi per partire =====
==== 8.1 `electron/main.cjs` ====
```js
const { app, BrowserWindow } = require('electron')
const path = require('path')
const isDev = !app.isPackaged
function createWindow() {
const win = new BrowserWindow({
width: 1400,
height: 900,
webPreferences: {
preload: path.join(__dirname, 'preload.cjs'),
contextIsolation: true,
nodeIntegration: false
}
})
if (isDev) {
win.loadURL('http://127.0.0.1:5173')
} else {
win.loadFile(path.join(__dirname, '../dist/index.html'))
}
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
```
==== 8.2 `electron/preload.cjs` ====