UNPKG

js13k-packer

Version:

Optimally package js13kGames files for upload.

298 lines (285 loc) 9.17 kB
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 });