This commit is contained in:
rosetta
2026-01-30 05:01:05 +02:00
commit 83f38dc63f
327 changed files with 18725 additions and 0 deletions

View 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
View 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
View 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);
}