UNPKG

@publiwrite/html-to-epub

Version:

A library to generate EPUB from HTML. Inspired by epub-gen.

87 lines (86 loc) 2.94 kB
import { visit } from 'unist-util-visit'; import { v4 as uuid } from 'uuid'; import mime from 'mime'; import { unified } from "unified"; import rehypeParse from "rehype-parse"; import rehypeStringify from "rehype-stringify"; const loadHtml = (content, plugins) => unified() .use(rehypeParse, { fragment: true }) .use(plugins) // Voids: [] is required for epub generation, and causes little/no harm for non-epub usage .use(rehypeStringify, { allowDangerousHtml: true, voids: [] }) .processSync(content) .toString(); const validateElements = (node, options, index) => { const attrs = node.properties; if (["img", "br", "hr"].includes(node.tagName)) { if (node.tagName === "img") { node.properties.alt = node.properties.alt || "image-placeholder"; } } for (const k of Object.keys(attrs)) { if (options.allowedAttributes.includes(k)) { if (k === "type") { if (attrs[k] !== "script") { delete node.properties[k]; } } } else { delete node.properties[k]; } } if (options.version === 2) { if (!options.allowedXhtml11Tags.includes(node.tagName)) { if (options.verbose) { console.log("Warning (content[" + index + "]):", node.tagName, "tag isn't allowed on EPUB 2/XHTML 1.1 DTD."); } node.tagName = "div"; } } }; const processImgTags = (node, options, dir) => { if (!["img", "input"].includes(node.tagName)) { return; } const url = node.properties.src; if (url === undefined || url === null) { return; } let extension, id; const image = options.images.find((element) => element.url === url); if (image) { id = image.id; extension = image.extension; } else { id = uuid(); const mediaType = mime.getType(url.replace(/\?.*/, "")); if (mediaType === null) { if (options.verbose) { console.error("[Image Error]", `The image can't be processed : ${url}`); } return; } extension = mime.getExtension(mediaType); if (extension === null) { if (options.verbose) { console.error("[Image Error]", `The image can't be processed : ${url}`); } return; } options.images.push({ id, url, dir, mediaType, extension }); } node.properties.src = `images/${id}.${extension}`; }; export const processHtmlContent = (content, options, dir) => { const html = loadHtml(content, [ () => (tree) => { visit(tree, "element", (node, index) => validateElements(node, options, index)); }, () => (tree) => { visit(tree, "element", (node) => processImgTags(node, options, dir)); }, ]); return html; };