const buildUtils = require("./buildutils"); const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); function computeIntegrityHash(fullPath, algorithm = "sha256") { const file = fs.readFileSync(fullPath); const hash = crypto.createHash(algorithm).update(file).digest("base64"); return algorithm + "-" + hash; } function gulptasksHTML($, gulp, buildFolder) { const commitHash = buildUtils.getRevision(); async function buildHtml( apiUrl, { analytics = false, standalone = false, app = false, integrity = true, enableCachebust = true } ) { function cachebust(url) { if (enableCachebust) { return buildUtils.cachebust(url, commitHash); } return url; } const hasLocalFiles = standalone || app; return gulp .src("../src/html/" + (standalone ? "index.standalone.html" : "index.html")) .pipe( $.dom( /** @this {Document} **/ function () { const document = this; // Preconnect to api const prefetchLink = document.createElement("link"); prefetchLink.rel = "preconnect"; prefetchLink.href = apiUrl; prefetchLink.setAttribute("crossorigin", "anonymous"); document.head.appendChild(prefetchLink); // Append css const css = document.createElement("link"); css.rel = "stylesheet"; css.type = "text/css"; css.media = "none"; css.setAttribute("onload", "this.media='all'"); css.href = cachebust("main.css"); if (integrity) { css.setAttribute( "integrity", computeIntegrityHash(path.join(buildFolder, "main.css")) ); } document.head.appendChild(css); // Append async css // const asyncCss = document.createElement("link"); // asyncCss.rel = "stylesheet"; // asyncCss.type = "text/css"; // asyncCss.media = "none"; // asyncCss.setAttribute("onload", "this.media='all'"); // asyncCss.href = cachebust("async-resources.css"); // if (integrity) { // asyncCss.setAttribute( // "integrity", // computeIntegrityHash(path.join(buildFolder, "async-resources.css")) // ); // } // document.head.appendChild(asyncCss); if (app) { // Append cordova link const cdv = document.createElement("script"); cdv.src = "cordova.js"; cdv.type = "text/javascript"; document.head.appendChild(cdv); } // Google analytics if (analytics) { const tagManagerScript = document.createElement("script"); tagManagerScript.src = "https://www.googletagmanager.com/gtag/js?id=UA-165342524-1"; tagManagerScript.setAttribute("async", ""); document.head.appendChild(tagManagerScript); const initScript = document.createElement("script"); initScript.textContent = ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-165342524-1', { anonymize_ip: true }); `; document.head.appendChild(initScript); const abTestingScript = document.createElement("script"); abTestingScript.setAttribute( "src", "https://www.googleoptimize.com/optimize.js?id=OPT-M5NHCV7" ); abTestingScript.setAttribute("async", ""); document.head.appendChild(abTestingScript); } // Do not need to preload in app or standalone if (!hasLocalFiles) { // Preload essentials const preloads = ["fonts/GameFont.woff2"]; preloads.forEach(src => { const preloadLink = document.createElement("link"); preloadLink.rel = "preload"; preloadLink.href = cachebust("res/" + src); if (src.endsWith(".woff2")) { preloadLink.setAttribute("crossorigin", "anonymous"); preloadLink.setAttribute("as", "font"); } else { preloadLink.setAttribute("as", "image"); } document.head.appendChild(preloadLink); }); } const loadingSvg = `background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0eWxlPSJtYXJnaW46YXV0bztiYWNrZ3JvdW5kOjAgMCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgZGlzcGxheT0iYmxvY2siPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5Mzc0NyIgc3Ryb2tlLXdpZHRoPSIzIiByPSI0MiIgc3Ryb2tlLWRhc2hhcnJheT0iMTk3LjkyMDMzNzE3NjE1Njk4IDY3Ljk3MzQ0NTcyNTM4NTY2IiB0cmFuc2Zvcm09InJvdGF0ZSg0OC4yNjUgNTAgNTApIj48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGR1cj0iNS41NTU1NTU1NTU1NTU1NTVzIiB2YWx1ZXM9IjAgNTAgNTA7MzYwIDUwIDUwIiBrZXlUaW1lcz0iMDsxIi8+PC9jaXJjbGU+PC9zdmc+")`; const loadingCss = ` @font-face { font-family: 'GameFont'; font-style: normal; font-weight: normal; font-display: swap; src: url('${cachebust("res/fonts/GameFont.woff2")}') format('woff2'); } #ll_fp { font-family: GameFont; font-size: 14px; position: fixed; z-index: -1; top: 0; left: 0; opacity: 0.05; } #ll_p { display: flex; position: fixed; z-index: 99999; top: 0; left: 0; right: 0; bottom: 0; justify-content: center; align-items: center; } #ll_p > div { position: absolute; text-align: center; bottom: 40px; left: 20px; right: 20px; color: #393747; font-family: 'GameFont', sans-serif; font-size: 20px; } #ll_p > span { width: 60px; height: 60px; display: inline-flex; background: center center / contain no-repeat; ${loadingSvg}; } `; const style = document.createElement("style"); style.setAttribute("type", "text/css"); style.textContent = loadingCss; document.head.appendChild(style); // Append loader, but not in standalone (directly include bundle there) if (standalone) { const bundleScript = document.createElement("script"); bundleScript.type = "text/javascript"; bundleScript.src = "bundle.js"; if (integrity) { bundleScript.setAttribute( "integrity", computeIntegrityHash(path.join(buildFolder, "bundle.js")) ); } document.head.appendChild(bundleScript); } else { const loadJs = document.createElement("script"); loadJs.type = "text/javascript"; let scriptContent = ""; scriptContent += `var bundleSrc = '${cachebust("bundle.js")}';\n`; scriptContent += `var bundleSrcTranspiled = '${cachebust( "bundle-transpiled.js" )}';\n`; if (integrity) { scriptContent += "var bundleIntegrity = '" + computeIntegrityHash(path.join(buildFolder, "bundle.js")) + "';\n"; scriptContent += "var bundleIntegrityTranspiled = '" + computeIntegrityHash(path.join(buildFolder, "bundle-transpiled.js")) + "';\n"; } else { scriptContent += "var bundleIntegrity = null;\n"; scriptContent += "var bundleIntegrityTranspiled = null;\n"; } scriptContent += fs.readFileSync("./bundle-loader.js").toString(); loadJs.textContent = scriptContent; document.head.appendChild(loadJs); } const bodyContent = `
_
${hasLocalFiles ? "Loading" : "Downloading"} Game Files
`; document.body.innerHTML = bodyContent; } ) ) .pipe( $.htmlmin({ caseSensitive: true, collapseBooleanAttributes: true, collapseInlineTagWhitespace: true, collapseWhitespace: true, preserveLineBreaks: true, minifyJS: true, minifyCSS: true, quoteCharacter: '"', useShortDoctype: true, }) ) .pipe($.htmlBeautify()) .pipe($.rename("index.html")) .pipe(gulp.dest(buildFolder)); } gulp.task("html.dev", () => { return buildHtml("http://localhost:5005", { analytics: false, integrity: false, enableCachebust: false, }); }); gulp.task("html.staging", () => { return buildHtml("https://api-staging.shapez.io", { analytics: true, }); }); gulp.task("html.prod", () => { return buildHtml("https://analytics.shapez.io", { analytics: true, }); }); gulp.task("html.standalone-dev", () => { return buildHtml("https://localhost:5005", { analytics: false, standalone: true, integrity: false, enableCachebust: false, }); }); gulp.task("html.standalone-beta", () => { return buildHtml("https://api-staging.shapez.io", { analytics: false, standalone: true, enableCachebust: false, }); }); gulp.task("html.standalone-prod", () => { return buildHtml("https://analytics.shapez.io", { analytics: false, standalone: true, enableCachebust: false, }); }); } module.exports = { gulptasksHTML, };