'use strict'; const index = (options = {}) => (tree) => { options.attributes = options.attributes || ["prevent-widows", "no-widows"]; options.minWords = options.minWords || 4; options.createWidows = options.createWidows || false; options.ignore = options.ignore || [ { start: "{{", end: "}}" }, // Handlebars, Liquid, Nunjucks, Twig, Jinja2, Mustache { start: "{%", end: "%}" }, // Liquid, Nunjucks, Twig, Jinja2 { start: "<%=", end: "%>" }, // EJS, ERB { start: "<%", end: "%>" }, // EJS, ERB { start: "{$", end: "}" }, // Smarty { start: "<\\?", end: "\\?>" }, // PHP { start: "#{", end: "}" } // Pug ]; const process = (node, shouldReplace = false) => { let replace = shouldReplace; if (typeof node === "string") { let parts = [node]; for (const pattern of options.ignore) { const regex = new RegExp(`(${pattern.start}.*?${pattern.end})`, "g"); parts = parts.flatMap((part) => part.split(regex)); } const processedParts = parts.map((part) => { for (const pattern of options.ignore) { const ignorePattern = new RegExp(`^${pattern.start}.*?${pattern.end}$`); if (ignorePattern.test(part)) { return part; } } const partWords = part.trim().split(/\s+/); if (replace && partWords.length >= options.minWords) { return options.createWidows ? part.replace(/ ([^ ]+)$/g, " $1") : part.replace(/ ([^ ]+)$/gm, " $1"); } return part; }); return processedParts.join(""); } if (node.tag && node.attrs) { for (const attr of options.attributes) { if (attr in node.attrs) { delete node.attrs[attr]; replace = true; break; } } } if (Array.isArray(node.content)) { node.content = node.content.map((child) => process(child, replace)); } return node; }; return tree.walk(process); }; module.exports = index;