js13k-packer
Version:
Optimally package js13kGames files for upload.
298 lines (285 loc) • 9.17 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.js
var src_exports = {};
__export(src_exports, {
default: () => src_default,
pack: () => pack
});
module.exports = __toCommonJS(src_exports);
var import_fs3 = require("fs");
var import_path3 = __toESM(require("path"), 1);
var import_jsdom2 = require("jsdom");
// src/utils/bundle-css.js
var import_fs = require("fs");
async function bundleCss(styles) {
let code = "";
for (let i = 0; i < styles.length; i++) {
const style = styles[i];
if (style.type === "inline") {
code += style.code;
} else {
code += await import_fs.promises.readFile(style.filePath, "utf8");
}
}
return code;
}
// src/utils/bundle-html.js
var import_jsdom = require("jsdom");
// src/constants.js
var scriptSelector = "script";
var styleSelector = "style, link[rel=stylesheet]";
var htmlMinifierDefaults = {
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true,
minifyCSS: true,
minifyJS: {
sourceMap: false,
ecma: 2022,
module: true,
toplevel: true,
compress: {
booleans_as_integers: true,
ecma: 2022,
keep_fargs: false,
module: true,
passes: 4,
toplevel: true,
unsafe: true,
unsafe_arrows: true,
unsafe_comps: true,
unsafe_math: true,
unsafe_methods: true,
unsafe_proto: true,
unsafe_regexp: true,
unsafe_undefined: true
},
mangle: {
module: true,
toplevel: true
},
format: {
comments: false,
ecma: 2022
}
},
noNewlinesBeforeTagClose: true,
removeAttributeQuotes: true,
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
};
var roadrollerDefaults = {
input: {
type: "text",
action: "write"
},
options: {
allowFreeVars: true
},
optimize: 2
};
// src/utils/bundle-html.js
async function bundleHtml(html, js, css) {
const dom = new import_jsdom.JSDOM(html);
const { document } = dom.window;
document.querySelectorAll(styleSelector).forEach((elm) => elm.remove());
const style = document.createElement("style");
style.innerHTML = css;
document.body.appendChild(style);
document.querySelectorAll(scriptSelector).forEach((elm) => elm.remove());
const script = document.createElement("script");
script.innerHTML = js;
document.body.appendChild(script);
return dom.serialize();
}
// src/utils/bundle-js.js
var import_esbuild = require("esbuild");
async function bundleJs(scripts, options) {
let code = "";
const { dir, filename } = options;
let { esbuildOptions } = options;
if (typeof esbuildOptions === "boolean") {
esbuildOptions = {
bundle: esbuildOptions
};
}
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
let buildOptions;
if (script.type === "inline") {
buildOptions = {
stdin: {
contents: script.code,
resolveDir: dir,
sourcefile: filename
}
};
} else {
buildOptions = {
entryPoints: [script.filePath]
};
}
const output = await (0, import_esbuild.build)({
...esbuildOptions,
...buildOptions,
minify: false,
write: false
});
code += output.outputFiles[0].text;
}
return code;
}
// src/utils/ect.js
var import_child_process = require("child_process");
var import_os = __toESM(require("os"), 1);
var import_path = __toESM(require("path"), 1);
var import_url = require("url");
var import_glob_promise = __toESM(require("glob-promise"), 1);
var import_meta = {};
function ect(outdirPath, output) {
return new Promise(async (resolve, reject) => {
const filename = typeof __filename === "undefined" ? (0, import_url.fileURLToPath)(import_meta.url) : __filename;
const dirname = import_path.default.dirname(filename);
const pattern = import_path.default.join(outdirPath, "**/*");
const files = await (0, import_glob_promise.default)(pattern, { ignore: `${pattern}.zip` });
const platform = import_os.default.platform();
const ect2 = platform === "win32" ? "ect.exe" : "ect";
const ectPath = import_path.default.join(dirname, "..", ect2);
(0, import_child_process.exec)(`${ectPath} -9 -strip -zip ${import_path.default.join(outdirPath, output)} ${files.join(" ")}`, (error, stdout, stderr) => {
resolve();
});
});
}
// src/utils/extract.js
var import_path2 = __toESM(require("path"), 1);
async function extract(dom, options) {
const { dir, selector, type } = options;
const { document } = dom.window;
return Promise.all(Array.from(document.querySelectorAll(selector)).map(async (elm) => {
const src = elm.getAttribute("src") ?? elm.getAttribute("href");
if (src) {
if (import_path2.default.isAbsolute(src)) {
throw new Error(`Cannot resolve absolute file path ${src}`);
}
return {
type: "linked",
filePath: import_path2.default.resolve(dir, src)
};
}
return {
type: "inline",
code: elm.innerHTML
};
}));
}
// src/utils/file-size.js
var import_fs2 = require("fs");
async function fileSize(file) {
const btyes = (await import_fs2.promises.stat(file)).size;
const kb = (btyes / 1024).toFixed(2);
console.log(`Zip file size: ${btyes} btyes; ${kb} kb`);
}
// src/utils/minify-html.js
var import_html_minifier_terser = require("html-minifier-terser");
async function minifyHtml(code, options) {
if (options === false)
return code;
if (options === true) {
options = htmlMinifierDefaults;
}
const result = await (0, import_html_minifier_terser.minify)(code, options);
return result;
}
// src/utils/roadroller.js
var import_roadroller = require("roadroller");
async function roadroller(data, roadrollerOptions) {
if (roadrollerOptions == false)
return data;
if (roadrollerOptions === true) {
roadrollerOptions = roadrollerDefaults;
}
const { input, options, optimize } = roadrollerOptions;
const inputs = [
{
data,
...input
}
];
const packer = new import_roadroller.Packer(inputs, options);
const results = await packer.optimize(optimize);
console.log("Roadroller results:", results);
const { firstLine, secondLine } = packer.makeDecoder();
return firstLine + "\n" + secondLine;
}
// src/index.js
async function pack(file, outdir, options = {}) {
const dir = import_path3.default.dirname(file);
const filename = import_path3.default.basename(file);
const outdirPath = import_path3.default.join(process.cwd(), outdir);
const {
bundle: esbuildOptions = true,
minify: minify2 = true,
pack: pack2 = true,
output = import_path3.default.basename(outdirPath) + ".zip"
} = options;
const html = await import_fs3.promises.readFile(file, "utf8");
const dom = new import_jsdom2.JSDOM(html);
const scripts = await extract(dom, {
dir,
selector: scriptSelector,
type: ".js"
});
const styles = await extract(dom, {
dir,
selector: styleSelector,
type: ".css"
});
let bundledJs = await bundleJs(scripts, {
dir,
filename,
esbuildOptions
});
const bundledCss = await bundleCss(styles);
let optimizedJs = bundledJs;
if (pack2?.input?.type === "js") {
optimizedJs = await roadroller(bundledJs, pack2);
}
const bundledHtml = await bundleHtml(html, optimizedJs, bundledCss, pack2);
let htmlOutput = await minifyHtml(bundledHtml, minify2);
if (pack2 === true || pack2?.input?.type === "text") {
htmlOutput = await roadroller(htmlOutput, pack2);
htmlOutput = `<script>${htmlOutput}<\/script>`;
}
await import_fs3.promises.mkdir(outdirPath, { recursive: true });
await import_fs3.promises.writeFile(import_path3.default.join(outdirPath, "index.html"), htmlOutput, "utf8");
await ect(outdirPath, output);
await fileSize(import_path3.default.join(outdirPath, output));
}
var js13kPacker = { pack };
var src_default = js13kPacker;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
pack
});