@markedjs/html-differ
Version:
Compares two HTML
114 lines (97 loc) • 3.48 kB
JavaScript
import { SAXParser } from 'parse5-sax-parser';
import * as serialize from './serialize.js';
import * as utils from './utils.js';
/**
* Parses the given HTML and modifies it according to the given options
* @param {String} value
* @param {Object} [options]
* @param {String[]} [options.ignoreAttributes]
* @param {String[]} [options.compareAttributesAsJSON]
* @param {Boolean} [options.ignoreWhitespaces=true]
* @param {Boolean} [options.ignoreComments=true]
* @param {Boolean} [options.ignoreEndTags=false]
* @param {Boolean} [options.ignoreSelfClosingSlash=false]
* returns {String}
*/
export default function modify(value, options) {
return new Promise((resolve, reject) => {
const modifiedValues = [];
const parser = new SAXParser();
/**
* @param {object} doctypeToken
* @param {String} doctypeToken.name
* @param {String} doctypeToken.publicId
* @param {String} doctypeToken.systemId
*/
parser.on('doctype', function(doctypeToken) {
const name = doctypeToken.name;
const publicId = doctypeToken.publicId;
const systemId = doctypeToken.systemId;
modifiedValues.push(serialize.doctype(name, publicId, systemId));
});
/**
* @param {object} startTagToken
* @param {String} startTagToken.tagName
* @param {Array} startTagToken.attrs
* @param {Boolean} startTagToken.selfClosing
*/
parser.on('startTag', function(startTagToken) {
let attrs = startTagToken.attrs;
let selfClosing = startTagToken.selfClosing;
const tagName = startTagToken.tagName;
if (options.ignoreSelfClosingSlash) {
selfClosing = false;
}
attrs = utils.sortAttrs(attrs)
&& utils.sortCssClasses(attrs)
&& utils.sortAttrsValues(attrs, options.compareAttributesAsJSON)
&& utils.removeAttrsValues(attrs, options.ignoreAttributes);
modifiedValues.push(serialize.startTag(tagName, attrs, selfClosing));
});
/**
* @param {object} endTagToken
* @param {String} endTagToken.tagName
*/
parser.on('endTag', function(endTagToken) {
const tagName = endTagToken.tagName;
if (!options.ignoreEndTags) {
modifiedValues.push(serialize.endTag(tagName));
}
});
/**
* @param {object} textToken
* @param {String} textToken.text
*/
parser.on('text', function(textToken) {
let text = textToken.text;
if (options.ignoreWhitespaces) {
text = utils.removeWhitespaces(text);
}
modifiedValues.push(serialize.text(text));
});
/**
* @param {object} commentToken
* @param {String} commentToken.text
*/
parser.on('comment', function(commentToken) {
modifiedValues.push(new Promise(resolve => {
const comment = commentToken.text;
resolve(utils.getConditionalComment(comment, modify, options));
}).then(conditionalComment => {
if (conditionalComment) {
return serialize.comment(conditionalComment);
} else if (!options.ignoreComments) {
return serialize.comment(commentToken.text);
}
}));
});
parser.on('end', async function() {
const values = await Promise.all(modifiedValues);
resolve(values.join(''));
});
parser.on('error', function(err) {
reject(err);
});
parser.end(value);
});
}