UNPKG

@reactml/loader

Version:

Webpack loader for react markup language.

130 lines (113 loc) 3.02 kB
const { DomHandler, Parser } = require('htmlparser2'); const partsStorage = new Map(); function getDomObject(html) { const handler = new DomHandler(() => { }, { withStartIndices: true, withEndIndices: true, }); const parser = new Parser(handler, { xmlMode: true }); parser.parseComplete(html); return handler.dom; } function innerHTML(dom, html) { const { children } = dom; if (children.length > 0) { const firstChildStartIndex = children[0].startIndex; const lastChildEndIndex = children[children.length - 1].endIndex; return html.substring(firstChildStartIndex, lastChildEndIndex + 1).trim(); } else { return ''; } } function outerHTML(dom, html) { const { startIndex, endIndex } = dom; return html.substring(startIndex, endIndex + 1).trim(); } function getResourcePart(key, part, rawContent) { let parts; if (partsStorage.has(key)) { parts = partsStorage.get(key); } else { parts = parseSFCParts(rawContent); partsStorage.set(key, parts); } return selectPart(parts, part); } /** * enum Part { * template, * script, * style, * importLinks, * } */ function selectPart(parts, part) { return parts[part] || null; } function parseSFCParts(html) { const result = {}; const dom = getDomObject(html); const importLinks = []; const templates = []; let script = '', style = ''; dom.forEach(node => { const { type, name, children, attribs } = node; switch (name) { case 'import': const from = attribs.from; if (from !== undefined) { importLinks.push(attribs); } else { let tag = '<import'; for (let attr in attribs) tag += ` ${attr}="${attribs[attr]}"`; tag += ' />'; console.warn('[RML] import tag should have it\'s from attribute: ' + tag); } result.importLinks = importLinks; break; case 'style': case 'script': if (result[name]) { throw new Error(`RML can only have 1 ${name} tag exists.`); } else { result[name] = { type: name, content: innerHTML(node, html), attrs: attribs, }; } break; default: let template; // pure text node. if (type === 'text' && !attribs) { template = node.data.trim(); if (template) template = JSON.stringify(template); } else { template = outerHTML(node, html); } if (template) templates.push(template); break; } }); if (templates.length > 0) { result.template = { type: 'template', content: templates, attrs: {}, }; } return result; } function preCompileParts(html, key) { const parts = parseSFCParts(html); partsStorage.set(key, parts); return parts; } exports.getResourcePart = getResourcePart; exports.selectPart = selectPart; exports.parseSFCParts = parseSFCParts; exports.preCompileParts = preCompileParts;