@jackdbd/eleventy-plugin-text-to-speech
Version:
Eleventy plugin that uses text-to-speech to generate audio assets for your website, then injects audio players in your HTML.
102 lines • 3.99 kB
JavaScript
import { createHash } from 'node:crypto';
import defDebug from 'debug';
import { JSDOM } from 'jsdom';
import { z } from 'zod';
import { css_selector, xpath_expression } from '@jackdbd/zod-schemas/dom';
import { DEBUG_PREFIX } from './constants.js';
import { cssSelectorMatchesToTexts, xPathExpressionMatchesToTexts } from './dom/matchers.js';
import { text_to_synthesize } from './synthesis/schemas.js';
import { validatedResult } from './validation.js';
const debug = defDebug(`${DEBUG_PREFIX}:rule-match-record`);
export const match_object = z.object({
/**
* The text that matched one or more CSS selector or one or more XPath expression.
*/
text: text_to_synthesize,
/**
* All the CSS selectors that matched the text.
*/
cssSelectors: z.array(css_selector),
/**
* All the XPath expressions that matched the text.
*/
xPathExpressions: z.array(xpath_expression)
});
export const rule_match_record = z.record(match_object);
export const config_schema = z.object({
dom: z.instanceof(JSDOM),
cssSelectors: z.array(css_selector),
xPathExpressions: z.array(xpath_expression)
});
/**
* Creates a rule match record, namely a hash map where all the keys are content
* hashes of the matched texts, and the values are those texts that match one or
* more CSS selectors, or one or more XPath expressions.
*/
export const ruleMatchRecord = (config) => {
const res = validatedResult(config, config_schema);
if (res.error) {
return { error: res.error };
}
const { dom, cssSelectors, xPathExpressions } = res.value;
const doc = dom.window.document;
const rec = {};
const errors = [];
const cssMatches = [];
cssSelectors.forEach((selector) => {
const res = cssSelectorMatchesToTexts({ dom, selector });
if (res.error) {
errors.push(res.error);
}
else {
cssMatches.push({ selector, texts: res.value });
}
});
debug(`${cssMatches.length} CSS selector match/es on document '${doc.title}'`);
for (const cssMatch of cssMatches) {
const { selector, texts } = cssMatch;
debug(`CSS selector ${selector} matches ${texts.length} text/s`);
for (const text of texts) {
const md5 = createHash('md5');
const key = md5.update(text).digest('hex');
if (rec[key] && !rec[key].cssSelectors.includes(selector)) {
rec[key].cssSelectors.push(selector);
}
else {
rec[key] = { text, cssSelectors: [selector], xPathExpressions: [] };
}
}
}
const xPathMatches = [];
xPathExpressions.forEach((expression) => {
const res = xPathExpressionMatchesToTexts({ dom, expression });
if (res.error) {
errors.push(res.error);
}
else {
xPathMatches.push({ expression, texts: res.value });
}
});
debug(`${xPathMatches.length} XPath expression match/es on document title: ${doc.title})`);
for (const xPathMatch of xPathMatches) {
const { expression, texts } = xPathMatch;
debug(`XPath expression ${expression} matches ${texts.length} text/s`);
for (const text of texts) {
const md5 = createHash('md5');
const key = md5.update(text).digest('hex');
if (rec[key] && !rec[key].xPathExpressions.includes(expression)) {
rec[key].xPathExpressions.push(expression);
}
else {
rec[key] = { text, cssSelectors: [], xPathExpressions: [expression] };
}
}
}
if (errors.length > 0) {
const summary = `Encountered ${errors.length} error/s while processing document '${doc.title}'`;
const details = errors.map((e) => e.message);
return { error: new Error(`${summary}\n${details.join('\n')}`) };
}
return { value: rec };
};
//# sourceMappingURL=rule-match-record.js.map