130 lines
4.9 KiB
TypeScript
130 lines
4.9 KiB
TypeScript
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);
|
|
} |