UNPKG

@polight/lego

Version:

Tiny Web-Components lib for future-proof HTML mentors

78 lines (70 loc) 2.74 kB
import { parseFragment } from 'parse5' import { isNativeEvent } from './native-events.js' function vnode(name, attributes = {}, children = '', indent = '') { const attrs = Object.keys(attributes).reduce((acc, name) => { acc.push(`"${name}": ${attributes[name]}`) return acc }, []) return `${indent}h("${name}", {${attrs.join(', ')}}, ${children})` } function cleanChildren(children = []) { return children.filter(c => { if(!c.nodeName.startsWith('#')) return true return c.value && c.value.trim() }) } function extractDirectives(node) { const directives = [] node.attrs = node.attrs.reduce((attrs, attr) => { let name = attr.name let value = attr.value if(name === ':for') directives.push({ name: 'for', value }) else if(name === ':if') directives.push({ name: 'if', value }) else if(name.startsWith(':')) { name = name.slice(1) attrs.push({ name, value }) } else if(name.startsWith('@')) { const onEventName = `on${name.slice(1)}` if (isNativeEvent(onEventName)) name = onEventName value = `this.${value}.bind(this)` attrs.push({ name, value }) } else attrs.push({ name, value: `\`${value}\`` }) return attrs }, []) return [node, directives] } function wrapDirectives(directives, content, indent = '') { directives.reverse().forEach(directive => { if(directive.name === 'if') return content = `((${directive.value}) ? ${content} : '')` if(directive.name === 'for') { const [_, item, items] = directive.value.match(/\s*(.*)\s+in\s+(.*)\s*/) return content = `((${items}).map((${item}) => (${content})))` } }) return indent + content } function convert(node, indentSize = 0) { const indent = ' '.repeat(indentSize) if(node.nodeName === '#text') { return node.value.trim() ? `\`${node.value}\`` : '' } if(node.nodeName === '#document-fragment') { return `[${indent}\n${cleanChildren(node.childNodes).map(c => convert(c, indentSize + 2)).join(',\n')}]` } let directives [node, directives] = extractDirectives(node) const attributes = node.attrs.reduce((attrs, attr) => Object.assign(attrs, {[attr.name]: attr.value }), {}) const children = node.childNodes ? cleanChildren(node.childNodes).map(c => convert(c, indent + 4)).join(',\n') : '' let childrenIndent if(!node.childNodes || node.childNodes.length === 0) childrenIndent = JSON.stringify('') else if(node.childNodes.length === 1) childrenIndent = children else childrenIndent = `[\n${children}\n]` return wrapDirectives(directives, vnode(node.nodeName, attributes, childrenIndent), indent) } function parse(html) { const document = parseFragment(html) return convert(document) } export default parse