// Ported from nanohtml 1.10.0 lib/server.js (MIT) — https://github.com/choojs/nanohtml // Server-side tagged template: renders straight to an escaped HTML string. // The browserify/babel transform branches of the original are gone for good — // this is a pure runtime tag. const BOOL_PROPS = [ 'async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defaultchecked', 'defer', 'disabled', 'formnovalidate', 'hidden', 'ismap', 'loop', 'multiple', 'muted', 'novalidate', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'selected' ] const boolPropRx = new RegExp('([^-a-z](' + BOOL_PROPS.join('|') + '))=["\']?$', 'i') const query = /(?:="|&)[^"]*=$/ export default function html (pieces, ...values) { let boolMatch let output = '' for (let i = 0; i < pieces.length; i++) { const piece = pieces[i] if (i < pieces.length - 1) { if ((boolMatch = boolPropRx.exec(piece))) { output += piece.slice(0, boolMatch.index) if (values[i]) { output += boolMatch[1] + '="' + boolMatch[2] + '"' } continue } const value = handleValue(values[i]) if (piece[piece.length - 1] === '=' && !query.test(piece)) { output += piece + '"' + value + '"' } else { output += piece + value } } else { output += piece } } // HACK: Avoid double encoding by marking encoded string // You cannot add properties to string literals // eslint-disable-next-line no-new-wrappers const wrapper = new String(output) wrapper.__encoded = true return wrapper } function handleValue (value) { // Handle each item in array as potential unescaped value if (Array.isArray(value)) return value.map(handleValue).join('') // Ignore event handlers. `onclick=${(e) => doSomething(e)}` // will become. `onclick=""` if (typeof value === 'function') return '' if (value === null || value === undefined) return '' if (value.__encoded) return value if (typeof value === 'object') { if (typeof value.outerHTML === 'string') return value.outerHTML return Object.keys(value).reduce(function (str, key) { if (str.length > 0) str += ' ' if (BOOL_PROPS.indexOf(key) !== -1) { if (value[key]) { return str + key + '="' + key + '"' } return str } const handled = handleValue(value[key]) return str + key + '="' + handled + '"' }, '') } const str = value.toString() return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') } export { html }