Build System: - Replace Grunt with npm scripts and Node.js helper scripts - Remove worker-loader, use native Webpack 5 worker syntax - Add Rspack config as faster alternative bundler (5-23x faster) - Replace platform-specific sed postinstall hacks with cross-platform Node.js script - Update browser targets from Chrome 50/Firefox 38 to Chrome 80/Firefox 78/Safari 14 - Fix import assertions (assert -> with) for Node 24 compatibility Dependencies: - Replace deprecated crypto-js with native RC4 implementation and @noble/hashes EVP KDF - Replace blakejs with @noble/hashes/blake2b and blake2s - Add @noble/hashes for MD5, SHA1/2/3, RIPEMD160, HMAC, HKDF (crypto-api fallback for legacy algos) - Replace lodash with native CaseConvert.mjs utility - Replace moment.js in web layer with date-fns - Add date-fns and date-fns-tz dependencies - Remove jquery, snackbarjs, arrive, bootstrap-colorpicker, bootstrap-material-design, popper.js v1, lodash, blakejs, crypto-js UI Modernization: - Upgrade Bootstrap 4.6.2 to Bootstrap 5.3 (87 data attribute renames) - Remove bootstrap-material-design (unmaintained) - Remove jQuery dependency entirely (38 calls replaced with native DOM + BS5 JS API) - Add custom Snackbar.mjs notification utility (replaces snackbarjs) - Replace bootstrap-colorpicker with native HTML color input Testing: - Add Vitest config and adapter for running existing TestRegister tests - Add Playwright config and E2E test replacing Nightwatch - Update CI workflows to use npm scripts and Playwright
87 lines
3.2 KiB
JavaScript
87 lines
3.2 KiB
JavaScript
/**
|
|
* Builds standalone CyberChef HTML file, creates zip archive, and calculates SHA256 hash.
|
|
* Replaces the Grunt tasks: copy:standalone, zip:standalone, clean:standalone, exec:calcDownloadHash
|
|
*
|
|
* @author CyberChef Modernization
|
|
* @copyright Crown Copyright 2016
|
|
* @license Apache-2.0
|
|
*/
|
|
|
|
import { readFileSync, writeFileSync, unlinkSync, createReadStream, createWriteStream, readdirSync, statSync } from "fs";
|
|
import { createHash } from "crypto";
|
|
import { join, relative } from "path";
|
|
import { createGzip } from "zlib";
|
|
import { pipeline } from "stream/promises";
|
|
|
|
const pkg = JSON.parse(readFileSync("package.json", "utf8"));
|
|
const version = pkg.version;
|
|
const buildDir = "build/prod";
|
|
|
|
// Step 1: Create standalone HTML (copy:standalone equivalent)
|
|
console.log("--- Creating standalone HTML ---");
|
|
let indexHtml = readFileSync(join(buildDir, "index.html"), "utf8");
|
|
|
|
// Replace download link with version number
|
|
indexHtml = indexHtml.replace(/<a [^>]+>Download CyberChef.+?<\/a>/,
|
|
`<span>Version ${version}</span>`);
|
|
|
|
const standaloneFilename = `CyberChef_v${version}.html`;
|
|
writeFileSync(join(buildDir, standaloneFilename), indexHtml);
|
|
console.log(`Created ${standaloneFilename}`);
|
|
|
|
// Step 2: Create zip archive (zip:standalone equivalent)
|
|
console.log("--- Creating zip archive ---");
|
|
const archiver = (await import("archiver")).default;
|
|
const zipFilename = `CyberChef_v${version}.zip`;
|
|
const zipPath = join(buildDir, zipFilename);
|
|
|
|
const output = createWriteStream(zipPath);
|
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
|
|
await new Promise((resolve, reject) => {
|
|
output.on("close", resolve);
|
|
archive.on("error", reject);
|
|
|
|
archive.pipe(output);
|
|
|
|
// Add all files from build/prod except index.html and BundleAnalyzerReport.html
|
|
const addDir = (dir) => {
|
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
const fullPath = join(dir, entry.name);
|
|
const relPath = relative(buildDir, fullPath);
|
|
|
|
if (entry.isDirectory()) {
|
|
addDir(fullPath);
|
|
} else if (entry.isFile()) {
|
|
if (relPath === "index.html" || relPath === "BundleAnalyzerReport.html") continue;
|
|
if (relPath.startsWith("CyberChef_v")) continue; // skip standalone files
|
|
archive.file(fullPath, { name: relPath });
|
|
}
|
|
}
|
|
};
|
|
|
|
addDir(buildDir);
|
|
archive.finalize();
|
|
});
|
|
|
|
console.log(`Created ${zipFilename}`);
|
|
|
|
// Step 3: Clean standalone HTML (clean:standalone equivalent)
|
|
unlinkSync(join(buildDir, standaloneFilename));
|
|
console.log(`Cleaned ${standaloneFilename}`);
|
|
|
|
// Step 4: Calculate SHA256 hash (exec:calcDownloadHash equivalent)
|
|
console.log("--- Calculating SHA256 hash ---");
|
|
const zipBuffer = readFileSync(zipPath);
|
|
const hash = createHash("sha256").update(zipBuffer).digest("hex");
|
|
writeFileSync(join(buildDir, "sha256digest.txt"), hash);
|
|
|
|
// Replace placeholder in index.html
|
|
let prodIndexHtml = readFileSync(join(buildDir, "index.html"), "utf8");
|
|
prodIndexHtml = prodIndexHtml.replace(/DOWNLOAD_HASH_PLACEHOLDER/g, hash);
|
|
writeFileSync(join(buildDir, "index.html"), prodIndexHtml);
|
|
|
|
console.log(`SHA256: ${hash}`);
|
|
console.log("--- Standalone build complete ---");
|