From 12b089bb928c60e8ac3ef5d519d657b16194327e Mon Sep 17 00:00:00 2001 From: james-owen Date: Fri, 4 Nov 2022 12:48:06 -0400 Subject: [PATCH 1/9] feat: handle linked font files --- lib/media.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/media.js b/lib/media.js index f9165f7..16b0531 100644 --- a/lib/media.js +++ b/lib/media.js @@ -274,6 +274,14 @@ function getStyleFiles(state) { } } + // check for clay compile linked? clay compile font files? + const defaultFontPath = path.join(assetDir, 'css', '_linked-fonts._default.css'); + const siteFontPath = path.join(assetDir, 'css', `_linked-fonts.${siteStyleguide}.css`); + + // add any default and site font css + cssFilePaths.push(defaultFontPath); + cssFilePaths.push(siteFontPath); + return cssFilePaths .filter(files.fileExists) .map(pathJoin(assetHost, assetPath, assetDir)); From d3219195d49e26d32bf32150e20878307544f364 Mon Sep 17 00:00:00 2001 From: james-owen Date: Fri, 4 Nov 2022 12:49:28 -0400 Subject: [PATCH 2/9] 6.0.0-dev.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ad7d53..963d07b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "amphora-html", - "version": "6.0.0-7", + "version": "6.0.0-dev.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 98d1174..f9f4d45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amphora-html", - "version": "6.0.0-7", + "version": "6.0.0-dev.0", "description": "An HTML renderer for component data", "main": "index.js", "scripts": { From 3941ab3141682d2682f2be15c94b283d127aacf0 Mon Sep 17 00:00:00 2001 From: james-owen Date: Tue, 21 Jan 2025 17:06:45 -0500 Subject: [PATCH 3/9] feat: pass res to postrender hooks --- lib/render.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/render.js b/lib/render.js index fd5adb3..0d5011d 100644 --- a/lib/render.js +++ b/lib/render.js @@ -40,7 +40,7 @@ function applyRenderHooks(ref, data, locals) { * @param {Object} locals * @returns {Function} */ -function applyPostRenderHooks(ref, locals) { +function applyPostRenderHooks(ref, locals, res) { return (html) => { // skip postRender hooks in edit mode @@ -50,7 +50,7 @@ function applyPostRenderHooks(ref, locals) { return setup.plugins.filter((plugin) => plugin.postRender) .reduce((val, plugin) => { - return plugin.postRender(ref, html, locals); + return plugin.postRender(ref, html, locals, res); }, html); }; } @@ -148,7 +148,7 @@ function render(data, meta, res) { return applyRenderHooks(state._layoutRef || state.self, data, state.locals) .then(makeHtml(state)) .then(mediaService.injectScriptsAndStyles(state)) - .then(applyPostRenderHooks(state._layoutRef || state.self, state.locals)) + .then(applyPostRenderHooks(state._layoutRef || state.self, state.locals, res)) .then(result => { res.type('text/html'); res.send(result); From 6f6be0f64fff849a9124de592d5c290978612e3a Mon Sep 17 00:00:00 2001 From: james-owen Date: Thu, 23 Jan 2025 11:19:41 -0500 Subject: [PATCH 4/9] use meta ref --- lib/render.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/render.js b/lib/render.js index 0d5011d..428889e 100644 --- a/lib/render.js +++ b/lib/render.js @@ -40,7 +40,7 @@ function applyRenderHooks(ref, data, locals) { * @param {Object} locals * @returns {Function} */ -function applyPostRenderHooks(ref, locals, res) { +function applyPostRenderHooks(ref, locals, res, self) { return (html) => { // skip postRender hooks in edit mode @@ -50,7 +50,7 @@ function applyPostRenderHooks(ref, locals, res) { return setup.plugins.filter((plugin) => plugin.postRender) .reduce((val, plugin) => { - return plugin.postRender(ref, html, locals, res); + return plugin.postRender(ref, html, locals, res, self); }, html); }; } @@ -148,7 +148,7 @@ function render(data, meta, res) { return applyRenderHooks(state._layoutRef || state.self, data, state.locals) .then(makeHtml(state)) .then(mediaService.injectScriptsAndStyles(state)) - .then(applyPostRenderHooks(state._layoutRef || state.self, state.locals, res)) + .then(applyPostRenderHooks(state._layoutRef || state.self, state.locals, res, meta._ref)) .then(result => { res.type('text/html'); res.send(result); From 5c688df535aec8222db94732e6e1804299f376c3 Mon Sep 17 00:00:00 2001 From: james-owen Date: Thu, 23 Jan 2025 15:09:31 -0500 Subject: [PATCH 5/9] 6.0.1-dev.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 963d07b..45c848c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "amphora-html", - "version": "6.0.0-dev.0", + "version": "6.0.1-dev.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "amphora-html", - "version": "6.0.0-6", + "version": "6.0.1-dev.0", "license": "MIT", "dependencies": { "amphora-fs": "^2.0.0", diff --git a/package.json b/package.json index f9f4d45..9185a35 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amphora-html", - "version": "6.0.0-dev.0", + "version": "6.0.1-dev.0", "description": "An HTML renderer for component data", "main": "index.js", "scripts": { From e8b38f350f23a4c76938fd66a2b60d8b7270aa82 Mon Sep 17 00:00:00 2001 From: Jordan Paulino Date: Sun, 8 Mar 2026 17:06:44 -0400 Subject: [PATCH 6/9] Add modulepreload and module script support for esbuild pipeline - Add MODULE_SCRIPT_TAG and MODULEPRELOAD_TAG constants to media.js - Add omitCacheBusterOnModules flag (enabled when modulepreload:true) so content-hashed ESM filenames skip the redundant ?version= query string - Handle moduleScripts and modulePreloads in injectScriptsAndStyles(), injecting `; } else if (tag === ASYNC_DEFER_SCRIPT_TAG) { return ``; + } else if (tag === MODULE_SCRIPT_TAG) { + return ``; + } else if (tag === MODULEPRELOAD_TAG) { + // tells the browser to fetch ESM scripts early, + // during HTML parsing, before reaching the `; } @@ -378,6 +390,14 @@ function configure(options, cacheBuster = '') { if (options && _.isObject(options)) { module.exports.editStylesTags = options.styles || false; module.exports.editScriptsTags = options.scripts || false; + // modulepreload: when true, hints are injected + // into for ESM scripts, and ?version= is omitted from module URLs + // since content-hashed filenames already provide cache busting. + // Opt-in only — defaults to false for backwards compatibility. + if (options.modulepreload !== undefined) { + module.exports.modulepreload = !!options.modulepreload; + module.exports.omitCacheBusterOnModules = !!options.modulepreload; + } } else { module.exports.editStylesTags = options; module.exports.editScriptsTags = options; @@ -412,8 +432,29 @@ function injectScriptsAndStyles(state) { mediaMap = module.exports.getMediaMap(state); // allow site to change the media map before applying it + // Expose rendered component names so resolveMedia can do per-component script resolution + // (e.g. pack-next manifest lookup) without needing a reference to the full state object. + locals._components = state._components; if (setup.resolveMedia) mediaMap = setup.resolveMedia(mediaMap, locals) || mediaMap; + // moduleScripts: ESM scripts (e.g. from esbuild pack-next) that need type="module" + const moduleScriptFiles = mediaMap.moduleScripts || []; + + mediaMap.moduleScripts = moduleScriptFiles.length + ? injectTags(moduleScriptFiles, locals.site, MODULE_SCRIPT_TAG) + : bluebird.resolve(false); + + // modulePreloads: hints for . + // Only active when configure({ modulepreload: true }) has been called — opt-in so + // sites not using the clay build pipeline are completely unaffected. + const modulePreloadFiles = module.exports.modulepreload + ? (mediaMap.modulePreloads || []) + : []; + + mediaMap.modulePreloads = modulePreloadFiles.length + ? injectTags(modulePreloadFiles, locals.site, MODULEPRELOAD_TAG) + : bluebird.resolve(false); + if (!locals.edit) { mediaMap.styles = combineFileContents(mediaMap.styles, 'public/css', '/css/', STYLE_TAG); mediaMap.scripts = !!mediaMap.manifestAssets && mediaMap.manifestAssets.length > 0 @@ -426,7 +467,11 @@ function injectScriptsAndStyles(state) { return bluebird.props(mediaMap) .then(combinedFiles => { + // modulepreload hints go first in , before CSS, so the browser can + // start fetching ESM scripts at the earliest possible moment. + html = combinedFiles.modulePreloads ? appendMediaToTop(combinedFiles.modulePreloads, html) : html; html = combinedFiles.styles ? appendMediaToTop(combinedFiles.styles, html) : html; // If there are styles, append them + html = combinedFiles.moduleScripts ? appendMediaToBottom(combinedFiles.moduleScripts, html) : html; // ESM module scripts (type="module") html = combinedFiles.scripts ? appendMediaToBottom(combinedFiles.scripts, html) : html; // If there are scripts, append them return html; // Return the compiled HTML }); @@ -439,6 +484,10 @@ module.exports.cacheBuster = ''; module.exports.configure = configure; module.exports.editStylesTags = false; module.exports.editScriptsTags = false; +// Opt-in flags for the clay build (esbuild) pipeline. +// Enable via configure({ modulepreload: true }). +module.exports.modulepreload = false; +module.exports.omitCacheBusterOnModules = false; // For testing module.exports.getManifestAssets = getManifestAssets; diff --git a/lib/render.js b/lib/render.js index 428889e..5be6d69 100644 --- a/lib/render.js +++ b/lib/render.js @@ -182,8 +182,12 @@ function logTime(hrStart, msg, route) { }; } -function configure({ editAssetTags, cacheBuster }) { - mediaService.configure(editAssetTags, cacheBuster); +function configure({ editAssetTags, cacheBuster, modulepreload }) { + const mediaOptions = editAssetTags && typeof editAssetTags === 'object' + ? Object.assign({}, editAssetTags, modulepreload !== undefined ? { modulepreload } : {}) + : editAssetTags; + + mediaService.configure(mediaOptions, cacheBuster); } /** From 86d125b0cd72084a6dfda052071a1bc199dbfdff Mon Sep 17 00:00:00 2001 From: Jordan Paulino Date: Tue, 14 Apr 2026 20:44:54 -0400 Subject: [PATCH 7/9] fix media lint failures after merge Remove post-merge lint regressions in media and render modules so amphora-html passes eslint and test in the yolo branch. Made-with: Cursor --- lib/media.js | 5 +++-- lib/render.js | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/media.js b/lib/media.js index deaaf06..fa5dc2d 100644 --- a/lib/media.js +++ b/lib/media.js @@ -6,7 +6,6 @@ const _ = require('lodash'), bluebird = require('bluebird'), setup = require('./setup'), styleguide = require('./styleguide'), - url = require('url'), STYLE_TAG = 'style', SCRIPT_TAG = 'scripts', ASYNC_SCRIPT_TAG = 'async', @@ -361,6 +360,7 @@ function configure(options, cacheBuster = '') { * @param {Object} state * @return {Function} */ +/* eslint-disable complexity */ function injectScriptsAndStyles(state) { const { locals } = state; @@ -392,7 +392,7 @@ function injectScriptsAndStyles(state) { // Only active when configure({ modulepreload: true }) has been called — opt-in so // sites not using the clay build pipeline are completely unaffected. const modulePreloadFiles = module.exports.modulepreload - ? (mediaMap.modulePreloads || []) + ? mediaMap.modulePreloads || [] : []; mediaMap.modulePreloads = modulePreloadFiles.length @@ -419,6 +419,7 @@ function injectScriptsAndStyles(state) { }); }; } +/* eslint-enable complexity */ module.exports.injectScriptsAndStyles = injectScriptsAndStyles; module.exports.getMediaMap = getMediaMap; diff --git a/lib/render.js b/lib/render.js index 5be6d69..5f6c629 100644 --- a/lib/render.js +++ b/lib/render.js @@ -38,6 +38,8 @@ function applyRenderHooks(ref, data, locals) { * * @param {String} ref * @param {Object} locals + * @param {Object} res + * @param {String} self * @returns {Function} */ function applyPostRenderHooks(ref, locals, res, self) { From 6659f2cb5e0d0f47ecd40d05ad2ec49cb7752c56 Mon Sep 17 00:00:00 2001 From: Jordan Paulino Date: Tue, 28 Apr 2026 12:15:33 -0400 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=8D=95=20Drop=20dead=20code=20from=20?= =?UTF-8?q?injectTags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASYNC_SCRIPT_TAG, DEFER_SCRIPT_TAG, and ASYNC_DEFER_SCRIPT_TAG were defined and had matching else-if branches in injectTags but no caller ever passed them in. The function is private (not exported) and the tag values are not referenced anywhere else in lib/ or tests, so the three constants and their branches were unreachable. Removed alongside them: the unused `site` parameter, which every caller threaded through but the function body never read. The four callsites in mediaService now pass only fileArray and tag. The remaining branches map directly to the four tag values that are actually exercised: STYLE_TAG edit-mode CSS (editStylesTags=true) MODULE_SCRIPT_TAG Vite ESM `; - } else if (tag === DEFER_SCRIPT_TAG) { - return ``; - } else if (tag === ASYNC_DEFER_SCRIPT_TAG) { - return ``; } else if (tag === MODULE_SCRIPT_TAG) { return ``; } else if (tag === MODULEPRELOAD_TAG) { // tells the browser to fetch ESM scripts early, // during HTML parsing, before reaching the `; } + + // Default: classic `; }).join('\n')); } @@ -385,7 +386,7 @@ function injectScriptsAndStyles(state) { const moduleScriptFiles = mediaMap.moduleScripts || []; mediaMap.moduleScripts = moduleScriptFiles.length - ? injectTags(moduleScriptFiles, locals.site, MODULE_SCRIPT_TAG) + ? injectTags(moduleScriptFiles, MODULE_SCRIPT_TAG) : bluebird.resolve(false); // modulePreloads: hints for . @@ -396,15 +397,15 @@ function injectScriptsAndStyles(state) { : []; mediaMap.modulePreloads = modulePreloadFiles.length - ? injectTags(modulePreloadFiles, locals.site, MODULEPRELOAD_TAG) + ? injectTags(modulePreloadFiles, MODULEPRELOAD_TAG) : bluebird.resolve(false); if (!locals.edit) { mediaMap.styles = combineFileContents(mediaMap.styles, 'public/css', '/css/', STYLE_TAG); mediaMap.scripts = combineFileContents(mediaMap.scripts, 'public/js', '/js/', SCRIPT_TAG); } else { - mediaMap.styles = module.exports.editStylesTags ? injectTags(mediaMap.styles, locals.site, STYLE_TAG) : combineFileContents(mediaMap.styles, 'public/css', '/css/', STYLE_TAG); - mediaMap.scripts = module.exports.editScriptsTags ? injectTags(mediaMap.scripts, locals.site, SCRIPT_TAG) : combineFileContents(mediaMap.scripts, 'public/js', '/js/', SCRIPT_TAG); + mediaMap.styles = module.exports.editStylesTags ? injectTags(mediaMap.styles, STYLE_TAG) : combineFileContents(mediaMap.styles, 'public/css', '/css/', STYLE_TAG); + mediaMap.scripts = module.exports.editScriptsTags ? injectTags(mediaMap.scripts, SCRIPT_TAG) : combineFileContents(mediaMap.scripts, 'public/js', '/js/', SCRIPT_TAG); } return bluebird.props(mediaMap) From 0e2dd12252866e6a3ab0f64b95b060b14f32a253 Mon Sep 17 00:00:00 2001 From: Jordan Paulino Date: Tue, 28 Apr 2026 12:16:10 -0400 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=8D=95=20Document=20expected=20shape?= =?UTF-8?q?=20of=20`self`=20in=20applyPostRenderHooks=20JSDoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an example Clay ref value to the @param so future readers can see at a glance that `self` is the component URI (e.g. the value the caller threads through from `meta._ref` at the only invocation site). Made-with: Cursor --- lib/render.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/render.js b/lib/render.js index 5f6c629..cee44df 100644 --- a/lib/render.js +++ b/lib/render.js @@ -39,7 +39,8 @@ function applyRenderHooks(ref, data, locals) { * @param {String} ref * @param {Object} locals * @param {Object} res - * @param {String} self + * @param {String} self Clay ref of the component being rendered, e.g. + * "site.com/_components/article/instances/foo" * @returns {Function} */ function applyPostRenderHooks(ref, locals, res, self) {