====== 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` ====