@mitre/nuxt-smartscript
Version:
Smart typography transformations for Nuxt - automatic superscript, subscript, and symbol formatting
197 lines (196 loc) • 6.03 kB
JavaScript
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");
}