You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

173 lines
5.8 KiB

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeOuter = exports.serialize = void 0;
const html_js_1 = require("../common/html.js");
const escape_js_1 = require("entities/lib/escape.js");
const default_js_1 = require("../tree-adapters/default.js");
// Sets
const VOID_ELEMENTS = new Set([
html_js_1.TAG_NAMES.AREA,
html_js_1.TAG_NAMES.BASE,
html_js_1.TAG_NAMES.BASEFONT,
html_js_1.TAG_NAMES.BGSOUND,
html_js_1.TAG_NAMES.BR,
html_js_1.TAG_NAMES.COL,
html_js_1.TAG_NAMES.EMBED,
html_js_1.TAG_NAMES.FRAME,
html_js_1.TAG_NAMES.HR,
html_js_1.TAG_NAMES.IMG,
html_js_1.TAG_NAMES.INPUT,
html_js_1.TAG_NAMES.KEYGEN,
html_js_1.TAG_NAMES.LINK,
html_js_1.TAG_NAMES.META,
html_js_1.TAG_NAMES.PARAM,
html_js_1.TAG_NAMES.SOURCE,
html_js_1.TAG_NAMES.TRACK,
html_js_1.TAG_NAMES.WBR,
]);
function isVoidElement(node, options) {
return (options.treeAdapter.isElementNode(node) &&
options.treeAdapter.getNamespaceURI(node) === html_js_1.NS.HTML &&
VOID_ELEMENTS.has(options.treeAdapter.getTagName(node)));
}
const defaultOpts = { treeAdapter: default_js_1.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.
*/
function serialize(node, options) {
const opts = Object.assign(Object.assign({}, defaultOpts), options);
if (isVoidElement(node, opts)) {
return '';
}
return serializeChildNodes(node, opts);
}
exports.serialize = serialize;
/**
* 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.
*/
function serializeOuter(node, options) {
const opts = Object.assign(Object.assign({}, defaultOpts), options);
return serializeNode(node, opts);
}
exports.serializeOuter = serializeOuter;
function serializeChildNodes(parentNode, options) {
let html = '';
// Get container of the child nodes
const container = options.treeAdapter.isElementNode(parentNode) &&
options.treeAdapter.getTagName(parentNode) === html_js_1.TAG_NAMES.TEMPLATE &&
options.treeAdapter.getNamespaceURI(parentNode) === html_js_1.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 html_js_1.NS.XML: {
html += `xml:${attr.name}`;
break;
}
case html_js_1.NS.XMLNS: {
if (attr.name !== 'xmlns') {
html += 'xmlns:';
}
html += attr.name;
break;
}
case html_js_1.NS.XLINK: {
html += `xlink:${attr.name}`;
break;
}
default: {
html += `${attr.prefix}:${attr.name}`;
}
}
html += `="${(0, escape_js_1.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) === html_js_1.NS.HTML &&
(0, html_js_1.hasUnescapedText)(parentTn, options.scriptingEnabled)
? content
: (0, escape_js_1.escapeText)(content);
}
function serializeCommentNode(node, { treeAdapter }) {
return `<!--${treeAdapter.getCommentNodeContent(node)}-->`;
}
function serializeDocumentTypeNode(node, { treeAdapter }) {
return `<!DOCTYPE ${treeAdapter.getDocumentTypeNodeName(node)}>`;
}
//# sourceMappingURL=index.js.map