File size: 5,299 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
import { TAG_NAMES as $, NS, hasUnescapedText } from '../common/html.js';
import { escapeText, escapeAttribute } from 'entities/lib/escape.js';
import { defaultTreeAdapter } from '../tree-adapters/default.js';
// Sets
const VOID_ELEMENTS = new Set([
$.AREA,
$.BASE,
$.BASEFONT,
$.BGSOUND,
$.BR,
$.COL,
$.EMBED,
$.FRAME,
$.HR,
$.IMG,
$.INPUT,
$.KEYGEN,
$.LINK,
$.META,
$.PARAM,
$.SOURCE,
$.TRACK,
$.WBR,
]);
function isVoidElement(node, options) {
return (options.treeAdapter.isElementNode(node) &&
options.treeAdapter.getNamespaceURI(node) === NS.HTML &&
VOID_ELEMENTS.has(options.treeAdapter.getTagName(node)));
}
const defaultOpts = { treeAdapter: defaultTreeAdapter, scriptingEnabled: true };
/**
* Serializes an AST node to an HTML string.
*
* @example
*
* ```js
* const parse5 = require('parse5');
*
* const document = parse5.parse('<!DOCTYPE html><html><head></head><body>Hi there!</body></html>');
*
* // Serializes a document.
* const html = parse5.serialize(document);
*
* // Serializes the <html> element content.
* const str = parse5.serialize(document.childNodes[1]);
*
* console.log(str); //> '<head></head><body>Hi there!</body>'
* ```
*
* @param node Node to serialize.
* @param options Serialization options.
*/
export function serialize(node, options) {
const opts = { ...defaultOpts, ...options };
if (isVoidElement(node, opts)) {
return '';
}
return serializeChildNodes(node, opts);
}
/**
* Serializes an AST element node to an HTML string, including the element node.
*
* @example
*
* ```js
* const parse5 = require('parse5');
*
* const document = parse5.parseFragment('<div>Hello, <b>world</b>!</div>');
*
* // Serializes the <div> element.
* const html = parse5.serializeOuter(document.childNodes[0]);
*
* console.log(str); //> '<div>Hello, <b>world</b>!</div>'
* ```
*
* @param node Node to serialize.
* @param options Serialization options.
*/
export function serializeOuter(node, options) {
const opts = { ...defaultOpts, ...options };
return serializeNode(node, opts);
}
function serializeChildNodes(parentNode, options) {
let html = '';
// Get container of the child nodes
const container = options.treeAdapter.isElementNode(parentNode) &&
options.treeAdapter.getTagName(parentNode) === $.TEMPLATE &&
options.treeAdapter.getNamespaceURI(parentNode) === NS.HTML
? options.treeAdapter.getTemplateContent(parentNode)
: parentNode;
const childNodes = options.treeAdapter.getChildNodes(container);
if (childNodes) {
for (const currentNode of childNodes) {
html += serializeNode(currentNode, options);
}
}
return html;
}
function serializeNode(node, options) {
if (options.treeAdapter.isElementNode(node)) {
return serializeElement(node, options);
}
if (options.treeAdapter.isTextNode(node)) {
return serializeTextNode(node, options);
}
if (options.treeAdapter.isCommentNode(node)) {
return serializeCommentNode(node, options);
}
if (options.treeAdapter.isDocumentTypeNode(node)) {
return serializeDocumentTypeNode(node, options);
}
// Return an empty string for unknown nodes
return '';
}
function serializeElement(node, options) {
const tn = options.treeAdapter.getTagName(node);
return `<${tn}${serializeAttributes(node, options)}>${isVoidElement(node, options) ? '' : `${serializeChildNodes(node, options)}</${tn}>`}`;
}
function serializeAttributes(node, { treeAdapter }) {
let html = '';
for (const attr of treeAdapter.getAttrList(node)) {
html += ' ';
if (!attr.namespace) {
html += attr.name;
}
else
switch (attr.namespace) {
case NS.XML: {
html += `xml:${attr.name}`;
break;
}
case NS.XMLNS: {
if (attr.name !== 'xmlns') {
html += 'xmlns:';
}
html += attr.name;
break;
}
case NS.XLINK: {
html += `xlink:${attr.name}`;
break;
}
default: {
html += `${attr.prefix}:${attr.name}`;
}
}
html += `="${escapeAttribute(attr.value)}"`;
}
return html;
}
function serializeTextNode(node, options) {
const { treeAdapter } = options;
const content = treeAdapter.getTextNodeContent(node);
const parent = treeAdapter.getParentNode(node);
const parentTn = parent && treeAdapter.isElementNode(parent) && treeAdapter.getTagName(parent);
return parentTn &&
treeAdapter.getNamespaceURI(parent) === NS.HTML &&
hasUnescapedText(parentTn, options.scriptingEnabled)
? content
: escapeText(content);
}
function serializeCommentNode(node, { treeAdapter }) {
return `<!--${treeAdapter.getCommentNodeContent(node)}-->`;
}
function serializeDocumentTypeNode(node, { treeAdapter }) {
return `<!DOCTYPE ${treeAdapter.getDocumentTypeNodeName(node)}>`;
}
//# sourceMappingURL=index.js.map |