diff --git a/dotcom-rendering/src/lib/fonts-css.ts b/dotcom-rendering/src/lib/fonts-css.ts index 4e3af47b7c2..daffc688560 100644 --- a/dotcom-rendering/src/lib/fonts-css.ts +++ b/dotcom-rendering/src/lib/fonts-css.ts @@ -8,16 +8,17 @@ type FontFamily = type FontStyle = 'normal' | 'italic'; -interface FontDisplay { +export interface FontDisplay { family: FontFamily; woff2: string; woff: string; ttf: string; weight: number; style: FontStyle; + uniqueName: string; } -const fontList: FontDisplay[] = [ +export const fontList: FontDisplay[] = [ // GH Guardian Headline, with legacy family name of Guardian Egyptian Web { family: 'GH Guardian Headline', @@ -26,6 +27,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-Light.ttf', weight: 300, style: 'normal', + uniqueName: 'GHGuardianHeadline-Light', }, { family: 'Guardian Egyptian Web', @@ -34,6 +36,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-Light.ttf', weight: 300, style: 'normal', + uniqueName: 'GuardianEgyptian-Light', }, { family: 'GH Guardian Headline', @@ -42,6 +45,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-LightItalic.ttf', weight: 300, style: 'italic', + uniqueName: 'GHGuardianHeadline-LightItalic', }, { family: 'Guardian Egyptian Web', @@ -50,6 +54,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-LightItalic.ttf', weight: 300, style: 'italic', + uniqueName: 'GuardianEgyptian-LightItalic', }, { family: 'GH Guardian Headline', @@ -58,6 +63,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-Medium.ttf', weight: 500, style: 'normal', + uniqueName: 'GHGuardianHeadline-Medium', }, { family: 'Guardian Egyptian Web', @@ -66,6 +72,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-Medium.ttf', weight: 500, style: 'normal', + uniqueName: 'GuardianEgyptian-Medium', }, { family: 'GH Guardian Headline', @@ -74,6 +81,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-MediumItalic.ttf', weight: 500, style: 'italic', + uniqueName: 'GHGuardianHeadline-MediumItalic', }, { family: 'Guardian Egyptian Web', @@ -82,6 +90,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-MediumItalic.ttf', weight: 500, style: 'italic', + uniqueName: 'GuardianEgyptian-MediumItalic', }, { family: 'GH Guardian Headline', @@ -90,6 +99,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-Bold.ttf', weight: 700, style: 'normal', + uniqueName: 'GHGuardianHeadline-Bold', }, { family: 'Guardian Egyptian Web', @@ -98,6 +108,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-Bold.ttf', weight: 700, style: 'normal', + uniqueName: 'GuardianEgyptian-Bold', }, { family: 'GH Guardian Headline', @@ -106,6 +117,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-BoldItalic.ttf', weight: 700, style: 'italic', + uniqueName: 'GHGuardianHeadline-BoldItalic', }, { family: 'Guardian Egyptian Web', @@ -114,6 +126,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-headline/latin1-not-hinted/GHGuardianHeadline-BoldItalic.ttf', weight: 700, style: 'italic', + uniqueName: 'GuardianEgyptian-BoldItalic', }, // GuardianTextEgyptian, with legacy family name of Guardian Text Egyptian Web { @@ -123,6 +136,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-Regular.ttf', weight: 400, style: 'normal', + uniqueName: 'GuardianTextEgyptian-Regular', }, { family: 'Guardian Text Egyptian Web', @@ -131,6 +145,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-Regular.ttf', weight: 400, style: 'normal', + uniqueName: 'GuardianTextEgyptianWeb-Regular', }, { family: 'GuardianTextEgyptian', @@ -139,6 +154,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-RegularItalic.ttf', weight: 400, style: 'italic', + uniqueName: 'GuardianTextEgyptian-RegularItalic', }, { family: 'Guardian Text Egyptian Web', @@ -147,6 +163,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-RegularItalic.ttf', weight: 400, style: 'italic', + uniqueName: 'GuardianTextEgyptianWeb-RegularItalic', }, { family: 'GuardianTextEgyptian', @@ -155,6 +172,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-Bold.ttf', weight: 700, style: 'normal', + uniqueName: 'GuardianTextEgyptian-Bold', }, { family: 'Guardian Text Egyptian Web', @@ -163,6 +181,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-Bold.ttf', weight: 700, style: 'normal', + uniqueName: 'GuardianTextEgyptianWeb-Bold', }, { family: 'GuardianTextEgyptian', @@ -171,6 +190,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-BoldItalic.ttf', weight: 700, style: 'italic', + uniqueName: 'GuardianTextEgyptian-BoldItalic', }, { family: 'Guardian Text Egyptian Web', @@ -179,6 +199,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textegyptian/latin1-not-hinted/GuardianTextEgyptian-BoldItalic.ttf', weight: 700, style: 'italic', + uniqueName: 'GuardianTextEgyptianWeb-BoldItalic', }, // GuardianTextSans, with legacy family name of Guardian Text Sans Web { @@ -188,6 +209,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-Regular.ttf', weight: 400, style: 'normal', + uniqueName: 'GuardianTextSans-Regular', }, { family: 'Guardian Text Sans Web', @@ -196,6 +218,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-Regular.ttf', weight: 400, style: 'normal', + uniqueName: 'GuardianTextSansWeb-Regular', }, { family: 'GuardianTextSans', @@ -204,6 +227,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-RegularItalic.ttf', weight: 400, style: 'italic', + uniqueName: 'GuardianTextSans-RegularItalic', }, { family: 'Guardian Text Sans Web', @@ -212,6 +236,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-RegularItalic.ttf', weight: 400, style: 'italic', + uniqueName: 'GuardianTextSansWeb-RegularItalic', }, { family: 'GuardianTextSans', @@ -220,6 +245,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-Bold.ttf', weight: 700, style: 'normal', + uniqueName: 'GuardianTextSans-Bold', }, { family: 'Guardian Text Sans Web', @@ -228,6 +254,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-Bold.ttf', weight: 700, style: 'normal', + uniqueName: 'GuardianTextSansWeb-Bold', }, { family: 'GuardianTextSans', @@ -236,6 +263,7 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-BoldItalic.ttf', weight: 700, style: 'italic', + uniqueName: 'GuardianTextSans-BoldItalic', }, { family: 'Guardian Text Sans Web', @@ -244,16 +272,14 @@ const fontList: FontDisplay[] = [ ttf: 'fonts/guardian-textsans/latin1-not-hinted/GuardianTextSans-BoldItalic.ttf', weight: 700, style: 'italic', + uniqueName: 'GuardianTextSansWeb-BoldItalic', }, ]; -const getFontUrl = (path: string): string => - `https://assets.guim.co.uk/static/frontend/${path}`; +const getFontUrl = (filePath: string): string => + `https://assets.guim.co.uk/static/frontend/${filePath}`; -export const rawFontsCss = fontList - .map( - (font) => ` -@font-face { +const getFontFaceCss = (font: FontDisplay): string => `@font-face { font-family: "${font.family}"; src: url(${getFontUrl(font.woff2)}) format("woff2"), url(${getFontUrl(font.woff)}) format("woff"), @@ -261,7 +287,18 @@ export const rawFontsCss = fontList font-weight: ${font.weight}; font-style: ${font.style}; font-display: swap; -} -`, - ) +}`; + +export const rawFontsCss = fontList.map(getFontFaceCss).join('\n'); + +export const rawFontsCssWithClassNames = fontList + .map((font) => { + return ` + ${getFontFaceCss(font)} + .${font.uniqueName} { + font-family: "${font.family}"; + font-weight: ${font.weight}; + font-style: ${font.style}; + }`; + }) .join('\n'); diff --git a/dotcom-rendering/src/server/handler.assets.apps.tsx b/dotcom-rendering/src/server/handler.assets.apps.tsx new file mode 100644 index 00000000000..2dbf3101527 --- /dev/null +++ b/dotcom-rendering/src/server/handler.assets.apps.tsx @@ -0,0 +1,69 @@ +import { createHash } from 'node:crypto'; +import { isString } from '@guardian/libs'; +import CleanCSS from 'clean-css'; +import { type RequestHandler } from 'express'; +import { renderToString } from 'react-dom/server'; +import { generateScriptTags, getPathFromManifest } from '../lib/assets'; +import { + type FontDisplay, + fontList, + rawFontsCssWithClassNames, +} from '../lib/fonts-css'; + +export const handleAppsAssets: RequestHandler = (req, res) => { + const clientScripts = [ + getPathFromManifest('client.apps', 'index.js'), + ].filter(isString); + + const scriptTags = generateScriptTags([...clientScripts]); + const html = buildHtml(scriptTags); + res.status(200).send(html); +}; + +type Props = { + fontList: FontDisplay[]; +}; + +export const buildHtml = (scriptTags: string[]) => { + const fontAssetsCss = new CleanCSS() + .minify(rawFontsCssWithClassNames) + .styles.trim(); + + const body = renderToString(); + + return ` + + + The Guardian Rendered Items Assets Html + + + + + + + ${scriptTags.join('\n')} + + + ${body} + + `; +}; + +const assetHash = (asset: string) => + createHash('sha256').update(asset).digest('base64'); + +export const AssetsPage = ({ fontList: fonts }: Props) => { + return ( + <> + {fonts.map((font) => ( +
+ . +
+ ))} + + ); +}; diff --git a/dotcom-rendering/src/server/server.dev.ts b/dotcom-rendering/src/server/server.dev.ts index 7431c6ee5f0..1d07fb730f4 100644 --- a/dotcom-rendering/src/server/server.dev.ts +++ b/dotcom-rendering/src/server/server.dev.ts @@ -12,6 +12,7 @@ import { handleBlocks, handleInteractive, } from './handler.article.web'; +import { handleAppsAssets } from './handler.assets.apps'; import { handleEditionsCrossword } from './handler.editionsCrossword'; import { handleFront, handleTagPage } from './handler.front.web'; import { @@ -119,6 +120,8 @@ renderer.post('/FootballTablesPage', handleFootballTablesPage); renderer.post('/CricketMatchPage', handleCricketMatchPage); renderer.post('/FootballMatchSummaryPage', handleFootballMatchPage); +renderer.get('/assets/rendered-items-assets', handleAppsAssets); + const router = Router(); router.use('/pages', pages); router.use('/targets', targets); diff --git a/dotcom-rendering/src/server/server.prod.ts b/dotcom-rendering/src/server/server.prod.ts index 188f24c5b66..821be7d9bc6 100644 --- a/dotcom-rendering/src/server/server.prod.ts +++ b/dotcom-rendering/src/server/server.prod.ts @@ -14,6 +14,7 @@ import { handleBlocks, handleInteractive, } from './handler.article.web'; +import { handleAppsAssets } from './handler.assets.apps'; import { handleEditionsCrossword } from './handler.editionsCrossword'; import { handleFront, handleTagPage } from './handler.front.web'; import { @@ -85,6 +86,8 @@ export const prodServer = (): void => { app.post('/AppsBlocks', logRenderTime, handleAppsBlocks); app.post('/EditionsCrossword', logRenderTime, handleEditionsCrossword); + app.get('/assets/rendered-items-assets', handleAppsAssets); + // All params to error handlers must be declared for express to identify them as error middleware // https://expressjs.com/en/api.html#:~:text=Error%2Dhandling%20middleware%20always,see%3A%20Error%20handling // eslint-disable-next-line @typescript-eslint/no-unused-vars -- all params to error handlers must be declared