const fs = require("fs").promises;
const fss = require("fs");
const glob = require("glob");
const zlib = require('zlib');
const { getLanguages } = require("./lib/language.js");
const { filter } = require("./lib/dependencies.js");
const config = require("./build_config.js");
const { install, installCleanCSS, mkdir } = require("./lib/makestuff.js");
const log = (...args) => console.log(...args);
const { buildCore } = require("./build_browser.js");
const { buildPackageJSON, writePackageJSON } = require("./build_node.js");
const path = require("path");
const bundling = require('./lib/bundling.js');
async function installPackageJSON(options) {
const json = buildPackageJSON(options);
json.name = "@highlightjs/cdn-assets";
json.description = json.description.concat(" (pre-compiled CDN assets)");
delete json.exports;
delete json.type;
delete json.main;
delete json.types;
await writePackageJSON(json);
}
let shas = {};
async function buildCDN(options) {
install("./LICENSE", "LICENSE");
install("./README.CDN.md", "README.md");
await installPackageJSON(options);
installStyles();
const languages = await getLanguages();
let esmCoreSize = {};
let esmCommonSize = {};
await installLanguages(languages, options);
let embedLanguages = filter(languages, options.languages);
if (embedLanguages.length === languages.length) {
embedLanguages = [];
}
const size = await buildCore("highlight", embedLanguages, { minify: options.minify, format: "cjs" });
if (options.esm) {
mkdir("es");
await fs.writeFile(`${process.env.BUILD_DIR}/es/package.json`, `{ "type": "module" }`);
esmCoreSize = await buildCore("core", [], { minify: options.minify, format: "es" });
esmCommonSize = await buildCore("highlight", embedLanguages, { minify: options.minify, format: "es" });
}
shas = {
...size.shas, ...esmCommonSize.shas, ...esmCoreSize.shas, ...shas
};
await buildSRIDigests(shas);
log("-----");
log("Embedded Lang :",
embedLanguages.map((el) => el.minified.length).reduce((acc, curr) => acc + curr, 0), "bytes");
log("All Lang :",
languages.map((el) => el.minified.length).reduce((acc, curr) => acc + curr, 0), "bytes");
log("highlight.js :",
size.fullSize, "bytes");
if (options.minify) {
log("highlight.min.js :", size.minified, "bytes");
log("highlight.min.js.gz :", zlib.gzipSync(size.minifiedSrc).length, "bytes");
} else {
log("highlight.js.gz :", zlib.gzipSync(size.fullSrc).length, "bytes");
}
if (options.esm) {
log("es/core.js :", esmCoreSize.fullSize, "bytes");
log("es/highlight.js :", esmCommonSize.fullSize, "bytes");
if (options.minify) {
log("es/core.min.js :", esmCoreSize.minified, "bytes");
log("es/core.min.js.gz :", zlib.gzipSync(esmCoreSize.minifiedSrc).length, "bytes");
log("es/highlight.min.js :", esmCommonSize.minified, "bytes");
log("es/highlight.min.js.gz :", zlib.gzipSync(esmCommonSize.minifiedSrc).length, "bytes");
} else {
log("es/highlight.js.gz :", zlib.gzipSync(esmCommonSize.fullSrc).length, "bytes");
}
}
log("-----");
}
async function buildSRIDigests(shas) {
const temp = await fs.readFile("./tools/templates/DIGESTS.md");
const DIGEST_MD = temp.toString();
const version = require("../package.json").version;
const digestList = Object.entries(shas).map(([k, v]) => `${v} ${k}`).join("\n");
const out = DIGEST_MD
.replace("<!-- $DIGEST_LIST -->", digestList)
.replace("<!-- $MIN_JS_DIGEST -->", shas["highlight.min.js"])
.replace("<!-- $GO_SHA -->", shas["languages/go.min.js"])
.replace(/<!-- \$VERSION -->/g, version);
fs.writeFile(`${process.env.BUILD_DIR}/DIGESTS.md`, out);
}
async function installLanguages(languages, options) {
log("Building language files.");
mkdir("languages");
if (options.esm) mkdir("es/languages");
await Promise.all(
languages.map(async(language) => {
await buildCDNLanguage(language, options);
process.stdout.write(".");
})
);
log("");
await Promise.all(
languages.filter((l) => l.third_party)
.map(async(lang) => await buildDistributable(lang, options))
);
log("");
}
function installStyles() {
log("Writing style files.");
mkdir("styles/base16");
glob.sync("**", { cwd: "./src/styles" }).forEach((file) => {
const stat = fss.statSync(`./src/styles/${file}`);
if (stat.isDirectory()) return;
if (file.endsWith(".css")) {
installCleanCSS(`./src/styles/${file}`, `styles/${file.replace(".css", ".min.css")}`);
} else {
install(`./src/styles/${file}`, `styles/${file}`);
}
});
}
async function buildDistributable(language, options) {
const filename = `${language.name}.min.js`;
const distDir = path.join(language.moduleDir, "dist");
log(`Building ${distDir}/${filename}.`);
await fs.mkdir(distDir, { recursive: true });
await fs.writeFile(path.join(language.moduleDir, "dist", filename), language.minified);
if (options.esm) {
await fs.writeFile(path.join(language.moduleDir, "dist", filename.replace(".min.js", ".es.min.js")), language.minifiedESM);
}
}
async function buildCDNLanguage(language, options) {
const name = `languages/${language.name}.min.js`;
await language.compile({ terser: config.terser });
shas[name] = bundling.sha384(language.minified);
await fs.writeFile(`${process.env.BUILD_DIR}/${name}`, language.minified);
if (options.esm) {
shas[`es/${name}`] = bundling.sha384(language.minifiedESM);
await fs.writeFile(`${process.env.BUILD_DIR}/es/${name}`, language.minifiedESM);
}
}
module.exports.build = buildCDN;