123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- 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 str = 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) {
- 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}`;
- }
- }
- }
- else {
- html += 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)}>`;
- }
|