From bdf3b2419a4f2a295f6cce5bdd5b71f9dc2c47da Mon Sep 17 00:00:00 2001 From: Niklan Date: Wed, 30 Jul 2025 20:45:34 +0500 Subject: [PATCH 1/2] Add request IDs to worker messages to fix code highlight race conditions --- app/modules/niklan/assets/js/hljs.init.js | 32 +++++++++++++++++---- app/modules/niklan/assets/js/hljs.worker.js | 26 ++++++++++------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/app/modules/niklan/assets/js/hljs.init.js b/app/modules/niklan/assets/js/hljs.init.js index b690da4b..4b0bc948 100644 --- a/app/modules/niklan/assets/js/hljs.init.js +++ b/app/modules/niklan/assets/js/hljs.init.js @@ -1,5 +1,8 @@ ((Drupal, once, drupalSettings) => { + let worker; + let requestId = 0; + const pendingRequests = new Map(); const createHighlightedEvent = (element, html) => new CustomEvent('niklan-highlight:highlighted', { @@ -18,28 +21,45 @@ const preElement = codeElement.parentElement; codeElement.classList.add('hljs'); + const currentRequestId = requestId++; + pendingRequests.set(currentRequestId, codeElement); + worker.postMessage({ + requestId: currentRequestId, code: codeElement.textContent, language: preElement.dataset.language, esModulesBasePath: drupalSettings.highlightJs.esModulesBasePath }); - - worker.addEventListener('message', ({ data }) => { - codeElement.innerHTML = data; - codeElement.dispatchEvent(createHighlightedEvent(codeElement, data)); - observer.unobserve(codeElement); - }, { once: true }); }); }; + const handleWorkerMessage = ({ data }) => { + const { requestId, result, error } = data; + const codeElement = pendingRequests.get(requestId); + + if (!codeElement) return; + + if (error) { + console.error(error); + return; + } + + codeElement.innerHTML = result; + codeElement.dispatchEvent(createHighlightedEvent(codeElement, result)); + pendingRequests.delete(requestId); + }; + const registerIntersectionObserver = () => { const observer = new IntersectionObserver(handleIntersection); Array .from(once('code-highlight', 'pre > code')) .forEach(code => observer.observe(code)); + worker = new Worker(drupalSettings.highlightJs.workerPath, { type: 'module' }); + worker.addEventListener('message', handleWorkerMessage); }; + const lazyCallback = callback => window.requestIdleCallback ? requestIdleCallback(callback) : callback(); diff --git a/app/modules/niklan/assets/js/hljs.worker.js b/app/modules/niklan/assets/js/hljs.worker.js index d770402d..7bd2e0be 100644 --- a/app/modules/niklan/assets/js/hljs.worker.js +++ b/app/modules/niklan/assets/js/hljs.worker.js @@ -14,30 +14,34 @@ const supportedLanguages = { onmessage = async (event) => { try { - const hljsModule = await import(`${event.data.esModulesBasePath}/highlight.min.js`); + const { requestId, code, language, esModulesBasePath } = event.data; + + const hljsModule = await import(`${esModulesBasePath}/highlight.min.js`); const hljs = hljsModule.default; - if (event.data.language && supportedLanguages[event.data.language]) { - const langModule = await import(`${event.data.esModulesBasePath}/${supportedLanguages[event.data.language]}`); - hljs.registerLanguage(event.data.language, langModule.default); + if (language && supportedLanguages[language]) { + const langModule = await import(`${esModulesBasePath}/${supportedLanguages[language]}`); + hljs.registerLanguage(language, langModule.default); } let result; - if (event.data.language && hljs.getLanguage(event.data.language)) { - result = hljs.highlight(event.data.code, { - language: event.data.language, - }); + if (language && hljs.getLanguage(language)) { + result = hljs.highlight(code, { language }); } else { - result = hljs.highlightAuto(event.data.code); + result = hljs.highlightAuto(code); } - postMessage(result.value); + postMessage({ + requestId, + result: result.value + }); } catch (error) { postMessage({ + requestId: event.data.requestId, error: `Processing failed: ${error.message}` }); } - }; + From a2f5d0acedcc005ae0fe669c36ee928ac5558ab9 Mon Sep 17 00:00:00 2001 From: Niklan Date: Wed, 30 Jul 2025 20:51:00 +0500 Subject: [PATCH 2/2] Add request IDs to worker messages to fix code highlight race conditions --- app/modules/niklan/assets/js/hljs.init.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/modules/niklan/assets/js/hljs.init.js b/app/modules/niklan/assets/js/hljs.init.js index 4b0bc948..d74a20b5 100644 --- a/app/modules/niklan/assets/js/hljs.init.js +++ b/app/modules/niklan/assets/js/hljs.init.js @@ -14,14 +14,15 @@ bubbles: true }); - const handleIntersection = (entries, observer) => { + const handleIntersection = (entries) => { entries.forEach(({ isIntersecting, target: codeElement }) => { if (!isIntersecting) return; const preElement = codeElement.parentElement; codeElement.classList.add('hljs'); - const currentRequestId = requestId++; + const currentRequestId = requestId; + requestId += 1; pendingRequests.set(currentRequestId, codeElement); worker.postMessage({ @@ -34,6 +35,7 @@ }; const handleWorkerMessage = ({ data }) => { + // eslint-disable-next-line no-shadow const { requestId, result, error } = data; const codeElement = pendingRequests.get(requestId);