diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..ad196181ae --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[*.js] +indent_size = 2 + +[*.css] +indent_size = 4 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 026dac98f7..9c78492f75 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--{repo}--{owner}.hlx.live/ -- After: https://--{repo}--{owner}.hlx.live/ +- Before: https://main--{repo}--{owner}.aem.live/ +- After: https://--{repo}--{owner}.aem.live/ diff --git a/.hlxignore b/.hlxignore index c505916696..58ce85d166 100644 --- a/.hlxignore +++ b/.hlxignore @@ -5,3 +5,4 @@ LICENSE package.json package-lock.json test/* +_* diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000000..0b01328761 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +node .husky/pre-commit.mjs diff --git a/.husky/pre-commit.mjs b/.husky/pre-commit.mjs new file mode 100644 index 0000000000..6e71533946 --- /dev/null +++ b/.husky/pre-commit.mjs @@ -0,0 +1,20 @@ +import { exec } from "node:child_process"; + +const run = (cmd) => new Promise((resolve, reject) => exec( + cmd, + (error, stdout) => { + if (error) reject(error); + else resolve(stdout); + } +)); + +const changeset = await run('git diff --cached --name-only --diff-filter=ACMR'); +const modifiedFiles = changeset.split('\n').filter(Boolean); + +// check if there are any model files staged +const modifledPartials = modifiedFiles.filter((file) => file.match(/(^|\/)_.*.json/)); +if (modifledPartials.length > 0) { + const output = await run('npm run build:json --silent'); + console.log(output); + await run('git add component-models.json component-definition.json component-filters.json'); +} diff --git a/404.html b/404.html index 3baecc8d9a..3e2081f1df 100644 --- a/404.html +++ b/404.html @@ -11,8 +11,6 @@ + diff --git a/README.md b/README.md index 896a0ea2de..f1873bf9cd 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ Your project's description... ## Environments -- Preview: https://main--{repo}--{owner}.hlx.page/ -- Live: https://main--{repo}--{owner}.hlx.live/ +- Preview: https://main--{repo}--{owner}.aem.page/ +- Live: https://main--{repo}--{owner}.aem.live/ ## Installation @@ -24,3 +24,19 @@ 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 `{repo}` directory in your favorite IDE and start coding :) + +## Prerequisites + +- nodejs 18.3.x or newer +- AEM Cloud Service release 2024.8 or newer (>= `17465`) + +## Resources + +### Documentation +- [Getting Started Guide](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/edge-delivery/wysiwyg-authoring/edge-dev-getting-started) +- [Creating Blocks](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/edge-delivery/wysiwyg-authoring/create-block) +- [Content Modelling](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/edge-delivery/wysiwyg-authoring/content-modeling) +- [Working with Tabular Data / Spreadsheets](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/edge-delivery/wysiwyg-authoring/tabular-data) + +### Presentations and Recordings +- [Getting started with AEM Authoring and Edge Delivery Services](https://experienceleague.adobe.com/en/docs/events/experience-manager-gems-recordings/gems2024/aem-authoring-and-edge-delivery) diff --git a/blocks/cards/_cards.json b/blocks/cards/_cards.json new file mode 100644 index 0000000000..1b2ca0e217 --- /dev/null +++ b/blocks/cards/_cards.json @@ -0,0 +1,63 @@ +{ + "definitions": [ + { + "title": "Cards", + "id": "cards", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/block/v1/block", + "template": { + "name": "Cards", + "filter": "cards" + } + } + } + } + }, + { + "title": "Card", + "id": "card", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/block/v1/block/item", + "template": { + "name": "Card", + "model": "card" + } + } + } + } + } + ], + "models": [ + { + "id": "card", + "fields": [ + { + "component": "reference", + "valueType": "string", + "name": "image", + "label": "Image", + "multi": false + }, + { + "component": "richtext", + "name": "text", + "value": "", + "label": "Text", + "valueType": "string" + } + ] + } + ], + "filters": [ + { + "id": "cards", + "components": [ + "card" + ] + } + ] +} diff --git a/blocks/cards/cards.css b/blocks/cards/cards.css index 715da6f66d..7d88439ab4 100644 --- a/blocks/cards/cards.css +++ b/blocks/cards/cards.css @@ -3,12 +3,12 @@ margin: 0; padding: 0; display: grid; - grid-template-columns: repeat(auto-fill, minmax(278px, 1fr)); - grid-gap: 16px; + grid-template-columns: repeat(auto-fill, minmax(257px, 1fr)); + grid-gap: 24px; } .cards > ul > li { - border: 1px solid var(--dark-color); + border: 1px solid #dadada; background-color: var(--background-color); } @@ -20,10 +20,6 @@ line-height: 0; } -.cards .cards-card-body > *:first-child { - margin-top: 0; -} - .cards > ul > li img { width: 100%; aspect-ratio: 4 / 3; diff --git a/blocks/columns/_columns.json b/blocks/columns/_columns.json new file mode 100644 index 0000000000..6f08e8ad43 --- /dev/null +++ b/blocks/columns/_columns.json @@ -0,0 +1,57 @@ +{ + "definitions": [ + { + "title": "Columns", + "id": "columns", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/columns/v1/columns", + "template": { + "columns": "2", + "rows": "1" + } + } + } + } + } + ], + "models": [ + { + "id": "columns", + "fields": [ + { + "component": "text", + "valueType": "number", + "name": "columns", + "value": "", + "label": "Columns" + }, + { + "component": "text", + "valueType": "number", + "name": "rows", + "value": "", + "label": "Rows" + } + ] + } + ], + "filters": [ + { + "id": "columns", + "components": [ + "column" + ] + }, + { + "id": "column", + "components": [ + "text", + "image", + "button", + "title" + ] + } + ] +} \ No newline at end of file diff --git a/blocks/columns/columns.css b/blocks/columns/columns.css index 99eda087c4..f2b203e76f 100644 --- a/blocks/columns/columns.css +++ b/blocks/columns/columns.css @@ -23,7 +23,7 @@ .columns > div { align-items: center; flex-direction: unset; - gap: 32px; + gap: 24px; } .columns > div > div { diff --git a/blocks/footer/footer.css b/blocks/footer/footer.css index 1976b73423..d8617dec55 100644 --- a/blocks/footer/footer.css +++ b/blocks/footer/footer.css @@ -1,14 +1,20 @@ footer { - padding: 2rem; background-color: var(--light-color); - font-size: var(--body-font-size-s); + font-size: var(--body-font-size-xs); } -footer .footer { - max-width: 1200px; +footer .footer > div { margin: auto; + max-width: 1200px; + padding: 40px 24px 24px; } footer .footer p { margin: 0; } + +@media (width >= 900px) { + footer .footer > div { + padding: 40px 32px 24px; + } +} diff --git a/blocks/fragment/_fragment.json b/blocks/fragment/_fragment.json new file mode 100644 index 0000000000..e7163c4f17 --- /dev/null +++ b/blocks/fragment/_fragment.json @@ -0,0 +1,32 @@ +{ + "definitions": [ + { + "title": "Fragment", + "id": "fragment", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/block/v1/block", + "template": { + "name": "Fragment", + "model": "fragment" + } + } + } + } + } + ], + "models": [ + { + "id": "fragment", + "fields": [ + { + "component": "aem-content", + "name": "reference", + "label": "Reference" + } + ] + } + ], + "filters": [] +} \ No newline at end of file diff --git a/blocks/fragment/fragment.css b/blocks/fragment/fragment.css index b38cf89607..ff7112406e 100644 --- a/blocks/fragment/fragment.css +++ b/blocks/fragment/fragment.css @@ -1,13 +1 @@ -/* suppress nested section padding */ -.fragment-wrapper > .section { - padding-left: 0; - padding-right: 0; -} - -.fragment-wrapper > .section:first-of-type { - padding-top: 0; -} - -.fragment-wrapper > .section:last-of-type { - padding-bottom: 0; -} +/* stylelint-disable no-empty-source */ diff --git a/blocks/fragment/fragment.js b/blocks/fragment/fragment.js index 648a70e103..a68a235fbb 100644 --- a/blocks/fragment/fragment.js +++ b/blocks/fragment/fragment.js @@ -9,7 +9,7 @@ import { } from '../../scripts/scripts.js'; import { - loadBlocks, + loadSections, } from '../../scripts/aem.js'; /** @@ -19,6 +19,8 @@ import { */ export async function loadFragment(path) { if (path && path.startsWith('/')) { + // eslint-disable-next-line no-param-reassign + path = path.replace(/(\.plain)?\.html/, ''); const resp = await fetch(`${path}.plain.html`); if (resp.ok) { const main = document.createElement('main'); @@ -34,7 +36,7 @@ export async function loadFragment(path) { resetAttributeBase('source', 'srcset'); decorateMain(main); - await loadBlocks(main); + await loadSections(main); return main; } } @@ -48,8 +50,9 @@ export default async function decorate(block) { if (fragment) { const fragmentSection = fragment.querySelector(':scope .section'); if (fragmentSection) { - block.closest('.section').classList.add(...fragmentSection.classList); - block.closest('.fragment').replaceWith(...fragment.childNodes); + block.classList.add(...fragmentSection.classList); + block.classList.remove('section'); + block.replaceChildren(...fragmentSection.childNodes); } } } diff --git a/blocks/header/header.css b/blocks/header/header.css index 741e2da19e..53e4e61954 100644 --- a/blocks/header/header.css +++ b/blocks/header/header.css @@ -13,11 +13,11 @@ header nav { 'hamburger brand tools' var(--nav-height) 'sections sections sections' 1fr / auto 1fr auto; align-items: center; - gap: 0 2em; + gap: 0 24px; margin: auto; - max-width: 1264px; + max-width: 1248px; height: var(--nav-height); - padding: 0 1rem; + padding: 0 24px; font-family: var(--body-font-family); } @@ -27,19 +27,16 @@ header nav[aria-expanded='true'] { 'sections sections' 1fr 'tools tools' var(--nav-height) / auto 1fr; overflow-y: auto; - min-height: 100vh; -} - -@media (width >= 600px) { - header nav { - padding: 0 2rem; - } + min-height: 100dvh; } @media (width >= 900px) { header nav { display: flex; justify-content: space-between; + gap: 0 32px; + max-width: 1264px; + padding: 0 32px; } header nav[aria-expanded='true'] { @@ -158,7 +155,6 @@ header nav .nav-sections { flex: 1 1 auto; display: none; visibility: hidden; - background-color: var(--overlay-color); } header nav[aria-expanded='true'] .nav-sections { @@ -171,11 +167,10 @@ header nav .nav-sections ul { list-style: none; padding-left: 0; font-size: var(--body-font-size-s); - font-weight: 500; } header nav .nav-sections ul > li { - font-weight: 700; + font-weight: 500; } header nav .nav-sections ul > li > ul { @@ -183,7 +178,7 @@ header nav .nav-sections ul > li > ul { } header nav .nav-sections ul > li > ul > li { - font-weight: 500; + font-weight: 400; } @media (width >= 900px) { @@ -225,15 +220,13 @@ header nav .nav-sections ul > li > ul > li { header nav .nav-sections ul { display: flex; - gap: 2em; + gap: 24px; margin: 0; - font-size: var(--body-font-size-xs); } header nav .nav-sections .default-content-wrapper > ul > li { flex: 0 1 auto; position: relative; - font-weight: 500; } header nav .nav-sections .default-content-wrapper > ul > li > ul { @@ -244,10 +237,10 @@ header nav .nav-sections ul > li > ul > li { header nav .nav-sections .default-content-wrapper > ul > li[aria-expanded='true'] > ul { display: block; position: absolute; - left: -1em; + left: -24px; width: 200px; - margin-top: 12px; - padding: 1em; + top: 150%; + padding: 16px; background-color: var(--light-color); white-space: initial; } @@ -256,7 +249,7 @@ header nav .nav-sections ul > li > ul > li { content: ''; position: absolute; top: -8px; - left: 8px; + left: 16px; width: 0; height: 0; border-left: 8px solid transparent; diff --git a/blocks/header/header.js b/blocks/header/header.js index f7f719d5c2..cb2157c3b8 100644 --- a/blocks/header/header.js +++ b/blocks/header/header.js @@ -21,6 +21,21 @@ function closeOnEscape(e) { } } +function closeOnFocusLost(e) { + const nav = e.currentTarget; + if (!nav.contains(e.relatedTarget)) { + const navSections = nav.querySelector('.nav-sections'); + const navSectionExpanded = navSections.querySelector('[aria-expanded="true"]'); + if (navSectionExpanded && isDesktop.matches) { + // eslint-disable-next-line no-use-before-define + toggleAllNavSections(navSections, false); + } else if (!isDesktop.matches) { + // eslint-disable-next-line no-use-before-define + toggleMenu(nav, navSections, false); + } + } +} + function openOnKeydown(e) { const focused = document.activeElement; const isNavDrop = focused.className === 'nav-drop'; @@ -65,24 +80,26 @@ function toggleMenu(nav, navSections, forceExpanded = null) { if (isDesktop.matches) { navDrops.forEach((drop) => { if (!drop.hasAttribute('tabindex')) { - drop.setAttribute('role', 'button'); drop.setAttribute('tabindex', 0); drop.addEventListener('focus', focusNavSection); } }); } else { navDrops.forEach((drop) => { - drop.removeAttribute('role'); drop.removeAttribute('tabindex'); drop.removeEventListener('focus', focusNavSection); }); } + // enable menu collapse on escape keypress if (!expanded || isDesktop.matches) { // collapse menu on escape press window.addEventListener('keydown', closeOnEscape); + // collapse menu on focus lost + nav.addEventListener('focusout', closeOnFocusLost); } else { window.removeEventListener('keydown', closeOnEscape); + nav.removeEventListener('focusout', closeOnFocusLost); } } diff --git a/blocks/hero/_hero.json b/blocks/hero/_hero.json new file mode 100644 index 0000000000..0e64ea4376 --- /dev/null +++ b/blocks/hero/_hero.json @@ -0,0 +1,48 @@ +{ + "definitions": [ + { + "title": "Hero", + "id": "hero", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/block/v1/block", + "template": { + "name": "Hero", + "model": "hero" + } + } + } + } + } + ], + "models": [ + { + "id": "hero", + "fields": [ + { + "component": "reference", + "valueType": "string", + "name": "image", + "label": "Image", + "multi": false + }, + { + "component": "text", + "valueType": "string", + "name": "imageAlt", + "label": "Alt", + "value": "" + }, + { + "component": "richtext", + "name": "text", + "value": "", + "label": "Text", + "valueType": "string" + } + ] + } + ], + "filters": [] +} diff --git a/blocks/hero/hero.css b/blocks/hero/hero.css index 1a377ac18e..974eaf2260 100644 --- a/blocks/hero/hero.css +++ b/blocks/hero/hero.css @@ -1,14 +1,11 @@ -main .hero-container > div { +.hero-container .hero-wrapper { max-width: unset; -} - -main .hero-container { padding: 0; } .hero { position: relative; - padding: 32px; + padding: 40px 24px; min-height: 300px; } @@ -16,7 +13,7 @@ main .hero-container { max-width: 1200px; margin-left: auto; margin-right: auto; - color: white; + color: var(--background-color); } .hero picture { @@ -32,3 +29,9 @@ main .hero-container { width: 100%; height: 100%; } + +@media (width >= 900px) { + .hero { + padding: 40px 32px; + } +} \ No newline at end of file diff --git a/blocks/quote/_quote.json b/blocks/quote/_quote.json new file mode 100644 index 0000000000..25cfd34b0e --- /dev/null +++ b/blocks/quote/_quote.json @@ -0,0 +1,43 @@ +{ + "definitions": [ + { + "title": "Quote", + "id": "quote", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/block/v1/block", + "template": { + "name": "Quote", + "model": "quote", + "quote": "

