import { Node , INode , HTMLElement , Element , IElement , DocumentType , IShadowRoot , SelfClosingElements , UnclosedElements } from 'happy-dom' ; import ShadowRootRenderer from './shadow-root/ShadowRootRenderer' ; import IHappyDOMServerRenderOptions from './IHappyDOMServerRenderOptions' ; import HappyDOMServerRenderResult from './HappyDOMServerRenderResult' ; import { encode } from 'he' ; class export default class HappyDOMServerRenderer { private renderOptions : IHappyDOMServerRenderOptions ; private shadowRootRenderer : ShadowRootRenderer ; param renderOptions constructor ( renderOptions = { } ) { this . renderOptions = renderOptions ; this . shadowRootRenderer = new ShadowRootRenderer ( renderOptions ) ; } param node param root returns public render ( root ) { return { html : this . serializeToString ( root ) , css : this . renderOptions . openShadowRoots ? this . shadowRootRenderer . getScopedCSS ( ) : [ ] } ; } param element param root returns private serializeToString ( root ) { switch ( root . nodeType ) { case Node . ELEMENT_NODE : const element = < HTMLElement > root ; const tagName = element . tagName . toLowerCase ( ) ; if ( UnclosedElements . includes ( tagName ) ) { return `<${ tagName }${ this . _getAttributes ( element ) }>` ; } else if ( SelfClosingElements . includes ( tagName ) ) { return `<${ tagName }${ this . _getAttributes ( element ) }/>` ; } let innerElement = < IElement | IShadowRoot > element ; let outerElement = < IElement > element ; let innerHTML = '' ; if ( this . renderOptions . openShadowRoots && element . shadowRoot ) { outerElement = this . shadowRootRenderer . getScopedClone ( element ) ; innerElement = outerElement . shadowRoot ; } for ( const node of innerElement . childNodes ) { innerHTML += this . render ( node ) . html ; } return `<${ tagName }${ this . _getAttributes ( outerElement ) }>${ innerHTML }</${ tagName }>` ; case Node . DOCUMENT_FRAGMENT_NODE : case Node . DOCUMENT_NODE : let html = '' ; for ( const node of root . childNodes ) { html += this . serializeToString ( node ) ; } return html ; case Node . COMMENT_NODE : return `<!--${ root . textContent }-->` ; case Node . TEXT_NODE : return root [ 'textContent' ] ; case Node . DOCUMENT_TYPE_NODE : const doctype = < DocumentType > root ; const identifier = doctype . publicId ? ' PUBLIC' : doctype . systemId ? ' SYSTEM' : '' ; const publicId = doctype . publicId ? ` "${ doctype . publicId }"` : '' ; const systemId = doctype . systemId ? ` "${ doctype . systemId }"` : '' ; return `<!DOCTYPE ${ doctype . name }${ identifier }${ publicId }${ systemId }>` ; } return '' ; } param element returns private _getAttributes ( element ) { const attributes = [ ] ; for ( const attribute of Object . values ( ( < Element > element ) . _attributes ) ) { if ( attribute . value !== null ) { attributes . push ( attribute . name + '="' + encode ( attribute . value ) + '"' ) ; } } return attributes . length > 0 ? ' ' + attributes . join ( ' ' ) : '' ; } }