From 9663674614fca4e845994d71a8dfa95cceb8bbc4 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 10:10:07 -0700 Subject: [PATCH 01/29] chore: initial commit --- scripts/lib-franklin.js | 21 ++++++++++++++++++++ scripts/scripts.js | 44 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 80b042e064..b35d86b3bc 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -659,6 +659,16 @@ export function loadFooter(footer) { return loadBlock(footerBlock); } +// Define an execution context +const pluginContext = { + getMetadata, + loadCSS, + loadScript, + sampleRUM, + toCamelCase, + toClassName, +}; + /** * Setup block utils. */ @@ -667,6 +677,17 @@ export function setup() { window.hlx.RUM_MASK_URL = 'full'; window.hlx.codeBasePath = ''; window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; + window.hlx.plugins = new Map(); + window.hlx.plugins.run = async (phase) => { + return [...window.hlx.plugins.values()] + .reduce((promise, plugin) => ( + plugin[phase] && (!plugin.condition || plugin.condition()) + ? promise.then(() => function(){ + plugin[phase](); + }.call(pluginContext)) + : promise + ), Promise.resolve()); + } window.hlx.patchBlockConfig = []; const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]'); diff --git a/scripts/scripts.js b/scripts/scripts.js index f6d84985d5..0a102fb014 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -15,6 +15,43 @@ import { const LCP_BLOCKS = []; // add your LCP blocks to the list +window.hlx.plugins.set('template-foo', { + condition: () => true, + loadEager: () => { + console.log('foo: eager'); + }, + loadLazy: () => { + console.log('foo: lazy'); + }, + loadDelayed: () => { + console.log('foo: delayed'); + }, +}); +window.hlx.plugins.set('plugin-bar', { + condition: () => true, + loadEager: () => { + console.log('bar: eager'); + }, + loadLazy: () => { + console.log('bar: lazy'); + }, + loadDelayed: () => { + console.log('bar: delayed'); + }, +}); +window.hlx.plugins.set('plugin-baz', { + condition: () => false, + loadEager: () => { + console.log('baz: eager'); + }, + loadLazy: () => { + console.log('baz: lazy'); + }, + loadDelayed: () => { + console.log('baz: delayed'); + }, +}); + /** * Builds hero block and prepends to main in a new section. * @param {Element} main The container element @@ -77,6 +114,7 @@ async function loadEager(doc) { document.documentElement.lang = 'en'; decorateTemplateAndTheme(); const main = doc.querySelector('main'); + window.hlx.plugins.run('loadEager'); if (main) { decorateMain(main); document.body.classList.add('appear'); @@ -114,6 +152,7 @@ async function loadLazy(doc) { sampleRUM('lazy'); sampleRUM.observe(main.querySelectorAll('div[data-block-name]')); sampleRUM.observe(main.querySelectorAll('picture > img')); + window.hlx.plugins.run('loadLazy'); } /** @@ -122,7 +161,10 @@ async function loadLazy(doc) { */ function loadDelayed() { // eslint-disable-next-line import/no-cycle - window.setTimeout(() => import('./delayed.js'), 3000); + window.setTimeout(() => { + import('./delayed.js'); + window.hlx.plugins.run('loadDelayed'); + }, 3000); // load anything that can be postponed to the latest here } From d75818763990b4fe1b928aee681284e9b33cd593 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:19:54 -0700 Subject: [PATCH 02/29] feat: minimal plugin and template system --- plugins/qux.js | 11 +++++++ scripts/lib-franklin.js | 70 +++++++++++++++++++++++++++++++++-------- scripts/scripts.js | 40 +++++++---------------- templates/bar.js | 11 +++++++ templates/foo.js | 11 +++++++ 5 files changed, 101 insertions(+), 42 deletions(-) create mode 100644 plugins/qux.js create mode 100644 templates/bar.js create mode 100644 templates/foo.js diff --git a/plugins/qux.js b/plugins/qux.js new file mode 100644 index 0000000000..aa0322f3a2 --- /dev/null +++ b/plugins/qux.js @@ -0,0 +1,11 @@ +export const loadEager = (doc, context) => { + console.log('plugin qux: eager', context); +}; + +export const loadLazy = (doc, context) => { + console.log('plugin qux: lazy', context); +}; + +export const loadDelayed = (doc, context) => { + console.log('plugin qux: delayed', context); +}; diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index b35d86b3bc..8745e8c397 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -572,14 +572,25 @@ export function normalizeHeadings(el, allowedHeadings) { /** * Set template (page structure) and theme (page styles). */ -export function decorateTemplateAndTheme() { +export async function decorateTemplateAndTheme() { const addClasses = (element, classes) => { classes.split(',').forEach((c) => { element.classList.add(toClassName(c.trim())); }); }; - const template = getMetadata('template'); - if (template) addClasses(document.body, template); + const template = getMetadata('template') || 'foo'; + if (template) { + addClasses(document.body, template); + // Load template plugin if we have one defined + if (window.hlx.templates.has(template)) { + try { + window.hlx.plugins.set(template, { url: window.hlx.templates.get(template) }); + } catch (err) { + // eslint-disable-next-line no-console + console.error('Could not load specified template', template); + } + } + } const theme = getMetadata('theme'); if (theme) addClasses(document.body, theme); } @@ -678,16 +689,49 @@ export function setup() { window.hlx.codeBasePath = ''; window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; window.hlx.plugins = new Map(); - window.hlx.plugins.run = async (phase) => { - return [...window.hlx.plugins.values()] - .reduce((promise, plugin) => ( - plugin[phase] && (!plugin.condition || plugin.condition()) - ? promise.then(() => function(){ - plugin[phase](); - }.call(pluginContext)) - : promise - ), Promise.resolve()); - } + window.hlx.templates = new Proxy(new Map(), { + get: (target, prop, receiver) => { + const value = Reflect.get(target, prop, receiver); + if (prop === 'set') { + return function() { + const [name, url] = arguments; + window.hlx.plugins.set(name, { condition: () => document.body.classList.contains(name), url }); + target[prop].call(target, name, url); + } + } + if (typeof value == 'function') { + return function() { + return target[prop].apply(target, arguments); + } + } + return value; + }, + set: (target, prop, val, receiver) => { + console.log('set'); + return Reflect.set(target, prop, val, receiver); + } + }); + window.hlx.plugins.load = async () => Promise.all( + [...window.hlx.plugins.entries()] + .filter(([, plugin]) => (!plugin.condition + || (plugin.condition(document, pluginContext) && plugin.url))) + .map(async ([key, plugin]) => { + const pluginApi = await import(plugin.url); + if (plugin.default) { + await plugin.default(); + } + if (plugin.default) { + await plugin.init(); + } + window.hlx.plugins.set(key, pluginApi); + }), + ); + window.hlx.plugins.run = async (phase) => [...window.hlx.plugins.values()] + .reduce((promise, plugin) => ( + plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) + ? promise.then(() => plugin[phase](document, pluginContext)) + : promise + ), Promise.resolve()); window.hlx.patchBlockConfig = []; const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]'); diff --git a/scripts/scripts.js b/scripts/scripts.js index 0a102fb014..925d29ab05 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -15,41 +15,22 @@ import { const LCP_BLOCKS = []; // add your LCP blocks to the list -window.hlx.plugins.set('template-foo', { +window.hlx.templates.set('foo', '/templates/foo.js'); +window.hlx.templates.set('bar', '/templates/bar.js'); +window.hlx.plugins.set('inline-plugin-baz', { condition: () => true, loadEager: () => { - console.log('foo: eager'); + console.log('plugin baz: eager'); }, loadLazy: () => { - console.log('foo: lazy'); + console.log('plugin baz: lazy'); }, loadDelayed: () => { - console.log('foo: delayed'); + console.log('plugin baz: delayed'); }, }); -window.hlx.plugins.set('plugin-bar', { - condition: () => true, - loadEager: () => { - console.log('bar: eager'); - }, - loadLazy: () => { - console.log('bar: lazy'); - }, - loadDelayed: () => { - console.log('bar: delayed'); - }, -}); -window.hlx.plugins.set('plugin-baz', { - condition: () => false, - loadEager: () => { - console.log('baz: eager'); - }, - loadLazy: () => { - console.log('baz: lazy'); - }, - loadDelayed: () => { - console.log('baz: delayed'); - }, +window.hlx.plugins.set('external-plugin-qux', { + url: '/plugins/qux.js', }); /** @@ -112,9 +93,10 @@ export function decorateMain(main) { */ async function loadEager(doc) { document.documentElement.lang = 'en'; - decorateTemplateAndTheme(); + await decorateTemplateAndTheme(); const main = doc.querySelector('main'); - window.hlx.plugins.run('loadEager'); + await window.hlx.plugins.load(); + await window.hlx.plugins.run('loadEager'); if (main) { decorateMain(main); document.body.classList.add('appear'); diff --git a/templates/bar.js b/templates/bar.js new file mode 100644 index 0000000000..7453cd3dbd --- /dev/null +++ b/templates/bar.js @@ -0,0 +1,11 @@ +export const loadEager = (doc, context) => { + console.log('bar: eager', context); +}; + +export const loadLazy = (doc, context) => { + console.log('bar: lazy', context); +}; + +export const loadDelayed = (doc, context) => { + console.log('bar: delayed', context); +}; diff --git a/templates/foo.js b/templates/foo.js new file mode 100644 index 0000000000..c918ee02be --- /dev/null +++ b/templates/foo.js @@ -0,0 +1,11 @@ +export const loadEager = (doc, context) => { + console.log('template foo: eager', context); +}; + +export const loadLazy = (doc, context) => { + console.log('template foo: lazy', context); +}; + +export const loadDelayed = (doc, context) => { + console.log('template foo: delayed', context); +}; From 815fbfb638fd8ae20d4680ee66f129578520b9f0 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:22:39 -0700 Subject: [PATCH 03/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 2 +- scripts/scripts.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 8745e8c397..f900e8892d 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -578,7 +578,7 @@ export async function decorateTemplateAndTheme() { element.classList.add(toClassName(c.trim())); }); }; - const template = getMetadata('template') || 'foo'; + const template = getMetadata('template') || 'foo'; // FIXME: just for the PoC if (template) { addClasses(document.body, template); // Load template plugin if we have one defined diff --git a/scripts/scripts.js b/scripts/scripts.js index 925d29ab05..27c65ae28c 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -15,9 +15,9 @@ import { const LCP_BLOCKS = []; // add your LCP blocks to the list -window.hlx.templates.set('foo', '/templates/foo.js'); -window.hlx.templates.set('bar', '/templates/bar.js'); -window.hlx.plugins.set('inline-plugin-baz', { +window.hlx.templates.set('foo', '/templates/foo.js'); // A template that will resolve by default +window.hlx.templates.set('bar', '/templates/bar.js'); // A template that will not resolve +window.hlx.plugins.set('inline-plugin-baz', { // An inline plugin condition: () => true, loadEager: () => { console.log('plugin baz: eager'); @@ -29,7 +29,7 @@ window.hlx.plugins.set('inline-plugin-baz', { console.log('plugin baz: delayed'); }, }); -window.hlx.plugins.set('external-plugin-qux', { +window.hlx.plugins.set('external-plugin-qux', { // An external plugin url: '/plugins/qux.js', }); From a8464edc6c070e5df4361e49d19d38c0b0c51daa Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:25:46 -0700 Subject: [PATCH 04/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index f900e8892d..06ac96c10d 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -583,12 +583,7 @@ export async function decorateTemplateAndTheme() { addClasses(document.body, template); // Load template plugin if we have one defined if (window.hlx.templates.has(template)) { - try { - window.hlx.plugins.set(template, { url: window.hlx.templates.get(template) }); - } catch (err) { - // eslint-disable-next-line no-console - console.error('Could not load specified template', template); - } + window.hlx.plugins.set(template, { url: window.hlx.templates.get(template) }); } } const theme = getMetadata('theme'); @@ -716,14 +711,19 @@ export function setup() { .filter(([, plugin]) => (!plugin.condition || (plugin.condition(document, pluginContext) && plugin.url))) .map(async ([key, plugin]) => { - const pluginApi = await import(plugin.url); - if (plugin.default) { - await plugin.default(); - } - if (plugin.default) { - await plugin.init(); + try { + const pluginApi = await import(plugin.url); + if (plugin.default) { + await plugin.default(); + } + if (plugin.default) { + await plugin.init(); + } + window.hlx.plugins.set(key, pluginApi); + } catch (err) { + // eslint-disable-next-line no-console + console.error('Could not load specified plugin', key); } - window.hlx.plugins.set(key, pluginApi); }), ); window.hlx.plugins.run = async (phase) => [...window.hlx.plugins.values()] From 657cc47317a798a7ff9e7cbb47fec48ed669c922 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:33:30 -0700 Subject: [PATCH 05/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 06ac96c10d..04a11851ee 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -688,23 +688,25 @@ export function setup() { get: (target, prop, receiver) => { const value = Reflect.get(target, prop, receiver); if (prop === 'set') { - return function() { - const [name, url] = arguments; - window.hlx.plugins.set(name, { condition: () => document.body.classList.contains(name), url }); + return function (name, url) { + window.hlx.plugins.set(name, { + condition: () => document.body.classList.contains(name), + url, + }); target[prop].call(target, name, url); - } + }; } - if (typeof value == 'function') { - return function() { - return target[prop].apply(target, arguments); - } + if (typeof value === 'function') { + return function (...args) { + return target[prop].call(target, ...args); + }; } return value; }, set: (target, prop, val, receiver) => { console.log('set'); return Reflect.set(target, prop, val, receiver); - } + }, }); window.hlx.plugins.load = async () => Promise.all( [...window.hlx.plugins.entries()] From 34a1da9cf08d29c4dd271530e275dca1a0b9cf01 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:40:51 -0700 Subject: [PATCH 06/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 04a11851ee..5c0c18be6b 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -665,7 +665,7 @@ export function loadFooter(footer) { return loadBlock(footerBlock); } -// Define an execution context +// Define an execution context for plugins const pluginContext = { getMetadata, loadCSS, @@ -685,17 +685,20 @@ export function setup() { window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; window.hlx.plugins = new Map(); window.hlx.templates = new Proxy(new Map(), { + // We proxy here to intercept additions to the map and replicate them on the plugins so + // we can guarantee the order of execution matches the order plugins were instrumented get: (target, prop, receiver) => { const value = Reflect.get(target, prop, receiver); if (prop === 'set') { return function (name, url) { + target[prop].call(target, name, url); window.hlx.plugins.set(name, { condition: () => document.body.classList.contains(name), url, }); - target[prop].call(target, name, url); }; } + // Functions need to be properly rescoped if (typeof value === 'function') { return function (...args) { return target[prop].call(target, ...args); @@ -703,18 +706,16 @@ export function setup() { } return value; }, - set: (target, prop, val, receiver) => { - console.log('set'); - return Reflect.set(target, prop, val, receiver); - }, }); window.hlx.plugins.load = async () => Promise.all( [...window.hlx.plugins.entries()] + // Filter plugins that don't match the execution conditions .filter(([, plugin]) => (!plugin.condition || (plugin.condition(document, pluginContext) && plugin.url))) .map(async ([key, plugin]) => { try { const pluginApi = await import(plugin.url); + // If the plugin has a default export or init function we executed it immediately if (plugin.default) { await plugin.default(); } @@ -729,7 +730,7 @@ export function setup() { }), ); window.hlx.plugins.run = async (phase) => [...window.hlx.plugins.values()] - .reduce((promise, plugin) => ( + .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) ? promise.then(() => plugin[phase](document, pluginContext)) : promise From ef9f1b8eaf3744437e43a28c367c75d3cd03cfcd Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:51:56 -0700 Subject: [PATCH 07/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 6 ++++-- scripts/scripts.js | 25 +++++++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 5c0c18be6b..88ac2b6184 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -710,8 +710,10 @@ export function setup() { window.hlx.plugins.load = async () => Promise.all( [...window.hlx.plugins.entries()] // Filter plugins that don't match the execution conditions - .filter(([, plugin]) => (!plugin.condition - || (plugin.condition(document, pluginContext) && plugin.url))) + .filter(([, plugin]) => ( + (!plugin.condition || plugin.condition(document, pluginContext)) + && plugin.url + )) .map(async ([key, plugin]) => { try { const pluginApi = await import(plugin.url); diff --git a/scripts/scripts.js b/scripts/scripts.js index 27c65ae28c..4203c23c36 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -15,9 +15,18 @@ import { const LCP_BLOCKS = []; // add your LCP blocks to the list -window.hlx.templates.set('foo', '/templates/foo.js'); // A template that will resolve by default -window.hlx.templates.set('bar', '/templates/bar.js'); // A template that will not resolve -window.hlx.plugins.set('inline-plugin-baz', { // An inline plugin +/* Templates */ + +// A template that will resolve by default +window.hlx.templates.set('foo', '/templates/foo.js'); + +// A template that will not resolve +window.hlx.templates.set('bar', '/templates/bar.js'); + +/* Plugins */ + +// An inline plugin +window.hlx.plugins.set('inline-plugin-baz', { condition: () => true, loadEager: () => { console.log('plugin baz: eager'); @@ -29,10 +38,18 @@ window.hlx.plugins.set('inline-plugin-baz', { // An inline plugin console.log('plugin baz: delayed'); }, }); -window.hlx.plugins.set('external-plugin-qux', { // An external plugin + +// An external plugin +window.hlx.plugins.set('external-plugin-qux', { url: '/plugins/qux.js', }); +// An external plugin that will never load +window.hlx.plugins.set('external-plugin-corge', { + condition: () => false, + url: '/plugins/corge.js', +}); + /** * Builds hero block and prepends to main in a new section. * @param {Element} main The container element From 8fed9e38064214970ef7ca0459bbcbbd9d2a309a Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 5 Sep 2023 17:53:45 -0700 Subject: [PATCH 08/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 88ac2b6184..e2a817ce03 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -707,6 +707,8 @@ export function setup() { return value; }, }); + // Load all plugins that are referenced by URL, and updated their configuration with the + // actual API they expose window.hlx.plugins.load = async () => Promise.all( [...window.hlx.plugins.entries()] // Filter plugins that don't match the execution conditions @@ -731,6 +733,7 @@ export function setup() { } }), ); + // Run a specific phase in the plugin window.hlx.plugins.run = async (phase) => [...window.hlx.plugins.values()] .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) From 1762e1130b0d47fdfd1130892488f04d3668b908 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Wed, 6 Sep 2023 08:03:44 -0700 Subject: [PATCH 09/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index e2a817ce03..c52b64ef25 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -667,7 +667,12 @@ export function loadFooter(footer) { // Define an execution context for plugins const pluginContext = { + createOptimizedPicture, getMetadata, + decorateBlock, + decorateButtons, + decorateIcons, + loadBlock, loadCSS, loadScript, sampleRUM, From 7a077126c8b0d58a28d384484b0cdf6c46955514 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Thu, 14 Sep 2023 09:09:03 -0700 Subject: [PATCH 10/29] chore: extract plugins and templates as individual classes --- plugins/grault.js | 0 scripts/lib-franklin.js | 104 ++++++++++++++++++++++------------------ scripts/scripts.js | 16 +++++-- 3 files changed, 68 insertions(+), 52 deletions(-) create mode 100644 plugins/grault.js diff --git a/plugins/grault.js b/plugins/grault.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index c52b64ef25..4b1b2a73dc 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ /* * Copyright 2022 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); @@ -581,10 +582,6 @@ export async function decorateTemplateAndTheme() { const template = getMetadata('template') || 'foo'; // FIXME: just for the PoC if (template) { addClasses(document.body, template); - // Load template plugin if we have one defined - if (window.hlx.templates.has(template)) { - window.hlx.plugins.set(template, { url: window.hlx.templates.get(template) }); - } } const theme = getMetadata('theme'); if (theme) addClasses(document.body, theme); @@ -680,42 +677,28 @@ const pluginContext = { toClassName, }; -/** - * Setup block utils. - */ -export function setup() { - window.hlx = window.hlx || {}; - window.hlx.RUM_MASK_URL = 'full'; - window.hlx.codeBasePath = ''; - window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; - window.hlx.plugins = new Map(); - window.hlx.templates = new Proxy(new Map(), { - // We proxy here to intercept additions to the map and replicate them on the plugins so - // we can guarantee the order of execution matches the order plugins were instrumented - get: (target, prop, receiver) => { - const value = Reflect.get(target, prop, receiver); - if (prop === 'set') { - return function (name, url) { - target[prop].call(target, name, url); - window.hlx.plugins.set(name, { - condition: () => document.body.classList.contains(name), - url, - }); - }; - } - // Functions need to be properly rescoped - if (typeof value === 'function') { - return function (...args) { - return target[prop].call(target, ...args); - }; - } - return value; - }, - }); +class Plugins { + constructor() { + this.plugins = new Map(); + } + + // Register a new plugin + add(id, config) { + const pluginId = !config + ? id.split('/').pop().replace(/\.js/, '') + : id; + const pluginConfig = typeof config === 'string' || !config + ? { url: config || id } + : config; + this.plugins.set(pluginId, pluginConfig); + } + + includes(id) { return !!this.plugins.has(id); } + // Load all plugins that are referenced by URL, and updated their configuration with the // actual API they expose - window.hlx.plugins.load = async () => Promise.all( - [...window.hlx.plugins.entries()] + async load() { + return [...this.plugins.entries()] // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( (!plugin.condition || plugin.condition(document, pluginContext)) @@ -731,21 +714,48 @@ export function setup() { if (plugin.default) { await plugin.init(); } - window.hlx.plugins.set(key, pluginApi); + this.plugins.set(key, pluginApi); } catch (err) { // eslint-disable-next-line no-console console.error('Could not load specified plugin', key); } - }), - ); + }); + } + // Run a specific phase in the plugin - window.hlx.plugins.run = async (phase) => [...window.hlx.plugins.values()] - .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially - plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) - ? promise.then(() => plugin[phase](document, pluginContext)) - : promise - ), Promise.resolve()); + async run(phase) { + return [...this.plugins.values()] + .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially + plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) + ? promise.then(() => plugin[phase](document, pluginContext)) + : promise + ), Promise.resolve()); + } +} + +class Templates { + // eslint-disable-next-line class-methods-use-this + add(id, url) { + window.hlx.plugins.add(id, url + ? { condition: () => document.body.classList.contains(id), url } + : null); + } + + // eslint-disable-next-line class-methods-use-this + includes(id) { return window.hlx.plugins.includes(id); } +} + +/** + * Setup block utils. + */ +export function setup() { + window.hlx = window.hlx || {}; + window.hlx.RUM_MASK_URL = 'full'; + window.hlx.codeBasePath = ''; + window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; window.hlx.patchBlockConfig = []; + window.hlx.plugins = new Plugins(); + window.hlx.templates = new Templates(); const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]'); if (scriptEl) { diff --git a/scripts/scripts.js b/scripts/scripts.js index 4203c23c36..2b68882556 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -18,15 +18,18 @@ const LCP_BLOCKS = []; // add your LCP blocks to the list /* Templates */ // A template that will resolve by default -window.hlx.templates.set('foo', '/templates/foo.js'); +window.hlx.templates.add('foo', '/templates/foo.js'); // A template that will not resolve -window.hlx.templates.set('bar', '/templates/bar.js'); +window.hlx.templates.add('bar', '/templates/bar.js'); + +// Shorthand +window.hlx.templates.add('/templates/garply.js'); /* Plugins */ // An inline plugin -window.hlx.plugins.set('inline-plugin-baz', { +window.hlx.plugins.add('inline-plugin-baz', { condition: () => true, loadEager: () => { console.log('plugin baz: eager'); @@ -40,16 +43,19 @@ window.hlx.plugins.set('inline-plugin-baz', { }); // An external plugin -window.hlx.plugins.set('external-plugin-qux', { +window.hlx.plugins.add('external-plugin-qux', { url: '/plugins/qux.js', }); // An external plugin that will never load -window.hlx.plugins.set('external-plugin-corge', { +window.hlx.plugins.add('external-plugin-corge', { condition: () => false, url: '/plugins/corge.js', }); +// Shorthand +window.hlx.plugins.add('/plugins/grault.js'); + /** * Builds hero block and prepends to main in a new section. * @param {Element} main The container element From 285655de60bbc7ad453b53f8be6411e8b1c65f8f Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Thu, 14 Sep 2023 10:18:16 -0700 Subject: [PATCH 11/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 15 ++++++++++----- scripts/scripts.js | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 4b1b2a73dc..e4390656ab 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -698,7 +698,7 @@ class Plugins { // Load all plugins that are referenced by URL, and updated their configuration with the // actual API they expose async load() { - return [...this.plugins.entries()] + return Promise.all([...this.plugins.entries()] // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( (!plugin.condition || plugin.condition(document, pluginContext)) @@ -719,7 +719,7 @@ class Plugins { // eslint-disable-next-line no-console console.error('Could not load specified plugin', key); } - }); + })); } // Run a specific phase in the plugin @@ -736,9 +736,14 @@ class Plugins { class Templates { // eslint-disable-next-line class-methods-use-this add(id, url) { - window.hlx.plugins.add(id, url - ? { condition: () => document.body.classList.contains(id), url } - : null); + const templateId = !url + ? id.split('/').pop().replace(/\.js/, '') + : id; + const templateConfig = { + condition: () => document.body.classList.contains(id), + url: url || id, + }; + window.hlx.plugins.add(templateId, templateConfig); } // eslint-disable-next-line class-methods-use-this diff --git a/scripts/scripts.js b/scripts/scripts.js index 2b68882556..52f5c870a6 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -23,7 +23,7 @@ window.hlx.templates.add('foo', '/templates/foo.js'); // A template that will not resolve window.hlx.templates.add('bar', '/templates/bar.js'); -// Shorthand +// Shorthand, also won't resolve window.hlx.templates.add('/templates/garply.js'); /* Plugins */ From 7c11fa197b1cac618220d9188ff9e378c3aa7c33 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Thu, 14 Sep 2023 10:24:44 -0700 Subject: [PATCH 12/29] feat: minimal plugin and template system --- scripts/lib-franklin.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index e4390656ab..6d6c9f0aa2 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -677,7 +677,7 @@ const pluginContext = { toClassName, }; -class Plugins { +class PluginsRegistry { constructor() { this.plugins = new Map(); } @@ -693,6 +693,10 @@ class Plugins { this.plugins.set(pluginId, pluginConfig); } + // Get the plugin + get(id) { return this.plugins.get(id); } + + // Check if the plugin exists includes(id) { return !!this.plugins.has(id); } // Load all plugins that are referenced by URL, and updated their configuration with the @@ -710,8 +714,7 @@ class Plugins { // If the plugin has a default export or init function we executed it immediately if (plugin.default) { await plugin.default(); - } - if (plugin.default) { + } else if (plugin.init) { await plugin.init(); } this.plugins.set(key, pluginApi); @@ -733,7 +736,8 @@ class Plugins { } } -class Templates { +class TemplatesRegistry { + // Register a new template // eslint-disable-next-line class-methods-use-this add(id, url) { const templateId = !url @@ -746,6 +750,11 @@ class Templates { window.hlx.plugins.add(templateId, templateConfig); } + // Get the template + // eslint-disable-next-line class-methods-use-this + get(id) { return window.hlx.plugins.get(id); } + + // Check if the template exists // eslint-disable-next-line class-methods-use-this includes(id) { return window.hlx.plugins.includes(id); } } @@ -759,8 +768,8 @@ export function setup() { window.hlx.codeBasePath = ''; window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; window.hlx.patchBlockConfig = []; - window.hlx.plugins = new Plugins(); - window.hlx.templates = new Templates(); + window.hlx.plugins = new PluginsRegistry(); + window.hlx.templates = new TemplatesRegistry(); const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]'); if (scriptEl) { From a56292129dfc24855eeecd15ad01e4e7215ad5d3 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Thu, 14 Sep 2023 10:37:06 -0700 Subject: [PATCH 13/29] fix: remove plugins that don't have a truthy condition --- plugins/grault.js | 11 +++++++++++ scripts/lib-franklin.js | 19 ++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/plugins/grault.js b/plugins/grault.js index e69de29bb2..cddfd32efd 100644 --- a/plugins/grault.js +++ b/plugins/grault.js @@ -0,0 +1,11 @@ +export const loadEager = (doc, context) => { + console.log('plugin grault: eager', context); +}; + +export const loadLazy = (doc, context) => { + console.log('plugin grault: lazy', context); +}; + +export const loadDelayed = (doc, context) => { + console.log('plugin grault: delayed', context); +}; diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 6d6c9f0aa2..bd03595f29 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -678,8 +678,10 @@ const pluginContext = { }; class PluginsRegistry { + #plugins; + constructor() { - this.plugins = new Map(); + this.#plugins = new Map(); } // Register a new plugin @@ -690,19 +692,22 @@ class PluginsRegistry { const pluginConfig = typeof config === 'string' || !config ? { url: config || id } : config; - this.plugins.set(pluginId, pluginConfig); + this.#plugins.set(pluginId, pluginConfig); } // Get the plugin - get(id) { return this.plugins.get(id); } + get(id) { return this.#plugins.get(id); } // Check if the plugin exists - includes(id) { return !!this.plugins.has(id); } + includes(id) { return !!this.#plugins.has(id); } // Load all plugins that are referenced by URL, and updated their configuration with the // actual API they expose async load() { - return Promise.all([...this.plugins.entries()] + [...this.#plugins.entries()] + .filter(([, plugin]) => plugin.condition && !plugin.condition(document, pluginContext)) + .map(([id]) => this.#plugins.delete(id)); + return Promise.all([...this.#plugins.entries()] // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( (!plugin.condition || plugin.condition(document, pluginContext)) @@ -717,7 +722,7 @@ class PluginsRegistry { } else if (plugin.init) { await plugin.init(); } - this.plugins.set(key, pluginApi); + this.#plugins.set(key, pluginApi); } catch (err) { // eslint-disable-next-line no-console console.error('Could not load specified plugin', key); @@ -727,7 +732,7 @@ class PluginsRegistry { // Run a specific phase in the plugin async run(phase) { - return [...this.plugins.values()] + return [...this.#plugins.values()] .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) ? promise.then(() => plugin[phase](document, pluginContext)) From e1e0c54589d469536ddbef5d69629bb64a7e8120 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Thu, 14 Sep 2023 10:38:22 -0700 Subject: [PATCH 14/29] feat: export the execution context --- scripts/lib-franklin.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index bd03595f29..9d6526463f 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -663,7 +663,7 @@ export function loadFooter(footer) { } // Define an execution context for plugins -const pluginContext = { +export const executionContext = { createOptimizedPicture, getMetadata, decorateBlock, @@ -705,12 +705,12 @@ class PluginsRegistry { // actual API they expose async load() { [...this.#plugins.entries()] - .filter(([, plugin]) => plugin.condition && !plugin.condition(document, pluginContext)) + .filter(([, plugin]) => plugin.condition && !plugin.condition(document, executionContext)) .map(([id]) => this.#plugins.delete(id)); return Promise.all([...this.#plugins.entries()] // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( - (!plugin.condition || plugin.condition(document, pluginContext)) + (!plugin.condition || plugin.condition(document, executionContext)) && plugin.url )) .map(async ([key, plugin]) => { @@ -734,8 +734,8 @@ class PluginsRegistry { async run(phase) { return [...this.#plugins.values()] .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially - plugin[phase] && (!plugin.condition || plugin.condition(document, pluginContext)) - ? promise.then(() => plugin[phase](document, pluginContext)) + plugin[phase] && (!plugin.condition || plugin.condition(document, executionContext)) + ? promise.then(() => plugin[phase](document, executionContext)) : promise ), Promise.resolve()); } From fa05c0f45dbf7959b2d71b9fac14aee306ac91af Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Fri, 15 Sep 2023 08:52:26 -0700 Subject: [PATCH 15/29] feat: add phased plugin loading --- plugins/qux.js | 2 +- scripts/lib-franklin.js | 12 ++++++------ scripts/scripts.js | 7 +++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/qux.js b/plugins/qux.js index aa0322f3a2..7c7d1347ab 100644 --- a/plugins/qux.js +++ b/plugins/qux.js @@ -1,5 +1,5 @@ export const loadEager = (doc, context) => { - console.log('plugin qux: eager', context); + console.error('plugin qux: eager', context); // should not run as plugin is loaded in lazy phase }; export const loadLazy = (doc, context) => { diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 9d6526463f..ab72665f20 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -579,7 +579,7 @@ export async function decorateTemplateAndTheme() { element.classList.add(toClassName(c.trim())); }); }; - const template = getMetadata('template') || 'foo'; // FIXME: just for the PoC + const template = getMetadata('template'); if (template) { addClasses(document.body, template); } @@ -691,7 +691,7 @@ class PluginsRegistry { : id; const pluginConfig = typeof config === 'string' || !config ? { url: config || id } - : config; + : { load: 'eager', ...config }; this.#plugins.set(pluginId, pluginConfig); } @@ -703,7 +703,7 @@ class PluginsRegistry { // Load all plugins that are referenced by URL, and updated their configuration with the // actual API they expose - async load() { + async load(phase) { [...this.#plugins.entries()] .filter(([, plugin]) => plugin.condition && !plugin.condition(document, executionContext)) .map(([id]) => this.#plugins.delete(id)); @@ -711,7 +711,7 @@ class PluginsRegistry { // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( (!plugin.condition || plugin.condition(document, executionContext)) - && plugin.url + && phase === plugin.load && plugin.url )) .map(async ([key, plugin]) => { try { @@ -722,7 +722,7 @@ class PluginsRegistry { } else if (plugin.init) { await plugin.init(); } - this.#plugins.set(key, pluginApi); + this.#plugins.set(key, { ...plugin, ...pluginApi }); } catch (err) { // eslint-disable-next-line no-console console.error('Could not load specified plugin', key); @@ -749,7 +749,7 @@ class TemplatesRegistry { ? id.split('/').pop().replace(/\.js/, '') : id; const templateConfig = { - condition: () => document.body.classList.contains(id), + condition: () => toClassName(getMetadata('template')) === id || id === 'foo', // FIXME: just for the PoC, url: url || id, }; window.hlx.plugins.add(templateId, templateConfig); diff --git a/scripts/scripts.js b/scripts/scripts.js index 52f5c870a6..fc600b7265 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -42,9 +42,10 @@ window.hlx.plugins.add('inline-plugin-baz', { }, }); -// An external plugin +// An external plugin that loads in the lazy phase window.hlx.plugins.add('external-plugin-qux', { url: '/plugins/qux.js', + load: 'lazy', }); // An external plugin that will never load @@ -118,7 +119,6 @@ async function loadEager(doc) { document.documentElement.lang = 'en'; await decorateTemplateAndTheme(); const main = doc.querySelector('main'); - await window.hlx.plugins.load(); await window.hlx.plugins.run('loadEager'); if (main) { decorateMain(main); @@ -167,6 +167,7 @@ async function loadLazy(doc) { function loadDelayed() { // eslint-disable-next-line import/no-cycle window.setTimeout(() => { + window.hlx.plugins.load('delayed'); import('./delayed.js'); window.hlx.plugins.run('loadDelayed'); }, 3000); @@ -174,7 +175,9 @@ function loadDelayed() { } async function loadPage() { + await window.hlx.plugins.load('eager'); await loadEager(document); + await window.hlx.plugins.load('lazy'); await loadLazy(document); loadDelayed(); } From 64babaab716162fd8ea29ed37b5208ec0eba38a3 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Fri, 15 Sep 2023 09:42:24 -0700 Subject: [PATCH 16/29] feat: pass proper context to init and default functions --- scripts/lib-franklin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index ab72665f20..2f57815bf3 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -718,9 +718,9 @@ class PluginsRegistry { const pluginApi = await import(plugin.url); // If the plugin has a default export or init function we executed it immediately if (plugin.default) { - await plugin.default(); + await plugin.default(document, executionContext); } else if (plugin.init) { - await plugin.init(); + await plugin.init(document, executionContext); } this.#plugins.set(key, { ...plugin, ...pluginApi }); } catch (err) { From 82466e6f8e9ac97ee6bddd0142e83141b98958a3 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Fri, 15 Sep 2023 18:05:59 -0700 Subject: [PATCH 17/29] feat: allow loading whole modules with both js and css files --- scripts/lib-franklin.js | 95 +++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 2f57815bf3..0744c51688 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -1,4 +1,3 @@ -/* eslint-disable max-classes-per-file */ /* * Copyright 2022 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); @@ -10,6 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ +/* eslint-disable max-classes-per-file */ /** * log RUM if part of the sample. @@ -432,6 +432,37 @@ export function buildBlock(blockName, content) { return (blockEl); } +/** + * Loads the specified module with its JS and CSS files and returns the JS API if applicable. + * @param {String} name The module name + * @param {String} cssPath A path to the CSS file to load, or null + * @param {String} jsPath A path to the JS file to load, or null + * @param {...any} args Arguments to use to call the default export on the JS file + * @returns a promsie that the module was loaded, and that returns the JS API is any + */ +async function loadModule(name, cssPath, jsPath, ...args) { + const cssLoaded = cssPath ? loadCSS(cssPath) : Promise.resolve(); + const decorationComplete = jsPath + ? new Promise((resolve) => { + (async () => { + let mod; + try { + mod = await import(jsPath); + if (mod.default) { + await mod.default.apply(null, args); + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(`failed to load module for ${name}`, error); + } + resolve(mod); + })(); + }) + : Promise.resolve(); + return Promise.all([cssLoaded, decorationComplete]) + .then(([, api]) => api); +} + /** * Gets the configuration for the given block, and also passes * the config through all custom patching helpers added to the project. @@ -462,22 +493,7 @@ export async function loadBlock(block) { block.dataset.blockStatus = 'loading'; const { blockName, cssPath, jsPath } = getBlockConfig(block); try { - const cssLoaded = loadCSS(cssPath); - const decorationComplete = new Promise((resolve) => { - (async () => { - try { - const mod = await import(jsPath); - if (mod.default) { - await mod.default(block); - } - } catch (error) { - // eslint-disable-next-line no-console - console.log(`failed to load module for ${blockName}`, error); - } - resolve(); - })(); - }); - await Promise.all([cssLoaded, decorationComplete]); + await loadModule(blockName, cssPath, jsPath, block); } catch (error) { // eslint-disable-next-line no-console console.log(`failed to load block ${blockName}`, error); @@ -677,6 +693,17 @@ export const executionContext = { toClassName, }; +function parsePluginParams(id, config) { + const pluginId = !config + ? id.split('/').splice(id.endsWith('/') ? -2 : -1, 1)[0].replace(/\.js/, '') + : id; + const pluginConfig = typeof config === 'string' || !config + ? { url: config || id } + : { load: 'eager', ...config }; + pluginConfig.url = pluginConfig.url.replace(/\/$/, ''); + return { id: pluginId, config: pluginConfig }; +} + class PluginsRegistry { #plugins; @@ -686,12 +713,7 @@ class PluginsRegistry { // Register a new plugin add(id, config) { - const pluginId = !config - ? id.split('/').pop().replace(/\.js/, '') - : id; - const pluginConfig = typeof config === 'string' || !config - ? { url: config || id } - : { load: 'eager', ...config }; + const { id: pluginId, config: pluginConfig } = parsePluginParams(id, config); this.#plugins.set(pluginId, pluginConfig); } @@ -715,12 +737,16 @@ class PluginsRegistry { )) .map(async ([key, plugin]) => { try { - const pluginApi = await import(plugin.url); - // If the plugin has a default export or init function we executed it immediately - if (plugin.default) { - await plugin.default(document, executionContext); - } else if (plugin.init) { - await plugin.init(document, executionContext); + const pluginApi = (await loadModule( + key, + !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.css` : null, + !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.js` : plugin.url, + document, + executionContext, + )) || {}; + // If the plugin has an init function we executed it immediately + if (pluginApi.init) { + await pluginApi.init(document, executionContext); } this.#plugins.set(key, { ...plugin, ...pluginApi }); } catch (err) { @@ -745,14 +771,9 @@ class TemplatesRegistry { // Register a new template // eslint-disable-next-line class-methods-use-this add(id, url) { - const templateId = !url - ? id.split('/').pop().replace(/\.js/, '') - : id; - const templateConfig = { - condition: () => toClassName(getMetadata('template')) === id || id === 'foo', // FIXME: just for the PoC, - url: url || id, - }; - window.hlx.plugins.add(templateId, templateConfig); + const { id: templatId, config: templateConfig } = parsePluginParams(id, url); + templateConfig.condition = () => toClassName(getMetadata('template')) === templatId; + window.hlx.plugins.add(templatId, templateConfig); } // Get the template From 2e82dfec51bb05475ab5774329a53b8abd0e4175 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Fri, 15 Sep 2023 18:14:32 -0700 Subject: [PATCH 18/29] feat: allow loading whole modules with both js and css files --- scripts/lib-franklin.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 0744c51688..4f1daca075 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -698,9 +698,8 @@ function parsePluginParams(id, config) { ? id.split('/').splice(id.endsWith('/') ? -2 : -1, 1)[0].replace(/\.js/, '') : id; const pluginConfig = typeof config === 'string' || !config - ? { url: config || id } + ? { url: (config || id).replace(/\/$/, '') } : { load: 'eager', ...config }; - pluginConfig.url = pluginConfig.url.replace(/\/$/, ''); return { id: pluginId, config: pluginConfig }; } From 5bd292a882c69711d371e145fd0a9832dd8b1314 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Fri, 15 Sep 2023 18:15:39 -0700 Subject: [PATCH 19/29] feat: allow loading whole modules with both js and css files --- scripts/lib-franklin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 4f1daca075..730835b4bc 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -770,9 +770,9 @@ class TemplatesRegistry { // Register a new template // eslint-disable-next-line class-methods-use-this add(id, url) { - const { id: templatId, config: templateConfig } = parsePluginParams(id, url); - templateConfig.condition = () => toClassName(getMetadata('template')) === templatId; - window.hlx.plugins.add(templatId, templateConfig); + const { id: templateId, config: templateConfig } = parsePluginParams(id, url); + templateConfig.condition = () => toClassName(getMetadata('template')) === templateId || templateId === 'foo'; // FIXME: just for the PoC + window.hlx.plugins.add(templateId, templateConfig); } // Get the template From 78a54756a3ecfabbf8e57eb04b0d5cac3133058b Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Fri, 15 Sep 2023 18:23:31 -0700 Subject: [PATCH 20/29] fix: remove redundant init function support --- scripts/lib-franklin.js | 5 +---- templates/bar.js | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 730835b4bc..49d6bbf257 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -736,6 +736,7 @@ class PluginsRegistry { )) .map(async ([key, plugin]) => { try { + // If the plugin has a default export, it will be executed immediately const pluginApi = (await loadModule( key, !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.css` : null, @@ -743,10 +744,6 @@ class PluginsRegistry { document, executionContext, )) || {}; - // If the plugin has an init function we executed it immediately - if (pluginApi.init) { - await pluginApi.init(document, executionContext); - } this.#plugins.set(key, { ...plugin, ...pluginApi }); } catch (err) { // eslint-disable-next-line no-console diff --git a/templates/bar.js b/templates/bar.js index 7453cd3dbd..9b5a0bc144 100644 --- a/templates/bar.js +++ b/templates/bar.js @@ -9,3 +9,5 @@ export const loadLazy = (doc, context) => { export const loadDelayed = (doc, context) => { console.log('bar: delayed', context); }; + +export default function () {} From 2d9311ed74055def6a22c7ee321b2317f16c3472 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Mon, 18 Sep 2023 11:28:09 -0700 Subject: [PATCH 21/29] feat: add capability to pass down options to plugins to configure them --- plugins/grault.js | 12 ++++++------ plugins/qux.js | 12 ++++++------ scripts/lib-franklin.js | 12 ++++++++---- templates/bar.js | 12 ++++++------ templates/foo.js | 12 ++++++------ 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/plugins/grault.js b/plugins/grault.js index cddfd32efd..6225c2e852 100644 --- a/plugins/grault.js +++ b/plugins/grault.js @@ -1,11 +1,11 @@ -export const loadEager = (doc, context) => { - console.log('plugin grault: eager', context); +export const loadEager = (doc, options, context) => { + console.log('plugin grault: eager', options, context); }; -export const loadLazy = (doc, context) => { - console.log('plugin grault: lazy', context); +export const loadLazy = (doc, options, context) => { + console.log('plugin grault: lazy', options, context); }; -export const loadDelayed = (doc, context) => { - console.log('plugin grault: delayed', context); +export const loadDelayed = (doc, options, context) => { + console.log('plugin grault: delayed', options, context); }; diff --git a/plugins/qux.js b/plugins/qux.js index 7c7d1347ab..dfcad6c668 100644 --- a/plugins/qux.js +++ b/plugins/qux.js @@ -1,11 +1,11 @@ -export const loadEager = (doc, context) => { - console.error('plugin qux: eager', context); // should not run as plugin is loaded in lazy phase +export const loadEager = (doc, options, context) => { + console.error('plugin qux: eager', options, context); // should not run as plugin is loaded in lazy phase }; -export const loadLazy = (doc, context) => { - console.log('plugin qux: lazy', context); +export const loadLazy = (doc, options, context) => { + console.log('plugin qux: lazy', options, context); }; -export const loadDelayed = (doc, context) => { - console.log('plugin qux: delayed', context); +export const loadDelayed = (doc, options, context) => { + console.log('plugin qux: delayed', options, context); }; diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 49d6bbf257..81cf54c165 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -700,6 +700,7 @@ function parsePluginParams(id, config) { const pluginConfig = typeof config === 'string' || !config ? { url: (config || id).replace(/\/$/, '') } : { load: 'eager', ...config }; + pluginConfig.options ||= {}; return { id: pluginId, config: pluginConfig }; } @@ -726,12 +727,13 @@ class PluginsRegistry { // actual API they expose async load(phase) { [...this.#plugins.entries()] - .filter(([, plugin]) => plugin.condition && !plugin.condition(document, executionContext)) + .filter(([, plugin]) => plugin.condition + && !plugin.condition(document, plugin.options, executionContext)) .map(([id]) => this.#plugins.delete(id)); return Promise.all([...this.#plugins.entries()] // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( - (!plugin.condition || plugin.condition(document, executionContext)) + (!plugin.condition || plugin.condition(document, plugin.options, executionContext)) && phase === plugin.load && plugin.url )) .map(async ([key, plugin]) => { @@ -742,6 +744,7 @@ class PluginsRegistry { !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.css` : null, !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.js` : plugin.url, document, + plugin.options, executionContext, )) || {}; this.#plugins.set(key, { ...plugin, ...pluginApi }); @@ -756,8 +759,9 @@ class PluginsRegistry { async run(phase) { return [...this.#plugins.values()] .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially - plugin[phase] && (!plugin.condition || plugin.condition(document, executionContext)) - ? promise.then(() => plugin[phase](document, executionContext)) + plugin[phase] && (!plugin.condition + || plugin.condition(document, plugin.options, executionContext)) + ? promise.then(() => plugin[phase](document, plugin.options, executionContext)) : promise ), Promise.resolve()); } diff --git a/templates/bar.js b/templates/bar.js index 9b5a0bc144..35264e8599 100644 --- a/templates/bar.js +++ b/templates/bar.js @@ -1,13 +1,13 @@ -export const loadEager = (doc, context) => { - console.log('bar: eager', context); +export const loadEager = (doc, options, context) => { + console.log('bar: eager', options, context); }; -export const loadLazy = (doc, context) => { - console.log('bar: lazy', context); +export const loadLazy = (doc, options, context) => { + console.log('bar: lazy', options, context); }; -export const loadDelayed = (doc, context) => { - console.log('bar: delayed', context); +export const loadDelayed = (doc, options, context) => { + console.log('bar: delayed', options, context); }; export default function () {} diff --git a/templates/foo.js b/templates/foo.js index c918ee02be..9f8c31d204 100644 --- a/templates/foo.js +++ b/templates/foo.js @@ -1,11 +1,11 @@ -export const loadEager = (doc, context) => { - console.log('template foo: eager', context); +export const loadEager = (doc, options, context) => { + console.log('template foo: eager', options, context); }; -export const loadLazy = (doc, context) => { - console.log('template foo: lazy', context); +export const loadLazy = (doc, options, context) => { + console.log('template foo: lazy', options, context); }; -export const loadDelayed = (doc, context) => { - console.log('template foo: delayed', context); +export const loadDelayed = (doc, options, context) => { + console.log('template foo: delayed', options, context); }; From d9e41e4bf0e18cc291ba7aa94ab4c20e72f76439 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Mon, 18 Sep 2023 13:48:29 -0700 Subject: [PATCH 22/29] feat: support setting the context on regular plugin functions --- scripts/lib-franklin.js | 35 +++++++++++++++++++++++++++++++---- templates/foo.js | 18 +++++++++--------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 81cf54c165..28901a0bf7 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -693,6 +693,13 @@ export const executionContext = { toClassName, }; +/** + * Parses the plugin id and config paramters and returns a proper config + * + * @param {String} id A string that idenfies the plugin, or a path to it + * @param {String|Object} [config] A string representing the path to the plugin, or a config object + * @returns an object returning the the plugin id and its config + */ function parsePluginParams(id, config) { const pluginId = !config ? id.split('/').splice(id.endsWith('/') ? -2 : -1, 1)[0].replace(/\.js/, '') @@ -704,6 +711,21 @@ function parsePluginParams(id, config) { return { id: pluginId, config: pluginConfig }; } +/** + * Executes a function with the given context and arguments. + * An arrow function will have the context as last parameter, while a regular function will also + * have the `this` variable set to the same context. + * @param {Function} fn the function to execute + * @param {Object[]} args the function arugments + * @param {Object} context the execution context to use + * @returns the result of the function execution + */ +function runFunctionWithContext(fn, args, context) { + return fn.toString().startsWith('function') + ? fn.call(context, ...args, context) + : fn(...args, context); +} + class PluginsRegistry { #plugins; @@ -728,12 +750,13 @@ class PluginsRegistry { async load(phase) { [...this.#plugins.entries()] .filter(([, plugin]) => plugin.condition - && !plugin.condition(document, plugin.options, executionContext)) + && !runFunctionWithContext(plugin.condition, [document, plugin.options], executionContext)) .map(([id]) => this.#plugins.delete(id)); return Promise.all([...this.#plugins.entries()] // Filter plugins that don't match the execution conditions .filter(([, plugin]) => ( - (!plugin.condition || plugin.condition(document, plugin.options, executionContext)) + (!plugin.condition + || runFunctionWithContext(plugin.condition, [document, plugin.options], executionContext)) && phase === plugin.load && plugin.url )) .map(async ([key, plugin]) => { @@ -760,8 +783,12 @@ class PluginsRegistry { return [...this.#plugins.values()] .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially plugin[phase] && (!plugin.condition - || plugin.condition(document, plugin.options, executionContext)) - ? promise.then(() => plugin[phase](document, plugin.options, executionContext)) + || runFunctionWithContext(plugin.condition, [document, plugin.options], executionContext)) + ? promise.then(() => runFunctionWithContext( + plugin[phase], + [document, plugin.options], + executionContext, + )) : promise ), Promise.resolve()); } diff --git a/templates/foo.js b/templates/foo.js index 9f8c31d204..a4325f671f 100644 --- a/templates/foo.js +++ b/templates/foo.js @@ -1,11 +1,11 @@ -export const loadEager = (doc, options, context) => { - console.log('template foo: eager', options, context); -}; +export function loadEager(doc, options) { + console.log('template foo: eager', options, this); +} -export const loadLazy = (doc, options, context) => { - console.log('template foo: lazy', options, context); -}; +export function loadLazy(doc, options) { + console.log('template foo: lazy', options, this); +} -export const loadDelayed = (doc, options, context) => { - console.log('template foo: delayed', options, context); -}; +export function loadDelayed(doc, options) { + console.log('template foo: delayed', options, this); +} From 53894d6c4ee843b60d609b9bc110512dfb3bf3fd Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Mon, 25 Sep 2023 17:47:51 +0200 Subject: [PATCH 23/29] fix: add better error handling --- scripts/lib-franklin.js | 43 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 28901a0bf7..0ea8e1818b 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -432,6 +432,21 @@ export function buildBlock(blockName, content) { return (blockEl); } +/** + * Executes a function with the given context and arguments. + * An arrow function will have the context as last parameter, while a regular function will also + * have the `this` variable set to the same context. + * @param {Function} fn the function to execute + * @param {Object[]} args the function arugments + * @param {Object} context the execution context to use + * @returns the result of the function execution + */ +function runFunctionWithContext(fn, args, context) { + return fn.toString().startsWith('function') + ? fn.call(context, ...args, context) + : fn(...args, context); +} + /** * Loads the specified module with its JS and CSS files and returns the JS API if applicable. * @param {String} name The module name @@ -449,7 +464,8 @@ async function loadModule(name, cssPath, jsPath, ...args) { try { mod = await import(jsPath); if (mod.default) { - await mod.default.apply(null, args); + // eslint-disable-next-line no-use-before-define + await runFunctionWithContext(mod.default, args, executionContext); } } catch (error) { // eslint-disable-next-line no-console @@ -705,25 +721,10 @@ function parsePluginParams(id, config) { ? id.split('/').splice(id.endsWith('/') ? -2 : -1, 1)[0].replace(/\.js/, '') : id; const pluginConfig = typeof config === 'string' || !config - ? { url: (config || id).replace(/\/$/, '') } + ? { load: 'eager', url: (config || id).replace(/\/$/, '') } : { load: 'eager', ...config }; pluginConfig.options ||= {}; - return { id: pluginId, config: pluginConfig }; -} - -/** - * Executes a function with the given context and arguments. - * An arrow function will have the context as last parameter, while a regular function will also - * have the `this` variable set to the same context. - * @param {Function} fn the function to execute - * @param {Object[]} args the function arugments - * @param {Object} context the execution context to use - * @returns the result of the function execution - */ -function runFunctionWithContext(fn, args, context) { - return fn.toString().startsWith('function') - ? fn.call(context, ...args, context) - : fn(...args, context); + return { id: toClassName(pluginId), config: pluginConfig }; } class PluginsRegistry { @@ -790,7 +791,11 @@ class PluginsRegistry { executionContext, )) : promise - ), Promise.resolve()); + ), Promise.resolve()) + .catch((err) => { + // eslint-disable-next-line no-console + console.error('Error in plugin', err); + }); } } From 7fa625ca32e96d7d29286b9e866a28b61b4578d3 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Mon, 25 Sep 2023 17:59:48 +0200 Subject: [PATCH 24/29] chore: cleanup PR --- plugins/grault.js | 11 ----------- plugins/qux.js | 11 ----------- scripts/lib-franklin.js | 6 ++---- scripts/scripts.js | 38 ++------------------------------------ templates/bar.js | 13 ------------- templates/foo.js | 11 ----------- 6 files changed, 4 insertions(+), 86 deletions(-) delete mode 100644 plugins/grault.js delete mode 100644 plugins/qux.js delete mode 100644 templates/bar.js delete mode 100644 templates/foo.js diff --git a/plugins/grault.js b/plugins/grault.js deleted file mode 100644 index 6225c2e852..0000000000 --- a/plugins/grault.js +++ /dev/null @@ -1,11 +0,0 @@ -export const loadEager = (doc, options, context) => { - console.log('plugin grault: eager', options, context); -}; - -export const loadLazy = (doc, options, context) => { - console.log('plugin grault: lazy', options, context); -}; - -export const loadDelayed = (doc, options, context) => { - console.log('plugin grault: delayed', options, context); -}; diff --git a/plugins/qux.js b/plugins/qux.js deleted file mode 100644 index dfcad6c668..0000000000 --- a/plugins/qux.js +++ /dev/null @@ -1,11 +0,0 @@ -export const loadEager = (doc, options, context) => { - console.error('plugin qux: eager', options, context); // should not run as plugin is loaded in lazy phase -}; - -export const loadLazy = (doc, options, context) => { - console.log('plugin qux: lazy', options, context); -}; - -export const loadDelayed = (doc, options, context) => { - console.log('plugin qux: delayed', options, context); -}; diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 0ea8e1818b..64125945fc 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -605,16 +605,14 @@ export function normalizeHeadings(el, allowedHeadings) { /** * Set template (page structure) and theme (page styles). */ -export async function decorateTemplateAndTheme() { +export function decorateTemplateAndTheme() { const addClasses = (element, classes) => { classes.split(',').forEach((c) => { element.classList.add(toClassName(c.trim())); }); }; const template = getMetadata('template'); - if (template) { - addClasses(document.body, template); - } + if (template) addClasses(document.body, template); const theme = getMetadata('theme'); if (theme) addClasses(document.body, theme); } diff --git a/scripts/scripts.js b/scripts/scripts.js index fc600b7265..1cd82d6d61 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -17,45 +17,11 @@ const LCP_BLOCKS = []; // add your LCP blocks to the list /* Templates */ -// A template that will resolve by default -window.hlx.templates.add('foo', '/templates/foo.js'); - -// A template that will not resolve -window.hlx.templates.add('bar', '/templates/bar.js'); - -// Shorthand, also won't resolve -window.hlx.templates.add('/templates/garply.js'); +// window.hlx.templates.add('foo', '/templates/foo.js'); /* Plugins */ -// An inline plugin -window.hlx.plugins.add('inline-plugin-baz', { - condition: () => true, - loadEager: () => { - console.log('plugin baz: eager'); - }, - loadLazy: () => { - console.log('plugin baz: lazy'); - }, - loadDelayed: () => { - console.log('plugin baz: delayed'); - }, -}); - -// An external plugin that loads in the lazy phase -window.hlx.plugins.add('external-plugin-qux', { - url: '/plugins/qux.js', - load: 'lazy', -}); - -// An external plugin that will never load -window.hlx.plugins.add('external-plugin-corge', { - condition: () => false, - url: '/plugins/corge.js', -}); - -// Shorthand -window.hlx.plugins.add('/plugins/grault.js'); +// window.hlx.plugins.add('external-plugin-qux', '/plugins/qux.js'); /** * Builds hero block and prepends to main in a new section. diff --git a/templates/bar.js b/templates/bar.js deleted file mode 100644 index 35264e8599..0000000000 --- a/templates/bar.js +++ /dev/null @@ -1,13 +0,0 @@ -export const loadEager = (doc, options, context) => { - console.log('bar: eager', options, context); -}; - -export const loadLazy = (doc, options, context) => { - console.log('bar: lazy', options, context); -}; - -export const loadDelayed = (doc, options, context) => { - console.log('bar: delayed', options, context); -}; - -export default function () {} diff --git a/templates/foo.js b/templates/foo.js deleted file mode 100644 index a4325f671f..0000000000 --- a/templates/foo.js +++ /dev/null @@ -1,11 +0,0 @@ -export function loadEager(doc, options) { - console.log('template foo: eager', options, this); -} - -export function loadLazy(doc, options) { - console.log('template foo: lazy', options, this); -} - -export function loadDelayed(doc, options) { - console.log('template foo: delayed', options, this); -} From 5ac657fe6655c536a0e9853ec9b042c0206c0fbe Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 26 Sep 2023 11:18:07 +0200 Subject: [PATCH 25/29] chore: cleanup PR --- scripts/scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/scripts.js b/scripts/scripts.js index 1cd82d6d61..efaa9f7aca 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -83,7 +83,7 @@ export function decorateMain(main) { */ async function loadEager(doc) { document.documentElement.lang = 'en'; - await decorateTemplateAndTheme(); + decorateTemplateAndTheme(); const main = doc.querySelector('main'); await window.hlx.plugins.run('loadEager'); if (main) { From 96de3e45831c2cc50fbc9dabcf6292ab45c5560a Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 26 Sep 2023 11:19:12 +0200 Subject: [PATCH 26/29] chore: cleanup PR --- scripts/lib-franklin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 64125945fc..b101900208 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -802,7 +802,7 @@ class TemplatesRegistry { // eslint-disable-next-line class-methods-use-this add(id, url) { const { id: templateId, config: templateConfig } = parsePluginParams(id, url); - templateConfig.condition = () => toClassName(getMetadata('template')) === templateId || templateId === 'foo'; // FIXME: just for the PoC + templateConfig.condition = () => toClassName(getMetadata('template')) === templateId; window.hlx.plugins.add(templateId, templateConfig); } From b65a381b50f60760949b7a921f154529ab0998ea Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 26 Sep 2023 11:26:42 +0200 Subject: [PATCH 27/29] refactor: minor code optimizations --- scripts/lib-franklin.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index b101900208..7b1918eff8 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -452,7 +452,7 @@ function runFunctionWithContext(fn, args, context) { * @param {String} name The module name * @param {String} cssPath A path to the CSS file to load, or null * @param {String} jsPath A path to the JS file to load, or null - * @param {...any} args Arguments to use to call the default export on the JS file + * @param {...any} args Arguments to use to call the default export on the JS file * @returns a promsie that the module was loaded, and that returns the JS API is any */ async function loadModule(name, cssPath, jsPath, ...args) { @@ -760,11 +760,12 @@ class PluginsRegistry { )) .map(async ([key, plugin]) => { try { + const isJsUrl = plugin.url.endsWith('.js'); // If the plugin has a default export, it will be executed immediately const pluginApi = (await loadModule( key, - !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.css` : null, - !plugin.url.endsWith('.js') ? `${plugin.url}/${key}.js` : plugin.url, + !isJsUrl ? `${plugin.url}/${key}.css` : null, + !isJsUrl ? `${plugin.url}/${key}.js` : plugin.url, document, plugin.options, executionContext, From 9802f6bc4b20ff05de084bcb3fc3b9ffb77647ea Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 26 Sep 2023 11:32:22 +0200 Subject: [PATCH 28/29] doc: update jsdoc --- scripts/lib-franklin.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 7b1918eff8..1264cf00d8 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -744,7 +744,7 @@ class PluginsRegistry { // Check if the plugin exists includes(id) { return !!this.#plugins.has(id); } - // Load all plugins that are referenced by URL, and updated their configuration with the + // Load all plugins that are referenced by URL, and update their configuration with the // actual API they expose async load(phase) { [...this.#plugins.entries()] @@ -778,7 +778,8 @@ class PluginsRegistry { })); } - // Run a specific phase in the plugin + // Run a specific method in the plugin + // Methods follow the loadEager/loadLazy/loadDelayed phases async run(phase) { return [...this.#plugins.values()] .reduce((promise, plugin) => ( // Using reduce to execute plugins sequencially @@ -792,6 +793,7 @@ class PluginsRegistry { : promise ), Promise.resolve()) .catch((err) => { + // Gracefully catch possible errors in the plugins to avoid bubbling up issues // eslint-disable-next-line no-console console.error('Error in plugin', err); }); From 68fdf476aa9e6d3414420a19a92d8dafb58eb0a2 Mon Sep 17 00:00:00 2001 From: Julien Ramboz Date: Tue, 26 Sep 2023 16:14:52 +0200 Subject: [PATCH 29/29] doc: update jsdoc --- scripts/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/scripts.js b/scripts/scripts.js index efaa9f7aca..8f030a2f2a 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -17,11 +17,11 @@ const LCP_BLOCKS = []; // add your LCP blocks to the list /* Templates */ -// window.hlx.templates.add('foo', '/templates/foo.js'); +// window.hlx.templates.add('my-template', '/templates/my-template.js'); /* Plugins */ -// window.hlx.plugins.add('external-plugin-qux', '/plugins/qux.js'); +// window.hlx.plugins.add('my-plugin', '/plugins/my-plugin.js'); /** * Builds hero block and prepends to main in a new section.