diff --git a/.eslintrc.js b/.eslintrc.js index 76f220db..deac7e89 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,6 @@ module.exports = { root: true, - extends: 'airbnb-base', + extends: ['airbnb-base', 'prettier'], env: { browser: true, }, @@ -11,11 +11,18 @@ module.exports = { requireConfigFile: false, }, rules: { + 'max-len': [2, 160, 2, { ignoreUrls: true }], + 'import/no-unresolved': [2, { commonjs: true }], + 'array-callback-return': 'off', // due to prettier + 'class-methods-use-this': 'off', // due to prettier // allow reassigning param 'no-param-reassign': [2, { props: false }], 'linebreak-style': ['error', 'unix'], - 'import/extensions': ['error', { - js: 'always', - }], + 'import/extensions': [ + 'error', + { + js: 'always', + }, + ], }, }; diff --git a/blocks/button/button.css b/blocks/button/button.css new file mode 100644 index 00000000..7358e7cb --- /dev/null +++ b/blocks/button/button.css @@ -0,0 +1,32 @@ +raqn-button { + width: 100%; + display: grid; + + & > div { + background-color: var(--scope-accent-background, #000); + color: var(--scope-accent-color, #fff); + text-transform: none; + border-radius: 20px; + border: 1px solid transparent; + overflow: hidden; + + &:hover { + background-color: var(--scope-accent-background-hover, #fff); + color: var(--scope-accent-color-hover, #fff); + border-color: currentcolor; + } + } +} + +raqn-button a { + color: currentcolor; + padding: 10px 20px; + text-decoration: none; +} + +raqn-button a:hover, +raqn-button a:visited, +raqn-button a:active { + color: currentcolor; + text-decoration: none; +} diff --git a/blocks/button/button.js b/blocks/button/button.js new file mode 100644 index 00000000..227717d3 --- /dev/null +++ b/blocks/button/button.js @@ -0,0 +1,8 @@ +import Column from '../column/column.js'; + +export default class Button extends Column { + render() { + this.setAttribute('role', 'button'); + this.setAttribute('tabindex', '0'); + } +} diff --git a/blocks/card/card.css b/blocks/card/card.css new file mode 100644 index 00000000..db0d2da7 --- /dev/null +++ b/blocks/card/card.css @@ -0,0 +1,10 @@ +raqn-card { + background-color: var(--scope-background, red); + display: grid; + grid-template-columns: var(--card-columns, 1fr); + gap: var(--scope-gap, 20px); +} + +raqn-card > picture { + grid-column: span var(--card-columns, 1fr); +} \ No newline at end of file diff --git a/blocks/card/card.js b/blocks/card/card.js new file mode 100644 index 00000000..cd9b1026 --- /dev/null +++ b/blocks/card/card.js @@ -0,0 +1,42 @@ +import ComponentBase from '../../scripts/component-base.js'; +import { eagerImage } from '../../scripts/libs.js'; + +export default class Card extends ComponentBase { + static get observedAttributes() { + return ['columns', 'ratio', 'eager']; + } + + connected() { + this.eager = parseInt(this.getAttribute('eager') || 0, 10); + this.setupColumns(this.getAttribute('columns')); + if (this.eager) { + eagerImage(this, this.eager); + } + } + + setupColumns(columns) { + if (!columns) { + return; + } + this.columns = parseInt(columns, 10); + this.area = Array.from(Array(parseInt(this.columns, 10))) + .map(() => '1fr') + .join(' '); + this.style.setProperty('--card-columns', this.area); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + switch (name) { + case 'columns': + this.setupColumns(newValue); + break; + case 'ratio': + this.style.setProperty('--card-ratio', newValue); + break; + default: + break; + } + } + } +} diff --git a/blocks/cards/cards.css b/blocks/cards/cards.css deleted file mode 100644 index a0315ede..00000000 --- a/blocks/cards/cards.css +++ /dev/null @@ -1,31 +0,0 @@ -.cards > ul { - list-style: none; - margin: 0; - padding: 0; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - grid-gap: 16px; -} - -.cards > ul > li { - border: 1px solid var(--highlight-background-color); - background-color: var(--background-color) -} - -.cards .cards-card-body { - margin: 16px; -} - -.cards .cards-card-image { - line-height: 0; -} - -.cards .cards-card-body > *:first-child { - margin-top: 0; -} - -.cards > ul > li img { - width: 100%; - aspect-ratio: 4 / 3; - object-fit: cover; -} diff --git a/blocks/cards/cards.js b/blocks/cards/cards.js deleted file mode 100644 index 717ec302..00000000 --- a/blocks/cards/cards.js +++ /dev/null @@ -1,18 +0,0 @@ -import { createOptimizedPicture } from '../../scripts/lib-franklin.js'; - -export default function decorate(block) { - /* change to ul, li */ - const ul = document.createElement('ul'); - [...block.children].forEach((row) => { - const li = document.createElement('li'); - while (row.firstElementChild) li.append(row.firstElementChild); - [...li.children].forEach((div) => { - if (div.children.length === 1 && div.querySelector('picture')) div.className = 'cards-card-image'; - else div.className = 'cards-card-body'; - }); - ul.append(li); - }); - ul.querySelectorAll('img').forEach((img) => img.closest('picture').replaceWith(createOptimizedPicture(img.src, img.alt, false, [{ width: '750' }]))); - block.textContent = ''; - block.append(ul); -} diff --git a/blocks/column/column.css b/blocks/column/column.css new file mode 100644 index 00000000..1879b40e --- /dev/null +++ b/blocks/column/column.css @@ -0,0 +1,3 @@ +raqn-column { + margin: var(--scope-margin, 0); +} diff --git a/blocks/column/column.js b/blocks/column/column.js new file mode 100644 index 00000000..71fe8f81 --- /dev/null +++ b/blocks/column/column.js @@ -0,0 +1,74 @@ +import ComponentBase from '../../scripts/component-base.js'; + +export default class Column extends ComponentBase { + static observedAttributes() { + return ['position', 'size']; + } + + connected() { + this.calculateGridTemplateColumns(); + } + + calculateGridTemplateColumns() { + this.position = parseInt(this.getAttribute('position'), 10); + this.size = this.getAttribute('size'); + this.justify = this.getAttribute('justify') || 'stretch'; + if (this.justify) { + this.style.justifyContent = this.justify; + } + if (this.position) { + const parent = this.parentElement; + const children = Array.from(parent.children); + this.parentElement.classList.add('raqn-grid'); + let parentGridTemplateColumns = parent.style.getPropertyValue( + '--grid-template-columns', + ); + if (!parentGridTemplateColumns) { + // we have no grid template columns yet + parentGridTemplateColumns = children + .map((child, index) => { + if (this.position === index + 1) { + return this.size || 'auto'; + } + return 'auto'; + }) + .join(' '); + // set the new grid template columns + parent.style.setProperty( + '--grid-template-columns', + parentGridTemplateColumns, + ); + } else { + const { position } = this; + const prio = children.indexOf(this) + 1; + parentGridTemplateColumns = parentGridTemplateColumns + .split(' ') + .map((size, i) => { + // we have a non standard value for this position + const hasValue = size !== 'auto'; + // we are at the position + const isPosition = i + 1 === position; + // we are at a position before the prio + const isBeforePrio = i + 1 <= prio; + // we have a non standard value for this position and we are at the position + if (!hasValue && isPosition) { + return this.size || 'auto'; + } + // we have a non standard value for this position and we are at a position before the prio + if (hasValue && isPosition && isBeforePrio) { + return this.size || size; + } + return size; + }) + .join(' '); + // set the new grid template columns + parent.style.setProperty( + '--grid-template-columns', + parentGridTemplateColumns, + ); + } + this.style.gridColumn = this.position; + this.style.gridRow = 1; + } + } +} diff --git a/blocks/columns/columns.css b/blocks/columns/columns.css deleted file mode 100644 index 4b09aeea..00000000 --- a/blocks/columns/columns.css +++ /dev/null @@ -1,65 +0,0 @@ -.columns > div { - display: flex; - flex-direction: column; -} - -.columns img { - width: 100%; -} - -@media (min-width: 900px) { - .columns > div { - align-items: center; - flex-direction: unset; - gap: var(--padding-vertical) var(--padding-horizontal); - padding: var(--padding-vertical) var(--padding-horizontal); - } - - .columns > div > div:nth-child(1) { - flex: var(--column0-flex, 0); - } - - .columns > div > div:nth-child(2) { - flex: var(--column1-flex, 0); - } - - .columns > div > div:nth-child(3) { - flex: var(--column2-flex, 0); - } - - .columns > div > div:nth-child(4) { - flex: var(--column3-flex, 0); - } - - .columns > div > div:nth-child(5) { - flex: var(--column4-flex, 0); - } - - .columns > div > div:nth-child(6) { - flex: var(--column5-flex, 0); - } - - .columns > div > div:nth-child(7) { - flex: var(--column6-flex, 0); - } - - .columns > div > div:nth-child(8) { - flex: var(--column7-flex, 0); - } - - .columns > div > div:nth-child(9) { - flex: var(--column8-flex, 0); - } - - .columns > div > div:nth-child(10) { - flex: var(--column9-flex, 0); - } - - .columns > div > div:nth-child(11) { - flex: var(--column10-flex, 0); - } - - .columns > div > div:nth-child(12) { - flex: var(--column11-flex, 0); - } -} diff --git a/blocks/columns/columns.js b/blocks/columns/columns.js deleted file mode 100644 index 823c6c18..00000000 --- a/blocks/columns/columns.js +++ /dev/null @@ -1,18 +0,0 @@ -import { - addCssVariables, -} from '../../scripts/lib-franklin.js'; - -export default function decorate(block) { - const columns = block.querySelectorAll(':scope > div > div'); - const columnCount = columns.length; - // following line regex matches partition sizes separated by dashes like 1-2-3 - const columnPartionRegex = /^\d{1,}(?:-\d{1,})*$/; - const columnPartions = [...block.classList].find((c) => columnPartionRegex.test(c))?.split('-') || []; - - const variables = {}; - for (let i = 0; i < columnCount; i += 1) { - const partition = columnPartions.length > i ? columnPartions[i] : 1; - variables[`column${i}-flex`] = partition; - } - addCssVariables(block, variables); -} diff --git a/blocks/footer/footer.css b/blocks/footer/footer.css index 19fc5607..23d16ae2 100644 --- a/blocks/footer/footer.css +++ b/blocks/footer/footer.css @@ -11,4 +11,4 @@ footer .footer { footer .footer p { margin: 0; -} \ No newline at end of file +} diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js index ca8c7a68..c8ff6b12 100644 --- a/blocks/footer/footer.js +++ b/blocks/footer/footer.js @@ -1,25 +1,8 @@ -import { readBlockConfig, decorateIcons } from '../../scripts/lib-franklin.js'; +import ComponentBase from '../../scripts/component-base.js'; -/** - * loads and decorates the footer - * @param {Element} block The footer block element - */ -export default async function decorate(block) { - const cfg = readBlockConfig(block); - block.textContent = ''; - - // fetch footer content - const footerPath = cfg.footer || '/footer'; - const resp = await fetch(`${footerPath}.plain.html`, window.location.pathname.endsWith('/footer') ? { cache: 'reload' } : {}); - - if (resp.ok) { - const html = await resp.text(); - - // decorate footer DOM - const footer = document.createElement('div'); - footer.innerHTML = html; - - decorateIcons(footer); - block.append(footer); +export default class Footer extends ComponentBase { + constructor() { + super(); + this.external = '/footer.plain.html'; } } diff --git a/blocks/grid/grid.css b/blocks/grid/grid.css deleted file mode 100644 index ba453ff5..00000000 --- a/blocks/grid/grid.css +++ /dev/null @@ -1,30 +0,0 @@ -.section.grid { - display: grid; - grid-template-columns: var(--grid-template-columns, unset); - grid-template-rows: var(--grid-template-rows, unset); -} - -.section.grid .element { - grid-column: var(--grid-column-start-position, auto) / var(--grid-column-end-position, auto); - grid-row: var(--grid-row-start-position, auto) / var(--grid-row-end-position, auto); - width: 100%; - max-width: unset; - background-color: var(--background-color, transparent); - color: var(--text-color); - margin: auto; -} - -.section.grid .element .image-wrapper { - margin: 0; - padding: 0; -} - -.section.grid .element img { - width: 100%; - height: auto; -} - -.section.grid .element p { - margin: 0; - padding: var(--padding-vertical) var(--padding-horizontal); -} diff --git a/blocks/grid/grid.js b/blocks/grid/grid.js deleted file mode 100644 index 5a9fc3f8..00000000 --- a/blocks/grid/grid.js +++ /dev/null @@ -1,29 +0,0 @@ -import { - addCssVariables, -} from '../../scripts/lib-franklin.js'; - -export default function decorate(block) { - const elements = [...block.querySelectorAll(':scope > div')]; - - const columnTemplate = elements.find((e) => e.dataset.gridColumns)?.dataset.gridColumns; - const rowTemplate = elements.find((e) => e.dataset.gridRows)?.dataset.gridRows; - if (columnTemplate || rowTemplate) { - addCssVariables(block, { - 'grid-template-columns': columnTemplate, - 'grid-template-rows': rowTemplate, - }); - } - - elements.forEach((e) => { - e.classList.add('element'); - - const [[startColumnPosition, startRowPosition], [endColumnPosition, endRowPosition]] = e.dataset.gridPosition.split(/\s*\/\s*/).map((p) => p.split(/\s*-\s*/)); - - addCssVariables(e, { - 'grid-column-start-position': startColumnPosition, - 'grid-row-start-position': startRowPosition, - 'grid-column-end-position': endColumnPosition, - 'grid-row-end-position': endRowPosition, - }); - }); -} diff --git a/blocks/header/header.css b/blocks/header/header.css index 09581712..87526d55 100644 --- a/blocks/header/header.css +++ b/blocks/header/header.css @@ -1,19 +1,8 @@ -header a:any-link { - color: inherit; -} - -header p, -header .section.grid .element p { - margin: 0; - padding: 0; -} - -a.button:any-link { - background-color: transparent; - color: inherit; - font-weight: normal; -} - -a.button:any-link .icon { - vertical-align: middle; +raqn-header { + display: grid; + max-width: var(--scope-max-width, 90vw); + margin: 0 auto; + align-items: center; + border-block-end: var(--scope-border-color, #000) solid + var(--scope-border-width, 1px); } diff --git a/blocks/header/header.js b/blocks/header/header.js index c8512c3f..5d373996 100644 --- a/blocks/header/header.js +++ b/blocks/header/header.js @@ -1,11 +1,11 @@ -import { - loadBlocks, -} from '../../scripts/lib-franklin.js'; -import { - decorateMain, -} from '../../scripts/scripts.js'; +import ComponentBase from '../../scripts/component-base.js'; +import { eagerImage } from '../../scripts/libs.js'; -export default async function decorate(block) { - decorateMain(block); - loadBlocks(block); +export default class Header extends ComponentBase { + external = '/header.plain.html'; + + async processExternal(response) { + await super.processExternal(response); + eagerImage(this, 1); + } } diff --git a/blocks/hero/hero.css b/blocks/hero/hero.css index cc5f4fa5..0483f623 100644 --- a/blocks/hero/hero.css +++ b/blocks/hero/hero.css @@ -1,39 +1,23 @@ /* block specific CSS goes here */ -main .hero-container > div { - max-width: unset; -} - -main .hero-container { - padding: 0; -} - -main .hero { - position: relative; - padding: 32px; - min-height: 300px; -} +raqn-hero { + --raqn-background-color: var(--scope-background, transparent); + --raqn-color: var(--scope-color, transparent); + --raqn-grid-template-columns: 0.6fr 0.4fr; -main .hero h1 { - max-width: 1200px; - margin-left: auto; - margin-right: auto; - color: white; -} + background-color: var(--raqn-background-color); + color: var(--raqn-color); + align-items: center; -main .hero picture { - position: absolute; - z-index: -1; - top: 0; - left: 0; - bottom: 0; - right: 0; - object-fit: cover; - box-sizing: border-box; -} + @media screen and (max-width: 768px) { + --raqn-grid-template-columns: 1fr; + } + + grid-template-columns: var(--raqn-grid-template-columns, 1fr); -main .hero img { - object-fit: cover; - width: 100%; - height: 100%; + @media screen and (min-width: 768px) { + & > div:first-child { + max-width: 30vw; + } + } } diff --git a/blocks/hero/hero.js b/blocks/hero/hero.js index e69de29b..67c2f5e6 100644 --- a/blocks/hero/hero.js +++ b/blocks/hero/hero.js @@ -0,0 +1,12 @@ +import ComponentBase from '../../scripts/component-base.js'; + +export default class Hero extends ComponentBase { + connected() { + const child = this.children[0]; + child.replaceWith(...child.children); + this.classList.add('full-width'); + this.setAttribute('role', 'banner'); + this.setAttribute('aria-label', 'hero'); + this.style.setProperty('--hero-columns', this.getAttribute('height')); + } +} diff --git a/blocks/icon/icon.css b/blocks/icon/icon.css new file mode 100644 index 00000000..bd707aa0 --- /dev/null +++ b/blocks/icon/icon.css @@ -0,0 +1,21 @@ +raqn-icon { + display: inline-flex; + font-size: 1em; + line-height: 1em; + min-width: var(--scope-icon-size, 1em); + min-height: var(--scope-icon-size, 1em); + text-align: center; + text-transform: none; + vertical-align: middle; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +raqn-icon svg { + display: inline-block; + max-height: 100%; + max-width: 100%; + fill: currentcolor; + overflow: hidden; + vertical-align: middle; +} diff --git a/blocks/icon/icon.js b/blocks/icon/icon.js new file mode 100644 index 00000000..b1ab2434 --- /dev/null +++ b/blocks/icon/icon.js @@ -0,0 +1,104 @@ +import ComponentBase from '../../scripts/component-base.js'; + +export default class Icon extends ComponentBase { + constructor() { + super(); + this.setupSprite(); + } + + setupSprite() { + this.svgSprite = document.getElementById('franklin-svg-sprite'); + if (!this.svgSprite) { + this.svgSprite = document.createElement('div'); + this.svgSprite.id = 'franklin-svg-sprite'; + document.body.append(this.svgSprite); + } + } + + get iconUrl() { + return `assets/icons/${this.iconName}.svg`; + } + + get cache() { + window.ICONS_CACHE = window.ICONS_CACHE || {}; + return window.ICONS_CACHE; + } + + async connected() { + this.iconName = this.getAttribute('icon'); + if (!this.cache[this.iconName]) { + this.cache[this.iconName] = { + loading: new Promise((resolve) => { + resolve(this.load(this.iconUrl)); + }), + }; + } else { + await this.cache[this.iconName].loading; + this.innerHTML = this.template(); + } + this.classList.add('loaded'); + } + + template() { + const { viewBox } = this.cache[this.iconName]; + const attributes = Object.keys({ viewBox }) + .map((k) => { + if (this.cache[this.iconName][k]) { + return `${k}="${this.cache[this.iconName][k]}"`; + } + return ''; + }) + .join(' '); + return ``; + } + + iconTemplate(iconName, svg, viewBox, width, height) { + return `${svg.innerHTML}`; + } + + async processExternal(response) { + if (response.ok) { + const { iconName } = this; + this.svg = await response.text(); + + if (this.svg.match(/( diff --git a/icons/search.svg b/icons/search.svg deleted file mode 100644 index 637c677b..00000000 --- a/icons/search.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/package-lock.json b/package-lock.json index c23bc9b6..2a33b8af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,9 @@ "chai": "4.3.7", "eslint": "8.35.0", "eslint-config-airbnb-base": "15.0.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "2.27.5", + "prettier-eslint": "^16.2.0", "semantic-release": "21.0.5", "sinon": "15.0.1", "stylelint": "15.2.0", @@ -539,6 +541,18 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -1623,6 +1637,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@sinonjs/commons": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", @@ -1932,6 +1952,160 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/parser": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", + "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.16.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@web/browser-logs": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.2.5.tgz", @@ -3027,6 +3201,15 @@ "node": ">=8" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -3574,6 +3757,12 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4016,6 +4205,18 @@ "eslint-plugin-import": "^2.25.2" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", @@ -5057,6 +5258,27 @@ "node": ">= 0.4.0" } }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -6449,6 +6671,84 @@ "node": ">=8" } }, + "node_modules/loglevel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/loupe": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.3.tgz", @@ -10766,6 +11066,70 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-eslint": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.2.0.tgz", + "integrity": "sha512-GDTSKc62VaLceiaI/qMaKo2oco2CIWtbj4Zr6ckhbTgcBL/uR0d9jkMzh9OtBIT/Z7iBoCB4OHj/aJ5YuNgAuA==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^6.7.5", + "common-tags": "^1.4.0", + "dlv": "^1.1.0", + "eslint": "^8.7.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^3.0.1", + "pretty-format": "^29.7.0", + "require-relative": "^0.8.7", + "typescript": "^5.2.2", + "vue-eslint-parser": "^9.1.0" + }, + "engines": { + "node": ">=16.10.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -10912,6 +11276,12 @@ "node": ">=0.10.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -11166,6 +11536,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -12658,6 +13034,18 @@ "node": ">=8" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -12751,6 +13139,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", @@ -12926,6 +13327,82 @@ "node": ">= 0.8" } }, + "node_modules/vue-eslint-parser": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz", + "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -13587,6 +14064,15 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -14341,6 +14827,12 @@ } } }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "@sinonjs/commons": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", @@ -14650,6 +15142,98 @@ "@types/node": "*" } }, + "@typescript-eslint/parser": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", + "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" + } + }, + "@typescript-eslint/types": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.16.0", + "eslint-visitor-keys": "^3.4.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } + } + }, "@web/browser-logs": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.2.5.tgz", @@ -15441,6 +16025,12 @@ } } }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true + }, "compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -15845,6 +16435,12 @@ "path-type": "^4.0.0" } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -16279,6 +16875,13 @@ "semver": "^6.3.0" } }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "requires": {} + }, "eslint-import-resolver-node": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", @@ -16985,6 +17588,23 @@ "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + } + } + }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -18030,6 +18650,64 @@ } } }, + "loglevel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "dev": true + }, + "loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, "loupe": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.3.tgz", @@ -20970,6 +21648,51 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true + }, + "prettier-eslint": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.2.0.tgz", + "integrity": "sha512-GDTSKc62VaLceiaI/qMaKo2oco2CIWtbj4Zr6ckhbTgcBL/uR0d9jkMzh9OtBIT/Z7iBoCB4OHj/aJ5YuNgAuA==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^6.7.5", + "common-tags": "^1.4.0", + "dlv": "^1.1.0", + "eslint": "^8.7.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^3.0.1", + "pretty-format": "^29.7.0", + "require-relative": "^0.8.7", + "typescript": "^5.2.2", + "vue-eslint-parser": "^9.1.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -21080,6 +21803,12 @@ } } }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -21269,6 +21998,12 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", + "dev": true + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -22370,6 +23105,13 @@ "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, + "ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "requires": {} + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -22441,6 +23183,12 @@ "is-typed-array": "^1.1.9" } }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true + }, "typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", @@ -22567,6 +23315,54 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "vue-eslint-parser": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz", + "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "dependencies": { + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index 4587406e..6d846dd2 100644 --- a/package.json +++ b/package.json @@ -21,19 +21,21 @@ }, "homepage": "https://github.com/adobe/helix-project-boilerplate#readme", "devDependencies": { + "@babel/core": "7.21.0", + "@babel/eslint-parser": "7.19.1", + "@esm-bundle/chai": "4.3.4-fix.0", "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", "@semantic-release/git": "10.0.1", - "semantic-release": "21.0.5", - "@babel/core": "7.21.0", - "@babel/eslint-parser": "7.19.1", + "@web/test-runner": "0.15.1", + "@web/test-runner-commands": "0.6.5", "chai": "4.3.7", "eslint": "8.35.0", "eslint-config-airbnb-base": "15.0.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "2.27.5", - "@esm-bundle/chai": "4.3.4-fix.0", - "@web/test-runner": "0.15.1", - "@web/test-runner-commands": "0.6.5", + "prettier-eslint": "^16.2.0", + "semantic-release": "21.0.5", "sinon": "15.0.1", "stylelint": "15.2.0", "stylelint-config-standard": "30.0.1" diff --git a/scripts/component-base.js b/scripts/component-base.js new file mode 100644 index 00000000..31e73567 --- /dev/null +++ b/scripts/component-base.js @@ -0,0 +1,49 @@ +import { init } from './init.js'; + +export default class ComponentBase extends HTMLElement { + static get breakpoints() { + return { + S: 0, + M: 768, + L: 1024, + XL: 1280, + XXL: 1920, + }; + } + + constructor() { + super(); + this.external = false; + this.uuid = `gen${crypto.randomUUID().split('-')[0]}`; + } + + async connectedCallback() { + this.setAttribute('id', this.uuid); + if (this.external) { + await this.load(this.external); + } + this.connected(); + this.render(); + } + + async load(block) { + const response = await fetch( + `${block}`, + window.location.pathname.endsWith(block) ? { cache: 'reload' } : {}, + ); + return this.processExternal(response); + } + + async processExternal(response) { + if (response.ok) { + const html = await response.text(); + this.innerHTML = html; + return init(this); + } + return response; + } + + connected() {} + + render() {} +} diff --git a/scripts/component-loader.js b/scripts/component-loader.js new file mode 100644 index 00000000..22c40390 --- /dev/null +++ b/scripts/component-loader.js @@ -0,0 +1,143 @@ +import { config, getBreakPoint } from './libs.js'; + +export default class ComponentLoader { + constructor(blockName, element) { + window.raqnComponents = window.raqnComponents || {}; + this.blockName = blockName; + this.setBlockPaths(); + this.block = element; + if (this.block) { + this.setParams(); + this.content = this.block.children; + } + } + + /** + * Loads a CSS file. + * @param {string} href URL to the CSS file + */ + async loadCSS(href) { + return new Promise((resolve, reject) => { + if (!document.querySelector(`head > link[href="${href}"]`)) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = href; + link.onload = resolve; + link.onerror = reject; + document.head.append(link); + } else { + resolve(); + } + }); + } + + /** + * Parse extra params from classList + */ + setParams() { + const mediaParams = {}; + this.params = { + ...Array.from(this.block.classList) + .filter((c) => c !== this.blockName && c !== 'block') + .reduce((acc, c) => { + const values = c.split('-'); + let key = values.shift(); + const breakpoint = getBreakPoint(); + if (breakpoint === key) { + key = values.shift(); + mediaParams[key] = values.join('-'); + return acc; + } + + if (config.breakpoints[key] !== undefined) { + return acc; + } + + if (acc[key] && Array.isArray(acc[key])) { + acc[key].push(values.join('-')); + } else if (acc[key]) { + acc[key] = [acc[key], values.join('-')]; + } else { + acc[key] = values.join('-'); + } + return acc; + }, {}), + ...mediaParams, + }; + } + + /** + * Set the configuration for the given block, and also passes + * the config through all custom patching helpers added to the project. + * + * @param {Element} block The block element + * @returns {Object} The block config (blockName, cssPath and jsPath) + */ + setBlockPaths() { + this.cssPath = `/blocks/${this.blockName}/${this.blockName}.css`; + this.jsPath = `/blocks/${this.blockName}/${this.blockName}.js`; + } + + setupElement() { + const elementName = `raqn-${this.blockName.toLowerCase()}`; + const element = document.createElement(elementName); + element.append(...this.block.children); + Object.keys(this.params).forEach((key) => { + // @TODO sanitize + const value = Array.isArray(this.params[key]) + ? this.params[key].join(' ') + : this.params[key]; + element.setAttribute(key, value); + }); + this.block.replaceWith(element); + } + + async loadWebComponent() { + return new Promise((resolve, reject) => { + (async () => { + try { + const mod = await import(this.jsPath); + if ( + mod.default && + mod.default.name && + mod.default.name !== 'decorate' + ) { + const { name } = mod.default; + const elementName = `raqn-${name.toLowerCase()}`; + // define the custom element if it doesn't exist + if (!window.raqnComponents[name]) { + const Contructor = mod.default; + customElements.define(elementName, Contructor); + window.raqnComponents[name] = Contructor; + } + if (this.block) { + this.setupElement(); + } + } else if (mod.default) { + await mod.default(this.block); + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(`failed to load module for ${this.blockName}`, error); + return reject(error); + } + return resolve(); + })(); + }); + } + + async decorate() { + if (window.raqnComponents[this.blockName]) { + return this.setupElement(); + } + try { + const cssLoaded = this.loadCSS(this.cssPath); + const decorationComplete = this.loadWebComponent(); + return Promise.all([cssLoaded, decorationComplete]); + } catch (error) { + // eslint-disable-next-line no-console + console.log(`failed to load module for ${this.blockName}`, error); + return Promise.resolve(); + } + } +} diff --git a/scripts/delayed.js b/scripts/delayed.js deleted file mode 100644 index 920b4ad8..00000000 --- a/scripts/delayed.js +++ /dev/null @@ -1,7 +0,0 @@ -// eslint-disable-next-line import/no-cycle -import { sampleRUM } from './lib-franklin.js'; - -// Core Web Vitals RUM collection -sampleRUM('cwv'); - -// add more delayed functionality here diff --git a/scripts/init.js b/scripts/init.js new file mode 100644 index 00000000..ebc9b3d9 --- /dev/null +++ b/scripts/init.js @@ -0,0 +1,83 @@ +import ComponentLoader from './component-loader.js'; +import { config, debounce, eagerImage, getBreakPoint } from './libs.js'; + +export function retriveDataFrom(blocks) { + return blocks.map((block) => { + let el = block; + const tagName = el.tagName.toLowerCase(); + let name = tagName; + if (!config.elementBlocks.includes(tagName)) { + [name] = Array.from(el.classList); + } else { + el = document.createElement('div'); + block.append(el); + } + return { + name, + el, + }; + }); +} + +function lcpPriority() { + const eagerImages = document.querySelector('meta[name="lcp"]'); + if (eagerImages) { + const length = parseInt(eagerImages.getAttribute('content'), 10); + eagerImage(document, length); + } + + const lcp = document.querySelector('meta[name="lcp"]'); + if (!lcp) { + return window.raqnLCP || []; + } + window.raqnLCP = + window.raqnLCP || + lcp + .getAttribute('content') + .split(',') + .map((name) => ({ name })); + return window.raqnLCP; +} + +export async function init(node = document) { + let blocks = Array.from(node.querySelectorAll('[class]:not([class^=style]')); + + if (node === document) { + const header = node.querySelector('header'); + const footer = node.querySelector('footer'); + blocks = [header, ...blocks, footer]; + } + + const data = retriveDataFrom(blocks); + const lcp = window.raqnLCP; + const prio = data.slice(0, 2); + const rest = data.slice(2); + const start = ({ name, el }) => { + const loader = new ComponentLoader(name, el); + return loader.decorate(); + }; + Promise.all([ + ...lcp.map(({ name, el }) => start({ name, el })), + ...prio.map(({ name, el }) => start({ name, el })), + ]); + setTimeout(() => { + Promise.all( + rest.map(({ name, el }) => setTimeout(() => start({ name, el }))), + ); + }); + // reload on breakpoint change + window.raqnBreakpoint = getBreakPoint(); + window.addEventListener( + 'resize', + debounce(() => { + // only on width changes + if (window.raqnBreakpoint !== getBreakPoint()) { + window.location.reload(); + } + }, 100), + ); +} +// mechanism of retrieving lang to be used in the app +document.documentElement.lang = document.documentElement.lang || 'en'; +lcpPriority(); +init(); diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 7b6cf0cf..d365f1bd 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -29,33 +29,52 @@ export function sampleRUM(checkpoint, data = {}) { .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; }; + sampleRUM.always.on = (chkpnt, fn) => { + sampleRUM.always[chkpnt] = fn; + }; + sampleRUM.on = (chkpnt, fn) => { + sampleRUM.cases[chkpnt] = fn; + }; defer('observe'); defer('cwv'); try { window.hlx = window.hlx || {}; 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 = usp.get('rum') === 'on' ? 1 : 100; // with parameter, weight is 1. Defaults to 100. // eslint-disable-next-line no-bitwise - const hashCode = (s) => s.split('').reduce((a, b) => (((a << 5) - a) + b.charCodeAt(0)) | 0, 0); - const id = `${hashCode(window.location.href)}-${new Date().getTime()}-${Math.random().toString(16).substr(2, 14)}`; + const hashCode = (s) => s.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0); + const id = `${hashCode( + window.location.href, + )}-${new Date().getTime()}-${Math.random().toString(16).substr(2, 14)}`; const random = Math.random(); - const isSelected = (random * weight < 1); + const isSelected = random * weight < 1; const urlSanitizers = { full: () => window.location.href, origin: () => window.location.origin, path: () => window.location.href.replace(/\?.*$/, ''), }; // eslint-disable-next-line object-curly-newline, max-len - window.hlx.rum = { weight, id, random, isSelected, sampleRUM, sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'] }; + window.hlx.rum = { + weight, + id, + random, + isSelected, + sampleRUM, + sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'], + }; } const { weight, id } = window.hlx.rum; if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) { const sendPing = (pdata = data) => { // 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, ...data }); + const body = JSON.stringify({ + weight, + id, + referer: window.hlx.rum.sanitizeURL(), + checkpoint, + ...data, + }); const url = `https://rum.hlx.page/.rum/${weight}`; // eslint-disable-next-line no-unused-expressions navigator.sendBeacon(url, body); @@ -73,9 +92,13 @@ export function sampleRUM(checkpoint, data = {}) { }, }; sendPing(data); - if (sampleRUM.cases[checkpoint]) { sampleRUM.cases[checkpoint](); } + if (sampleRUM.cases[checkpoint]) { + sampleRUM.cases[checkpoint](); + } + } + if (sampleRUM.always[checkpoint]) { + sampleRUM.always[checkpoint](data); } - if (sampleRUM.always[checkpoint]) { sampleRUM.always[checkpoint](data); } } catch (error) { // something went wrong } @@ -112,7 +135,7 @@ export async function loadScript(src, attrs) { const script = document.createElement('script'); script.src = src; if (attrs) { - // eslint-disable-next-line no-restricted-syntax, guard-for-in + // eslint-disable-next-line no-restricted-syntax, guard-for-in for (const attr in attrs) { script.setAttribute(attr, attrs[attr]); } @@ -133,7 +156,9 @@ export async function loadScript(src, attrs) { */ export function getMetadata(name) { const attr = name && name.includes(':') ? 'property' : 'name'; - const meta = [...document.head.querySelectorAll(`meta[${attr}="${name}"]`)].map((m) => m.content).join(', '); + const meta = [...document.head.querySelectorAll(`meta[${attr}="${name}"]`)] + .map((m) => m.content) + .join(', '); return meta || ''; } @@ -144,7 +169,11 @@ export function getMetadata(name) { */ export function toClassName(name) { return typeof name === 'string' - ? name.toLowerCase().replace(/[^0-9a-z]/gi, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') + ? name + .toLowerCase() + .replace(/[^0-9a-z]/gi, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, '') : ''; } @@ -174,47 +203,62 @@ export async function decorateIcons(element) { // Download all new icons const icons = [...element.querySelectorAll('span.icon')]; - await Promise.all(icons.map(async (span) => { - const iconName = Array.from(span.classList).find((c) => c.startsWith('icon-')).substring(5); - if (!ICONS_CACHE[iconName]) { - ICONS_CACHE[iconName] = true; - try { - const response = await fetch(`${window.hlx.iconsPath}/${iconName}.svg`); - if (!response.ok) { + await Promise.all( + icons.map(async (span) => { + const iconName = Array.from(span.classList) + .find((c) => c.startsWith('icon-')) + .substring(5); + if (!ICONS_CACHE[iconName]) { + ICONS_CACHE[iconName] = true; + try { + const response = await fetch( + `${window.hlx.iconsPath}/${iconName}.svg`, + ); + if (!response.ok) { + ICONS_CACHE[iconName] = false; + return; + } + // Styled icons don't play nice with the sprite approach because of shadow dom isolation + // and same for internal references + const svg = await response.text(); + if (svg.match(/(