UNPKG

infuse.host

Version:

Infuse your HTML with dynamic content.

99 lines (83 loc) 3.19 kB
import crypto from 'crypto'; import domino from 'domino'; // Default doctype. const DEFAULT_DOCTYPE = '<!DOCTYPE html>'; /** * Gets the DOCTYPE string of a document. Returns `null` if the document has no DTD: * https://developer.mozilla.org/en-US/docs/Web/API/document/doctype * Inspired by https://stackoverflow.com/a/10162353/588283 * * @function getDoctype * @param {Document} document The document object. * @returns {string} The document's DOCTYPE string or `null` if it doesn't have one. */ export const getDoctype = function getDoctypeString(document) { const type = document.doctype; if (type === null) { return null; } let specs = type.name; if (type.publicId) { specs += ` PUBLIC "${ type.publicId }"`; } if (!type.publicId && type.systemId) { specs += ' SYSTEM'; } if (type.systemId) { specs += ` "${ type.systemId }"`; } return `<!DOCTYPE ${ specs }>`; }; /** * Parses a HTML document. An MD5 hash is alse generated using the provided `htmlDocument`. * * @function parseDocument * @param {(string|Document)} htmlDocument The HTML source code to parse or a parsed `Document`. * @returns {Object} An object with the following attributes: * * `document`: The parsed document. * * `doctype`: The document's DOCTYPE string. * * `window`: The window used to parse the document. * * `hash`: The first few characters of the MD5 hash of the provided `htmlDocument` */ export default function parseDocument(htmlDocument) { let document, doctype, html, window; if (typeof htmlDocument === 'string') { // If the `htmlDocument` is a string, use domino to parse it into a document. html = htmlDocument.trim(); // If `html` has a DOCTYPE, remove it and assign it to `doctype`. if (html.substr(0, 10).toUpperCase() === '<!DOCTYPE ') { const i = html.indexOf('>', 10); doctype = html.substring(0, i + 1); html = html.substr(i + 1).trim(); } else { doctype = DEFAULT_DOCTYPE; } // If `html` doesn't have a <html> and a <body>, put it inside a <body> element. if (html.indexOf('<html') === -1 && html.indexOf('<body') === -1) { /** * If `html` doesn't have a <body> and doesn't start with a <template> assume that the * entire HTML code is meant to be inside a single <template> element. */ if (html.substr(0, 9).toLowerCase() !== '<template') { html = `<template>${ html }</template>`; } html = `<body>${ html }</body>`; } // Parse `html` into a `document`. document = domino.createDocument(`${ doctype }\n${ html }`); // Use the original `htmlDocument` to calculate the hash below. html = htmlDocument; // Use domino's implementation of the window interface as `window`. window = domino.impl; } else { // If `htmlDocument` is NOT a string, assume it's an instance of `Document`. document = htmlDocument; window = document.defaultView; doctype = getDoctype(document) || DEFAULT_DOCTYPE; html = `${ doctype }\n${ document.documentElement.outerHTML }`; } // Calculate a hash of `html`. const hash = crypto.createHash('md5').update(html).digest('hex'); // eslint-disable-next-line object-curly-newline return { doctype, document, hash, window }; }