From 45e35c9c149b97716515b6c6a03f0b9a9800433f Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 10 Jun 2026 16:45:05 +0200 Subject: [PATCH 1/6] f --- .../javascript-wc-indexeddb/scripts/build.js | 4 +- .../todomvc-localstorage/scripts/build.js | 3 +- index.html | 7 + resources/benchmark-configurator.mjs | 2 + resources/default-tests.mjs | 1 + resources/main.css | 60 +++++++- resources/main.mjs | 131 +++++++++++++++++ .../service-worker-startup/service-worker.js | 50 +++++++ resources/shared/params.mjs | 3 + resources/shared/sw-messages.mjs | 6 + resources/suite-runner.mjs | 3 +- .../angular-complex/scripts/build.js | 2 + .../backbone-complex/scripts/build.js | 2 + .../backbone/scripts/build.js | 3 +- .../jquery-complex/scripts/build.js | 4 +- .../jquery/scripts/build.js | 3 +- .../lit-complex/scripts/build.js | 2 + .../preact-complex/scripts/build.js | 2 + .../react-complex/scripts/build.js | 2 + .../react-redux-complex/scripts/build.js | 2 + .../svelte-complex/scripts/build.js | 2 + .../vue-complex/scripts/build.js | 2 + .../javascript-es5-complex/scripts/build.js | 2 + .../javascript-es5/dist/resources.txt | 10 ++ .../javascript-es5/scripts/build.js | 3 +- .../scripts/build.js | 2 + .../scripts/build.js | 2 + .../scripts/build.js | 4 +- sw.mjs | 135 ++++++++++++++++++ 29 files changed, 438 insertions(+), 16 deletions(-) create mode 100644 resources/service-worker-startup/service-worker.js create mode 100644 resources/shared/sw-messages.mjs create mode 100644 resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt create mode 100644 sw.mjs diff --git a/experimental/javascript-wc-indexeddb/scripts/build.js b/experimental/javascript-wc-indexeddb/scripts/build.js index 8540a774e..b9415f1b9 100644 --- a/experimental/javascript-wc-indexeddb/scripts/build.js +++ b/experimental/javascript-wc-indexeddb/scripts/build.js @@ -1,5 +1,7 @@ const fs = require("fs").promises; const { dirname } = require("path"); +const path = require("path"); +const { generateResourcesFile } = require("../../../resources/shared/generate-resources"); /** * createDirectory @@ -164,4 +166,4 @@ const build = async () => { console.log("Done with building!"); }; -build(); +build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/experimental/todomvc-localstorage/scripts/build.js b/experimental/todomvc-localstorage/scripts/build.js index 046de0e7e..550a45021 100644 --- a/experimental/todomvc-localstorage/scripts/build.js +++ b/experimental/todomvc-localstorage/scripts/build.js @@ -1,5 +1,6 @@ const fs = require("fs").promises; const path = require("path"); +const { generateResourcesFile } = require("../../../resources/shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -53,4 +54,4 @@ const build = async () => { console.log("done!!"); }; -build(); +build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/index.html b/index.html index 31ba85ee4..972e8051d 100644 --- a/index.html +++ b/index.html @@ -33,6 +33,13 @@ Test Instructions +
+ +
+
+
+
+
diff --git a/resources/benchmark-configurator.mjs b/resources/benchmark-configurator.mjs index ee250a78d..b6c53d136 100644 --- a/resources/benchmark-configurator.mjs +++ b/resources/benchmark-configurator.mjs @@ -56,6 +56,8 @@ export class BenchmarkConfigurator { this.#suites.forEach((suite) => { if (!suite.tags) suite.tags = []; + if (!("measurePrepare" in suite)) + suite.measurePrepare = false; if (suite.url.startsWith("experimental/")) suite.tags.unshift("all", "experimental"); else diff --git a/resources/default-tests.mjs b/resources/default-tests.mjs index ecfbc336a..51cb0d2d6 100644 --- a/resources/default-tests.mjs +++ b/resources/default-tests.mjs @@ -7,6 +7,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-JavaScript-ES5", url: "resources/todomvc/vanilla-examples/javascript-es5/dist/index.html", + resources: "resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt", tags: ["default", "todomvc"], async prepare(page) { (await page.waitForElement(".new-todo")).focus(); diff --git a/resources/main.css b/resources/main.css index 279f2a413..40929ff96 100644 --- a/resources/main.css +++ b/resources/main.css @@ -374,7 +374,16 @@ button.show-about { display: none; } -#progress { +#preload-progress { + display: none; +} + +:root[data-benchmark-state="PRELOADING"] #preload-progress, +:root[data-benchmark-state="PRELOADING"] #preload-info { + display: block; +} + +#progress, #preload-progress { position: absolute; bottom: -6px; left: 60px; @@ -384,7 +393,7 @@ button.show-about { border-right: 6px solid var(--background); } -#progress-completed { +#progress-completed, #preload-progress-completed { position: absolute; top: 0; left: 0; @@ -395,11 +404,11 @@ button.show-about { background-color: var(--inactive-color); } -#progress-completed::-webkit-progress-value { +#progress-completed::-webkit-progress-value, #preload-progress-completed::-webkit-progress-value { background-color: var(--foreground); } -#progress-completed::-moz-progress-bar { +#progress-completed::-moz-progress-bar, #preload-progress-completed::-moz-progress-bar { background-color: var(--foreground); } @@ -410,7 +419,8 @@ button.show-about { background-color: var(--background); } -#info { +#info, #preload-info { + display: none; position: absolute; bottom: -25px; left: 60px; @@ -420,11 +430,11 @@ button.show-about { text-align: center; font-size: 12px; } -#info-label { +#info-label, #preload-info-label { position: absolute; left: 6px; } -#info-progress { +#info-progress, #preload-info-progress { position: absolute; right: 6px; text-align: right; @@ -900,3 +910,39 @@ section#about .note { color: #fff; stroke: #fff; } + +#preload-progress, #preload-info { + display: none; +} + +[data-benchmark-state="PRELOADING"] { + cursor: wait; +} + +[data-benchmark-state="PRELOADING"] #preload-progress, +[data-benchmark-state="PRELOADING"] #preload-info { + display: block; +} + +[data-benchmark-state="PRELOADING"] .start-tests-button { + color: var(--foreground); + background-image: + linear-gradient( + -45deg, + var(--foreground) 15%, + transparent 15%, + transparent 50%, + var(--foreground) 50%, + var(--foreground) 65%, + transparent 65%, + transparent 100% + ); + background-size: 20px 20px; + animation: barber-pole 1s linear infinite; + pointer-events: none; +} + +@keyframes barber-pole { + 0% { background-position: 0 0, 0 0; } + 100% { background-position: 20px 0, 0 0; } +} diff --git a/resources/main.mjs b/resources/main.mjs index fb287dcb3..02799c984 100644 --- a/resources/main.mjs +++ b/resources/main.mjs @@ -1,9 +1,79 @@ import { BenchmarkRunner } from "./benchmark-runner.mjs"; import * as Statistics from "./statistics.mjs"; +import { SW_MESSAGES } from "./shared/sw-messages.mjs"; import { renderMetricView } from "./metric-ui.mjs"; import { defaultParams, params } from "./shared/params.mjs"; import { createDeveloperModeContainer } from "./developer-mode.mjs"; +export class PreloadServiceWorker { + constructor() { + this.registration = null; + this.sw = null; + } + + async setup() { + const existingRegistrations = await navigator.serviceWorker.getRegistrations(); + for (const existing of existingRegistrations) { + await existing.unregister(); + } + + this.registration = await navigator.serviceWorker.register("/sw.mjs", { type: "module" }); + await this.registration.update(); + await navigator.serviceWorker.ready; + + this.sw = navigator.serviceWorker.controller || this.registration.active; + return true; + } + + async precacheSuites(suites, resourceLoadDelay, onProgress) { + if (suites.length === 0) return; + + const suitesData = suites.filter(s => s.resources).map(s => ({ + name: s.name, + url: new URL(s.url, window.location.href).href, + resources: new URL(s.resources, window.location.href).href + })); + + if (suitesData.length === 0) return; + + const startTime = performance.now(); + return new Promise((resolve) => { + const channel = new MessageChannel(); + channel.port1.onmessage = (event) => { + if (event.data?.type === SW_MESSAGES.PRECACHE_DONE) { + const timeTakenMs = performance.now() - startTime; + const { totalSize, count } = event.data; + const sizeMB = (totalSize / (1024 * 1024)).toFixed(2); + const timeSec = (timeTakenMs / 1000).toFixed(2); + console.log(`Preloaded ${count} files (${sizeMB} MB) in ${timeSec}s`); + resolve(); + } else if (event.data?.type === SW_MESSAGES.PRECACHE_PROGRESS) { + onProgress(event.data); + } + }; + this.sw.postMessage({ + type: SW_MESSAGES.PRECACHE_SUITES, + suites: suitesData, + delay: resourceLoadDelay + }, [channel.port2]); + }); + } + + setState(state) { + this.sw.postMessage({ type: SW_MESSAGES.SET_STATE, state }); + } +} + + +const BENCHMARK_STATE = Object.freeze({ + IDLE: "IDLE", + PRELOADING: "PRELOADING", + READY: "READY", + RUNNING: "RUNNING", + DONE: "DONE", + ERROR: "ERROR", +}); + // FIXME(camillobruni): Add base class class MainBenchmarkClient { developerMode = false; @@ -16,6 +86,9 @@ class MainBenchmarkClient { _hasResults = false; _developerModeContainer = null; _metrics = Object.create(null); + _developerModeContainer = null; + _progressCompleted = null; + preloadServiceWorker = new PreloadServiceWorker(); _steppingPromise = null; _steppingResolver = null; _benchmarkConfiguratorPromise = null; @@ -71,6 +144,8 @@ class MainBenchmarkClient { if (this._isRunning) return false; + this._setBenchmarkState(BENCHMARK_STATE.RUNNING); + const { benchmarkConfigurator } = await this._benchmarkConfiguratorPromise; const enabledSuites = benchmarkConfigurator.suites.filter((suite) => suite.enabled); @@ -146,6 +221,7 @@ class MainBenchmarkClient { this._isRunning = false; this._hasResults = true; this._metrics = metrics; + this._setBenchmarkState(BENCHMARK_STATE.DONE); const scoreResults = this._computeResults(this._measuredValuesList, "score"); if (scoreResults.isValid) @@ -166,6 +242,7 @@ class MainBenchmarkClient { this._isRunning = false; this._hasResults = true; this._metrics = Object.create(null); + this._setBenchmarkState(BENCHMARK_STATE.ERROR); this._populateInvalidScore(); this.showResultsSummary(); throw error; @@ -343,6 +420,7 @@ class MainBenchmarkClient { document.getElementById("copy-csv").onclick = this.copyCSVResults.bind(this); document.querySelectorAll(".start-tests-button").forEach((button) => { button.onclick = this._startBenchmarkHandler.bind(this); + button.disabled = true; }); } @@ -357,10 +435,63 @@ class MainBenchmarkClient { document.body.append(this._developerModeContainer); } + await this._setupServiceWorker(benchmarkConfigurator); + if (params.startAutomatically) this.start(); } + async _setupServiceWorker(benchmarkConfigurator) { + await this.preloadServiceWorker.setup(); + + this._setBenchmarkState(BENCHMARK_STATE.PRELOADING); + const enabledSuites = benchmarkConfigurator.suites.filter((suite) => suite.enabled); + + try { + await this.preloadServiceWorker.precacheSuites(enabledSuites, params.resourceLoadDelay, (progressData) => { + const { loaded, total, url, suiteName } = progressData; + document.body.style.setProperty("--preload-progress", `${total > 0 ? (loaded / total) * 100 : 100}%`); + document.getElementById("preload-progress-completed").max = total; + document.getElementById("preload-progress-completed").value = loaded; + const filename = url ? url.substring(url.lastIndexOf("/") + 1) : ""; + const labelText = suiteName ? `${suiteName}: ${filename}` : filename; + document.getElementById("preload-info-label").textContent = labelText; + document.getElementById("preload-info-progress").textContent = `${loaded} / ${total}`; + }); + this._enableStartButtons(); + } catch (error) { + console.error("Service Worker setup failed:", error); + this._setBenchmarkState(BENCHMARK_STATE.ERROR); + this._enableStartButtons(); + } + } + + _enableStartButtons() { + this._setBenchmarkState(BENCHMARK_STATE.READY); + document.querySelectorAll(".start-tests-button").forEach((button) => { + button.disabled = false; + }); + } + + _setBenchmarkState(state) { + document.body.setAttribute("data-benchmark-state", state); + if (this.preloadServiceWorker) { + this.preloadServiceWorker.setState(state); + } + + const startButton = document.querySelector(".start-tests-button"); + if (state === BENCHMARK_STATE.PRELOADING) { + document.getElementById("preload-progress-completed").value = 0; + document.getElementById("preload-info-label").textContent = "Connecting to Service Worker..."; + document.getElementById("preload-info-progress").textContent = ""; + document.body.style.setProperty("--preload-progress", "0%"); + if (startButton) startButton.textContent = "Preloading..."; + } else if (state === BENCHMARK_STATE.READY || state === BENCHMARK_STATE.IDLE || state === BENCHMARK_STATE.DONE || state === BENCHMARK_STATE.ERROR) { + document.body.style.removeProperty("--preload-progress"); + if (startButton) startButton.textContent = "Start Test"; + } + } + _hashChangeHandler() { this._showSection(window.location.hash); } diff --git a/resources/service-worker-startup/service-worker.js b/resources/service-worker-startup/service-worker.js new file mode 100644 index 000000000..d976611f8 --- /dev/null +++ b/resources/service-worker-startup/service-worker.js @@ -0,0 +1,50 @@ +const CACHE_NAME = "sw-startup-workload-v1"; +let ASSETS = ["./app/index.html", "./app/main.mjs", "./app/style.css", "./app/work_schedule.json", "https://esm.sh/react@18", "https://esm.sh/react-dom@18/client"]; + +async function cacheAssets() { + const cache = await caches.open(CACHE_NAME); + for (const url of ASSETS) { + try { + const response = await fetch(url); + if (response.ok) { + await cache.put(url, response); + console.log("SW caching:", url); + } + } catch (e) { + console.error(`Failed to cache ${url}:`, e); + } + } +} + +async function installAssets(event) { + await cacheAssets(); + self.skipWaiting(); +} + +self.addEventListener("install", (event) => { + event.waitUntil(installAssets(event)); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("fetch", (event) => { + event.respondWith( + caches.match(event.request).then((response) => { + return response || fetch(event.request); + }) + ); +}); + +async function prefetchAssets(event) { + await cacheAssets(); + event?.ports[0].postMessage({ + type: "PREFETCH_COMPLETE", + }); +} + +self.addEventListener("message", (event) => { + if (event.data.type === "PREFETCH") + event.waitUntil(prefetchAssets(event)); +}); diff --git a/resources/shared/params.mjs b/resources/shared/params.mjs index 520679d24..bc8ba51bb 100644 --- a/resources/shared/params.mjs +++ b/resources/shared/params.mjs @@ -35,6 +35,8 @@ export class Params { measurePrepare = false; // External config url to override internal tests. config = ""; + // Resource load delay in ms for the service worker pre-caching. + resourceLoadDelay = 0; constructor(searchParams = undefined) { if (searchParams) @@ -68,6 +70,7 @@ export class Params { this.layoutMode = this._parseEnumParam(searchParams, "layoutMode", LAYOUT_MODES); this.measurePrepare = this._parseBooleanParam(searchParams, "measurePrepare"); this.config = this._parseConfig(searchParams); + this.resourceLoadDelay = this._parseIntParam(searchParams, "resourceLoadDelay", 0); const unused = Array.from(searchParams.keys()); if (unused.length > 0) diff --git a/resources/shared/sw-messages.mjs b/resources/shared/sw-messages.mjs new file mode 100644 index 000000000..251d23d28 --- /dev/null +++ b/resources/shared/sw-messages.mjs @@ -0,0 +1,6 @@ +export const SW_MESSAGES = Object.freeze({ + SET_STATE: "SET_STATE", + PRECACHE_SUITES: "PRECACHE_SUITES", + PRECACHE_PROGRESS: "PRECACHE_PROGRESS", + PRECACHE_DONE: "PRECACHE_DONE" +}); diff --git a/resources/suite-runner.mjs b/resources/suite-runner.mjs index 5cfc0b50a..1872a7e87 100644 --- a/resources/suite-runner.mjs +++ b/resources/suite-runner.mjs @@ -96,7 +96,7 @@ export class SuiteRunner { const { suiteTotal, suitePrepare } = this.#suiteResults.total; if (suiteTotal === 0) throw new Error(`Got invalid 0-time total for suite ${this.#suite.name}: ${suiteTotal}`); - if (this.#params.measurePrepare && suitePrepare === 0) + if ((this.#params.measurePrepare || this.#suite.measurePrepare) && suitePrepare === 0) throw new Error(`Got invalid 0-time prepare time for suite ${this.#suite.name}: ${suitePrepare}`); } @@ -172,7 +172,6 @@ export class RemoteSuiteRunner extends SuiteRunner { this.appId = response?.appId; performance.mark(suitePrepareEndLabel); - const entry = performance.measure(`suite-${suiteName}-prepare`, suitePrepareStartLabel, suitePrepareEndLabel); this.#prepareTime = entry.duration; } diff --git a/resources/todomvc/architecture-examples/angular-complex/scripts/build.js b/resources/todomvc/architecture-examples/angular-complex/scripts/build.js index 3a13c300a..fe1b2f80c 100644 --- a/resources/todomvc/architecture-examples/angular-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/angular-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -17,3 +18,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js b/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js index 802c9f845..56979dbe6 100644 --- a/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -17,3 +18,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/backbone/scripts/build.js b/resources/todomvc/architecture-examples/backbone/scripts/build.js index ff06b6773..48a4d6b3f 100644 --- a/resources/todomvc/architecture-examples/backbone/scripts/build.js +++ b/resources/todomvc/architecture-examples/backbone/scripts/build.js @@ -1,5 +1,6 @@ const fs = require("fs").promises; const path = require("path"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -58,4 +59,4 @@ const build = async () => { console.log("done!!"); }; -build(); +build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js b/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js index 6f69450f0..686794700 100644 --- a/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -19,4 +20,5 @@ const options = { cssFilesToAddLinksFor: ["big-dom-with-stacking-context-scrollable.css"], }; -buildComplex(options); \ No newline at end of file +buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); \ No newline at end of file diff --git a/resources/todomvc/architecture-examples/jquery/scripts/build.js b/resources/todomvc/architecture-examples/jquery/scripts/build.js index 06924222d..f03383346 100644 --- a/resources/todomvc/architecture-examples/jquery/scripts/build.js +++ b/resources/todomvc/architecture-examples/jquery/scripts/build.js @@ -1,5 +1,6 @@ const fs = require("fs").promises; const path = require("path"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -51,4 +52,4 @@ const build = async () => { console.log("done!!"); }; -build(); +build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/lit-complex/scripts/build.js b/resources/todomvc/architecture-examples/lit-complex/scripts/build.js index a8b2448ca..8553c547e 100644 --- a/resources/todomvc/architecture-examples/lit-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/lit-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -24,3 +25,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/preact-complex/scripts/build.js b/resources/todomvc/architecture-examples/preact-complex/scripts/build.js index d9309af11..5987ecd7e 100644 --- a/resources/todomvc/architecture-examples/preact-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/preact-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -15,3 +16,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/react-complex/scripts/build.js b/resources/todomvc/architecture-examples/react-complex/scripts/build.js index 814d42210..4d7f98b63 100644 --- a/resources/todomvc/architecture-examples/react-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/react-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -14,3 +15,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js b/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js index b51b34ec0..159788d5c 100644 --- a/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -15,3 +16,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js b/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js index 42fcbdefc..261292720 100644 --- a/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -15,3 +16,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/architecture-examples/vue-complex/scripts/build.js b/resources/todomvc/architecture-examples/vue-complex/scripts/build.js index cb46077d8..093d4cbd8 100644 --- a/resources/todomvc/architecture-examples/vue-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/vue-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -17,3 +18,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js index 2590e7875..bc3d2c06a 100644 --- a/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -17,3 +18,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt b/resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt new file mode 100644 index 000000000..3a9a2b41d --- /dev/null +++ b/resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt @@ -0,0 +1,10 @@ +app.js +base.css +controller.js +helpers.js +index.css +index.html +model.js +store.js +template.js +view.js diff --git a/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js index 046de0e7e..7707ef610 100644 --- a/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js @@ -1,5 +1,6 @@ const fs = require("fs").promises; const path = require("path"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -53,4 +54,4 @@ const build = async () => { console.log("done!!"); }; -build(); +build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js index 9e9481a91..8a2113d37 100644 --- a/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -14,3 +15,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js index 338594b9c..576911094 100644 --- a/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js @@ -3,6 +3,7 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -23,3 +24,4 @@ const options = { }; buildComplex(options); +generateResourcesFile(path.join(__dirname, "../dist")); diff --git a/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js index be4499d96..b087510e3 100644 --- a/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js @@ -1,5 +1,7 @@ const fs = require("fs").promises; const { dirname } = require("path"); +const path = require("path"); +const { generateResourcesFile } = require("../../../../shared/generate-resources"); /** * createDirectory @@ -153,4 +155,4 @@ const build = async () => { console.log("Done with building!"); }; -build(); +build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/sw.mjs b/sw.mjs new file mode 100644 index 000000000..7959db98e --- /dev/null +++ b/sw.mjs @@ -0,0 +1,135 @@ +const CACHE_NAME = "speedometer-cache-v4.0"; + +const BENCHMARK_STATE = { + IDLE: "IDLE", + RUNNING: "RUNNING", +}; + +import { SW_MESSAGES } from "./resources/shared/sw-messages.mjs"; + +let currentState = BENCHMARK_STATE.IDLE; +let cachedUrls = new Set(); +let cachedSuitesPrefixes = new Set(); + +function replyToClient(event, msg) { + event.ports[0].postMessage(msg); +} + +function delayAsync(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +async function handlePrecache(event, { suites = [], delay = 0 }) { + await caches.delete(CACHE_NAME); + const cache = await caches.open(CACHE_NAME); + + let loaded = 0; + let totalSize = 0; + const urlsToCache = []; + + cachedSuitesPrefixes.clear(); + for (const suite of suites) { + if (!suite.resources) continue; + const prefix = new URL(".", suite.url).href; + cachedSuitesPrefixes.add(prefix); + urlsToCache.push(...await parseSuiteResources(suite)); + } + + const total = urlsToCache.length; + const promises = urlsToCache.map(async (item, index) => { + const size = await fetchAndCache(cache, item.url, delay * index); + totalSize += size; + loaded++; + replyToClient(event, { type: SW_MESSAGES.PRECACHE_PROGRESS, loaded, total, url: item.url, suiteName: item.suiteName }); + }); + + await Promise.all(promises); + + cachedUrls = new Set(urlsToCache.map(item => item.url)); + replyToClient(event, { type: SW_MESSAGES.PRECACHE_DONE, totalSize, count: urlsToCache.length }); +} + +async function parseSuiteResources(suite) { + try { + const response = await fetch(suite.resources); + if (!response.ok) return []; + const text = await response.text(); + const lines = text.split("\n").map(l => l.trim()).filter(l => l.length > 0); + return lines.map(resourceUrl => ({ + url: new URL(resourceUrl, suite.url).href, + suiteName: suite.name + })); + } catch (e) { + console.warn("Failed to fetch resources.txt for", suite.name); + return []; + } +} + +async function fetchAndCache(cache, url, delayMs) { + if (delayMs) await delayAsync(delayMs); + const request = new Request(url, { cache: "no-cache" }); + try { + await cache.add(request); + const cachedResponse = await cache.match(request); + if (!cachedResponse) return 0; + const blob = await cachedResponse.blob(); + return blob.size; + } catch (e) { + console.warn("Cache failed for", url, e); + return 0; + } +} + +self.addEventListener("install", function () { + self.skipWaiting(); +}); + +self.addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("message", function (event) { + const { data } = event; + if (!data) return; + + if (data.type === SW_MESSAGES.SET_STATE) { + currentState = data.state; + } else if (data.type === SW_MESSAGES.PRECACHE_SUITES) { + event.waitUntil(handlePrecache(event, data)); + } +}); + +self.addEventListener("fetch", function (event) { + const isCached = cachedUrls.has(event.request.url); + + if (isCached) { + event.respondWith(handleFetch(event.request)); + return; + } + + // We only enforce strict blocking when the benchmark is actively RUNNING + // to allow runner resources to be loaded. + if (currentState !== BENCHMARK_STATE.RUNNING) return; + + let isCachedSuite = false; + for (const prefix of cachedSuitesPrefixes) { + if (event.request.url.startsWith(prefix) || event.request.referrer.startsWith(prefix)) { + isCachedSuite = true; + break; + } + } + + if (isCachedSuite) { + console.warn(`Blocked uncached request for cached suite: ${event.request.url} (referrer: ${event.request.referrer})`); + event.respondWith(Promise.resolve(Response.error())); + return; + } + // Bypass Service Worker for everything else +}); + +async function handleFetch(request) { + const cache = await caches.open(CACHE_NAME); + const cachedResponse = await cache.match(request); + if (cachedResponse) return cachedResponse; + return fetch(request); +} From 29a283c0fb7b8dd5a9e20ffadd02d7f86a40f31f Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 10 Jun 2026 16:51:57 +0200 Subject: [PATCH 2/6] add --- .../dist/resources.txt | 39 +++++++++++++++ .../javascript-wc-indexeddb/scripts/build.js | 3 +- .../todomvc-localstorage/scripts/build.js | 3 +- .../service-worker-startup/service-worker.js | 50 ------------------- resources/shared/generate-resources.mjs | 27 ++++++++++ .../angular-complex/scripts/build.js | 3 +- .../backbone-complex/scripts/build.js | 3 +- .../backbone/scripts/build.js | 3 +- .../jquery-complex/scripts/build.js | 3 +- .../jquery/scripts/build.js | 3 +- .../lit-complex/scripts/build.js | 3 +- .../preact-complex/scripts/build.js | 3 +- .../react-complex/scripts/build.js | 3 +- .../react-redux-complex/scripts/build.js | 3 +- .../svelte-complex/scripts/build.js | 3 +- .../vue-complex/scripts/build.js | 3 +- .../javascript-es5-complex/scripts/build.js | 3 +- .../javascript-es5/scripts/build.js | 3 +- .../scripts/build.js | 3 +- .../scripts/build.js | 3 +- .../scripts/build.js | 3 +- 21 files changed, 84 insertions(+), 86 deletions(-) create mode 100644 experimental/javascript-wc-indexeddb/dist/resources.txt delete mode 100644 resources/service-worker-startup/service-worker.js create mode 100644 resources/shared/generate-resources.mjs diff --git a/experimental/javascript-wc-indexeddb/dist/resources.txt b/experimental/javascript-wc-indexeddb/dist/resources.txt new file mode 100644 index 000000000..7dc424065 --- /dev/null +++ b/experimental/javascript-wc-indexeddb/dist/resources.txt @@ -0,0 +1,39 @@ +index.html +libs/dexie.mjs +src/components/todo-app/todo-app.component.js +src/components/todo-app/todo-app.template.js +src/components/todo-bottombar/todo-bottombar.component.js +src/components/todo-bottombar/todo-bottombar.template.js +src/components/todo-item/todo-item.component.js +src/components/todo-item/todo-item.template.js +src/components/todo-list/todo-list.component.js +src/components/todo-list/todo-list.template.js +src/components/todo-topbar/todo-topbar.component.js +src/components/todo-topbar/todo-topbar.template.js +src/hooks/useDoubleClick.js +src/hooks/useKeyListener.js +src/hooks/useRouter.js +src/index.mjs +src/speedometer-utils/benchmark.mjs +src/speedometer-utils/helpers.mjs +src/speedometer-utils/params.mjs +src/speedometer-utils/test-invoker.mjs +src/speedometer-utils/test-runner.mjs +src/speedometer-utils/todomvc-utils.mjs +src/speedometer-utils/translations.mjs +src/storage/base-storage-manager.js +src/storage/dexieDB-manager.js +src/storage/indexedDB-manager.js +src/storage/storage-factory.js +src/utils/nanoid.js +src/workload-test.mjs +styles/app.constructable.js +styles/bottombar.constructable.js +styles/footer.css +styles/global.constructable.js +styles/global.css +styles/header.css +styles/main.constructable.js +styles/todo-item.constructable.js +styles/todo-list.constructable.js +styles/topbar.constructable.js diff --git a/experimental/javascript-wc-indexeddb/scripts/build.js b/experimental/javascript-wc-indexeddb/scripts/build.js index b9415f1b9..723a88731 100644 --- a/experimental/javascript-wc-indexeddb/scripts/build.js +++ b/experimental/javascript-wc-indexeddb/scripts/build.js @@ -1,7 +1,6 @@ const fs = require("fs").promises; const { dirname } = require("path"); const path = require("path"); -const { generateResourcesFile } = require("../../../resources/shared/generate-resources"); /** * createDirectory @@ -166,4 +165,4 @@ const build = async () => { console.log("Done with building!"); }; -build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); +build().then(() => import("../../../resources/shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); diff --git a/experimental/todomvc-localstorage/scripts/build.js b/experimental/todomvc-localstorage/scripts/build.js index 550a45021..452400417 100644 --- a/experimental/todomvc-localstorage/scripts/build.js +++ b/experimental/todomvc-localstorage/scripts/build.js @@ -1,6 +1,5 @@ const fs = require("fs").promises; const path = require("path"); -const { generateResourcesFile } = require("../../../resources/shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -54,4 +53,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); +build().then(() => import("../../../resources/shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); diff --git a/resources/service-worker-startup/service-worker.js b/resources/service-worker-startup/service-worker.js deleted file mode 100644 index d976611f8..000000000 --- a/resources/service-worker-startup/service-worker.js +++ /dev/null @@ -1,50 +0,0 @@ -const CACHE_NAME = "sw-startup-workload-v1"; -let ASSETS = ["./app/index.html", "./app/main.mjs", "./app/style.css", "./app/work_schedule.json", "https://esm.sh/react@18", "https://esm.sh/react-dom@18/client"]; - -async function cacheAssets() { - const cache = await caches.open(CACHE_NAME); - for (const url of ASSETS) { - try { - const response = await fetch(url); - if (response.ok) { - await cache.put(url, response); - console.log("SW caching:", url); - } - } catch (e) { - console.error(`Failed to cache ${url}:`, e); - } - } -} - -async function installAssets(event) { - await cacheAssets(); - self.skipWaiting(); -} - -self.addEventListener("install", (event) => { - event.waitUntil(installAssets(event)); -}); - -self.addEventListener("activate", (event) => { - event.waitUntil(self.clients.claim()); -}); - -self.addEventListener("fetch", (event) => { - event.respondWith( - caches.match(event.request).then((response) => { - return response || fetch(event.request); - }) - ); -}); - -async function prefetchAssets(event) { - await cacheAssets(); - event?.ports[0].postMessage({ - type: "PREFETCH_COMPLETE", - }); -} - -self.addEventListener("message", (event) => { - if (event.data.type === "PREFETCH") - event.waitUntil(prefetchAssets(event)); -}); diff --git a/resources/shared/generate-resources.mjs b/resources/shared/generate-resources.mjs new file mode 100644 index 000000000..d4ce09e76 --- /dev/null +++ b/resources/shared/generate-resources.mjs @@ -0,0 +1,27 @@ +import fs from 'fs'; +import path from 'path'; + +function walkDir(dir, fileList = []) { + const files = fs.readdirSync(dir); + for (const file of files) { + const filePath = path.join(dir, file); + if (fs.statSync(filePath).isDirectory()) { + walkDir(filePath, fileList); + } else { + fileList.push(filePath); + } + } + return fileList; +} + +export function generateResourcesFile(distPath) { + if (!fs.existsSync(distPath)) { + console.warn(`Directory ${distPath} does not exist, skipping resources.txt generation.`); + return; + } + const absoluteDist = path.resolve(distPath); + const files = walkDir(absoluteDist); + const relativePaths = files.map(f => path.relative(absoluteDist, f)).filter(f => f !== 'resources.txt'); + fs.writeFileSync(path.join(absoluteDist, 'resources.txt'), relativePaths.join('\n') + '\n', 'utf8'); + console.log(`Generated resources.txt at ${distPath}`); +} diff --git a/resources/todomvc/architecture-examples/angular-complex/scripts/build.js b/resources/todomvc/architecture-examples/angular-complex/scripts/build.js index fe1b2f80c..42c51cd1d 100644 --- a/resources/todomvc/architecture-examples/angular-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/angular-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -18,4 +17,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js b/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js index 56979dbe6..abd31b40d 100644 --- a/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -18,4 +17,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/backbone/scripts/build.js b/resources/todomvc/architecture-examples/backbone/scripts/build.js index 48a4d6b3f..7a9e9e0bd 100644 --- a/resources/todomvc/architecture-examples/backbone/scripts/build.js +++ b/resources/todomvc/architecture-examples/backbone/scripts/build.js @@ -1,6 +1,5 @@ const fs = require("fs").promises; const path = require("path"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -59,4 +58,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); +build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); diff --git a/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js b/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js index 686794700..da240303a 100644 --- a/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -21,4 +20,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); \ No newline at end of file +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); \ No newline at end of file diff --git a/resources/todomvc/architecture-examples/jquery/scripts/build.js b/resources/todomvc/architecture-examples/jquery/scripts/build.js index f03383346..8ff05bbaa 100644 --- a/resources/todomvc/architecture-examples/jquery/scripts/build.js +++ b/resources/todomvc/architecture-examples/jquery/scripts/build.js @@ -1,6 +1,5 @@ const fs = require("fs").promises; const path = require("path"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -52,4 +51,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); +build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); diff --git a/resources/todomvc/architecture-examples/lit-complex/scripts/build.js b/resources/todomvc/architecture-examples/lit-complex/scripts/build.js index 8553c547e..50a585b66 100644 --- a/resources/todomvc/architecture-examples/lit-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/lit-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -25,4 +24,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/preact-complex/scripts/build.js b/resources/todomvc/architecture-examples/preact-complex/scripts/build.js index 5987ecd7e..0739fe789 100644 --- a/resources/todomvc/architecture-examples/preact-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/preact-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -16,4 +15,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/react-complex/scripts/build.js b/resources/todomvc/architecture-examples/react-complex/scripts/build.js index 4d7f98b63..53032dcfc 100644 --- a/resources/todomvc/architecture-examples/react-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/react-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -15,4 +14,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js b/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js index 159788d5c..e384b8f14 100644 --- a/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -16,4 +15,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js b/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js index 261292720..db20258c1 100644 --- a/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -16,4 +15,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/architecture-examples/vue-complex/scripts/build.js b/resources/todomvc/architecture-examples/vue-complex/scripts/build.js index 093d4cbd8..dc7e48d71 100644 --- a/resources/todomvc/architecture-examples/vue-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/vue-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -18,4 +17,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js index bc3d2c06a..93f6ddb6d 100644 --- a/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -18,4 +17,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js index 7707ef610..fbecbd875 100644 --- a/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js @@ -1,6 +1,5 @@ const fs = require("fs").promises; const path = require("path"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const rootDirectory = "./"; const sourceDirectory = "./src"; @@ -54,4 +53,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); +build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); diff --git a/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js index 8a2113d37..531d6d507 100644 --- a/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -15,4 +14,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js index 576911094..7c7a99e4e 100644 --- a/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js @@ -3,7 +3,6 @@ */ const path = require("path"); const { buildComplex } = require("big-dom-generator/utils/buildComplex"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); const options = { callerDirectory: path.resolve(__dirname), @@ -24,4 +23,4 @@ const options = { }; buildComplex(options); -generateResourcesFile(path.join(__dirname, "../dist")); +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); diff --git a/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js index b087510e3..86a42ad8c 100644 --- a/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js @@ -1,7 +1,6 @@ const fs = require("fs").promises; const { dirname } = require("path"); const path = require("path"); -const { generateResourcesFile } = require("../../../../shared/generate-resources"); /** * createDirectory @@ -155,4 +154,4 @@ const build = async () => { console.log("Done with building!"); }; -build().then(() => generateResourcesFile(path.join(__dirname, "../dist"))); +build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); From d0ba583daea4711a37761698ad2fb012be337b54 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 10 Jun 2026 17:12:03 +0200 Subject: [PATCH 3/6] add-resources --- .../javascript-wc-indexeddb/scripts/build.js | 2 +- experimental/tests.mjs | 38 ++ .../todomvc-localstorage/dist/base.css | 141 ------- .../todomvc-localstorage/dist/index.css | 393 ------------------ .../todomvc-localstorage/dist/index.html | 18 +- .../todomvc-localstorage/scripts/build.js | 2 +- resources/default-tests.mjs | 33 +- resources/main.css | 44 +- resources/main.mjs | 49 ++- resources/shared/generate-resources.mjs | 13 +- resources/shared/sw-messages.mjs | 2 +- .../angular-complex/scripts/build.js | 2 +- .../backbone-complex/scripts/build.js | 2 +- .../backbone/scripts/build.js | 2 +- .../jquery-complex/scripts/build.js | 2 +- .../jquery/scripts/build.js | 2 +- .../lit-complex/scripts/build.js | 2 +- .../preact-complex/scripts/build.js | 2 +- .../react-complex/scripts/build.js | 2 +- .../react-redux-complex/scripts/build.js | 2 +- .../svelte-complex/scripts/build.js | 2 +- .../vue-complex/scripts/build.js | 2 +- .../javascript-es5-complex/scripts/build.js | 2 +- .../javascript-es5/scripts/build.js | 2 +- .../scripts/build.js | 2 +- .../scripts/build.js | 2 +- .../scripts/build.js | 2 +- sw.mjs | 42 +- tests/unittests/suites.mjs | 27 ++ 29 files changed, 205 insertions(+), 631 deletions(-) delete mode 100644 experimental/todomvc-localstorage/dist/base.css delete mode 100644 experimental/todomvc-localstorage/dist/index.css diff --git a/experimental/javascript-wc-indexeddb/scripts/build.js b/experimental/javascript-wc-indexeddb/scripts/build.js index 723a88731..3ae0f1739 100644 --- a/experimental/javascript-wc-indexeddb/scripts/build.js +++ b/experimental/javascript-wc-indexeddb/scripts/build.js @@ -165,4 +165,4 @@ const build = async () => { console.log("Done with building!"); }; -build().then(() => import("../../../resources/shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); +build().then(() => import("../../../resources/shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist")))); diff --git a/experimental/tests.mjs b/experimental/tests.mjs index 136fb7cd5..1e54c9c99 100644 --- a/experimental/tests.mjs +++ b/experimental/tests.mjs @@ -4,9 +4,39 @@ import { numberOfItemsToAdd } from "../resources/shared/todomvc-utils.mjs"; import { freezeSuites } from "../resources/suites-helper.mjs"; export const ExperimentalSuites = freezeSuites([ + { + name: "TodoMVC-JavaScript-ES5-Cached", + url: "resources/todomvc/vanilla-examples/javascript-es5/dist/index.html", + resources: "resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt", + tags: ["todomvc", "experimental", "cached"], + async prepare(page) { + (await page.waitForElement(".new-todo")).focus(); + }, + tests: [ + new BenchmarkTestStep(`Adding${numberOfItemsToAdd}Items`, (page) => { + const newTodo = page.querySelector(".new-todo"); + for (let i = 0; i < numberOfItemsToAdd; i++) { + newTodo.setValue(getTodoText("ja", i)); + newTodo.dispatchEvent("change"); + newTodo.enter("keypress"); + } + }), + new BenchmarkTestStep("CompletingAllItems", (page) => { + const checkboxes = page.querySelectorAll(".toggle"); + for (let i = 0; i < numberOfItemsToAdd; i++) + checkboxes[i].click(); + }), + new BenchmarkTestStep("DeletingAllItems", (page) => { + const deleteButtons = page.querySelectorAll(".destroy"); + for (let i = numberOfItemsToAdd - 1; i >= 0; i--) + deleteButtons[i].click(); + }), + ], + }, { name: "TodoMVC-LocalStorage", url: "experimental/todomvc-localstorage/dist/index.html", + // resources: "experimental/todomvc-localstorage/dist/resources.txt", tags: ["todomvc", "experimental"], async prepare(page) { (await page.waitForElement(".new-todo")).focus(); @@ -36,6 +66,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "TodoMVC-Emoji", url: "resources/todomvc/vanilla-examples/javascript-web-components/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-web-components/dist/resources.txt", tags: ["todomvc", "experimental"], async prepare(page) { await page.waitForElement("todo-app"); @@ -68,6 +99,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "TodoMVC-WebComponents-PostMessage", url: "resources/todomvc/vanilla-examples/javascript-web-components/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-web-components/dist/resources.txt", tags: ["experimental", "todomvc", "webcomponents"], async prepare() {}, type: "remote", @@ -78,6 +110,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "TodoMVC-Jaspr-Dart2JS-O4", url: "experimental/todomvc-dart-jaspr/dist/out-dart2js-O4/index.html", + // resources: "experimental/todomvc-dart-jaspr/dist/out-dart2js-O4/resources.txt", tags: ["todomvc", "experimental"], async prepare(page) { (await page.waitForElement(".new-todo")).focus(); @@ -106,6 +139,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "TodoMVC-Jaspr-Dart2Wasm-O2", url: "experimental/todomvc-dart-jaspr/dist/out-dart2wasm-O2/index.html", + // resources: "experimental/todomvc-dart-jaspr/dist/out-dart2wasm-O2/resources.txt", tags: ["todomvc", "experimental"], disabled: true, async prepare(page) { @@ -135,6 +169,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "NewsSite-PostMessage", url: "resources/newssite/news-next/dist/index.html", + // resources: "resources/newssite/news-next/dist/resources.txt", tags: ["experimental", "newssite", "language"], async prepare() {}, type: "remote", @@ -145,6 +180,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "TodoMVC-WebComponents-IndexedDB", url: "experimental/javascript-wc-indexeddb/dist/index.html?useAsyncSteps=true&storageType=vanilla", + // resources: "experimental/javascript-wc-indexeddb/dist/resources.txt", tags: ["todomvc", "webcomponents", "experimental"], async prepare() {}, type: "remote", @@ -155,6 +191,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "TodoMVC-WebComponents-DexieJS", url: "experimental/javascript-wc-indexeddb/dist/index.html?useAsyncSteps=true&storageType=dexie", + // resources: "experimental/javascript-wc-indexeddb/dist/resources.txt", tags: ["todomvc", "webcomponents", "experimental"], async prepare() {}, type: "remote", @@ -165,6 +202,7 @@ export const ExperimentalSuites = freezeSuites([ { name: "Responsive-Design", url: "experimental/responsive-design/dist/index.html", + // resources: "experimental/responsive-design/dist/resources.txt", tags: ["responsive-design", "webcomponents", "experimental"], type: "async", async prepare(page) { diff --git a/experimental/todomvc-localstorage/dist/base.css b/experimental/todomvc-localstorage/dist/base.css deleted file mode 100644 index da65968a7..000000000 --- a/experimental/todomvc-localstorage/dist/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/experimental/todomvc-localstorage/dist/index.css b/experimental/todomvc-localstorage/dist/index.css deleted file mode 100644 index fcc3da583..000000000 --- a/experimental/todomvc-localstorage/dist/index.css +++ /dev/null @@ -1,393 +0,0 @@ -@charset "utf-8"; - -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #111111; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-weight: 300; -} - -.hidden { - display: none; -} - -.todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -.todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 400; - color: rgba(0, 0, 0, 0.4); -} - -.todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 400; - color: rgba(0, 0, 0, 0.4); -} - -.todoapp input::input-placeholder { - font-style: italic; - font-weight: 400; - color: rgba(0, 0, 0, 0.4); -} - -.todoapp h1 { - position: absolute; - top: -140px; - width: 100%; - font-size: 80px; - font-weight: 200; - text-align: center; - color: #b83f45; - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -.new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.new-todo { - padding: 16px 16px 16px 60px; - height: 65px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -.main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -.toggle-all { - width: 1px; - height: 1px; - border: none; /* Mobile Safari */ - opacity: 0; - position: absolute; - right: 100%; - bottom: 100%; -} - -.toggle-all + label { - display: flex; - align-items: center; - justify-content: center; - width: 45px; - height: 65px; - font-size: 0; - position: absolute; - top: -65px; - left: -0; -} - -.toggle-all + label:before { - content: '❯'; - display: inline-block; - font-size: 22px; - color: #949494; - padding: 10px 27px 10px 27px; - -webkit-transform: rotate(90deg); - transform: rotate(90deg); -} - -.toggle-all:checked + label:before { - color: #484848; -} - -.todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -.todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -.todo-list li:last-child { - border-bottom: none; -} - -.todo-list li.editing { - border-bottom: none; - padding: 0; -} - -.todo-list li.editing .edit { - display: block; - width: calc(100% - 43px); - padding: 12px 16px; - margin: 0 0 0 43px; -} - -.todo-list li.editing .view { - display: none; -} - -.todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - appearance: none; -} - -.todo-list li .toggle { - opacity: 0; -} - -.todo-list li .toggle + label { - /* - Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 - IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ - */ - background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23949494%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); - background-repeat: no-repeat; - background-position: center left; -} - -.todo-list li .toggle:checked + label { - background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E'); -} - -.todo-list li label { - word-break: break-all; - padding: 15px 15px 15px 60px; - display: block; - line-height: 1.2; - transition: color 0.4s; - font-weight: 400; - color: #484848; -} - -.todo-list li.completed label { - color: #949494; - text-decoration: line-through; -} - -.todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #949494; - transition: color 0.2s ease-out; -} - -.todo-list li .destroy:hover, -.todo-list li .destroy:focus { - color: #C18585; -} - -.todo-list li .destroy:after { - content: '×'; - display: block; - height: 100%; - line-height: 1.1; -} - -.todo-list li:hover .destroy { - display: block; -} - -.todo-list li .edit { - display: none; -} - -.todo-list li.editing:last-child { - margin-bottom: -1px; -} - -.footer { - padding: 10px 15px; - height: 20px; - text-align: center; - font-size: 15px; - border-top: 1px solid #e6e6e6; -} - -.footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -.todo-count { - float: left; - text-align: left; -} - -.todo-count strong { - font-weight: 300; -} - -.filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -.filters li { - display: inline; -} - -.filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -.filters li a:hover { - border-color: #DB7676; -} - -.filters li a.selected { - border-color: #CE4646; -} - -.clear-completed, -html .clear-completed:active { - float: right; - position: relative; - line-height: 19px; - text-decoration: none; - cursor: pointer; -} - -.clear-completed:hover { - text-decoration: underline; -} - -.info { - margin: 65px auto 0; - color: #4d4d4d; - font-size: 11px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -.info p { - line-height: 1; -} - -.info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -.info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - .toggle-all, - .todo-list li .toggle { - background: none; - } - - .todo-list li .toggle { - height: 40px; - } -} - -@media (max-width: 430px) { - .footer { - height: 50px; - } - - .filters { - bottom: 10px; - } -} - -:focus, -.toggle:focus + label, -.toggle-all:focus + label { - box-shadow: 0 0 2px 2px #CF7D7D; - outline: 0; -} diff --git a/experimental/todomvc-localstorage/dist/index.html b/experimental/todomvc-localstorage/dist/index.html index ab573ce1b..2b016620f 100644 --- a/experimental/todomvc-localstorage/dist/index.html +++ b/experimental/todomvc-localstorage/dist/index.html @@ -6,8 +6,8 @@ TodoMVC: JavaScript Es5 - - + +
@@ -44,12 +44,12 @@

todos

Press 'enter' to add the todo.

Double-click to edit a todo

- - - - - - - + + + + + + + diff --git a/experimental/todomvc-localstorage/scripts/build.js b/experimental/todomvc-localstorage/scripts/build.js index 452400417..9997908a4 100644 --- a/experimental/todomvc-localstorage/scripts/build.js +++ b/experimental/todomvc-localstorage/scripts/build.js @@ -53,4 +53,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => import("../../../resources/shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); +build().then(() => import("../../../resources/shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist")))); diff --git a/resources/default-tests.mjs b/resources/default-tests.mjs index 51cb0d2d6..b33553e8d 100644 --- a/resources/default-tests.mjs +++ b/resources/default-tests.mjs @@ -7,7 +7,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-JavaScript-ES5", url: "resources/todomvc/vanilla-examples/javascript-es5/dist/index.html", - resources: "resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt", + // resources: "resources/todomvc/vanilla-examples/javascript-es5/dist/resources.txt", tags: ["default", "todomvc"], async prepare(page) { (await page.waitForElement(".new-todo")).focus(); @@ -36,6 +36,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-JavaScript-ES5-Complex-DOM", url: "resources/todomvc/vanilla-examples/javascript-es5-complex/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-es5-complex/dist/resources.txt", tags: ["todomvc", "complex"], async prepare(page) { (await page.waitForElement(".new-todo")).focus(); @@ -64,6 +65,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-JavaScript-ES6-Webpack", url: "resources/todomvc/vanilla-examples/javascript-es6-webpack/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-es6-webpack/dist/resources.txt", tags: ["todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -93,6 +95,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-JavaScript-ES6-Webpack-Complex-DOM", url: "resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/dist/resources.txt", tags: ["default", "todomvc", "complex", "complex-default"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -122,6 +125,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-WebComponents", url: "resources/todomvc/vanilla-examples/javascript-web-components/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-web-components/dist/resources.txt", tags: ["default", "todomvc", "webcomponents"], async prepare(page) { await page.waitForElement("todo-app"); @@ -154,6 +158,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-WebComponents-Complex-DOM", url: "resources/todomvc/vanilla-examples/javascript-web-components-complex/dist/index.html", + // resources: "resources/todomvc/vanilla-examples/javascript-web-components-complex/dist/resources.txt", tags: ["todomvc", "webcomponents", "complex"], async prepare(page) { await page.waitForElement("todo-app"); @@ -186,6 +191,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-React", url: "resources/todomvc/architecture-examples/react/dist/index.html#/home", + // resources: "resources/todomvc/architecture-examples/react/dist/resources.txt", tags: ["todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -215,6 +221,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-React-Complex-DOM", url: "resources/todomvc/architecture-examples/react-complex/dist/index.html#/home", + // resources: "resources/todomvc/architecture-examples/react-complex/dist/resources.txt", tags: ["default", "todomvc", "complex", "complex-default"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -244,6 +251,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-React-Redux", url: "resources/todomvc/architecture-examples/react-redux/dist/index.html", + // resources: "resources/todomvc/architecture-examples/react-redux/dist/resources.txt", tags: ["default", "todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -272,6 +280,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-React-Redux-Complex-DOM", url: "resources/todomvc/architecture-examples/react-redux-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/react-redux-complex/dist/resources.txt", tags: ["todomvc", "complex"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -300,6 +309,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Backbone", url: "resources/todomvc/architecture-examples/backbone/dist/index.html", + // resources: "resources/todomvc/architecture-examples/backbone/dist/resources.txt", tags: ["default", "todomvc"], async prepare(page) { await page.waitForElement("#appIsReady"); @@ -330,6 +340,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Backbone-Complex-DOM", url: "resources/todomvc/architecture-examples/backbone-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/backbone-complex/dist/resources.txt", tags: ["todomvc", "complex"], async prepare(page) { await page.waitForElement("#appIsReady"); @@ -360,6 +371,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Angular", url: "resources/todomvc/architecture-examples/angular/dist/index.html", + // resources: "resources/todomvc/architecture-examples/angular/dist/resources.txt", tags: ["todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -389,6 +401,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Angular-Complex-DOM", url: "resources/todomvc/architecture-examples/angular-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/angular-complex/dist/resources.txt", tags: ["default", "todomvc", "complex", "complex-default"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -418,6 +431,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Vue", url: "resources/todomvc/architecture-examples/vue/dist/index.html", + // resources: "resources/todomvc/architecture-examples/vue/dist/resources.txt", tags: ["default", "todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -447,6 +461,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Vue-Complex-DOM", url: "resources/todomvc/architecture-examples/vue-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/vue-complex/dist/resources.txt", tags: ["todomvc", "complex", "complex-default"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -476,6 +491,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-jQuery", url: "resources/todomvc/architecture-examples/jquery/dist/index.html", + // resources: "resources/todomvc/architecture-examples/jquery/dist/resources.txt", tags: ["default", "todomvc"], async prepare(page) { await page.waitForElement("#appIsReady"); @@ -503,6 +519,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-jQuery-Complex-DOM", url: "resources/todomvc/architecture-examples/jquery-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/jquery-complex/dist/resources.txt", tags: ["todomvc", "complex"], async prepare(page) { await page.waitForElement("#appIsReady"); @@ -530,6 +547,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Preact", url: "resources/todomvc/architecture-examples/preact/dist/index.html#/home", + // resources: "resources/todomvc/architecture-examples/preact/dist/resources.txt", tags: ["todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -558,6 +576,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Preact-Complex-DOM", url: "resources/todomvc/architecture-examples/preact-complex/dist/index.html#/home", + // resources: "resources/todomvc/architecture-examples/preact-complex/dist/resources.txt", tags: ["default", "todomvc", "complex", "complex-default"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -586,6 +605,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Svelte", url: "resources/todomvc/architecture-examples/svelte/dist/index.html", + // resources: "resources/todomvc/architecture-examples/svelte/dist/resources.txt", tags: ["todomvc"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -614,6 +634,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Svelte-Complex-DOM", url: "resources/todomvc/architecture-examples/svelte-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/svelte-complex/dist/resources.txt", tags: ["default", "todomvc", "complex", "complex-default"], async prepare(page) { const element = await page.waitForElement(".new-todo"); @@ -642,6 +663,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Lit", url: "resources/todomvc/architecture-examples/lit/dist/index.html", + // resources: "resources/todomvc/architecture-examples/lit/dist/resources.txt", tags: ["todomvc", "webcomponents"], async prepare(page) { await page.waitForElement("todo-app"); @@ -673,6 +695,7 @@ export const DefaultSuites = freezeSuites([ { name: "TodoMVC-Lit-Complex-DOM", url: "resources/todomvc/architecture-examples/lit-complex/dist/index.html", + // resources: "resources/todomvc/architecture-examples/lit-complex/dist/resources.txt", tags: ["default", "todomvc", "webcomponents", "complex", "complex-default"], async prepare(page) { await page.waitForElement("todo-app"); @@ -704,6 +727,7 @@ export const DefaultSuites = freezeSuites([ { name: "NewsSite-Next", url: "resources/newssite/news-next/dist/index.html", + // resources: "resources/newssite/news-next/dist/resources.txt", tags: ["default", "newssite", "language"], async prepare(page) { await page.waitForElement("#navbar-dropdown-toggle"); @@ -744,6 +768,7 @@ export const DefaultSuites = freezeSuites([ { name: "NewsSite-Nuxt", url: "resources/newssite/news-nuxt/dist/index.html", + // resources: "resources/newssite/news-nuxt/dist/resources.txt", tags: ["default", "newssite"], async prepare(page) { await page.waitForElement("#navbar-dropdown-toggle"); @@ -784,6 +809,7 @@ export const DefaultSuites = freezeSuites([ { name: "Editor-CodeMirror", url: "resources/editors/dist/codemirror.html", + // resources: "resources/editors/dist/resources.txt", tags: ["default", "editor"], async prepare(page) {}, tests: [ @@ -802,6 +828,7 @@ export const DefaultSuites = freezeSuites([ { name: "Editor-TipTap", url: "resources/editors/dist/tiptap.html", + // resources: "resources/editors/dist/resources.txt", tags: ["default", "editor"], async prepare(page) {}, tests: [ @@ -820,6 +847,7 @@ export const DefaultSuites = freezeSuites([ { name: "Charts-observable-plot", url: "resources/charts/dist/observable-plot.html", + // resources: "resources/charts/dist/resources.txt", tags: ["default", "chart"], async prepare(page) {}, tests: [ @@ -846,6 +874,7 @@ export const DefaultSuites = freezeSuites([ { name: "Charts-chartjs", url: "resources/charts/dist/chartjs.html", + // resources: "resources/charts/dist/resources.txt", tags: ["default", "chart"], async prepare(page) {}, tests: [ @@ -865,6 +894,7 @@ export const DefaultSuites = freezeSuites([ { name: "React-Stockcharts-SVG", url: "resources/react-stockcharts/build/index.html?type=svg", + // resources: "resources/react-stockcharts/build/resources.txt", tags: ["default", "chart", "svg"], async prepare(page) { await page.waitForElement("#render"); @@ -904,6 +934,7 @@ export const DefaultSuites = freezeSuites([ { name: "Perf-Dashboard", url: "resources/perf.webkit.org/public/v3/#/charts/?since=1678991819934&paneList=((55-1974-null-null-(5-2.5-500)))", + // resources: "resources/perf.webkit.org/public/v3/resources.txt", tags: ["default", "chart", "webcomponents"], async prepare(page) { await page.waitForElement("#app-is-ready"); diff --git a/resources/main.css b/resources/main.css index 40929ff96..a7e569a64 100644 --- a/resources/main.css +++ b/resources/main.css @@ -383,7 +383,8 @@ button.show-about { display: block; } -#progress, #preload-progress { +#progress, +#preload-progress { position: absolute; bottom: -6px; left: 60px; @@ -393,7 +394,8 @@ button.show-about { border-right: 6px solid var(--background); } -#progress-completed, #preload-progress-completed { +#progress-completed, +#preload-progress-completed { position: absolute; top: 0; left: 0; @@ -404,11 +406,13 @@ button.show-about { background-color: var(--inactive-color); } -#progress-completed::-webkit-progress-value, #preload-progress-completed::-webkit-progress-value { +#progress-completed::-webkit-progress-value, +#preload-progress-completed::-webkit-progress-value { background-color: var(--foreground); } -#progress-completed::-moz-progress-bar, #preload-progress-completed::-moz-progress-bar { +#progress-completed::-moz-progress-bar, +#preload-progress-completed::-moz-progress-bar { background-color: var(--foreground); } @@ -419,7 +423,8 @@ button.show-about { background-color: var(--background); } -#info, #preload-info { +#info, +#preload-info { display: none; position: absolute; bottom: -25px; @@ -430,11 +435,13 @@ button.show-about { text-align: center; font-size: 12px; } -#info-label, #preload-info-label { +#info-label, +#preload-info-label { position: absolute; left: 6px; } -#info-progress, #preload-info-progress { +#info-progress, +#preload-info-progress { position: absolute; right: 6px; text-align: right; @@ -911,7 +918,8 @@ section#about .note { stroke: #fff; } -#preload-progress, #preload-info { +#preload-progress, +#preload-info { display: none; } @@ -926,23 +934,17 @@ section#about .note { [data-benchmark-state="PRELOADING"] .start-tests-button { color: var(--foreground); - background-image: - linear-gradient( - -45deg, - var(--foreground) 15%, - transparent 15%, - transparent 50%, - var(--foreground) 50%, - var(--foreground) 65%, - transparent 65%, - transparent 100% - ); + background-image: linear-gradient(-45deg, var(--foreground) 15%, transparent 15%, transparent 50%, var(--foreground) 50%, var(--foreground) 65%, transparent 65%, transparent 100%); background-size: 20px 20px; animation: barber-pole 1s linear infinite; pointer-events: none; } @keyframes barber-pole { - 0% { background-position: 0 0, 0 0; } - 100% { background-position: 20px 0, 0 0; } + 0% { + background-position: 0 0, 0 0; + } + 100% { + background-position: 20px 0, 0 0; + } } diff --git a/resources/main.mjs b/resources/main.mjs index 02799c984..6b7c35e31 100644 --- a/resources/main.mjs +++ b/resources/main.mjs @@ -13,9 +13,8 @@ export class PreloadServiceWorker { async setup() { const existingRegistrations = await navigator.serviceWorker.getRegistrations(); - for (const existing of existingRegistrations) { + for (const existing of existingRegistrations) await existing.unregister(); - } this.registration = await navigator.serviceWorker.register("/sw.mjs", { type: "module" }); await this.registration.update(); @@ -26,18 +25,22 @@ export class PreloadServiceWorker { } async precacheSuites(suites, resourceLoadDelay, onProgress) { - if (suites.length === 0) return; + if (suites.length === 0) + return; - const suitesData = suites.filter(s => s.resources).map(s => ({ - name: s.name, - url: new URL(s.url, window.location.href).href, - resources: new URL(s.resources, window.location.href).href - })); + const suitesData = suites + .filter((s) => s.resources) + .map((s) => ({ + name: s.name, + url: new URL(s.url, window.location.href).href, + resources: new URL(s.resources, window.location.href).href, + })); - if (suitesData.length === 0) return; + if (suitesData.length === 0) + return; const startTime = performance.now(); - return new Promise((resolve) => { + await new Promise((resolve) => { const channel = new MessageChannel(); channel.port1.onmessage = (event) => { if (event.data?.type === SW_MESSAGES.PRECACHE_DONE) { @@ -51,11 +54,14 @@ export class PreloadServiceWorker { onProgress(event.data); } }; - this.sw.postMessage({ - type: SW_MESSAGES.PRECACHE_SUITES, - suites: suitesData, - delay: resourceLoadDelay - }, [channel.port2]); + this.sw.postMessage( + { + type: SW_MESSAGES.PRECACHE_SUITES, + suites: suitesData, + delay: resourceLoadDelay, + }, + [channel.port2] + ); }); } @@ -64,7 +70,6 @@ export class PreloadServiceWorker { } } - const BENCHMARK_STATE = Object.freeze({ IDLE: "IDLE", PRELOADING: "PRELOADING", @@ -84,10 +89,7 @@ class MainBenchmarkClient { _progressCompleted = null; _isRunning = false; _hasResults = false; - _developerModeContainer = null; _metrics = Object.create(null); - _developerModeContainer = null; - _progressCompleted = null; preloadServiceWorker = new PreloadServiceWorker(); _steppingPromise = null; _steppingResolver = null; @@ -475,9 +477,8 @@ class MainBenchmarkClient { _setBenchmarkState(state) { document.body.setAttribute("data-benchmark-state", state); - if (this.preloadServiceWorker) { + if (this.preloadServiceWorker) this.preloadServiceWorker.setState(state); - } const startButton = document.querySelector(".start-tests-button"); if (state === BENCHMARK_STATE.PRELOADING) { @@ -485,10 +486,12 @@ class MainBenchmarkClient { document.getElementById("preload-info-label").textContent = "Connecting to Service Worker..."; document.getElementById("preload-info-progress").textContent = ""; document.body.style.setProperty("--preload-progress", "0%"); - if (startButton) startButton.textContent = "Preloading..."; + if (startButton) + startButton.textContent = "Preloading..."; } else if (state === BENCHMARK_STATE.READY || state === BENCHMARK_STATE.IDLE || state === BENCHMARK_STATE.DONE || state === BENCHMARK_STATE.ERROR) { document.body.style.removeProperty("--preload-progress"); - if (startButton) startButton.textContent = "Start Test"; + if (startButton) + startButton.textContent = "Start Test"; } } diff --git a/resources/shared/generate-resources.mjs b/resources/shared/generate-resources.mjs index d4ce09e76..8fb54f656 100644 --- a/resources/shared/generate-resources.mjs +++ b/resources/shared/generate-resources.mjs @@ -1,15 +1,14 @@ -import fs from 'fs'; -import path from 'path'; +import fs from "fs"; +import path from "path"; function walkDir(dir, fileList = []) { const files = fs.readdirSync(dir); for (const file of files) { const filePath = path.join(dir, file); - if (fs.statSync(filePath).isDirectory()) { + if (fs.statSync(filePath).isDirectory()) walkDir(filePath, fileList); - } else { + else fileList.push(filePath); - } } return fileList; } @@ -21,7 +20,7 @@ export function generateResourcesFile(distPath) { } const absoluteDist = path.resolve(distPath); const files = walkDir(absoluteDist); - const relativePaths = files.map(f => path.relative(absoluteDist, f)).filter(f => f !== 'resources.txt'); - fs.writeFileSync(path.join(absoluteDist, 'resources.txt'), relativePaths.join('\n') + '\n', 'utf8'); + const relativePaths = files.map((f) => path.relative(absoluteDist, f)).filter((f) => f !== "resources.txt"); + fs.writeFileSync(path.join(absoluteDist, "resources.txt"), `${relativePaths.join("\n")}\n`, "utf8"); console.log(`Generated resources.txt at ${distPath}`); } diff --git a/resources/shared/sw-messages.mjs b/resources/shared/sw-messages.mjs index 251d23d28..076a84733 100644 --- a/resources/shared/sw-messages.mjs +++ b/resources/shared/sw-messages.mjs @@ -2,5 +2,5 @@ export const SW_MESSAGES = Object.freeze({ SET_STATE: "SET_STATE", PRECACHE_SUITES: "PRECACHE_SUITES", PRECACHE_PROGRESS: "PRECACHE_PROGRESS", - PRECACHE_DONE: "PRECACHE_DONE" + PRECACHE_DONE: "PRECACHE_DONE", }); diff --git a/resources/todomvc/architecture-examples/angular-complex/scripts/build.js b/resources/todomvc/architecture-examples/angular-complex/scripts/build.js index 42c51cd1d..cd2122f8d 100644 --- a/resources/todomvc/architecture-examples/angular-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/angular-complex/scripts/build.js @@ -17,4 +17,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js b/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js index abd31b40d..11bb3f4cc 100644 --- a/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/backbone-complex/scripts/build.js @@ -17,4 +17,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/backbone/scripts/build.js b/resources/todomvc/architecture-examples/backbone/scripts/build.js index 7a9e9e0bd..6e6de847e 100644 --- a/resources/todomvc/architecture-examples/backbone/scripts/build.js +++ b/resources/todomvc/architecture-examples/backbone/scripts/build.js @@ -58,4 +58,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); +build().then(() => import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist")))); diff --git a/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js b/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js index da240303a..e8b8ecb18 100644 --- a/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/jquery-complex/scripts/build.js @@ -20,4 +20,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); \ No newline at end of file +import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/jquery/scripts/build.js b/resources/todomvc/architecture-examples/jquery/scripts/build.js index 8ff05bbaa..25a15a106 100644 --- a/resources/todomvc/architecture-examples/jquery/scripts/build.js +++ b/resources/todomvc/architecture-examples/jquery/scripts/build.js @@ -51,4 +51,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); +build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist")))); diff --git a/resources/todomvc/architecture-examples/lit-complex/scripts/build.js b/resources/todomvc/architecture-examples/lit-complex/scripts/build.js index 50a585b66..32c17118d 100644 --- a/resources/todomvc/architecture-examples/lit-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/lit-complex/scripts/build.js @@ -24,4 +24,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/preact-complex/scripts/build.js b/resources/todomvc/architecture-examples/preact-complex/scripts/build.js index 0739fe789..62beccabc 100644 --- a/resources/todomvc/architecture-examples/preact-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/preact-complex/scripts/build.js @@ -15,4 +15,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/react-complex/scripts/build.js b/resources/todomvc/architecture-examples/react-complex/scripts/build.js index 53032dcfc..7b309f619 100644 --- a/resources/todomvc/architecture-examples/react-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/react-complex/scripts/build.js @@ -14,4 +14,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js b/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js index e384b8f14..064cde823 100644 --- a/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/react-redux-complex/scripts/build.js @@ -15,4 +15,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js b/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js index db20258c1..675be7bcc 100644 --- a/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/svelte-complex/scripts/build.js @@ -15,4 +15,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/architecture-examples/vue-complex/scripts/build.js b/resources/todomvc/architecture-examples/vue-complex/scripts/build.js index dc7e48d71..b72c4f84c 100644 --- a/resources/todomvc/architecture-examples/vue-complex/scripts/build.js +++ b/resources/todomvc/architecture-examples/vue-complex/scripts/build.js @@ -17,4 +17,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js index 93f6ddb6d..973da1277 100644 --- a/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es5-complex/scripts/build.js @@ -17,4 +17,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js index fbecbd875..3afe9ffa0 100644 --- a/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es5/scripts/build.js @@ -53,4 +53,4 @@ const build = async () => { console.log("done!!"); }; -build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); +build().then(() => import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist")))); diff --git a/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js index 531d6d507..c2b6e3983 100644 --- a/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-es6-webpack-complex/scripts/build.js @@ -14,4 +14,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js index 7c7a99e4e..e80d21879 100644 --- a/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-web-components-complex/scripts/build.js @@ -23,4 +23,4 @@ const options = { }; buildComplex(options); -import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));); +import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist"))); diff --git a/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js b/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js index 86a42ad8c..7d9f8e924 100644 --- a/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js +++ b/resources/todomvc/vanilla-examples/javascript-web-components/scripts/build.js @@ -154,4 +154,4 @@ const build = async () => { console.log("Done with building!"); }; -build().then(() => import("../../../../shared/generate-resources.mjs").then(m => m.generateResourcesFile(path.join(__dirname, "../dist"));)); +build().then(() => import("../../../../shared/generate-resources.mjs").then((m) => m.generateResourcesFile(path.join(__dirname, "../dist")))); diff --git a/sw.mjs b/sw.mjs index 7959db98e..dd906063c 100644 --- a/sw.mjs +++ b/sw.mjs @@ -16,7 +16,7 @@ function replyToClient(event, msg) { } function delayAsync(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } async function handlePrecache(event, { suites = [], delay = 0 }) { @@ -29,7 +29,8 @@ async function handlePrecache(event, { suites = [], delay = 0 }) { cachedSuitesPrefixes.clear(); for (const suite of suites) { - if (!suite.resources) continue; + if (!suite.resources) + continue; const prefix = new URL(".", suite.url).href; cachedSuitesPrefixes.add(prefix); urlsToCache.push(...await parseSuiteResources(suite)); @@ -45,19 +46,22 @@ async function handlePrecache(event, { suites = [], delay = 0 }) { await Promise.all(promises); - cachedUrls = new Set(urlsToCache.map(item => item.url)); + cachedUrls = new Set(urlsToCache.map((item) => item.url)); replyToClient(event, { type: SW_MESSAGES.PRECACHE_DONE, totalSize, count: urlsToCache.length }); } async function parseSuiteResources(suite) { try { const response = await fetch(suite.resources); - if (!response.ok) return []; - const text = await response.text(); - const lines = text.split("\n").map(l => l.trim()).filter(l => l.length > 0); - return lines.map(resourceUrl => ({ + if (!response.ok) + return []; + let text = await response.text(); + if (text.endsWith("\n")) + text = text.slice(0, -1); + const lines = text.split("\n").map((l) => l.trim()); + return lines.map((resourceUrl) => ({ url: new URL(resourceUrl, suite.url).href, - suiteName: suite.name + suiteName: suite.name, })); } catch (e) { console.warn("Failed to fetch resources.txt for", suite.name); @@ -66,12 +70,14 @@ async function parseSuiteResources(suite) { } async function fetchAndCache(cache, url, delayMs) { - if (delayMs) await delayAsync(delayMs); + if (delayMs) + await delayAsync(delayMs); const request = new Request(url, { cache: "no-cache" }); try { await cache.add(request); const cachedResponse = await cache.match(request); - if (!cachedResponse) return 0; + if (!cachedResponse) + return 0; const blob = await cachedResponse.blob(); return blob.size; } catch (e) { @@ -90,13 +96,13 @@ self.addEventListener("activate", function (event) { self.addEventListener("message", function (event) { const { data } = event; - if (!data) return; + if (!data) + return; - if (data.type === SW_MESSAGES.SET_STATE) { + if (data.type === SW_MESSAGES.SET_STATE) currentState = data.state; - } else if (data.type === SW_MESSAGES.PRECACHE_SUITES) { + else if (data.type === SW_MESSAGES.PRECACHE_SUITES) event.waitUntil(handlePrecache(event, data)); - } }); self.addEventListener("fetch", function (event) { @@ -107,9 +113,10 @@ self.addEventListener("fetch", function (event) { return; } - // We only enforce strict blocking when the benchmark is actively RUNNING + // We only enforce strict blocking when the benchmark is actively RUNNING // to allow runner resources to be loaded. - if (currentState !== BENCHMARK_STATE.RUNNING) return; + if (currentState !== BENCHMARK_STATE.RUNNING) + return; let isCachedSuite = false; for (const prefix of cachedSuitesPrefixes) { @@ -130,6 +137,7 @@ self.addEventListener("fetch", function (event) { async function handleFetch(request) { const cache = await caches.open(CACHE_NAME); const cachedResponse = await cache.match(request); - if (cachedResponse) return cachedResponse; + if (cachedResponse) + return cachedResponse; return fetch(request); } diff --git a/tests/unittests/suites.mjs b/tests/unittests/suites.mjs index 243b3a98d..86b520ad8 100644 --- a/tests/unittests/suites.mjs +++ b/tests/unittests/suites.mjs @@ -64,6 +64,33 @@ for (const [name, suites] of Object.entries(Suites)) { expect(suite.url.length).to.be.greaterThan(0); }); }); + it("should have resources.txt listing only valid files", async () => { + const baseUrl = `${window.location.origin}/`; + for (const suite of suites) { + if (!suite.resources) + continue; + const resourcesUrl = new URL(suite.resources, baseUrl).href; + const res = await fetch(resourcesUrl); + expect(res.ok).to.be(true); + let text = await res.text(); + expect(text.endsWith("\n")).to.be(true); + text = text.slice(0, -1); + + const files = text.split("\n"); + for (const file of files) + expect(file.trim().length).to.be.greaterThan(0); + + await Promise.all( + files.map(async (file) => { + const fileUrl = new URL(file, resourcesUrl).href; + const fileRes = await fetch(fileUrl, { method: "HEAD" }); + if (!fileRes.ok) + throw new Error(`Failed to load ${fileUrl} (listed in ${resourcesUrl})`); + expect(fileRes.ok).to.be(true); + }) + ); + } + }); }); } From f4b8259c0de90e575d488658d4ad94e5e87d6e96 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 10 Jun 2026 17:36:07 +0200 Subject: [PATCH 4/6] more-sw-fixes --- .../dist/src/speedometer-utils/params.mjs | 7 +++ .../responsive-design/package-lock.json | 4 ++ .../todomvc-localstorage/dist/index.html | 18 +++--- .../todomvc-localstorage/package-lock.json | 4 +- resources/main.mjs | 55 ++++++++++++------- resources/shared/params.mjs | 4 ++ resources/suite-runner.mjs | 6 +- sw.mjs | 37 ++++++++----- tests/unittests/params.mjs | 18 ++++++ tests/unittests/suites.mjs | 7 +-- 10 files changed, 109 insertions(+), 51 deletions(-) diff --git a/experimental/javascript-wc-indexeddb/dist/src/speedometer-utils/params.mjs b/experimental/javascript-wc-indexeddb/dist/src/speedometer-utils/params.mjs index 520679d24..d15b68d76 100644 --- a/experimental/javascript-wc-indexeddb/dist/src/speedometer-utils/params.mjs +++ b/experimental/javascript-wc-indexeddb/dist/src/speedometer-utils/params.mjs @@ -35,6 +35,8 @@ export class Params { measurePrepare = false; // External config url to override internal tests. config = ""; + // Resource load delay in ms for the service worker pre-caching. + resourceLoadDelay = 0; constructor(searchParams = undefined) { if (searchParams) @@ -68,6 +70,7 @@ export class Params { this.layoutMode = this._parseEnumParam(searchParams, "layoutMode", LAYOUT_MODES); this.measurePrepare = this._parseBooleanParam(searchParams, "measurePrepare"); this.config = this._parseConfig(searchParams); + this.resourceLoadDelay = this._parseIntParam(searchParams, "resourceLoadDelay", 0); const unused = Array.from(searchParams.keys()); if (unused.length > 0) @@ -203,6 +206,10 @@ export class Params { toSearchParams() { return this.toSearchParamsObject().toString(); } + + isDefault() { + return this === defaultParams; + } } function isValidJsonUrl(url) { diff --git a/experimental/responsive-design/package-lock.json b/experimental/responsive-design/package-lock.json index b78e3b8fb..b50e599b3 100644 --- a/experimental/responsive-design/package-lock.json +++ b/experimental/responsive-design/package-lock.json @@ -1015,6 +1015,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1219,6 +1220,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -3327,6 +3329,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -4132,6 +4135,7 @@ "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.7" }, diff --git a/experimental/todomvc-localstorage/dist/index.html b/experimental/todomvc-localstorage/dist/index.html index 2b016620f..ab573ce1b 100644 --- a/experimental/todomvc-localstorage/dist/index.html +++ b/experimental/todomvc-localstorage/dist/index.html @@ -6,8 +6,8 @@ TodoMVC: JavaScript Es5 - - + +
@@ -44,12 +44,12 @@

todos

Press 'enter' to add the todo.

Double-click to edit a todo

- - - - - - - + + + + + + + diff --git a/experimental/todomvc-localstorage/package-lock.json b/experimental/todomvc-localstorage/package-lock.json index 140b1e004..22d21efe6 100644 --- a/experimental/todomvc-localstorage/package-lock.json +++ b/experimental/todomvc-localstorage/package-lock.json @@ -1,11 +1,11 @@ { - "name": "todomvc-javascript-es5", + "name": "todomvc-localstorage", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "todomvc-javascript-es5", + "name": "todomvc-localstorage", "version": "1.0.0", "dependencies": { "todomvc-app-css": "^2.4.2", diff --git a/resources/main.mjs b/resources/main.mjs index 6b7c35e31..456c40c55 100644 --- a/resources/main.mjs +++ b/resources/main.mjs @@ -24,7 +24,7 @@ export class PreloadServiceWorker { return true; } - async precacheSuites(suites, resourceLoadDelay, onProgress) { + async precacheSuites(suites, resourceLoadDelay, clearCache = true, onProgress) { if (suites.length === 0) return; @@ -59,6 +59,7 @@ export class PreloadServiceWorker { type: SW_MESSAGES.PRECACHE_SUITES, suites: suitesData, delay: resourceLoadDelay, + clearCache: clearCache, }, [channel.port2] ); @@ -146,10 +147,13 @@ class MainBenchmarkClient { if (this._isRunning) return false; - this._setBenchmarkState(BENCHMARK_STATE.RUNNING); - const { benchmarkConfigurator } = await this._benchmarkConfiguratorPromise; + if (!params.isDefault()) + await this._cacheResources(benchmarkConfigurator); + + this._setBenchmarkState(BENCHMARK_STATE.RUNNING); + const enabledSuites = benchmarkConfigurator.suites.filter((suite) => suite.enabled); const totalSuitesCount = enabledSuites.length; @@ -445,29 +449,42 @@ class MainBenchmarkClient { async _setupServiceWorker(benchmarkConfigurator) { await this.preloadServiceWorker.setup(); + await this._cacheResources(benchmarkConfigurator); + } - this._setBenchmarkState(BENCHMARK_STATE.PRELOADING); + async _cacheResources(benchmarkConfigurator) { const enabledSuites = benchmarkConfigurator.suites.filter((suite) => suite.enabled); + const clearCache = !params.isDefault(); + this._setBenchmarkState(BENCHMARK_STATE.PRELOADING); try { - await this.preloadServiceWorker.precacheSuites(enabledSuites, params.resourceLoadDelay, (progressData) => { - const { loaded, total, url, suiteName } = progressData; - document.body.style.setProperty("--preload-progress", `${total > 0 ? (loaded / total) * 100 : 100}%`); - document.getElementById("preload-progress-completed").max = total; - document.getElementById("preload-progress-completed").value = loaded; - const filename = url ? url.substring(url.lastIndexOf("/") + 1) : ""; - const labelText = suiteName ? `${suiteName}: ${filename}` : filename; - document.getElementById("preload-info-label").textContent = labelText; - document.getElementById("preload-info-progress").textContent = `${loaded} / ${total}`; - }); + await this.preloadServiceWorker.precacheSuites( + enabledSuites, + params.resourceLoadDelay, + clearCache, + this._updateCacheProgress.bind(this) + ); + this._didInitialPrecache = true; this._enableStartButtons(); } catch (error) { - console.error("Service Worker setup failed:", error); + console.error("Service Worker precache failed:", error); this._setBenchmarkState(BENCHMARK_STATE.ERROR); this._enableStartButtons(); } } + _updateCacheProgress(progressData) { + const { loaded, total, url, suiteName } = progressData; + document.body.style.setProperty("--preload-progress", `${total > 0 ? (loaded / total) * 100 : 100}%`); + const progress = document.getElementById("preload-progress-completed") + progress.max = total; + progress.value = loaded; + const filename = url ? url.substring(url.lastIndexOf("/") + 1) : ""; + const labelText = suiteName ? `${suiteName}: ${filename}` : filename; + document.getElementById("preload-info-label").textContent = labelText; + document.getElementById("preload-info-progress").textContent = `${loaded} / ${total}`; + } + _enableStartButtons() { this._setBenchmarkState(BENCHMARK_STATE.READY); document.querySelectorAll(".start-tests-button").forEach((button) => { @@ -483,15 +500,13 @@ class MainBenchmarkClient { const startButton = document.querySelector(".start-tests-button"); if (state === BENCHMARK_STATE.PRELOADING) { document.getElementById("preload-progress-completed").value = 0; - document.getElementById("preload-info-label").textContent = "Connecting to Service Worker..."; + document.getElementById("preload-info-label").textContent = ""; document.getElementById("preload-info-progress").textContent = ""; document.body.style.setProperty("--preload-progress", "0%"); - if (startButton) - startButton.textContent = "Preloading..."; + startButton.textContent = "Preloading..."; } else if (state === BENCHMARK_STATE.READY || state === BENCHMARK_STATE.IDLE || state === BENCHMARK_STATE.DONE || state === BENCHMARK_STATE.ERROR) { document.body.style.removeProperty("--preload-progress"); - if (startButton) - startButton.textContent = "Start Test"; + startButton.textContent = "Start Test"; } } diff --git a/resources/shared/params.mjs b/resources/shared/params.mjs index bc8ba51bb..d15b68d76 100644 --- a/resources/shared/params.mjs +++ b/resources/shared/params.mjs @@ -206,6 +206,10 @@ export class Params { toSearchParams() { return this.toSearchParamsObject().toString(); } + + isDefault() { + return this === defaultParams; + } } function isValidJsonUrl(url) { diff --git a/resources/suite-runner.mjs b/resources/suite-runner.mjs index 1872a7e87..5f2e4cfeb 100644 --- a/resources/suite-runner.mjs +++ b/resources/suite-runner.mjs @@ -105,8 +105,10 @@ export class SuiteRunner { const frame = this.#frame; frame.onload = () => resolve(); frame.onerror = () => reject(); - const splitUrl = this.#suite.url.split("?"); - frame.src = `${splitUrl[0]}?${splitUrl[1] ?? ""}&${this.#params.toSearchParams()}`; + const urlObj = new URL(this.#suite.url, window.location.href); + for (const [key, value] of this.#params.toSearchParamsObject()) + urlObj.searchParams.append(key, value); + frame.src = urlObj.href; }); } diff --git a/sw.mjs b/sw.mjs index dd906063c..59bbf50ef 100644 --- a/sw.mjs +++ b/sw.mjs @@ -19,15 +19,16 @@ function delayAsync(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } -async function handlePrecache(event, { suites = [], delay = 0 }) { - await caches.delete(CACHE_NAME); +async function handlePrecache(event, { suites = [], delay = 0, clearCache = true }) { + if (clearCache) { + await caches.delete(CACHE_NAME); + cachedSuitesPrefixes.clear(); + } const cache = await caches.open(CACHE_NAME); let loaded = 0; let totalSize = 0; const urlsToCache = []; - - cachedSuitesPrefixes.clear(); for (const suite of suites) { if (!suite.resources) continue; @@ -46,7 +47,9 @@ async function handlePrecache(event, { suites = [], delay = 0 }) { await Promise.all(promises); - cachedUrls = new Set(urlsToCache.map((item) => item.url)); + for (const item of urlsToCache) { + cachedUrls.add(item.url); + } replyToClient(event, { type: SW_MESSAGES.PRECACHE_DONE, totalSize, count: urlsToCache.length }); } @@ -55,12 +58,9 @@ async function parseSuiteResources(suite) { const response = await fetch(suite.resources); if (!response.ok) return []; - let text = await response.text(); - if (text.endsWith("\n")) - text = text.slice(0, -1); - const lines = text.split("\n").map((l) => l.trim()); - return lines.map((resourceUrl) => ({ - url: new URL(resourceUrl, suite.url).href, + const text = await response.text(); + return text.trim().split("\n").map((resourceUrl) => ({ + url: new URL(resourceUrl.trim(), suite.url).href, suiteName: suite.name, })); } catch (e) { @@ -70,9 +70,16 @@ async function parseSuiteResources(suite) { } async function fetchAndCache(cache, url, delayMs) { + const request = new Request(url, { cache: "no-cache" }); + const existing = await cache.match(request); + if (existing) { + const blob = await existing.blob(); + return blob.size; + } + if (delayMs) await delayAsync(delayMs); - const request = new Request(url, { cache: "no-cache" }); + try { await cache.add(request); const cachedResponse = await cache.match(request); @@ -106,7 +113,9 @@ self.addEventListener("message", function (event) { }); self.addEventListener("fetch", function (event) { - const isCached = cachedUrls.has(event.request.url); + const urlObj = new URL(event.request.url); + const cleanUrl = urlObj.origin + urlObj.pathname; + const isCached = cachedUrls.has(cleanUrl) || cachedUrls.has(event.request.url); if (isCached) { event.respondWith(handleFetch(event.request)); @@ -136,7 +145,7 @@ self.addEventListener("fetch", function (event) { async function handleFetch(request) { const cache = await caches.open(CACHE_NAME); - const cachedResponse = await cache.match(request); + const cachedResponse = await cache.match(request, { ignoreSearch: true }); if (cachedResponse) return cachedResponse; return fetch(request); diff --git a/tests/unittests/params.mjs b/tests/unittests/params.mjs index e3efe45dc..21ffab292 100644 --- a/tests/unittests/params.mjs +++ b/tests/unittests/params.mjs @@ -76,6 +76,24 @@ describe("Params", () => { }); }); + describe("isDefault", () => { + it("should return true for defaultParams", () => { + expect(defaultParams.isDefault()).to.be(true); + }); + it("should return false for newly instantiated empty Params", () => { + const params = new Params(); + expect(params.isDefault()).to.be(false); + }); + it("should return false for custom params", () => { + const params = new Params( + new URLSearchParams({ + iterationCount: "100", + }) + ); + expect(params.isDefault()).to.be(false); + }); + }); + describe("parse input params", () => { it("should parse custom viewport", () => { const params = new Params( diff --git a/tests/unittests/suites.mjs b/tests/unittests/suites.mjs index 86b520ad8..37e9fbf9d 100644 --- a/tests/unittests/suites.mjs +++ b/tests/unittests/suites.mjs @@ -72,11 +72,10 @@ for (const [name, suites] of Object.entries(Suites)) { const resourcesUrl = new URL(suite.resources, baseUrl).href; const res = await fetch(resourcesUrl); expect(res.ok).to.be(true); - let text = await res.text(); - expect(text.endsWith("\n")).to.be(true); - text = text.slice(0, -1); + const text = await res.text(); + expect(text.trim().length).to.be.greaterThan(0, `resources.txt for ${suite.name} is empty`); - const files = text.split("\n"); + const files = text.trim().split("\n"); for (const file of files) expect(file.trim().length).to.be.greaterThan(0); From 6a6fe032532f63692714990795c9c0aadb493125 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 10 Jun 2026 17:39:54 +0200 Subject: [PATCH 5/6] pre-format --- resources/main.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/main.css b/resources/main.css index a7e569a64..721cd422e 100644 --- a/resources/main.css +++ b/resources/main.css @@ -932,6 +932,10 @@ section#about .note { display: block; } +[data-benchmark-state="RUNNING"] #info { + display: block; +} + [data-benchmark-state="PRELOADING"] .start-tests-button { color: var(--foreground); background-image: linear-gradient(-45deg, var(--foreground) 15%, transparent 15%, transparent 50%, var(--foreground) 50%, var(--foreground) 65%, transparent 65%, transparent 100%); From a7057a4a1f196907dd19205c73dc2d7a1accdd49 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 10 Jun 2026 17:43:00 +0200 Subject: [PATCH 6/6] formatting --- resources/main.mjs | 9 ++------- sw.mjs | 15 +++++++++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/resources/main.mjs b/resources/main.mjs index 456c40c55..e7b0e498d 100644 --- a/resources/main.mjs +++ b/resources/main.mjs @@ -458,12 +458,7 @@ class MainBenchmarkClient { this._setBenchmarkState(BENCHMARK_STATE.PRELOADING); try { - await this.preloadServiceWorker.precacheSuites( - enabledSuites, - params.resourceLoadDelay, - clearCache, - this._updateCacheProgress.bind(this) - ); + await this.preloadServiceWorker.precacheSuites(enabledSuites, params.resourceLoadDelay, clearCache, this._updateCacheProgress.bind(this)); this._didInitialPrecache = true; this._enableStartButtons(); } catch (error) { @@ -476,7 +471,7 @@ class MainBenchmarkClient { _updateCacheProgress(progressData) { const { loaded, total, url, suiteName } = progressData; document.body.style.setProperty("--preload-progress", `${total > 0 ? (loaded / total) * 100 : 100}%`); - const progress = document.getElementById("preload-progress-completed") + const progress = document.getElementById("preload-progress-completed"); progress.max = total; progress.value = loaded; const filename = url ? url.substring(url.lastIndexOf("/") + 1) : ""; diff --git a/sw.mjs b/sw.mjs index 59bbf50ef..470efbf73 100644 --- a/sw.mjs +++ b/sw.mjs @@ -47,9 +47,9 @@ async function handlePrecache(event, { suites = [], delay = 0, clearCache = true await Promise.all(promises); - for (const item of urlsToCache) { + for (const item of urlsToCache) cachedUrls.add(item.url); - } + replyToClient(event, { type: SW_MESSAGES.PRECACHE_DONE, totalSize, count: urlsToCache.length }); } @@ -59,10 +59,13 @@ async function parseSuiteResources(suite) { if (!response.ok) return []; const text = await response.text(); - return text.trim().split("\n").map((resourceUrl) => ({ - url: new URL(resourceUrl.trim(), suite.url).href, - suiteName: suite.name, - })); + return text + .trim() + .split("\n") + .map((resourceUrl) => ({ + url: new URL(resourceUrl.trim(), suite.url).href, + suiteName: suite.name, + })); } catch (e) { console.warn("Failed to fetch resources.txt for", suite.name); return [];