'init'
This commit is contained in:
67
lib/main/boot/bootloader.ts
Normal file
67
lib/main/boot/bootloader.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { app, BrowserWindow, ipcMain } from "electron";
|
||||
import fs from 'fs/promises'
|
||||
import { WORKING_DIR } from "../constants";
|
||||
import path from "path";
|
||||
import { Logger } from "../logger";
|
||||
|
||||
const logger = Logger('bootloader');
|
||||
|
||||
|
||||
ipcMain.handleOnce('report-boot-process-failed', async () => {
|
||||
/**
|
||||
* Если процесс загрузки не завершился успешно, то preload показывает
|
||||
* экран ошибки, а нам нужно откатиться назад к загрузке dev.html
|
||||
* и удалить скомпилированные файлы, чтобы при следующем запуске
|
||||
* приложение попыталось загрузиться в режиме разработки.
|
||||
*/
|
||||
let filePath = path.join(WORKING_DIR, 'b');
|
||||
await fs.rmdir(filePath, { recursive: true });
|
||||
logger.log("Boot process failed, removed compiled files");
|
||||
logger.log(`Removed compiled files at ${filePath}`);
|
||||
logger.log(`Restarting application in safe mode`);
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
/**
|
||||
* Boot функция, эта функция запускает приложение
|
||||
* @param window окно
|
||||
*/
|
||||
export async function boot(window : BrowserWindow) {
|
||||
if (!app.isPackaged && process.env['ELECTRON_RENDERER_URL']) {
|
||||
await bootDevelopment(window);
|
||||
// await bootProduction(window);
|
||||
window.webContents.openDevTools({ mode: 'detach' });
|
||||
//console.info(window.webContents);
|
||||
return;
|
||||
}
|
||||
//window.webContents.openDevTools({ mode: 'detach' });
|
||||
await bootProduction(window);
|
||||
}
|
||||
|
||||
async function bootProduction(window : BrowserWindow) {
|
||||
logger.log("Booting in production mode");
|
||||
let html = path.join(WORKING_DIR, 'b', 'j.html');
|
||||
if(await existsFile(html)){
|
||||
logger.log(`Loading compiled file`);
|
||||
window.loadFile(html);
|
||||
} else {
|
||||
logger.log(`Loading conatiner file`);
|
||||
window.loadFile(path.join(__dirname, '../renderer/dev.html'));
|
||||
}
|
||||
}
|
||||
|
||||
async function bootDevelopment(window : BrowserWindow) {
|
||||
logger.log("Booting in development mode");
|
||||
window.loadURL(process.env['ELECTRON_RENDERER_URL'] + "/dev.html");
|
||||
window.webContents.openDevTools({ mode: 'detach' });
|
||||
}
|
||||
|
||||
async function existsFile(filePath: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
130
lib/main/boot/compiler.ts
Normal file
130
lib/main/boot/compiler.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import path from "path";
|
||||
import { WORKING_DIR } from "../constants";
|
||||
import fs from 'fs/promises'
|
||||
import JSZip from "jszip";
|
||||
import crypto from "crypto";
|
||||
import { Logger } from "../logger";
|
||||
|
||||
const logger = Logger('compiler');
|
||||
|
||||
export interface BundleFile {
|
||||
file: string;
|
||||
type: string;
|
||||
main: boolean;
|
||||
}
|
||||
|
||||
const binaryFileTypes = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'mp4', 'mp3', 'wav', 'ogg', 'pdf', 'zip', 'rar', '7z'];
|
||||
|
||||
|
||||
export async function compileBundleFile(zip : JSZip) {
|
||||
let timestart = Date.now();
|
||||
logger.log("Starting compilation of bundle file");
|
||||
let files : BundleFile[] = await getAllFilesInBundleForCompile(zip);
|
||||
if(files.length == 0){
|
||||
logger.log("No files to compile in bundle");
|
||||
throw new Error("No files to compile in bundle");
|
||||
}
|
||||
let compileOutputDir = path.join(WORKING_DIR, 'b');
|
||||
await fs.mkdir(compileOutputDir, { recursive: true });
|
||||
|
||||
let mainFiles = files.filter(f => f.main);
|
||||
let otherFiles = files.filter(f => !f.main);
|
||||
|
||||
logger.log(`Compiling ${files.length} files, ${mainFiles.length} main files and ${otherFiles.length} other files`);
|
||||
|
||||
for (let file of files) {
|
||||
let zipFile = zip.file(file.file);
|
||||
if (zipFile) {
|
||||
let content = await zipFile.async('nodebuffer');
|
||||
let outputPath = path.join(compileOutputDir, await hashFileName(path.basename(file.file)));
|
||||
await fs.writeFile(outputPath, content);
|
||||
}
|
||||
}
|
||||
|
||||
await compileHtmlFile(mainFiles);
|
||||
await transitionFilenamesInFiles([...mainFiles, ...otherFiles]);
|
||||
logger.log("Compilation done successfully in " + (Date.now() - timestart) + "ms");
|
||||
}
|
||||
|
||||
async function transitionFilenamesInFiles(otherFiles: BundleFile[]) {
|
||||
logger.log(`Transitioning filenames in ${otherFiles.length} files`);
|
||||
for(let i = 0; i < otherFiles.length; i++){
|
||||
let file = otherFiles[i];
|
||||
let filePath = path.join(WORKING_DIR, 'b', await hashFileName(path.basename(file.file)));
|
||||
let buffer = await fs.readFile(filePath, {
|
||||
encoding: file.type && binaryFileTypes.includes(file.type) ? 'binary' : 'utf-8'
|
||||
});
|
||||
let content = buffer.toString();
|
||||
logger.log(`Processing file ${path.basename(file.file)}`);
|
||||
|
||||
for(let j = 0; j < otherFiles.length; j++){
|
||||
let targetFile = otherFiles[j];
|
||||
let originalName = path.basename(targetFile.file);
|
||||
let hashedName = await hashFileName(originalName);
|
||||
let entries = content.split(originalName);
|
||||
if(entries.length <= 0){
|
||||
continue;
|
||||
}
|
||||
logger.log(`In file ${path.basename(file.file)} replacing ${originalName} with ${hashedName} (${entries.length - 1} occurrences)`);
|
||||
content = entries.join(hashedName);
|
||||
}
|
||||
logger.log(`Flush transitioned file ${path.basename(file.file)}`);
|
||||
await fs.writeFile(filePath, content, {
|
||||
encoding: file.type && binaryFileTypes.includes(file.type) ? 'binary' : 'utf-8'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function compileHtmlFile(mainFiles: BundleFile[]) {
|
||||
let html = await constructHtmlTemplateToCompile();
|
||||
html = html.replace(`<script type="module" src="/renderer.tsx"></script>`, '');
|
||||
let scripts = mainFiles.filter(f => f.type === 'js').map(f => f.file);
|
||||
let styles = mainFiles.filter(f => f.type === 'css').map(f => f.file);
|
||||
|
||||
let scriptTags : string = "";
|
||||
for (let script of scripts) {
|
||||
let hashedName = await hashFileName(path.basename(script));
|
||||
scriptTags += `<script type="module" src="${hashedName}"></script>\n`;
|
||||
}
|
||||
|
||||
let styleTags : string = "";
|
||||
for (let style of styles) {
|
||||
let hashedName = await hashFileName(path.basename(style));
|
||||
styleTags += `<link rel="stylesheet" href="${hashedName}">\n`;
|
||||
}
|
||||
|
||||
|
||||
html = html.replace('<!--RR-->', `${scriptTags}\n${styleTags}`);
|
||||
html = html.replace('\n', '');
|
||||
|
||||
|
||||
let outputHtmlPath = path.join(WORKING_DIR, 'b', 'j.html');
|
||||
await fs.writeFile(outputHtmlPath, html);
|
||||
}
|
||||
|
||||
export async function getAllFilesInBundleForCompile(zip : JSZip) : Promise<BundleFile[]> {
|
||||
const mainFilePrefixes = ['index-', 'main-'];
|
||||
return Object.keys(zip.files)
|
||||
.filter(filePath => filePath.indexOf('__MACOSX') == -1)
|
||||
.map(filePath => {
|
||||
const ext = path.extname(filePath).slice(1);
|
||||
const isMain = mainFilePrefixes.some(prefix => path.basename(filePath).startsWith(prefix));
|
||||
return {
|
||||
file: filePath,
|
||||
type: ext,
|
||||
main: isMain
|
||||
} as BundleFile;
|
||||
});
|
||||
}
|
||||
|
||||
async function constructHtmlTemplateToCompile() {
|
||||
let buffer = await fs.readFile(
|
||||
path.join(__dirname, '../../resources/prod.html')
|
||||
);
|
||||
let html = buffer.toString();
|
||||
return html;
|
||||
}
|
||||
|
||||
async function hashFileName(name : string) : Promise<string> {
|
||||
return crypto.createHash('md5').update(name).digest('hex') + path.extname(name);
|
||||
}
|
||||
10
lib/main/boot/updater.ts
Normal file
10
lib/main/boot/updater.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import fs from 'fs/promises'
|
||||
import JSZip from "jszip";
|
||||
import { compileBundleFile } from './compiler';
|
||||
|
||||
export async function installServiceUpdate(pathToUpdate : string) {
|
||||
let data = await fs.readFile(pathToUpdate);
|
||||
let zip = await JSZip.loadAsync(data);
|
||||
await compileBundleFile(zip);
|
||||
await fs.unlink(pathToUpdate);
|
||||
}
|
||||
Reference in New Issue
Block a user