@jackdbd/eleventy-plugin-text-to-speech
Version:
Eleventy plugin for the Google Cloud Text-to-Speech API
158 lines • 7.17 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.insertAudioPlayersMatchingXPathExpression = exports.insertAudioPlayersMatchingCssSelector = exports.defaultAudioInnerHTML = exports.xPathExpressionMatchesToTexts = exports.cssSelectorMatchesToTexts = void 0;
const node_path_1 = __importDefault(require("node:path"));
const debug_1 = __importDefault(require("debug"));
const html_to_text_1 = require("html-to-text");
const constants_js_1 = require("./constants.js");
const utils_js_1 = require("./utils.js");
const debug = (0, debug_1.default)(`${constants_js_1.DEBUG_PREFIX}/dom`);
const cssSelectorMatchesToTexts = ({ dom, selector, shouldThrowWhenNoMatches = false }) => {
const texts = [];
const elements = dom.window.document.querySelectorAll(selector);
if (elements.length === 0) {
const message = `dit NOT find any HTML that matches CSS selector ${selector}`;
if (shouldThrowWhenNoMatches) {
throw new Error(message);
}
else {
debug(message);
}
}
elements.forEach((el) => {
debug(`found HTML that matches CSS selector ${selector}`);
// el.ariaValueText
// el.textContent
// el.outerHTML
if (el.textContent === el.innerHTML) {
texts.push(el.textContent);
}
else {
// https://github.com/html-to-text/node-html-to-text#options
// TODO: maybe allow the user to specify options for html-to-text? Maybe a
// subset of the available options?
const text = (0, html_to_text_1.htmlToText)(el.innerHTML, {
selectors: [
{ selector: 'a', options: { ignoreHref: true } },
{ selector: 'ul', options: { itemPrefix: ' * ' } }
],
wordwrap: false
});
texts.push(text);
}
});
return texts;
};
exports.cssSelectorMatchesToTexts = cssSelectorMatchesToTexts;
const xPathExpressionMatchesToTexts = ({ dom, expression, shouldThrowWhenNoMatches = false }) => {
const doc = dom.window.document;
const texts = [];
const contextNode = doc;
const resolver = null;
// https://github.com/jsdom/jsdom/blob/04f6c13f4a4d387c7fc979b8f62c6f68d8a0c639/lib/jsdom/level3/xpath.js#L1765
const xPathResultType = dom.window.XPathResult.ANY_TYPE;
debug(`evalutate XPath expression ${expression} on page ${doc.title}`);
const xPathResult = doc.evaluate(expression, contextNode, resolver, xPathResultType, null);
const node = xPathResult.iterateNext();
if (node) {
// node.nodeName
// node.nodeType
if (node.textContent) {
debug(`found HTML that matches XPath expression ${expression} on page ${doc.title}`);
texts.push(node.textContent);
}
}
else {
const message = `dit NOT find any HTML that matches XPath expression ${expression} on page ${doc.title}`;
if (shouldThrowWhenNoMatches) {
throw new Error(message);
}
else {
debug(message);
}
}
return texts;
};
exports.xPathExpressionMatchesToTexts = xPathExpressionMatchesToTexts;
const defaultAudioInnerHTML = (hrefs) => {
const aTags = [];
const sourceTags = [];
hrefs.forEach((href) => {
const { error, value } = (0, utils_js_1.mediaType)(node_path_1.default.extname(href));
if (error && value) {
throw new Error(`You can't have both an error and a value`);
}
if (error) {
sourceTags.push(`<p>${error.message}. Here is a <a href="${href}">link to the audio</a> instead.</p>`);
}
if (value) {
aTags.push(`<a href="${href}" target="_blank">${href}</a>`);
sourceTags.push(`<source src=${href} type="${value}">`);
}
});
const p = `<p>Your browser doesn't support HTML5 <code>audio</code>. Here are the links to the audio files instead:</p>`;
const ul = `<ul>${aTags.map((a) => `<li>${a}</li>`).join('')}</ul>`;
const fallback = `${p}${ul}`;
return `${sourceTags.join('')}${fallback}`;
};
exports.defaultAudioInnerHTML = defaultAudioInnerHTML;
const insertAudioPlayersMatchingCssSelector = ({ audioInnerHTML, cssSelector, dom, hrefs }) => {
const doc = dom.window.document;
const elements = doc.querySelectorAll(cssSelector);
if (elements.length === 0) {
const message = `found no matches for the CSS selector ${cssSelector} on page ${doc.title}. No audio tags will be injected into the HTML`;
debug(message);
}
const position = 'afterend';
elements.forEach((el, i) => {
const innerHTML = `<audio controls>${audioInnerHTML(hrefs[i])}</audio>`;
debug(`insert player ${position} CSS selector ${cssSelector} match ${i}`);
el.insertAdjacentHTML(position, innerHTML);
});
};
exports.insertAudioPlayersMatchingCssSelector = insertAudioPlayersMatchingCssSelector;
const insertAudioPlayersMatchingXPathExpression = ({ audioInnerHTML, dom, expression, hrefs }) => {
const doc = dom.window.document;
const contextNode = doc;
const resolver = null;
// https://github.com/jsdom/jsdom/blob/04f6c13f4a4d387c7fc979b8f62c6f68d8a0c639/lib/jsdom/level3/xpath.js#L1765
const xPathResultType = dom.window.XPathResult.ANY_TYPE;
debug(`evalutate XPath expression ${expression} on page ${doc.title}`);
const xPathResult = doc.evaluate(expression, contextNode, resolver, xPathResultType, null);
let i = 0;
while (hrefs[i]) {
const node = xPathResult.iterateNext();
if (node) {
const audio = doc.createElement('audio');
audio.controls = true;
audio.innerHTML = audioInnerHTML(hrefs[i]);
if (node.parentNode) {
if (node.nextSibling) {
const message = `node that matches XPath expression ${expression} on page ${doc.title} has both a parent and a sibling`;
debug(message);
node.parentNode.insertBefore(audio, node.nextSibling);
}
else {
const message = `node that matches XPath expression ${expression} on page ${doc.title} has a parent but no sibling`;
debug(message);
node.parentNode.appendChild(audio);
}
}
else {
const message = `node that matches XPath expression ${expression} on page ${doc.title} has no`;
debug(message);
// maybe try inserting using previousSibling?
}
}
else {
const message = `found no matches for the XPath expression ${expression} on page ${doc.title}. No audio tags will be injected into the HTML`;
debug(message);
}
i++;
}
};
exports.insertAudioPlayersMatchingXPathExpression = insertAudioPlayersMatchingXPathExpression;
//# sourceMappingURL=dom.js.map