Javascript en el Escritorio con Electron

Originalmente fabricado por GitHub, la red social para desarroladores y repositorios de software que fué adquirida por Microsoft, Electron consiste en un marco de trabajo que posibilita aplicaciones de escritorio multiplataforma (Windows, macOS, Linux) desarrolladas con el concepto Web, es decir, usando javascript, html y css. La filosofía de esta herramienta puede resumirse en lo que se cita en su sitio:

“Si puedes crear un sitio web, puedes crear una aplicación de escritorio”

Personalmente considero que requiere un salto, aspectos de conocimiento en desarrollo de software que deben cubrirse, pero en el sentido práctico para casos muy simples se cumple la cita, y en definitiva se aprovecha el conocimiento cuando se fabrica software familiarizado con javascript y Node.js. Electron se basa en un navegador Chromium embebido de un modo eficiente y ligero para cada sistema de escritorio (Windows, macOS, Linux) a 64bits, combinando un entorno Node.js que usa el motor V8 y dónde javascript juega el rol principal como lo veremos a continuación. Visual Studio Code (o VSCode) es un ejemplo de aplicaciones reconocidas que se basan en Electron.

Primeros Pasos

Para empezar configuramos nuestro package.json, para ello nos ubicamos en una carpeta para el proyecto (por ejemplo: desktop) dónde crearemos este archivo con el siguiente contenido:

{
  "name": "electron-test",
  "version": "1.0.0",
  "description": "Electron test",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "license": "UNLICENSED",
  "devDependencies": {
    "electron": "^7.1.2"
  }
}

Lo más relevante es identificar el elemento main que corresponde a nuestro programa principal y el “script” para start. Luego abrimos una terminal y ubicados desde nuestra carpeta se ejecuta:

npm install

También podría iniciarse de modo rápido clonando el proyecto electron-quick-start del repositorio de GitHub pero un paso a paso puede ser útil para el aprendizaje.

Nuestro programa principal main.js puede iniciar básicamente con 14 líneas de código, veamos:

const { app, BrowserWindow } = require('electron')
let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({ width: 900, height: 500 })  // define Window
  mainWindow.loadFile('index.html')                            // to load WebApp
  mainWindow.on('closed', function () { mainWindow = null })   // when is closed
}

app.on('ready', createWindow)

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

app.on('activate', function () {
  if (mainWindow === null) createWindow()  // to re-create a window on macOS
})

El archivo de plantilla (./index.html) que se carga puede tener el siguiente contenido:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src: 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'">
    <meta http-equiv="X-Content-Security-Policy" content="default-src: 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'">
    <title>Hi there!</title>
  </head>
  <body>
    <h1>Hi there!</h1>
  </body>
</html>

Para lanzar la aplicación se ejecutaría:

npm start

El primer proyecto

Para algo más elaborado usaremos el último código del documento titulado Javascript para Alojamiento Web, tomando o copiando la carpeta de archivos distribuibles (web) del último proyecto propuesto. El index.html anterior se puede borrar (puesto que usaremos la versión que se encuentra en web) y debe cumplirse la siguiente distribución:

Al reubicar los archivos deben revisarse las rutas, por lo que debe buscarse el impacto en las invocaciones a los programas. Por tanto, en el archivo main.js se debe reemplazar la línea que carga el archivo index.html (mainWindow.loadFile('index.html')) por:

  mainWindow.loadFile('web/index.html')

Nuestro archivo package.json seguiría intacto. Ahora simplemente nos ubicamos en la carpeta del proyecto dentro de una terminal para instalar los paquetes y lanzar la aplicación así:

npm install
npm start

Para implementar algo más estándar en el archivo main.js resulta conveniente reemplazar la línea mainWindow.loadFile('web/index.html') por:

  mainWindow.autoHideMenuBar = true
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'web', 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

Lo anterior implica que también se incorpore al inicio de main.js lo siguiente:

const path = require('path')
const url = require('url')

La librería path permite gestionar las rutas del sistema de modo apropiado, mientras url lo hace con direcciones web. mainWindow.autoHideMenuBar = true oculta la barra de menu de modo automático.

Prueba ahora cerrando la ventana o el proceso e iniciándolo de nuevo con npm start (recordando guardar los cambios).

El ejercicio realizado se basa en archivos o recursos estáticos, pero es posible usar el concepto de WebView dentro del archivo de plantilla html, el cual consiste en visualizar una aplicación web embebida (como un navegador que gestiona envío de peticiones http o https), de ese modo un proyecto operaría en red pero debe estar diseñado para ello.

Generando el paquete ejecutable

Dado que un usuario no va a usar el comando npm start, es posible generar un ejecutable con herramientas como electron-packager (u otra similar). Instalemos entonces este paquete así:

npm install electron-packager --save-dev

Se edita el archivo package.json para incuir bajo el elemento scripts a make-win, make-mac y make-lin, así:

  "scripts": {
    "start": "electron .",
    "make-win": "electron-packager . electron-test --overwrite --asar --platform=win32 --arch=ia32 --prune=true --out=build --version-string.CompanyName=Owner --version-string.FileDescription=ElectronTest --version-string.ProductName=\"Desktop DEMO\"",
    "make-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --prune=true --out=build",
    "make-lin": "electron-packager . electron-test --overwrite --asar --platform=linux --arch=x64 --prune=true --out=build"
  },

Ahora simplemente para obtener el ejecutable se invoca el script correspondiente para nuestro sistema operativo, por ejemplo:

npm run make-win

Habiendo comprendido lo anterior, ahora puedes investigar sobre cómo hacer un instalador. Como información adicional, existe herramienta electron-forge que utiliza internamente electron-packager y soporta webpack para un proyecto desde cero (usando su propia cli), los instaladores hacen parte del concepto que denominan makers (que se refiere a sus multiples opciones). Personalmente, pienso que la elección de la herramienta varía según el nivel de control que desea tenerse sobre el proyecto, pero para muchos proyectos puede resultar más sencillo y completo el ciclo de desarrollo de aplicaciones bajo Electron usando electron-forge, lo cual implicaría haber iniciado del siguiente modo:

npm install -g @electron-forge/cli
electron-forge init electron-test
cd electron-test
electron-forge make

Cumplido el objetivo de introducir el concepto sobre el uso de Javascript en el desarrollo de aplicaciones para el escritorio (Windows, macOS, Linux), queda la tarea de aplicarlo en tu propio proyecto o en alguno que se requiera.


© 2019 by César Arcila