From 2cadc5d1f4b6447d1a1b742bb3b0bb6aaeefe154 Mon Sep 17 00:00:00 2001 From: Vladimir Mozgovoy Date: Mon, 4 Mar 2024 18:22:29 +0100 Subject: [PATCH 1/5] feat: export html parts renderers --- README.md | 49 +++++++++ src/index.ts | 5 +- src/render.ts | 153 ++--------------------------- src/types.ts | 20 ++-- src/utils.ts | 1 + src/utils/generateRenderContent.ts | 120 ++++++++++++++++++++++ src/utils/renderBodyContent.ts | 13 +++ src/utils/renderHeadContent.ts | 34 +++++++ 8 files changed, 245 insertions(+), 150 deletions(-) create mode 100644 src/utils/generateRenderContent.ts create mode 100644 src/utils/renderBodyContent.ts create mode 100644 src/utils/renderHeadContent.ts diff --git a/README.md b/README.md index 26a12a3..642118c 100644 --- a/README.md +++ b/README.md @@ -438,3 +438,52 @@ app.get((req, res) => { })); }) ``` + +## Alternative usage + +With parts renderers `generateRenderContent`, `renderHeadContent`, `renderBodyContent` via html streaming: + +```js +import express from 'express'; +import {createRenderFunction} from '@gravity-ui/app-layout'; + +const app = express(); + +const renderLayout = createRenderFunction(); + +app.get('/', async function (req, res) { + res.writeHead(200, { + 'Content-Type': 'text/html', + 'Transfer-Encoding': 'chunked', + }); + + const content = generateRenderContent(plugins, { + title: 'Home page', + }); + + const {htmlAttributes, helpers, bodyContent} = content; + + res.write(` + + + + ${renderHeadContent(content)} + + + ${renderBodyContent(content)} + `); + + const data = await getUserData(); + + res.write(` + ${content.renderHelpers.renderInlineScript(` + window.__DATA__ = ${htmlescape(data)}; + `)} + + + `); + res.end(); +}); + +app.listen(3000); +``` diff --git a/src/index.ts b/src/index.ts index c6f42e3..a3fd707 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,7 @@ -export {createRenderFunction, generateRenderContent} from './render.js'; +export {generateRenderContent} from './utils/generateRenderContent.js'; +export {renderHeadContent} from './utils/renderHeadContent.js'; +export {renderBodyContent} from './utils/renderBodyContent.js'; +export {createRenderFunction} from './render.js'; export { createGoogleAnalyticsPlugin, createYandexMetrikaPlugin, diff --git a/src/render.ts b/src/render.ts index e6f8e35..72c2b93 100644 --- a/src/render.ts +++ b/src/render.ts @@ -1,155 +1,22 @@ -import htmlescape from 'htmlescape'; - -import type {Icon, Meta, Plugin, RenderContent, RenderParams} from './types.js'; -import {attrs, getRenderHelpers, hasProperty} from './utils.js'; - -function getRootClassName(theme?: string) { - if (!theme) { - return []; - } - const classes = ['g-root']; - if (theme) { - classes.push(`g-root_theme_${theme}`); - } - return classes; -} - -const defaultIcon: Icon = { - type: 'image/png', - sizes: '16x16', - href: '/favicon.png', -}; - -const defaultMeta: Meta[] = [ - { - name: 'viewport', - content: 'width=device-width, initial-scale=1.0', - }, -]; - -/** - * Generates the content to be rendered in an HTML page. - * @param plugins - An array of plugins. - * @param params - An object containing various parameters for rendering the content. - * @returns An object containing the generated content for an HTML page. - */ -export function generateRenderContent( - plugins: Plugins | undefined, - params: RenderParams, -): RenderContent { - const helpers = getRenderHelpers(params); - const htmlAttributes: Record = {}; - const meta = params.meta ?? []; - // in terms of sets: meta = params.meta ∪ (defaultMeta ∖ params.meta) - defaultMeta.forEach((defaultMetaItem) => { - if (!meta.find(({name}) => name === defaultMetaItem.name)) { - meta.push(defaultMetaItem); - } - }); - const styleSheets = params.styleSheets || []; - const scripts = params.scripts || []; - const inlineStyleSheets = params.inlineStyleSheets || []; - const inlineScripts = params.inlineScripts || []; - const links = params.links || []; - - inlineScripts.unshift(`window.__DATA__ = ${htmlescape(params.data || {})};`); - - const content = params.bodyContent ?? {}; - const {theme, className} = content; - const bodyClasses = Array.from( - new Set([...getRootClassName(theme), ...(className ? className.split(' ') : [])]), - ); - const bodyContent = { - className: bodyClasses, - root: content.root, - beforeRoot: content.beforeRoot ? [content.beforeRoot] : [], - afterRoot: content.afterRoot ? [content.afterRoot] : [], - }; - - const {lang, isMobile, title, pluginsOptions = {}} = params; - for (const plugin of plugins ?? []) { - plugin.apply({ - options: hasProperty(pluginsOptions, plugin.name) - ? pluginsOptions[plugin.name] - : undefined, - renderContent: { - htmlAttributes, - meta, - links, - styleSheets, - scripts, - inlineStyleSheets, - inlineScripts, - bodyContent, - }, - commonOptions: {title, lang, isMobile}, - utils: helpers, - }); - } - - if (lang) { - htmlAttributes.lang = lang; - } - - return { - htmlAttributes, - meta, - links, - styleSheets, - scripts, - inlineStyleSheets, - inlineScripts, - bodyContent, - }; -} +import type {Plugin, RenderParams} from './types.js'; +import {generateRenderContent} from './utils/generateRenderContent.js'; +import {renderBodyContent} from './utils/renderBodyContent.js'; +import {renderHeadContent} from './utils/renderHeadContent.js'; export function createRenderFunction(plugins?: Plugins) { return function render(params: RenderParams) { - const { - htmlAttributes, - meta, - styleSheets, - scripts, - inlineStyleSheets, - inlineScripts, - links, - bodyContent, - } = generateRenderContent(plugins, params); - - const helpers = getRenderHelpers(params); + const content = generateRenderContent(plugins, params); - const icon: Icon = { - ...defaultIcon, - ...params.icon, - }; + const {htmlAttributes, helpers, bodyContent} = content; return ` - + - - ${params.title} - - ${[ - ...scripts.map(({src, crossOrigin}) => - helpers.renderLink({href: src, crossOrigin, rel: 'preload', as: 'script'}), - ), - ...links.map((link) => helpers.renderLink(link)), - ...meta.map((metaData) => helpers.renderMeta(metaData)), - ...styleSheets.map((style) => helpers.renderStyle(style)), - ...inlineStyleSheets.map((text) => helpers.renderInlineStyle(text)), - ...scripts.map((script) => helpers.renderScript(script)), - ...inlineScripts.map((text) => helpers.renderInlineScript(text)), - ] - .filter(Boolean) - .join('\n')} + ${renderHeadContent(content)} - - ${bodyContent.beforeRoot.join('\n')} -
- ${bodyContent.root ?? ''} -
- ${bodyContent.afterRoot.join('\n')} + + ${renderBodyContent(content)} `.trim(); diff --git a/src/types.ts b/src/types.ts index 69878bd..61d762a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,6 +36,14 @@ export interface CommonOptions { isMobile?: boolean; } +export interface BodyContent { + attributes: Record; + className: string[]; + beforeRoot: string[]; + root?: string; + afterRoot: string[]; +} + export interface RenderContent { htmlAttributes: Record; meta: Meta[]; @@ -44,12 +52,10 @@ export interface RenderContent { styleSheets: Stylesheet[]; inlineScripts: string[]; inlineStyleSheets: string[]; - bodyContent: { - className: string[]; - beforeRoot: string[]; - root?: string; - afterRoot: string[]; - }; + bodyContent: BodyContent; + helpers: RenderHelpers; + icon: Icon; + title: string; } export interface RenderHelpers { @@ -59,6 +65,7 @@ export interface RenderHelpers { renderInlineStyle(content: string): string; renderMeta(meta: Meta): string; renderLink(link: Link): string; + attrs(obj: Attributes): string; } export interface Plugin { name: Name; @@ -71,6 +78,7 @@ export interface Plugin { } export interface RenderParams extends CommonOptions { data?: Data; + skipRenderDataScript?: boolean; icon?: Icon; nonce?: string; // content diff --git a/src/utils.ts b/src/utils.ts index b37d235..6202a4e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -56,6 +56,7 @@ export function getRenderHelpers(params: {nonce?: string}): RenderHelpers { renderInlineStyle, renderMeta, renderLink, + attrs, }; } diff --git a/src/utils/generateRenderContent.ts b/src/utils/generateRenderContent.ts new file mode 100644 index 0000000..2db1bcf --- /dev/null +++ b/src/utils/generateRenderContent.ts @@ -0,0 +1,120 @@ +import htmlescape from 'htmlescape'; + +import type {BodyContent, Icon, Meta, Plugin, RenderContent, RenderParams} from '../types.js'; +import {getRenderHelpers, hasProperty} from '../utils.js'; + +function getRootClassName(theme?: string) { + if (!theme) { + return []; + } + const classes = ['g-root']; + if (theme) { + classes.push(`g-root_theme_${theme}`); + } + return classes; +} + +const defaultIcon: Icon = { + type: 'image/png', + sizes: '16x16', + href: '/favicon.png', +}; + +const defaultMeta: Meta[] = [ + { + name: 'viewport', + content: 'width=device-width, initial-scale=1.0', + }, +]; + +/** + * Generates the content to be rendered in an HTML page. + * @param plugins - An array of plugins. + * @param params - An object containing various parameters for rendering the content. + * @returns An object containing the generated content for an HTML page. + */ +export function generateRenderContent( + plugins: Plugins | undefined, + params: RenderParams, +): RenderContent { + const helpers = getRenderHelpers(params); + const htmlAttributes: Record = {}; + const meta = params.meta ?? []; + // in terms of sets: meta = params.meta ∪ (defaultMeta ∖ params.meta) + defaultMeta.forEach((defaultMetaItem) => { + if (!meta.find(({name}) => name === defaultMetaItem.name)) { + meta.push(defaultMetaItem); + } + }); + const styleSheets = params.styleSheets || []; + const scripts = params.scripts || []; + const inlineStyleSheets = params.inlineStyleSheets || []; + const inlineScripts = params.inlineScripts || []; + const links = params.links || []; + + if (!params.skipRenderDataScript) { + inlineScripts.unshift(`window.__DATA__ = ${htmlescape(params.data || {})};`); + } + + const content = params.bodyContent ?? {}; + const {theme, className} = content; + const bodyClasses = Array.from( + new Set([...getRootClassName(theme), ...(className ? className.split(' ') : [])]), + ); + const bodyContent: BodyContent = { + attributes: { + class: bodyClasses.filter(Boolean).join(' '), + }, + className: bodyClasses, + root: content.root, + beforeRoot: content.beforeRoot ? [content.beforeRoot] : [], + afterRoot: content.afterRoot ? [content.afterRoot] : [], + }; + + const icon: Icon = { + ...defaultIcon, + ...params.icon, + }; + + const {lang, isMobile, title, pluginsOptions = {}} = params; + for (const plugin of plugins ?? []) { + plugin.apply({ + options: hasProperty(pluginsOptions, plugin.name) + ? pluginsOptions[plugin.name] + : undefined, + renderContent: { + htmlAttributes, + meta, + links, + styleSheets, + scripts, + inlineStyleSheets, + inlineScripts, + bodyContent, + helpers, + icon, + title, + }, + commonOptions: {title, lang, isMobile}, + utils: helpers, + }); + } + + if (lang) { + htmlAttributes.lang = lang; + } + + return { + htmlAttributes, + meta, + links, + styleSheets, + scripts, + inlineStyleSheets, + inlineScripts, + bodyContent, + helpers, + icon, + title, + }; +} diff --git a/src/utils/renderBodyContent.ts b/src/utils/renderBodyContent.ts new file mode 100644 index 0000000..0236f37 --- /dev/null +++ b/src/utils/renderBodyContent.ts @@ -0,0 +1,13 @@ +import type {RenderContent} from '../types.js'; + +export function renderBodyContent(content: RenderContent): string { + const {bodyContent} = content; + + return ` + ${bodyContent.beforeRoot.join('\n')} +
+ ${bodyContent.root ?? ''} +
+ ${bodyContent.afterRoot.join('\n')} + `.trim(); +} diff --git a/src/utils/renderHeadContent.ts b/src/utils/renderHeadContent.ts new file mode 100644 index 0000000..7b393a7 --- /dev/null +++ b/src/utils/renderHeadContent.ts @@ -0,0 +1,34 @@ +import type {RenderContent} from '../types.js'; + +export function renderHeadContent(content: RenderContent): string { + const { + icon, + scripts, + helpers, + links, + meta, + styleSheets, + inlineStyleSheets, + inlineScripts, + title, + } = content; + + return ` + + ${title} + + ${[ + ...scripts.map(({src, crossOrigin}) => + helpers.renderLink({href: src, crossOrigin, rel: 'preload', as: 'script'}), + ), + ...links.map((link) => helpers.renderLink(link)), + ...meta.map((metaData) => helpers.renderMeta(metaData)), + ...styleSheets.map((style) => helpers.renderStyle(style)), + ...inlineStyleSheets.map((text) => helpers.renderInlineStyle(text)), + ...scripts.map((script) => helpers.renderScript(script)), + ...inlineScripts.map((text) => helpers.renderInlineScript(text)), + ] + .filter(Boolean) + .join('\n')} + `.trim(); +} From 281e99f89439391261f863b49f300c9d69531294 Mon Sep 17 00:00:00 2001 From: Vladimir Mozgovoy Date: Tue, 12 Mar 2024 17:40:40 +0100 Subject: [PATCH 2/5] fix: fix code review issues --- README.md | 50 +++++++++++++++++++++--------- src/render.ts | 8 +++-- src/types.ts | 28 +++++++++-------- src/utils/generateRenderContent.ts | 13 +++----- src/utils/renderBodyContent.ts | 12 +++---- src/utils/renderHeadContent.ts | 18 +++-------- 6 files changed, 70 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 642118c..80d9e2a 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ interface Link { href: string; rel?: string; type?: string; + sizes?: string; title?: HTMLLinkElement['title']; crossOrigin?: '' | 'anonymous' | 'use-credentials'; } @@ -236,6 +237,7 @@ interface Plugin { options: Options | undefined; // passed through `renderLayout` function in `pluginsOptions` parameter. commonOptions: CommonOptions; renderContent: RenderContent; + /** @deprecated use `renderContent.helpers` instead */ utils: RenderHelpers; }) => void; } @@ -247,20 +249,27 @@ interface CommonOptions { isMobile?: boolean; } -interface RenderContent { - meta: Meta[]; - links: Link[]; +export interface HeadContent { scripts: Script[]; + helpers: RenderHelpers; + links: Link[]; + meta: Meta[]; styleSheets: Stylesheet[]; - inlineScripts: string[]; inlineStyleSheets: string[]; - bodyContent: { - theme?: string; - className: string[]; - beforeRoot: string[]; - root?: string; - afterRoot: string[]; - }; + inlineScripts: string[]; + title: string; +} + +export interface BodyContent { + className: string[]; + beforeRoot: string[]; + root?: string; + afterRoot: string[]; +} + +export interface RenderContent extends HeadContent { + htmlAttributes: Attributes; + bodyContent: BodyContent; } export interface RenderHelpers { @@ -270,6 +279,7 @@ export interface RenderHelpers { renderInlineStyle(content: string): string; renderMeta(meta: Meta): string; renderLink(link: Link): string; + attrs(obj: Attributes): string; } ``` @@ -445,31 +455,41 @@ With parts renderers `generateRenderContent`, `renderHeadContent`, `renderBodyCo ```js import express from 'express'; -import {createRenderFunction} from '@gravity-ui/app-layout'; +import htmlescape from 'htmlescape'; +import { + generateRenderContent, + renderHeadContent, + renderBodyContent, + createDefaultPlugins, +} from '@gravity-ui/app-layout'; const app = express(); -const renderLayout = createRenderFunction(); - app.get('/', async function (req, res) { res.writeHead(200, { 'Content-Type': 'text/html', 'Transfer-Encoding': 'chunked', }); + const plugins = createDefaultPlugins({layout: {manifest: 'path/to/assets-manifest.json'}}); + const content = generateRenderContent(plugins, { title: 'Home page', }); const {htmlAttributes, helpers, bodyContent} = content; + const bodyAttributes = { + class: bodyContent.className.filter(Boolean).join(' '), + }; + res.write(` ${renderHeadContent(content)} - + ${renderBodyContent(content)} `); diff --git a/src/render.ts b/src/render.ts index 72c2b93..48774f9 100644 --- a/src/render.ts +++ b/src/render.ts @@ -9,14 +9,18 @@ export function createRenderFunction(plugins?: Plugins const {htmlAttributes, helpers, bodyContent} = content; + const bodyAttributes = { + class: bodyContent.className.filter(Boolean).join(' '), + }; + return ` ${renderHeadContent(content)} - - ${renderBodyContent(content)} + + ${renderBodyContent(bodyContent)} `.trim(); diff --git a/src/types.ts b/src/types.ts index 61d762a..85aa82b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,6 +17,7 @@ export interface Link { href: string; rel?: string; type?: string; + sizes?: string; title?: HTMLLinkElement['title']; crossOrigin?: '' | 'anonymous' | 'use-credentials'; hreflang?: HTMLLinkElement['hreflang']; @@ -36,26 +37,27 @@ export interface CommonOptions { isMobile?: boolean; } +export interface HeadContent { + scripts: Script[]; + helpers: RenderHelpers; + links: Link[]; + meta: Meta[]; + styleSheets: Stylesheet[]; + inlineStyleSheets: string[]; + inlineScripts: string[]; + title: string; +} + export interface BodyContent { - attributes: Record; className: string[]; beforeRoot: string[]; root?: string; afterRoot: string[]; } -export interface RenderContent { - htmlAttributes: Record; - meta: Meta[]; - links: Link[]; - scripts: Script[]; - styleSheets: Stylesheet[]; - inlineScripts: string[]; - inlineStyleSheets: string[]; +export interface RenderContent extends HeadContent { + htmlAttributes: Attributes; bodyContent: BodyContent; - helpers: RenderHelpers; - icon: Icon; - title: string; } export interface RenderHelpers { @@ -73,12 +75,12 @@ export interface Plugin { options: Options | undefined; renderContent: RenderContent; commonOptions: CommonOptions; + /** @deprecated use `renderContent.helpers` instead */ utils: RenderHelpers; }) => void; } export interface RenderParams extends CommonOptions { data?: Data; - skipRenderDataScript?: boolean; icon?: Icon; nonce?: string; // content diff --git a/src/utils/generateRenderContent.ts b/src/utils/generateRenderContent.ts index 2db1bcf..0838077 100644 --- a/src/utils/generateRenderContent.ts +++ b/src/utils/generateRenderContent.ts @@ -52,9 +52,7 @@ export function generateRenderContent( const inlineScripts = params.inlineScripts || []; const links = params.links || []; - if (!params.skipRenderDataScript) { - inlineScripts.unshift(`window.__DATA__ = ${htmlescape(params.data || {})};`); - } + inlineScripts.unshift(`window.__DATA__ = ${htmlescape(params.data || {})};`); const content = params.bodyContent ?? {}; const {theme, className} = content; @@ -62,9 +60,6 @@ export function generateRenderContent( new Set([...getRootClassName(theme), ...(className ? className.split(' ') : [])]), ); const bodyContent: BodyContent = { - attributes: { - class: bodyClasses.filter(Boolean).join(' '), - }, className: bodyClasses, root: content.root, beforeRoot: content.beforeRoot ? [content.beforeRoot] : [], @@ -76,6 +71,10 @@ export function generateRenderContent( ...params.icon, }; + if (icon.href) { + links.unshift({rel: 'icon', type: icon.type, sizes: icon.sizes, href: icon.href}); + } + const {lang, isMobile, title, pluginsOptions = {}} = params; for (const plugin of plugins ?? []) { plugin.apply({ @@ -92,7 +91,6 @@ export function generateRenderContent( inlineScripts, bodyContent, helpers, - icon, title, }, commonOptions: {title, lang, isMobile}, @@ -114,7 +112,6 @@ export function generateRenderContent( inlineScripts, bodyContent, helpers, - icon, title, }; } diff --git a/src/utils/renderBodyContent.ts b/src/utils/renderBodyContent.ts index 0236f37..e2ea854 100644 --- a/src/utils/renderBodyContent.ts +++ b/src/utils/renderBodyContent.ts @@ -1,13 +1,11 @@ -import type {RenderContent} from '../types.js'; - -export function renderBodyContent(content: RenderContent): string { - const {bodyContent} = content; +import type {BodyContent} from '../types.js'; +export function renderBodyContent(content: BodyContent): string { return ` - ${bodyContent.beforeRoot.join('\n')} + ${content.beforeRoot.join('\n')}
- ${bodyContent.root ?? ''} + ${content.root ?? ''}
- ${bodyContent.afterRoot.join('\n')} + ${content.afterRoot.join('\n')} `.trim(); } diff --git a/src/utils/renderHeadContent.ts b/src/utils/renderHeadContent.ts index 7b393a7..926760d 100644 --- a/src/utils/renderHeadContent.ts +++ b/src/utils/renderHeadContent.ts @@ -1,22 +1,12 @@ -import type {RenderContent} from '../types.js'; +import type {HeadContent} from '../types.js'; -export function renderHeadContent(content: RenderContent): string { - const { - icon, - scripts, - helpers, - links, - meta, - styleSheets, - inlineStyleSheets, - inlineScripts, - title, - } = content; +export function renderHeadContent(content: HeadContent): string { + const {scripts, helpers, links, meta, styleSheets, inlineStyleSheets, inlineScripts, title} = + content; return ` ${title} - ${[ ...scripts.map(({src, crossOrigin}) => helpers.renderLink({href: src, crossOrigin, rel: 'preload', as: 'script'}), From 5b1ca8318a15d9a66562481794dfb3f1b913e5e8 Mon Sep 17 00:00:00 2001 From: Vladimir Mozgovoy Date: Mon, 18 Mar 2024 09:08:01 +0100 Subject: [PATCH 3/5] fix: additional code review fixes --- README.md | 8 +++----- src/types.ts | 6 +++++- src/utils/generateRenderContent.ts | 3 +++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 80d9e2a..d54aa17 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,8 @@ export interface HeadContent { } export interface BodyContent { + attributes: Attributes; + /** @deprecated use attributes.class instead */ className: string[]; beforeRoot: string[]; root?: string; @@ -479,17 +481,13 @@ app.get('/', async function (req, res) { const {htmlAttributes, helpers, bodyContent} = content; - const bodyAttributes = { - class: bodyContent.className.filter(Boolean).join(' '), - }; - res.write(` ${renderHeadContent(content)} - + ${renderBodyContent(content)} `); diff --git a/src/types.ts b/src/types.ts index 85aa82b..56f9d92 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,12 +49,16 @@ export interface HeadContent { } export interface BodyContent { + attributes: Attributes; + /** @deprecated use attributes.class instead */ className: string[]; beforeRoot: string[]; root?: string; afterRoot: string[]; } +export type OldBodyContent = Omit; + export interface RenderContent extends HeadContent { htmlAttributes: Attributes; bodyContent: BodyContent; @@ -73,7 +77,7 @@ export interface Plugin { name: Name; apply: (params: { options: Options | undefined; - renderContent: RenderContent; + renderContent: Omit & {bodyContent: OldBodyContent}; commonOptions: CommonOptions; /** @deprecated use `renderContent.helpers` instead */ utils: RenderHelpers; diff --git a/src/utils/generateRenderContent.ts b/src/utils/generateRenderContent.ts index 0838077..a375cff 100644 --- a/src/utils/generateRenderContent.ts +++ b/src/utils/generateRenderContent.ts @@ -60,6 +60,9 @@ export function generateRenderContent( new Set([...getRootClassName(theme), ...(className ? className.split(' ') : [])]), ); const bodyContent: BodyContent = { + attributes: { + class: bodyClasses.filter(Boolean).join(' '), + }, className: bodyClasses, root: content.root, beforeRoot: content.beforeRoot ? [content.beforeRoot] : [], From daf635f26afc65c755644290a40a18d78148735d Mon Sep 17 00:00:00 2001 From: Vladimir Mozgovoy Date: Mon, 18 Mar 2024 09:15:34 +0100 Subject: [PATCH 4/5] fix: add body attributes after plugins --- src/utils/generateRenderContent.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/utils/generateRenderContent.ts b/src/utils/generateRenderContent.ts index a375cff..39f326d 100644 --- a/src/utils/generateRenderContent.ts +++ b/src/utils/generateRenderContent.ts @@ -60,9 +60,7 @@ export function generateRenderContent( new Set([...getRootClassName(theme), ...(className ? className.split(' ') : [])]), ); const bodyContent: BodyContent = { - attributes: { - class: bodyClasses.filter(Boolean).join(' '), - }, + attributes: {}, className: bodyClasses, root: content.root, beforeRoot: content.beforeRoot ? [content.beforeRoot] : [], @@ -101,6 +99,11 @@ export function generateRenderContent( }); } + bodyContent.attributes = { + ...bodyContent.attributes, + class: bodyContent.className.filter(Boolean).join(' '), + }; + if (lang) { htmlAttributes.lang = lang; } From b8c9df002a33c464d562190e32d87d019df5b6cd Mon Sep 17 00:00:00 2001 From: Vladimir Mozgovoy Date: Mon, 18 Mar 2024 09:58:56 +0100 Subject: [PATCH 5/5] fix: attributes bug fix --- src/render.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/render.ts b/src/render.ts index 48774f9..44a437b 100644 --- a/src/render.ts +++ b/src/render.ts @@ -9,17 +9,13 @@ export function createRenderFunction(plugins?: Plugins const {htmlAttributes, helpers, bodyContent} = content; - const bodyAttributes = { - class: bodyContent.className.filter(Boolean).join(' '), - }; - return ` ${renderHeadContent(content)} - + ${renderBodyContent(bodyContent)}