Empacotamento de Aplicações Angular com Electron

 

Uma das últimas fases do processo de desenvolvimento envolve o empacotamento da aplicação, no caso do Angular podemos usar o Electron para tal feito, um framework para aplicações Web extremamente utilizado, sendo base do Chromium, Visual Studio Code, Spotify, etc.

Esse processo muitas vezes pode ser doloroso já que qualquer erro pode inviabilizar o feito, este artigo portanto tratará de fornecer uma série de instruções e arquivos para realizar essa etapa.

É importante salientar que esse documento abordará somente o empacotamento para a plataforma Windows.

As seguintes fontes são extremamente completas e podem ser usadas para melhor compreensão do assunto:

Compreendendo a estrutura

Como durante o processo serão necessárias algumas mudanças entre os diretórios é importante deixar claro desde o início como a aplicação está estruturada:

Main_Directory
└── Angular_Application
    ├── dist
    ├── e2e
    ├── node_modules
    ├── src
    ├── angular.json
    ├── browserslist
    ├── karma.conf.js
    ├── package-lock.json
    ├── package.json
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.spec.json
    └── tslint.json

Utilizarei um Main_Directory no qual todos os procedimentos serão feitos. No momento é somente necessário que a pasta da aplicação Angular, no meu caso nomeada de Angular_Application, esteja presente. Os nomes dos diretórios podem ser tranquilamente trocados conforme desejado, os escolhidos aqui são somente de cunho ilustrativo.

Instalando as dependências

No diretório Angular_Application instale as seguintes dependências:

$ npm i electron --save 
$ npm i electron-squirrel-startup --save
$ npm i -g electron-packager

Verifique se os pacotes foram realmente adicionados na parte de dependencies no arquivo package.json, durante minhas tentativas provavelmente por culpa de algum bug eles foram adicionados em devDependencies. A seguir está ilustrado como o arquivo deve estar:

{
  ...
  "dependencies": {
    ...
    "electron": "^9.0.0",
    "electron-squirrel-startup": "1.0.0"
  },
  ...
}

As versões listadas podem mudar conforme a data de instalação.

Criando o arquivo main.js

Este arquivo é de extrema importância e contém todas as instruções do Electron e de como os procedimentos de instalação, remoção, update devem ocorrer.

Na raiz da pasta Angular_Application crie um arquivo chamado main.js com o seguinte conteúdo:

const { app, BrowserWindow } = require('electron')
let win;
let MenuPrincipal = null;
if (require('electron-squirrel-startup')) return;
if (handleSquirrelEvent()) {
    return;
}
function handleSquirrelEvent() {
    if (process.argv.length === 1) {
        return false;
    }
    const ChildProcess = require('child_process');
    const path = require('path');
    const appFolder = path.resolve(process.execPath, '..');
    const rootAtomFolder = path.resolve(appFolder, '..');
    const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));

    const exeName = path.basename(process.execPath);
    const spawn = function (command, args) {
        let spawnedProcess, error;
        try {
            spawnedProcess = ChildProcess.spawn(command, args, { detached: true });
        } catch (error) { }
        return spawnedProcess;
    };
    const spawnUpdate = function (args) {
        return spawn(updateDotExe, args);
    };
    const squirrelEvent = process.argv[1];
    switch (squirrelEvent) {
        case '--squirrel-install':
        case '--squirrel-updated':
            spawnUpdate(['--createShortcut', exeName]);
            setTimeout(app.quit, 1000);
            return true;
        case '--squirrel-uninstall':
            spawnUpdate(['--removeShortcut', exeName]);
            setTimeout(app.quit, 1000);
            return true;
        case '--squirrel-obsolete':
            app.quit();
            return true;
    }
};
function createWindow() {
    win = new BrowserWindow({
        width: 1280,
        height: 720,
        backgroundColor: '#ffffff',
        //Trocar caminho para corresponder ao ícone da aplicação
        icon: `${ __dirname}/dist/Angular_Application/assets/img/logo.png`
    })
    win.setMenu(MenuPrincipal);
    //Trocar caminho para corresponder ao index.html da aplicação
    win.loadFile('dist/Angular_Application/index.html')
    win.on('closed', function () {
        win = null
    })
}
app.on('ready', createWindow)

Provavelmente a única função que voce terá que se preocupar é a createWindow(), já que nela será necessário trocar o caminho para o ícone e do index.html para corresponder ao da sua aplicação

O Electron possue uma barra de ferramentas, na qual é possível abrir o painel do desenvolvedor por exemplo, caso não deseje esconder essa barra comente as seguintes linhas:

let MenuPrincipal = null
win.setMenu(MenuPrincipal)

Adicionando scripts

Ainda na raiz do projeto, em Angular_Application, edite o arquivo package.json para conter:

{
  ...
  "main": "main.js",
  "scripts": {
    ...
    "electron": "electron .",
    "electron:build": "ng build --base-href ./ && electron .",
    "package:win": "electron-packager . --platform=win32 --arch=ia32 --executable-name=\"Angular_Application\" --win32metadata ProductName=\"Angular_Application\" --win32m etadata.CompanyName=\"Company\" --asar --overwrite"
  },
  ...
}

