From e793ae07a40e204f6d9db83b15a66d9c2a1c84d3 Mon Sep 17 00:00:00 2001 From: Tony Klapatch Date: Wed, 6 Nov 2024 14:59:13 -0500 Subject: [PATCH 1/4] Change hlx urls to aem --- .github/pull_request_template.md | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a1d177d..c47dce3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,5 +3,5 @@ Please always provide the [GitHub issue(s)](../issues) your PR is for, as well a Fix # Test URLs: -- Before: https://main--best-cigars-guide--famous-smoke.hlx.live/ -- After: https://--best-cigars-guide--famous-smoke.hlx.live/ +- Before: https://main--best-cigars-guide--famous-smoke.aem.live/ +- After: https://--best-cigars-guide--famous-smoke.aem.live/ diff --git a/README.md b/README.md index 2d76709..d5b4767 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ Find the best recommended Cigars by year, occasion, shape, size, country, food p - https://www.famous-smoke.com/best-cigars-guide/ ## AEM Preview Environments -- Preview: https://main--best-cigars-guide--famous-smoke.hlx.page/best-cigars-guide -- Live: https://main--best-cigars-guide--famous-smoke.hlx.live/best-cigars-guide +- Preview: https://main--best-cigars-guide--famous-smoke.aem.page/best-cigars-guide +- Live: https://main--best-cigars-guide--famous-smoke.aem.live/best-cigars-guide ## Installation @@ -27,4 +27,4 @@ npm run lint 1. Install the [AEM CLI](https://github.com/adobe/helix-cli): `npm install -g @adobe/aem-cli` 1. Start AEM Proxy: `aem up` (opens your browser at `http://localhost:3000`) 1. Open the `best-cigars-guide` directory in your favorite IDE and start coding :) -1. Once you are are ready to push your changes, simply use Git to add, commit, and push and your code to your preview `https://----.hlx.page/` and production `https://----.hlx.live/` sites. +1. Once you are are ready to push your changes, simply use Git to add, commit, and push and your code to your preview `https://----.aem.page/` and production `https://----.aem.live/` sites. From f363440a076178c1210db50baa7891a18d4c73f8 Mon Sep 17 00:00:00 2001 From: Tony Klapatch Date: Thu, 7 Nov 2024 09:38:31 -0500 Subject: [PATCH 2/4] Update aem.js, fix breaking changes in scripts.js --- best-cigars-guide/blocks/fragment/fragment.js | 4 +- best-cigars-guide/scripts/aem.js | 306 ++++++++---------- best-cigars-guide/scripts/scripts.js | 24 +- 3 files changed, 138 insertions(+), 196 deletions(-) diff --git a/best-cigars-guide/blocks/fragment/fragment.js b/best-cigars-guide/blocks/fragment/fragment.js index 648a70e..9d9195c 100644 --- a/best-cigars-guide/blocks/fragment/fragment.js +++ b/best-cigars-guide/blocks/fragment/fragment.js @@ -9,7 +9,7 @@ import { } from '../../scripts/scripts.js'; import { - loadBlocks, + loadSections, } from '../../scripts/aem.js'; /** @@ -34,7 +34,7 @@ export async function loadFragment(path) { resetAttributeBase('source', 'srcset'); decorateMain(main); - await loadBlocks(main); + await loadSections(main); return main; } } diff --git a/best-cigars-guide/scripts/aem.js b/best-cigars-guide/scripts/aem.js index 3aef990..40662e6 100644 --- a/best-cigars-guide/scripts/aem.js +++ b/best-cigars-guide/scripts/aem.js @@ -11,137 +11,114 @@ */ /* eslint-env browser */ - -/** - * log RUM if part of the sample. - * @param {string} checkpoint identifies the checkpoint in funnel - * @param {Object} data additional data for RUM sample - * @param {string} data.source DOM node that is the source of a checkpoint event, - * identified by #id or .classname - * @param {string} data.target subject of the checkpoint event, - * for instance the href of a link, or a search term - */ -function sampleRUM(checkpoint, data = {}) { - const SESSION_STORAGE_KEY = 'aem-rum'; - sampleRUM.baseURL = sampleRUM.baseURL - || new URL(window.RUM_BASE == null ? 'https://rum.hlx.page' : window.RUM_BASE, window.location); - sampleRUM.defer = sampleRUM.defer || []; - const defer = (fnname) => { - sampleRUM[fnname] = sampleRUM[fnname] || ((...args) => sampleRUM.defer.push({ fnname, args })); - }; - sampleRUM.drain = sampleRUM.drain - || ((dfnname, fn) => { - sampleRUM[dfnname] = fn; - sampleRUM.defer - .filter(({ fnname }) => dfnname === fnname) - .forEach(({ fnname, args }) => sampleRUM[fnname](...args)); - }); - sampleRUM.always = sampleRUM.always || []; - sampleRUM.always.on = (chkpnt, fn) => { - sampleRUM.always[chkpnt] = fn; - }; - sampleRUM.on = (chkpnt, fn) => { - sampleRUM.cases[chkpnt] = fn; - }; - defer('observe'); - defer('cwv'); +function sampleRUM(checkpoint, data) { + // eslint-disable-next-line max-len + const timeShift = () => (window.performance ? window.performance.now() : Date.now() - window.hlx.rum.firstReadTime); try { window.hlx = window.hlx || {}; + sampleRUM.enhance = () => {}; if (!window.hlx.rum) { - const usp = new URLSearchParams(window.location.search); - const weight = usp.get('rum') === 'on' ? 1 : 100; // with parameter, weight is 1. Defaults to 100. - const id = Array.from({ length: 75 }, (_, i) => String.fromCharCode(48 + i)) - .filter((a) => /\d|[A-Z]/i.test(a)) - .filter(() => Math.random() * 75 > 70) - .join(''); - const random = Math.random(); - const isSelected = random * weight < 1; - const firstReadTime = window.performance ? window.performance.timeOrigin : Date.now(); - const urlSanitizers = { - full: () => window.location.href, - origin: () => window.location.origin, - path: () => window.location.href.replace(/\?.*$/, ''), - }; - // eslint-disable-next-line max-len - const rumSessionStorage = sessionStorage.getItem(SESSION_STORAGE_KEY) - ? JSON.parse(sessionStorage.getItem(SESSION_STORAGE_KEY)) - : {}; - // eslint-disable-next-line max-len - rumSessionStorage.pages = (rumSessionStorage.pages ? rumSessionStorage.pages : 0) - + 1 - /* noise */ + (Math.floor(Math.random() * 20) - 10); - sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(rumSessionStorage)); + const param = new URLSearchParams(window.location.search).get('rum'); + const weight = (window.SAMPLE_PAGEVIEWS_AT_RATE === 'high' && 10) + || (window.SAMPLE_PAGEVIEWS_AT_RATE === 'low' && 1000) + || (param === 'on' && 1) + || 100; + const id = Math.random().toString(36).slice(-4); + const isSelected = param !== 'off' && Math.random() * weight < 1; // eslint-disable-next-line object-curly-newline, max-len window.hlx.rum = { weight, id, - random, isSelected, - firstReadTime, + firstReadTime: window.performance ? window.performance.timeOrigin : Date.now(), sampleRUM, - sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'], - rumSessionStorage, + queue: [], + collector: (...args) => window.hlx.rum.queue.push(args), }; - } + if (isSelected) { + const dataFromErrorObj = (error) => { + const errData = { source: 'undefined error' }; + try { + errData.target = error.toString(); + errData.source = error.stack + .split('\n') + .filter((line) => line.match(/https?:\/\//)) + .shift() + .replace(/at ([^ ]+) \((.+)\)/, '$1@$2') + .replace(/ at /, '@') + .trim(); + } catch (err) { + /* error structure was not as expected */ + } + return errData; + }; + + window.addEventListener('error', ({ error }) => { + const errData = dataFromErrorObj(error); + sampleRUM('error', errData); + }); + + window.addEventListener('unhandledrejection', ({ reason }) => { + let errData = { + source: 'Unhandled Rejection', + target: reason || 'Unknown', + }; + if (reason instanceof Error) { + errData = dataFromErrorObj(reason); + } + sampleRUM('error', errData); + }); + + sampleRUM.baseURL = sampleRUM.baseURL || new URL(window.RUM_BASE || '/', new URL('https://rum.hlx.page')); + sampleRUM.collectBaseURL = sampleRUM.collectBaseURL || sampleRUM.baseURL; + sampleRUM.sendPing = (ck, time, pingData = {}) => { + // eslint-disable-next-line max-len, object-curly-newline + const rumData = JSON.stringify({ + weight, + id, + referer: window.location.href, + checkpoint: ck, + t: time, + ...pingData, + }); + const urlParams = window.RUM_PARAMS + ? `?${new URLSearchParams(window.RUM_PARAMS).toString()}` + : ''; + const { href: url, origin } = new URL( + `.rum/${weight}${urlParams}`, + sampleRUM.collectBaseURL, + ); + const body = origin === window.location.origin + ? new Blob([rumData], { type: 'application/json' }) + : rumData; + navigator.sendBeacon(url, body); + // eslint-disable-next-line no-console + console.debug(`ping:${ck}`, pingData); + }; + sampleRUM.sendPing('top', timeShift()); + + sampleRUM.enhance = () => { + // only enhance once + if (document.querySelector('script[src*="rum-enhancer"]')) return; - const { weight, id, firstReadTime } = window.hlx.rum; - if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) { - const knownProperties = [ - 'weight', - 'id', - 'referer', - 'checkpoint', - 't', - 'source', - 'target', - 'cwv', - 'CLS', - 'FID', - 'LCP', - 'INP', - 'TTFB', - ]; - const sendPing = (pdata = data) => { - // eslint-disable-next-line max-len - const t = Math.round( - window.performance ? window.performance.now() : Date.now() - firstReadTime, - ); - // eslint-disable-next-line object-curly-newline, max-len, no-use-before-define - const body = JSON.stringify( - { - weight, id, referer: window.hlx.rum.sanitizeURL(), checkpoint, t, ...data, - }, - knownProperties, - ); - const url = new URL(`.rum/${weight}`, sampleRUM.baseURL).href; - navigator.sendBeacon(url, body); - // eslint-disable-next-line no-console - console.debug(`ping:${checkpoint}`, pdata); - }; - sampleRUM.cases = sampleRUM.cases || { - load: () => sampleRUM('pagesviewed', { source: window.hlx.rum.rumSessionStorage.pages }) || true, - cwv: () => sampleRUM.cwv(data) || true, - lazy: () => { - // use classic script to avoid CORS issues const script = document.createElement('script'); script.src = new URL( - '.rum/@adobe/helix-rum-enhancer@^1/src/index.js', + '.rum/@adobe/helix-rum-enhancer@^2/src/index.js', sampleRUM.baseURL, ).href; document.head.appendChild(script); - return true; - }, - }; - sendPing(data); - if (sampleRUM.cases[checkpoint]) { - sampleRUM.cases[checkpoint](); + }; + if (!window.hlx.RUM_MANUAL_ENHANCE) { + sampleRUM.enhance(); + } } } - if (sampleRUM.always[checkpoint]) { - sampleRUM.always[checkpoint](data); + if (window.hlx.rum && window.hlx.rum.isSelected && checkpoint) { + window.hlx.rum.collector(checkpoint, data, timeShift()); } + document.dispatchEvent(new CustomEvent('rum', { detail: { checkpoint, data } })); } catch (error) { - // something went wrong + // something went awry } } @@ -151,6 +128,7 @@ function sampleRUM(checkpoint, data = {}) { function setup() { window.hlx = window.hlx || {}; window.hlx.RUM_MASK_URL = 'full'; + window.hlx.RUM_MANUAL_ENHANCE = true; window.hlx.codeBasePath = ''; window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; @@ -171,17 +149,7 @@ function setup() { function init() { setup(); - sampleRUM('top'); - - window.addEventListener('load', () => sampleRUM('load')); - - window.addEventListener('unhandledrejection', (event) => { - sampleRUM('error', { source: event.reason.sourceURL, target: event.reason.line }); - }); - - window.addEventListener('error', (event) => { - sampleRUM('error', { source: event.filename, target: event.lineno }); - }); + sampleRUM(); } /** @@ -561,30 +529,6 @@ async function fetchPlaceholders(prefix = 'default') { return window.placeholders[`${prefix}`]; } -/** - * Updates all section status in a container element. - * @param {Element} main The container element - */ -function updateSectionsStatus(main) { - const sections = [...main.querySelectorAll(':scope > div.section')]; - for (let i = 0; i < sections.length; i += 1) { - const section = sections[i]; - const status = section.dataset.sectionStatus; - if (status !== 'loaded') { - const loadingBlock = section.querySelector( - '.block[data-block-status="initialized"], .block[data-block-status="loading"]', - ); - if (loadingBlock) { - section.dataset.sectionStatus = 'loading'; - break; - } else { - section.dataset.sectionStatus = 'loaded'; - section.style.display = null; - } - } - } -} - /** * Builds a block DOM Element from a two dimensional array, string, or object * @param {string} blockName name of the block @@ -653,20 +597,6 @@ async function loadBlock(block) { return block; } -/** - * Loads JS and CSS for all blocks in a container element. - * @param {Element} main The container element - */ -async function loadBlocks(main) { - updateSectionsStatus(main); - const blocks = [...main.querySelectorAll('div.block')]; - for (let i = 0; i < blocks.length; i += 1) { - // eslint-disable-next-line no-await-in-loop - await loadBlock(blocks[i]); - updateSectionsStatus(main); - } -} - /** * Decorates a block. * @param {Element} block The block element @@ -718,17 +648,11 @@ async function loadFooter(footer) { } /** - * Load LCP block and/or wait for LCP in default content. - * @param {Array} lcpBlocks Array of blocks + * Wait for Image. + * @param {Element} section section element */ -async function waitForLCP(lcpBlocks) { - const block = document.querySelector('.block'); - const hasLCPBlock = block && lcpBlocks.includes(block.dataset.blockName); - if (hasLCPBlock) await loadBlock(block); - - document.body.style.display = null; - const lcpCandidate = document.querySelector('main img'); - +async function waitForFirstImage(section) { + const lcpCandidate = section.querySelector('img'); await new Promise((resolve) => { if (lcpCandidate && !lcpCandidate.complete) { lcpCandidate.setAttribute('loading', 'eager'); @@ -740,6 +664,42 @@ async function waitForLCP(lcpBlocks) { }); } +/** + * Loads all blocks in a section. + * @param {Element} section The section element + */ + +async function loadSection(section, loadCallback) { + const status = section.dataset.sectionStatus; + if (!status || status === 'initialized') { + section.dataset.sectionStatus = 'loading'; + const blocks = [...section.querySelectorAll('div.block')]; + for (let i = 0; i < blocks.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + await loadBlock(blocks[i]); + } + if (loadCallback) await loadCallback(section); + section.dataset.sectionStatus = 'loaded'; + section.style.display = null; + } +} + +/** + * Loads all sections. + * @param {Element} element The parent element of sections to load + */ + +async function loadSections(element) { + const sections = [...element.querySelectorAll('div.section')]; + for (let i = 0; i < sections.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + await loadSection(sections[i]); + if (i === 0 && sampleRUM.enhance) { + sampleRUM.enhance(); + } + } +} + init(); export { @@ -754,17 +714,17 @@ export { fetchPlaceholders, getMetadata, loadBlock, - loadBlocks, loadCSS, loadFooter, loadHeader, loadScript, + loadSection, + loadSections, readBlockConfig, sampleRUM, setup, toCamelCase, toClassName, - updateSectionsStatus, - waitForLCP, + waitForFirstImage, wrapTextNodes, }; diff --git a/best-cigars-guide/scripts/scripts.js b/best-cigars-guide/scripts/scripts.js index b54a234..de97cad 100644 --- a/best-cigars-guide/scripts/scripts.js +++ b/best-cigars-guide/scripts/scripts.js @@ -1,8 +1,7 @@ /* eslint-disable max-len */ // eslint-disable-next-line object-curly-newline -import { sampleRUM, buildBlock, loadHeader, loadFooter, decorateButtons, decorateIcons, decorateSections, decorateBlocks, decorateTemplateAndTheme, waitForLCP, loadBlocks, loadCSS, decorateBlock } from './aem.js'; +import { buildBlock, loadHeader, loadFooter, decorateButtons, decorateIcons, decorateSections, decorateBlocks, decorateTemplateAndTheme, waitForFirstImage, loadSections, loadSection, loadCSS, decorateBlock } from './aem.js'; -const LCP_BLOCKS = []; // add your LCP blocks to the list const CATEGORY_INDEX_PATH = '/best-cigars-guide/index/category-index.json'; const ARTICLE_INDEX_PATH = '/best-cigars-guide/index/query-index.json'; const SUBFOLDER_PATH = '/best-cigars-guide'; @@ -242,31 +241,18 @@ export function isInternal(path) { } } -/** - * Add hreflang link attribute to head - */ -function addHreflang() { - const el = document.createElement('link'); - el.rel = 'alternate'; - el.hreflang = 'en'; - el.href = window.location.href; - - document.head.appendChild(el); -} - /** * Loads everything needed to get to LCP. * @param {Element} doc The container element */ async function loadEager(doc) { document.documentElement.lang = 'en'; - addHreflang(); decorateTemplateAndTheme(); const main = doc.querySelector('main'); if (main) { decorateMain(main); document.body.classList.add('appear'); - await waitForLCP(LCP_BLOCKS); + await loadSection(main.querySelector('.section'), waitForFirstImage); } try { @@ -285,7 +271,7 @@ async function loadEager(doc) { */ async function loadLazy(doc) { const main = doc.querySelector('main'); - await loadBlocks(main); + await loadSections(main); const { hash } = window.location; const element = hash ? doc.getElementById(hash.substring(1)) : false; @@ -296,10 +282,6 @@ async function loadLazy(doc) { loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`); loadFonts(); - - sampleRUM('lazy'); - sampleRUM.observe(main.querySelectorAll('div[data-block-name]')); - sampleRUM.observe(main.querySelectorAll('picture > img')); } /** From 68a1aaae08ce0b9d35102ac7dc3aa5716ec25c06 Mon Sep 17 00:00:00 2001 From: Tony Klapatch Date: Thu, 7 Nov 2024 10:01:38 -0500 Subject: [PATCH 3/4] Fix sidebar block after aem.js update --- best-cigars-guide/scripts/scripts.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/best-cigars-guide/scripts/scripts.js b/best-cigars-guide/scripts/scripts.js index de97cad..88b6a8c 100644 --- a/best-cigars-guide/scripts/scripts.js +++ b/best-cigars-guide/scripts/scripts.js @@ -1,6 +1,21 @@ /* eslint-disable max-len */ // eslint-disable-next-line object-curly-newline -import { buildBlock, loadHeader, loadFooter, decorateButtons, decorateIcons, decorateSections, decorateBlocks, decorateTemplateAndTheme, waitForFirstImage, loadSections, loadSection, loadCSS, decorateBlock } from './aem.js'; +import { + buildBlock, + loadHeader, + loadFooter, + decorateButtons, + decorateIcons, + decorateSections, + decorateBlocks, + decorateTemplateAndTheme, + waitForFirstImage, + loadSections, + loadSection, + loadCSS, + decorateBlock, + loadBlock +} from './aem.js'; const CATEGORY_INDEX_PATH = '/best-cigars-guide/index/category-index.json'; const ARTICLE_INDEX_PATH = '/best-cigars-guide/index/query-index.json'; @@ -181,6 +196,7 @@ function buildSidebarBlock(main) { const block = buildBlock('sidebar', ''); sidebar.append(block); decorateBlock(block); + loadBlock(block); container.classList.add('article-wrapper'); container.append(sidebar); From d5f13adcf73aa98a6a6a501721aaab78259521f2 Mon Sep 17 00:00:00 2001 From: Tony Klapatch Date: Thu, 7 Nov 2024 10:04:12 -0500 Subject: [PATCH 4/4] Fix linting issue --- best-cigars-guide/scripts/scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best-cigars-guide/scripts/scripts.js b/best-cigars-guide/scripts/scripts.js index 88b6a8c..e562b12 100644 --- a/best-cigars-guide/scripts/scripts.js +++ b/best-cigars-guide/scripts/scripts.js @@ -14,7 +14,7 @@ import { loadSection, loadCSS, decorateBlock, - loadBlock + loadBlock, } from './aem.js'; const CATEGORY_INDEX_PATH = '/best-cigars-guide/index/category-index.json';