UNPKG

@mitre/nuxt-smartscript

Version:

Smart typography transformations for Nuxt - automatic superscript, subscript, and symbol formatting

197 lines (196 loc) 6.03 kB
import { logger } from "./logger.js"; import { PatternExtractors, PatternMatchers } from "./patterns.js"; const textResultCache = /* @__PURE__ */ new Map(); const MAX_CACHE_SIZE = 1e3; function getCachedOrProcess(text, pattern) { const cacheKey = `${text}::${pattern.source}`; const cached = textResultCache.get(cacheKey); if (cached) { logger.trace("Cache hit for text:", text.substring(0, 20)); return cached; } const result = processTextInternal(text, pattern); if (textResultCache.size >= MAX_CACHE_SIZE) { const firstKey = textResultCache.keys().next().value; if (firstKey !== void 0) { textResultCache.delete(firstKey); } } textResultCache.set(cacheKey, result); return result; } export function processMatch(matched) { logger.debug("processMatch called with:", matched); if (PatternMatchers.isTrademark(matched)) { logger.debug("Trademark match confirmed for:", matched); return { modified: true, parts: [{ type: "super", content: "\u2122", subtype: "trademark" }] }; } if (PatternMatchers.isRegistered(matched)) { logger.debug("Registered match confirmed for:", matched); return { modified: true, parts: [{ type: "super", content: "\xAE", subtype: "registered" }] }; } if (PatternMatchers.isCopyright(matched)) { logger.debug("Copyright match confirmed for:", matched); return { modified: true, parts: [{ type: "text", content: "\xA9" }] }; } if (PatternMatchers.isOrdinal(matched)) { const ordinal = PatternExtractors.extractOrdinal(matched); if (ordinal) { const num = Number.parseInt(ordinal.number, 10); const lastDigit = num % 10; const lastTwoDigits = num % 100; let expectedSuffix; if (lastTwoDigits >= 11 && lastTwoDigits <= 13) { expectedSuffix = "th"; } else if (lastDigit === 1) { expectedSuffix = "st"; } else if (lastDigit === 2) { expectedSuffix = "nd"; } else if (lastDigit === 3) { expectedSuffix = "rd"; } else { expectedSuffix = "th"; } if (ordinal.suffix === expectedSuffix) { logger.debug("Ordinal match confirmed:", matched, "\u2192", ordinal); return { modified: true, parts: [ { type: "text", content: ordinal.number }, { type: "super", content: ordinal.suffix, subtype: "ordinal" } ] }; } else { logger.trace("Invalid ordinal suffix:", matched); return { modified: false, parts: [{ type: "text", content: matched }] }; } } } if (PatternMatchers.isChemicalParentheses(matched)) { const number = PatternExtractors.extractChemicalParentheses(matched); if (number) { logger.debug("Chemical parentheses match:", matched, "\u2192", number); return { modified: true, parts: [ { type: "text", content: ")" }, { type: "sub", content: number, subtype: "chemical" } ] }; } } if (PatternMatchers.isChemicalElement(matched)) { const chemical = PatternExtractors.extractChemicalElement(matched); if (chemical) { logger.debug("Chemical element match:", matched, "\u2192", chemical); return { modified: true, parts: [ { type: "text", content: chemical.element }, { type: "sub", content: chemical.count, subtype: "chemical" } ] }; } } if (PatternMatchers.isMathSuperscript(matched)) { const parts = PatternExtractors.extractMathWithVariable(matched); if (parts) { logger.debug("Math superscript match:", matched, "\u2192", parts); return { modified: true, parts: [ { type: "text", content: parts.variable }, { type: "super", content: parts.script, subtype: "math" } ] }; } } if (PatternMatchers.isMathSubscript(matched)) { const parts = PatternExtractors.extractMathWithVariable(matched); if (parts) { logger.debug("Math subscript match:", matched, "\u2192", parts); return { modified: true, parts: [ { type: "text", content: parts.variable }, { type: "sub", content: parts.script, subtype: "math" } ] }; } } logger.trace("No transformation for:", matched); return { modified: false, parts: [{ type: "text", content: matched }] }; } function processTextInternal(text, pattern) { const parts = []; let lastIndex = 0; let match; const execPattern = new RegExp(pattern.source, pattern.flags); while ((match = execPattern.exec(text)) !== null) { const matchedText = match[0]; if (/^H[1-6]$/.test(matchedText)) { const nextCharIndex = match.index + matchedText.length; const nextChar = text[nextCharIndex]; if (!nextChar || !/[A-Z]/.test(nextChar)) { logger.trace("Skipping standalone H1-H6 pattern:", matchedText); execPattern.lastIndex = match.index + 1; continue; } } if (match.index > lastIndex) { parts.push({ type: "text", content: text.substring(lastIndex, match.index) }); } const result = processMatch(matchedText); logger.trace("processMatch returned:", result); parts.push(...result.parts); lastIndex = match.index + match[0].length; } if (lastIndex < text.length) { parts.push({ type: "text", content: text.substring(lastIndex) }); } logger.trace("processText returning parts:", parts); return parts; } export function processText(text, pattern) { return getCachedOrProcess(text, pattern); } export function needsProcessing(text, pattern) { const testPattern = new RegExp(pattern.source, pattern.flags); return testPattern.test(text); } export function clearProcessingCaches() { textResultCache.clear(); logger.debug("Processing caches cleared"); }