Think, McFly! Think!

", + "author": "Biff Tannen" + } + } + } + } + } + ], + "models": [ + { + "id": "quote", + "fields": [ + { + "component": "text-area", + "name": "quote", + "value": "", + "label": "Quote", + "valueType": "string" + }, + { + "component": "text-input", + "valueType": "string", + "name": "author", + "label": "Author", + "value": "" + } + ] + } + ], + "filters": [] +} \ No newline at end of file diff --git a/component-definition.json b/component-definition.json index 87dcdfbc61..8e7ee42523 100644 --- a/component-definition.json +++ b/component-definition.json @@ -23,7 +23,9 @@ "xwalk": { "page": { "resourceType": "core/franklin/components/title/v1/title", - "template": {} + "template": { + "model": "title" + } } } } @@ -47,7 +49,9 @@ "xwalk": { "page": { "resourceType": "core/franklin/components/button/v1/button", - "template": {} + "template": { + "model": "button" + } } } } @@ -65,7 +69,9 @@ "xwalk": { "page": { "resourceType": "core/franklin/components/section/v1/section", - "template": {} + "template": { + "model": "section" + } } } } @@ -77,60 +83,75 @@ "id": "blocks", "components": [ { - "title": "Columns", - "id": "columns", + "title": "Cards", + "id": "cards", "plugins": { "xwalk": { "page": { - "resourceType": "core/franklin/components/columns/v1/columns", + "resourceType": "core/franklin/components/block/v1/block", "template": { - "columns": "2", - "rows": "1" + "name": "Cards", + "filter": "cards" } } } } }, { - "title": "Hero", - "id": "hero", + "title": "Card", + "id": "card", "plugins": { "xwalk": { "page": { - "resourceType": "core/franklin/components/block/v1/block", + "resourceType": "core/franklin/components/block/v1/block/item", "template": { - "name": "Hero", - "model": "hero" + "name": "Card", + "model": "card" } } } } }, { - "title": "Cards", - "id": "cards", + "title": "Columns", + "id": "columns", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/columns/v1/columns", + "template": { + "columns": "2", + "rows": "1" + } + } + } + } + }, + { + "title": "Fragment", + "id": "fragment", "plugins": { "xwalk": { "page": { "resourceType": "core/franklin/components/block/v1/block", "template": { - "name": "Cards", - "filter": "cards" + "name": "Fragment", + "model": "fragment" } } } } }, { - "title": "Card", - "id": "card", + "title": "Hero", + "id": "hero", "plugins": { "xwalk": { "page": { - "resourceType": "core/franklin/components/block/v1/block/item", + "resourceType": "core/franklin/components/block/v1/block", "template": { - "name": "Card", - "model": "card" + "name": "Hero", + "model": "hero" } } } @@ -156,4 +177,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/component-filters.json b/component-filters.json index 4dadb3d726..739250dc33 100644 --- a/component-filters.json +++ b/component-filters.json @@ -5,19 +5,6 @@ "section" ] }, - { - "id": "columns", - "components": [] - }, - { - "id": "column", - "components": [ - "text", - "image", - "button", - "title" - ] - }, { "id": "section", "components": [ @@ -28,6 +15,7 @@ "hero", "cards", "columns", + "fragment", "quote" ] }, @@ -36,5 +24,20 @@ "components": [ "card" ] + }, + { + "id": "columns", + "components": [ + "column" + ] + }, + { + "id": "column", + "components": [ + "text", + "image", + "button", + "title" + ] } -] +] \ No newline at end of file diff --git a/component-models.json b/component-models.json index 0fd76eeb98..0aa40d3644 100644 --- a/component-models.json +++ b/component-models.json @@ -1,20 +1,32 @@ [ + { + "id": "image", + "fields": [ + { + "component": "reference", + "name": "image", + "label": "Image", + "multi": false + }, + { + "component": "text", + "name": "imageAlt", + "label": "Alt Text" + } + ] + }, { "id": "title", "fields": [ { "component": "text", - "valueType": "string", - "name": "jcr:title", - "value": "", - "label": "Text" + "name": "title", + "label": "Title" }, { "component": "select", - "name": "type", - "value": "h1", - "label": "Type", - "valueType": "string", + "name": "titleType", + "label": "Title Type", "options": [ { "name": "h1", @@ -44,55 +56,28 @@ } ] }, - { - "id": "image", - "fields": [ - { - "component": "reference", - "valueType": "string", - "name": "fileReference", - "label": "Image", - "multi": false - }, - { - "component": "text", - "valueType": "string", - "name": "alt", - "value": "Default alt", - "label": "Alt Text" - } - ] - }, { "id": "button", "fields": [ { - "component": "text", - "valueType": "string", - "name": "href", - "value": "", + "component": "aem-content", + "name": "link", "label": "Link" }, { "component": "text", - "valueType": "string", - "name": "text", - "value": "", + "name": "linkText", "label": "Text" }, { "component": "text", - "valueType": "string", - "name": "title", - "value": "", + "name": "linkTitle", "label": "Title" }, { "component": "select", - "name": "type", - "value": "", + "name": "linkType", "label": "Type", - "valueType": "string", "options": [ { "name": "default", @@ -111,21 +96,18 @@ ] }, { - "id": "columns", + "id": "section", "fields": [ { - "component": "text", - "valueType": "number", - "name": "columns", - "value": "", - "label": "Columns" - }, - { - "component": "text", - "valueType": "number", - "name": "rows", - "value": "", - "label": "Rows" + "component": "multiselect", + "name": "style", + "label": "Style", + "options": [ + { + "name": "Highlight", + "value": "highlight" + } + ] } ] }, @@ -149,14 +131,31 @@ ] }, { - "id": "section", + "id": "columns", "fields": [ { "component": "text", - "name": "style", + "valueType": "number", + "name": "columns", "value": "", - "label": "Style", - "valueType": "string" + "label": "Columns" + }, + { + "component": "text", + "valueType": "number", + "name": "rows", + "value": "", + "label": "Rows" + } + ] + }, + { + "id": "fragment", + "fields": [ + { + "component": "aem-content", + "name": "reference", + "label": "Reference" } ] }, @@ -189,20 +188,20 @@ { "id": "quote", "fields": [ - { - "component": "text-area", - "name": "quote", - "value": "", - "label": "Quote", - "valueType": "string" - }, - { - "component": "text-input", - "valueType": "string", - "name": "author", - "label": "Author", - "value": "" - } - ] + { + "component": "text-area", + "name": "quote", + "value": "", + "label": "Quote", + "valueType": "string" + }, + { + "component": "text-input", + "valueType": "string", + "name": "author", + "label": "Author", + "value": "" + } + ] } -] +] \ No newline at end of file diff --git a/fonts/roboto-condensed-bold.woff2 b/fonts/roboto-condensed-bold.woff2 new file mode 100644 index 0000000000..dd0eb2becc Binary files /dev/null and b/fonts/roboto-condensed-bold.woff2 differ diff --git a/fonts/roboto-medium.woff2 b/fonts/roboto-medium.woff2 new file mode 100644 index 0000000000..8b1aebb23d Binary files /dev/null and b/fonts/roboto-medium.woff2 differ diff --git a/helix-query.yaml b/helix-query.yaml new file mode 100644 index 0000000000..1f032ddb46 --- /dev/null +++ b/helix-query.yaml @@ -0,0 +1,16 @@ +version: 1 + +indices: + pages: + include: + - '/**' + exclude: + - '/**.json' + target: /query-index.json + properties: + lastModified: + select: none + value: parseTimestamp(headers["last-modified"], "ddd, DD MMM YYYY hh:mm:ss GMT") + robots: + select: head > meta[name="robots"] + value: attribute(el, "content") \ No newline at end of file diff --git a/helix-sitemap.yaml b/helix-sitemap.yaml new file mode 100644 index 0000000000..ac41d5b54b --- /dev/null +++ b/helix-sitemap.yaml @@ -0,0 +1,5 @@ +sitemaps: + default: + source: /query-index.json + destination: /sitemap.xml + lastmod: YYYY-MM-DD \ No newline at end of file diff --git a/models/_button.json b/models/_button.json new file mode 100644 index 0000000000..a11c5e2036 --- /dev/null +++ b/models/_button.json @@ -0,0 +1,59 @@ +{ + "definitions": [ + { + "title": "Button", + "id": "button", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/button/v1/button", + "template": { + "model": "button" + } + } + } + } + } + ], + "models": [ + { + "id": "button", + "fields": [ + { + "component": "aem-content", + "name": "link", + "label": "Link" + }, + { + "component": "text", + "name": "linkText", + "label": "Text" + }, + { + "component": "text", + "name": "linkTitle", + "label": "Title" + }, + { + "component": "select", + "name": "linkType", + "label": "Type", + "options": [ + { + "name": "default", + "value": "" + }, + { + "name": "primary", + "value": "primary" + }, + { + "name": "secondary", + "value": "secondary" + } + ] + } + ] + } + ] +} diff --git a/models/_component-definition.json b/models/_component-definition.json new file mode 100644 index 0000000000..800e247df3 --- /dev/null +++ b/models/_component-definition.json @@ -0,0 +1,40 @@ +{ + "groups": [ + { + "title": "Default Content", + "id": "default", + "components": [ + { + "...": "./_text.json#/definitions" + }, + { + "...": "./_title.json#/definitions" + }, + { + "...": "./_image.json#/definitions" + }, + { + "...": "./_button.json#/definitions" + } + ] + }, + { + "title": "Sections", + "id": "sections", + "components": [ + { + "...": "./_section.json#/definitions" + } + ] + }, + { + "title": "Blocks", + "id": "blocks", + "components": [ + { + "...": "../blocks/*/_*.json#/definitions" + } + ] + } + ] +} diff --git a/models/_component-filters.json b/models/_component-filters.json new file mode 100644 index 0000000000..c22120420d --- /dev/null +++ b/models/_component-filters.json @@ -0,0 +1,14 @@ +[ + { + "id": "main", + "components": [ + "section" + ] + }, + { + "...": "./_section.json#/filters" + }, + { + "...": "../blocks/*/_*.json#/filters" + } +] diff --git a/models/_component-models.json b/models/_component-models.json new file mode 100644 index 0000000000..ab958de263 --- /dev/null +++ b/models/_component-models.json @@ -0,0 +1,20 @@ +[ + { + "...": "./_image.json#/models" + }, + { + "...": "./_title.json#/models" + }, + { + "...": "./_text.json#/models" + }, + { + "...": "./_button.json#/models" + }, + { + "...": "./_section.json#/models" + }, + { + "...": "../blocks/*/_*.json#/models" + } +] diff --git a/models/_image.json b/models/_image.json new file mode 100644 index 0000000000..add08e0786 --- /dev/null +++ b/models/_image.json @@ -0,0 +1,34 @@ +{ + "definitions": [ + { + "title": "Image", + "id": "image", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/image/v1/image", + "template": {} + } + } + } + } + ], + "models": [ + { + "id": "image", + "fields": [ + { + "component": "reference", + "name": "image", + "label": "Image", + "multi": false + }, + { + "component": "text", + "name": "imageAlt", + "label": "Alt Text" + } + ] + } + ] +} diff --git a/models/_section.json b/models/_section.json new file mode 100644 index 0000000000..70ddc69e20 --- /dev/null +++ b/models/_section.json @@ -0,0 +1,52 @@ +{ + "definitions": [ + { + "title": "Section", + "id": "section", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/section/v1/section", + "template": { + "model": "section" + } + } + } + } + } + ], + "models": [ + { + "id": "section", + "fields": [ + { + "component": "multiselect", + "name": "style", + "label": "Style", + "options": [ + { + "name": "Highlight", + "value": "highlight" + } + ] + } + ] + } + ], + "filters": [ + { + "id": "section", + "components": [ + "text", + "image", + "button", + "title", + "hero", + "cards", + "columns", + "fragment", + "quote" + ] + } + ] +} diff --git a/models/_text.json b/models/_text.json new file mode 100644 index 0000000000..133c8a7046 --- /dev/null +++ b/models/_text.json @@ -0,0 +1,17 @@ +{ + "definitions": [ + { + "title": "Text", + "id": "text", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/text/v1/text", + "template": {} + } + } + } + } + ], + "models": [] +} diff --git a/models/_title.json b/models/_title.json new file mode 100644 index 0000000000..922a799f00 --- /dev/null +++ b/models/_title.json @@ -0,0 +1,61 @@ +{ + "definitions": [ + { + "title": "Title", + "id": "title", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/franklin/components/title/v1/title", + "template": { + "model": "title" + } + } + } + } + } + ], + "models": [ + { + "id": "title", + "fields": [ + { + "component": "text", + "name": "title", + "label": "Title" + }, + { + "component": "select", + "name": "titleType", + "label": "Title Type", + "options": [ + { + "name": "h1", + "value": "h1" + }, + { + "name": "h2", + "value": "h2" + }, + { + "name": "h3", + "value": "h3" + }, + { + "name": "h4", + "value": "h4" + }, + { + "name": "h5", + "value": "h5" + }, + { + "name": "h6", + "value": "h6" + } + ] + } + ] + } + ] +} diff --git a/package-lock.json b/package-lock.json index 3ab5318389..d8d0b82444 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,17 @@ "version": "1.3.0", "license": "Apache License 2.0", "devDependencies": { - "@babel/eslint-parser": "7.24.1", + "@babel/eslint-parser": "7.25.1", "eslint": "8.57.0", "eslint-config-airbnb-base": "15.0.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-json": "3.1.0", - "eslint-plugin-xwalk": "github:adobe-rnd/eslint-plugin-xwalk#v0.1.0", - "stylelint": "16.6.1", - "stylelint-config-standard": "36.0.0" + "eslint-plugin-xwalk": "github:adobe-rnd/eslint-plugin-xwalk#v0.1.2", + "husky": "9.1.1", + "merge-json-cli": "1.0.4", + "npm-run-all": "4.1.5", + "stylelint": "16.8.2", + "stylelint-config-standard": "36.0.1" } }, "node_modules/@ampproject/remapping": { @@ -88,9 +91,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz", - "integrity": "sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.1.tgz", + "integrity": "sha512-Y956ghgTT4j7rKesabkh5WeqgSFZVFwaPR0IWFm7KFHFmmJ4afbG49SmfW4S+GyRPx0Dy5jxEWA5t0rpxfElWg==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -102,7 +105,7 @@ }, "peerDependencies": { "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0" + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/@babel/generator": { @@ -363,9 +366,9 @@ } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz", - "integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", + "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", "dev": true, "funding": [ { @@ -378,16 +381,16 @@ } ], "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-tokenizer": "^3.0.1" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz", - "integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", + "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", "dev": true, "funding": [ { @@ -400,13 +403,13 @@ } ], "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz", - "integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", "dev": true, "funding": [ { @@ -419,17 +422,17 @@ } ], "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1" } }, "node_modules/@csstools/selector-specificity": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz", - "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", "dev": true, "funding": [ { @@ -442,10 +445,10 @@ } ], "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, "peerDependencies": { - "postcss-selector-parser": "^6.0.13" + "postcss-selector-parser": "^6.1.0" } }, "node_modules/@dual-bundle/import-meta-resolve": { @@ -1193,9 +1196,9 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1642,8 +1645,8 @@ } }, "node_modules/eslint-plugin-xwalk": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/adobe-rnd/eslint-plugin-xwalk.git#15036031c7e6c8e736f74a84e612f237c878a66d", + "version": "0.1.1", + "resolved": "git+ssh://git@github.com/adobe-rnd/eslint-plugin-xwalk.git#669dd8df0164d404d669163de54412a9a457e38d", "dev": true, "license": "Apache License 2.0", "dependencies": { @@ -2258,6 +2261,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2345,6 +2354,12 @@ "node": ">= 0.4" } }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "node_modules/html-tags": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", @@ -2357,10 +2372,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/husky": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.1.tgz", + "integrity": "sha512-fCqlqLXcBnXa/TJXmT93/A36tJsjdJkibQ1MuIiFyCCYUlpYpIaj2mv1w+3KR6Rzu1IC3slFTje5f6DUp2A2rg==", + "dev": true, + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -2753,6 +2783,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -2809,9 +2845,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.31.0.tgz", - "integrity": "sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", "dev": true }, "node_modules/levn": { @@ -2833,6 +2869,34 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2892,6 +2956,15 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", @@ -2904,6 +2977,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-json-cli": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-json-cli/-/merge-json-cli-1.0.4.tgz", + "integrity": "sha512-LUHbIFh/aPh0MebiVEAcWRaqR6a5e+HjTp2LyDxwSAaNnPruAq+AWZukeieSi3HKHdrX1Wy6tUEZdSLS0KjwJA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "merge-json-cli": "src/cli.js" + }, + "engines": { + "node": ">=18.3.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2914,10 +3003,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -2977,6 +3067,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -2984,6 +3080,27 @@ "dev": true, "peer": true }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2993,6 +3110,98 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -3238,6 +3447,27 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -3248,9 +3478,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -3268,7 +3498,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -3276,9 +3506,9 @@ } }, "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", "dev": true }, "node_modules/postcss-safe-parser": { @@ -3308,9 +3538,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -3364,6 +3594,32 @@ } ] }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -3563,6 +3819,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -3661,6 +3926,38 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "dev": true + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3675,6 +3972,24 @@ "node": ">=8" } }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -3758,9 +4073,9 @@ } }, "node_modules/stylelint": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.6.1.tgz", - "integrity": "sha512-yNgz2PqWLkhH2hw6X9AweV9YvoafbAD5ZsFdKN9BvSDVwGvPh+AUIrn7lYwy1S7IHmtFin75LLfX1m0D2tHu8Q==", + "version": "16.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.2.tgz", + "integrity": "sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==", "dev": true, "funding": [ { @@ -3773,17 +4088,17 @@ } ], "dependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1", - "@csstools/media-query-list-parser": "^2.1.11", - "@csstools/selector-specificity": "^3.1.1", + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "@csstools/selector-specificity": "^4.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.2", "css-tree": "^2.3.1", - "debug": "^4.3.4", + "debug": "^4.3.6", "fast-glob": "^3.3.2", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^9.0.0", @@ -3791,19 +4106,19 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^5.3.1", + "ignore": "^5.3.2", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.31.0", + "known-css-properties": "^0.34.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.7", "normalize-path": "^3.0.0", "picocolors": "^1.0.1", - "postcss": "^8.4.38", - "postcss-resolve-nested-selector": "^0.1.1", + "postcss": "^8.4.41", + "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^6.1.0", + "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", @@ -3821,24 +4136,44 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz", - "integrity": "sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", + "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "engines": { "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.0.0" + "stylelint": "^16.1.0" } }, "node_modules/stylelint-config-standard": { - "version": "36.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.0.tgz", - "integrity": "sha512-3Kjyq4d62bYFp/Aq8PMKDwlgUyPU4nacXsjDLWJdNPRUgpuxALu1KnlAHIj36cdtxViVhXexZij65yM0uNIHug==", + "version": "36.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz", + "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "dependencies": { - "stylelint-config-recommended": "^14.0.0" + "stylelint-config-recommended": "^14.0.1" }, "engines": { "node": ">=18.12.0" @@ -4226,6 +4561,16 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vscode-json-languageservice": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz", @@ -4348,6 +4693,15 @@ "dev": true, "peer": true }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index e9d984e649..1a27cea5a8 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,14 @@ "version": "1.3.0", "description": "Starter project for Adobe Helix", "scripts": { - "lint:js": "eslint . --ext .json,.js", + "lint:js": "eslint . --ext .json,.js,.mjs", "lint:css": "stylelint blocks/**/*.css styles/*.css", - "lint": "npm run lint:js && npm run lint:css" + "lint": "npm run lint:js && npm run lint:css", + "build:json": "npm-run-all -p build:json:models build:json:definitions build:json:filters", + "build:json:models": "merge-json-cli -i \"models/_component-models.json\" -o \"component-models.json\"", + "build:json:definitions": "merge-json-cli -i \"models/_component-definition.json\" -o \"component-definition.json\"", + "build:json:filters": "merge-json-cli -i \"models/_component-filters.json\" -o \"component-filters.json\"", + "prepare": "husky" }, "repository": { "type": "git", @@ -19,13 +24,16 @@ }, "homepage": "https://github.com/adobe/aem-boilerplate#readme", "devDependencies": { - "@babel/eslint-parser": "7.24.1", + "@babel/eslint-parser": "7.25.1", "eslint": "8.57.0", "eslint-config-airbnb-base": "15.0.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-json": "3.1.0", - "eslint-plugin-xwalk": "github:adobe-rnd/eslint-plugin-xwalk#v0.1.0", - "stylelint": "16.6.1", - "stylelint-config-standard": "36.0.0" + "eslint-plugin-xwalk": "github:adobe-rnd/eslint-plugin-xwalk#v0.1.2", + "husky": "9.1.1", + "merge-json-cli": "1.0.4", + "npm-run-all": "4.1.5", + "stylelint": "16.8.2", + "stylelint-config-standard": "36.0.1" } } diff --git a/paths.json b/paths.json index 235d73a901..a3a889bb52 100644 --- a/paths.json +++ b/paths.json @@ -1,6 +1,7 @@ { "mappings": [ "/content/aem-boilerplate/:/", - "/content/aem-boilerplate/configuration:/.helix/config.json" + "/content/aem-boilerplate/configuration:/.helix/config.json", + "/content/aem-boilerplate/metadata:/metadata.json" ] } diff --git a/scripts/aem.js b/scripts/aem.js index 035c8cc6e5..de3beb14d1 100644 --- a/scripts/aem.js +++ b/scripts/aem.js @@ -11,88 +11,102 @@ */ /* 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 = {}) { - 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 weight = (window.SAMPLE_PAGEVIEWS_AT_RATE === 'high' && 10) + || (window.SAMPLE_PAGEVIEWS_AT_RATE === 'low' && 1000) + || (new URLSearchParams(window.location.search).get('rum') === 'on' && 1) + || 100; const id = Math.random().toString(36).slice(-4); - 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(/\?.*$/, ''), - }; + const isSelected = Math.random() * weight < 1; // eslint-disable-next-line object-curly-newline, max-len - window.hlx.rum = { weight, id, random, isSelected, firstReadTime, sampleRUM, sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'] }; - } - - 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); + window.hlx.rum = { + weight, + id, + isSelected, + firstReadTime: window.performance ? window.performance.timeOrigin : Date.now(), + sampleRUM, + queue: [], + collector: (...args) => window.hlx.rum.queue.push(args), }; - sampleRUM.cases = sampleRUM.cases || { - cwv: () => sampleRUM.cwv(data) || true, - lazy: () => { - // use classic script to avoid CORS issues + 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 { href: url, origin } = new URL(`.rum/${weight}`, 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 = () => { const script = document.createElement('script'); - script.src = new URL('.rum/@adobe/helix-rum-enhancer@^1/src/index.js', sampleRUM.baseURL).href; + script.src = new URL( + '.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 } @@ -104,6 +118,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'; @@ -129,17 +144,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(); } /** @@ -459,10 +464,10 @@ function decorateSections(main) { const wrappers = []; let defaultContent = false; [...section.children].forEach((e) => { - if (e.tagName === 'DIV' || !defaultContent) { + if ((e.tagName === 'DIV' && e.className) || !defaultContent) { const wrapper = document.createElement('div'); wrappers.push(wrapper); - defaultContent = e.tagName !== 'DIV'; + defaultContent = e.tagName !== 'DIV' || !e.className; if (defaultContent) wrapper.classList.add('default-content-wrapper'); } wrappers[wrappers.length - 1].append(e); @@ -529,30 +534,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 @@ -621,20 +602,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 @@ -688,17 +655,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'); @@ -710,6 +671,39 @@ 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]); + } +} + init(); export { @@ -724,17 +718,17 @@ export { fetchPlaceholders, getMetadata, loadBlock, - loadBlocks, loadCSS, loadFooter, loadHeader, loadScript, + loadSection, + loadSections, readBlockConfig, sampleRUM, setup, toCamelCase, toClassName, - updateSectionsStatus, - waitForLCP, + waitForFirstImage, wrapTextNodes, }; diff --git a/scripts/delayed.js b/scripts/delayed.js index 4f632597be..28fa26c8e2 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -1,7 +1 @@ -// eslint-disable-next-line import/no-cycle -import { sampleRUM } from './aem.js'; - -// Core Web Vitals RUM collection -sampleRUM('cwv'); - -// add more delayed functionality here +// add delayed functionality here diff --git a/scripts/editor-support.js b/scripts/editor-support.js index 4bdae906ff..2fe55f7a50 100644 --- a/scripts/editor-support.js +++ b/scripts/editor-support.js @@ -5,7 +5,7 @@ import { decorateIcons, decorateSections, loadBlock, - loadBlocks, + loadSections, } from './aem.js'; import { decorateRichtext } from './editor-support-rte.js'; import { decorateMain } from './scripts.js'; @@ -33,7 +33,7 @@ async function applyChanges(event) { element.insertAdjacentElement('afterend', newMain); decorateMain(newMain); decorateRichtext(newMain); - await loadBlocks(newMain); + await loadSections(newMain); element.remove(); newMain.style.display = null; // eslint-disable-next-line no-use-before-define @@ -71,7 +71,7 @@ async function applyChanges(event) { decorateRichtext(newSection); decorateSections(parentElement); decorateBlocks(parentElement); - await loadBlocks(parentElement); + await loadSections(parentElement); element.remove(); newSection.style.display = null; } else { @@ -95,6 +95,7 @@ function attachEventListners(main) { 'aue:content-add', 'aue:content-move', 'aue:content-remove', + 'aue:content-copy', ].forEach((eventType) => main?.addEventListener(eventType, async (event) => { event.stopPropagation(); const applied = await applyChanges(event); diff --git a/scripts/scripts.js b/scripts/scripts.js index 1e206db924..5e1288eaf8 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -7,13 +7,12 @@ import { decorateSections, decorateBlocks, decorateTemplateAndTheme, - waitForLCP, - loadBlocks, + waitForFirstImage, + loadSection, + loadSections, loadCSS, } from './aem.js'; -const LCP_BLOCKS = []; // add your LCP blocks to the list - /** * Moves all the attributes from a given elmenet to another given element. * @param {Element} from the element to copy attributes from @@ -98,9 +97,11 @@ async function loadEager(doc) { if (main) { decorateMain(main); document.body.classList.add('appear'); - await waitForLCP(LCP_BLOCKS); + await loadSection(main.querySelector('.section'), waitForFirstImage); } + sampleRUM.enhance(); + try { /* if desktop (proxy for fast connection) or fonts already loaded, load fonts.css */ if (window.innerWidth >= 900 || sessionStorage.getItem('fonts-loaded')) { @@ -117,7 +118,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; @@ -128,10 +129,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')); } /** diff --git a/styles/fonts.css b/styles/fonts.css index a9c913fb6e..319c400ae7 100644 --- a/styles/fonts.css +++ b/styles/fonts.css @@ -1,4 +1,13 @@ /* stylelint-disable max-line-length */ +@font-face { + font-family: roboto-condensed; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('../fonts/roboto-condensed-bold.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + @font-face { font-family: roboto; font-style: normal; @@ -8,6 +17,15 @@ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } +@font-face { + font-family: roboto; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('../fonts/roboto-medium.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + @font-face { font-family: roboto; font-style: normal; diff --git a/styles/styles.css b/styles/styles.css index ff5fa546a3..24425c9054 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -12,61 +12,72 @@ :root { /* colors */ - --link-color: #035fe6; - --link-hover-color: #136ff6; --background-color: white; - --light-color: #eee; - --dark-color: #ccc; - --text-color: black; + --light-color: #f8f8f8; + --dark-color: #505050; + --text-color: #131313; + --link-color: #3b63fb; + --link-hover-color: #1d3ecf; /* fonts */ - --body-font-family: roboto, roboto-fallback; - --heading-font-family: var(--body-font-family); - --fixed-font-family: 'Roboto Mono', menlo, consolas, 'Liberation Mono', monospace; + --body-font-family: roboto, roboto-fallback, sans-serif; + --heading-font-family: roboto-condensed, roboto-condensed-fallback, sans-serif; /* body sizes */ --body-font-size-m: 22px; - --body-font-size-s: 18px; - --body-font-size-xs: 16px; + --body-font-size-s: 19px; + --body-font-size-xs: 17px; /* heading sizes */ - --heading-font-size-xxl: 48px; - --heading-font-size-xl: 40px; - --heading-font-size-l: 32px; - --heading-font-size-m: 24px; - --heading-font-size-s: 20px; - --heading-font-size-xs: 18px; + --heading-font-size-xxl: 55px; + --heading-font-size-xl: 44px; + --heading-font-size-l: 34px; + --heading-font-size-m: 27px; + --heading-font-size-s: 24px; + --heading-font-size-xs: 22px; /* nav height */ --nav-height: 64px; } +/* fallback fonts */ +@font-face { + font-family: roboto-condensed-fallback; + size-adjust: 88.82%; + src: local('Arial'); +} + @font-face { font-family: roboto-fallback; - size-adjust: 100.06%; - ascent-override: 95%; + size-adjust: 99.529%; src: local('Arial'); } @media (width >= 900px) { :root { - --heading-font-size-xxl: 60px; - --heading-font-size-xl: 48px; - --heading-font-size-l: 36px; - --heading-font-size-m: 30px; - --heading-font-size-s: 24px; - --heading-font-size-xs: 22px; + /* body sizes */ + --body-font-size-m: 18px; + --body-font-size-s: 16px; + --body-font-size-xs: 14px; + + /* heading sizes */ + --heading-font-size-xxl: 45px; + --heading-font-size-xl: 36px; + --heading-font-size-l: 28px; + --heading-font-size-m: 22px; + --heading-font-size-s: 20px; + --heading-font-size-xs: 18px; } } body { - font-size: var(--body-font-size-m); + display: none; margin: 0; + background-color: var(--background-color); + color: var(--text-color); font-family: var(--body-font-family); + font-size: var(--body-font-size-m); line-height: 1.6; - color: var(--text-color); - background-color: var(--background-color); - display: none; } body.appear { @@ -77,18 +88,28 @@ header { height: var(--nav-height); } +header .header, +footer .footer { + visibility: hidden; +} + +header .header[data-block-status="loaded"], +footer .footer[data-block-status="loaded"] { + visibility: visible; +} + h1, h2, h3, h4, h5, h6 { + margin-top: 0.8em; + margin-bottom: 0.25em; font-family: var(--heading-font-family); font-weight: 600; line-height: 1.25; - margin-top: 1em; - margin-bottom: 0.5em; - scroll-margin: calc(var(--nav-height) + 1em); + scroll-margin: 40px; } h1 { font-size: var(--heading-font-size-xxl); } @@ -104,63 +125,68 @@ ol, ul, pre, blockquote { - margin-top: 1em; - margin-bottom: 1em; + margin-top: 0.8em; + margin-bottom: 0.25em; } code, pre { - font-family: var(--fixed-font-family); font-size: var(--body-font-size-s); } -code { - padding: 0.125em; -} - pre { - overflow: scroll; -} - -main pre { + padding: 16px; + border-radius: 8px; background-color: var(--light-color); - padding: 1em; - border-radius: 0.25em; overflow-x: auto; white-space: pre; } +main > div { + margin: 40px 16px; +} + +input, +textarea, +select, +button { + font: inherit; +} + /* links */ a:any-link { color: var(--link-color); text-decoration: none; + word-break: break-word; } a:hover { - text-decoration: underline; color: var(--link-hover-color); + text-decoration: underline; } /* buttons */ a.button:any-link, button { - font-family: var(--body-font-family); - display: inline-block; box-sizing: border-box; - text-decoration: none; + display: inline-block; + max-width: 100%; + margin: 12px 0; border: 2px solid transparent; - padding: 5px 30px; - text-align: center; + border-radius: 2.4em; + padding: 0.5em 1.2em; + font-family: var(--body-font-family); font-style: normal; - font-weight: 600; - cursor: pointer; - color: var(--background-color); + font-weight: 500; + line-height: 1.25; + text-align: center; + text-decoration: none; background-color: var(--link-color); - margin: 16px 0; - white-space: nowrap; + color: var(--background-color); + cursor: pointer; overflow: hidden; text-overflow: ellipsis; - border-radius: 30px; + white-space: nowrap; } a.button:hover, @@ -202,20 +228,23 @@ main img { } /* sections */ -main .section { - padding: 64px 16px; +main > .section { + margin: 40px 0; } -@media (width >= 600px) { - main .section { - padding: 64px 32px; - } +main > .section > div { + max-width: 1200px; + margin: auto; + padding: 0 24px; +} + +main > .section:first-of-type { + margin-top: 0; } @media (width >= 900px) { - .section > div { - max-width: 1200px; - margin: auto; + main > .section > div { + padding: 0 32px; } } @@ -223,4 +252,6 @@ main .section { main .section.light, main .section.highlight { background-color: var(--light-color); + margin: 0; + padding: 40px 0; }