Lembre-se de trocar o Angular_Application e Company para o nome desejado.

O objetivo aqui é primeiro informar para o node.js que esse o main.js existe, o restante do conteúdo em scripts provêm uma série de comandos úteis, sendo:

  • electron permite executar a aplicação sem compilar novamente o Electron;
  • electron:build compila a aplicação e depois executa o Electron;
  • package:win cria um executável do Angular utilizando o Electron.

Modificando index.html

Localize o arquivo index.html, encontrado dentro da pasta src.

Main_Directory
└── Angular_Application
    ├── dist
    ├── e2e
    ├── node_modules
    ├── src
    │   └── index.html
    ├── angular.json
    ├── browserslist
    ├── karma.conf.js
    ├── main.js
    ├── package-lock.json
    ├── package.json
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.spec.json
    └── tslint.json

Edite o mesmo para que contenha um “.” em href:

<head>
    ...
    <base href="./">
    ...
</head>

Usando o electron-packager

Em Angular_Application, execute o seguinte comando no terminal:

$ npm run electron:build

Caso tudo tenha sido feito corretamente uma janela do Electron com a aplicação deve ter aparecido, se isso aconteceu pode-se prosseguir, caso contrário recomenda-se verificar se o nome de todos os campos foram ajustados corretamente.

Prossiga para criar o executável através do terminal, com o comando:

$ npm run package:win

Surgirá uma pasta nova contendo o executável do Electron e todas as dependências necessárias para o mesmo, no meu caso angular_application-win32-ia32, a pasta pode ser visualizada abaixo:

Main_Directory
└── Angular_Application
    ├── angular_application-win32-ia32
    ├── dist
    ├── e2e
    ├── node_modules
    ├── src
    ├── angular.json
    ├── browserslist
    ├── karma.conf.js
    ├── main.js    
    ├── package-lock.json
    ├── package.json
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.spec.json
    └── tslint.json

A aplicação nesse ponto já está totalmente isolada e funcionando com o Electron, porém para um programa mais profissional pode-se continuar o tutorial para a criação de um único executável .exe

Instalando o electron-winstaller

Crie agora uma pasta chamada Electron dentro de Main_Directory:

Main_Directory
├── Angular_Application
│   ├── angular_application-win32-ia32
│   ├── dist
│   ├── e2e
│   ├── node_modules
│   ├── src
│   ├── angular.json
│   ├── browserslist
│   ├── karma.conf.js
│   ├── main.js
│   ├── package-lock.json
│   ├── package.json
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.spec.json
│   └── tslint.json
└── Electron

Dentro da pasta Electron inicie um projeto node e instale o electron-winstaller, executando os comandos:

$ npm init
$ npm i electron-winstaller --save-dev

Copie a pasta criada no tópico anterior, no meu caso angular_application-win32-ia32 para dentro do diretório Electron, a sua estrutura deve ser basicamente essa:

Main_Directory
├── Angular_Application
│   ├── dist
│   ├── e2e
│   ├── node_modules
│   ├── src
│   ├── angular.json
│   ├── browserslist
│   ├── karma.conf.js
│   ├── main.js
│   ├── package-lock.json
│   ├── package.json
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.spec.json
│   └── tslint.json
└── Electron
    ├── angular_application-win32-ia32
    ├── node_modules
    └── package.json

Criando o arquivo build.js

Na pasta Electron crie um arquivo nomeado de build.js, contendo:

const electronInstaller = require('electron-winstaller');
resultPromise = electronInstaller.createWindowsInstaller({
    //Troque o nome da pasta para corresponder a sua aplicação
    appDirectory: './angular_application-win32-ia32',
    outputDirectory: './Installer',
    //Troque o nome dos autores
    authors: 'Company_Name',
    //Troque o nome do executável
    exe: 'Angular_Application.exe',
    description: 'App de controle de pedidos'
});
resultPromise.then(
    () => console.log("Success"),
    (e) => console.log(`Error: ${e.message}`)
);

Compilando o executável

No diretório Electron execute no terminal:

$ node build.js

Caso tudo tenha ocorrido corretamente uma pasta chamada Installer deve ter sido criada contendo o executável .exe para a instalação da aplicação.

Informações Adicionais

Se por acaso no tópico anterior o terminal indicar o erro:

$ node build.js

creating windows installer
Error: Failed with exit code: 1
Output:
Tentando construir o pacote de 'teste.nuspec'.
O caminho especificado, o nome do arquivo ou ambos sao muito longos. O nome de arquivo totalmente qualificado deve ter menos de 260 caracteres e o nome do diretório menos de 248 caracteres.

Recomenda-se habilitar a política [Enable NTFS long paths policy] do Windows, para tal:

  1. Aperte a tecla do Windows, digite gpedit.msc e aperte Enter
  2. Navegue para Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem > NTFS
  3. Dê um duplo clique em Enable NTFS long paths e habilite a opção

Apesar do instalador funcionar corretamente e criar um atalho na área de trabalho, o caminho do mesmo estará errado e não abrirá a aplicação. Recomenda-se verificar o local no qual a aplicação foi instalada e criar o atalho manualmente.