UNPKG

cz-typography

Version:

Universal Czech typography fixer for JavaScript, React, Next.js, and any SSR framework. Non-breaking spaces after one-letter prepositions, units, dates, ordinals and more.

337 lines (324 loc) 9.61 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); // src/rules/dates.js var FULL_DATE_REGEX = /(\d{1,2})\.[ \t]+(\d{1,2})\.[ \t]+(\d{2,4})\b/g; var DAY_MONTH_REGEX = /(\d{1,2})\.[ \t]+(\d{1,2})\.(?=[^\p{L}\p{N}]|$)/gu; function applyDates(text) { return text.replace(FULL_DATE_REGEX, "$1.\xA0$2.\xA0$3").replace(DAY_MONTH_REGEX, "$1.\xA0$2."); } // src/rules/initials.js var INITIAL_REGEX = new RegExp("(\\b\\p{Lu})\\.[ \\t]+(?=\\p{Lu})", "gu"); function applyInitials(text) { let previous; let current = text; do { previous = current; current = current.replace(INITIAL_REGEX, "$1.\xA0"); } while (current !== previous); return current; } // src/rules/ordinals.js var ORDINAL_REGEX = new RegExp("(\\d{1,2})\\.[ \\t]+(?=\\p{Ll})", "gu"); function applyOrdinals(text) { return text.replace(ORDINAL_REGEX, "$1.\xA0"); } // src/data/prepositions.js var PREPOSITIONS = Object.freeze([ "a", "i", "k", "o", "s", "u", "v", "z" ]); // src/rules/prepositions.js var PREPOSITION_REGEX = new RegExp( `(?<=^|[\\s(\\["'\xA0\u2018\u201C\xBB\u2014\u2013-])(${PREPOSITIONS.join("|")})[ \\t]+(?=\\S)`, "giu" ); function applyPrepositions(text) { return text.replace(PREPOSITION_REGEX, "$1\xA0"); } // src/rules/roman.js var ROMAN_NUMERAL = "(?:(?=[IVXLCDM])M{0,3}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3}))"; var ROMAN_REGEX = new RegExp( `(\\b\\p{Lu}\\p{L}+)[ \\t]+(?=${ROMAN_NUMERAL}\\.(?=[^\\p{L}\\p{N}]|$))`, "gu" ); function applyRoman(text) { return text.replace(ROMAN_REGEX, "$1\xA0"); } // src/rules/thousands.js var THOUSANDS_REGEX = /(\d)[ \t](\d{3})(?=\D|$)/g; function applyThousands(text) { let previous; let current = text; do { previous = current; current = current.replace(THOUSANDS_REGEX, "$1\xA0$2"); } while (current !== previous); return current; } // src/data/units.js var SAFE_UNITS = Object.freeze( [ "km/h", "m/s", "kWh", "mAh", "mbar", "kbps", "Mbps", "Gbit", "kPa", "bar", "kg", "mg", "cm", "mm", "\xB5m", "nm", "km", "ml", "cl", "dl", "kJ", "Nm", "Wb", "lm", "lx", "cd", "mol", "min", "ms", "ns", "Hz", "Pa", "kW", "MW", "Ohm", "\xB0C", "\xB0F", "\xB0", "\u2030", "%", "px", "pt", "dpi", "bpm", "EUR", "USD", "CZK", "K\u010D", "mg/m\xB3", "str", "osob", "ks", "MJ", "TB", "GB", "MB", "kB", "bit", "GBit", "mol", "\u03A9", "\u20AC", "$", "\xA3" ].sort((a, b) => b.length - a.length) ); var STRICT_UNITS = Object.freeze( ["m", "s", "l", "t", "g", "V", "A", "W", "J", "K", "B"].sort( (a, b) => b.length - a.length ) ); // src/rules/units.js function escapeRegex(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } var SAFE_PATTERN = SAFE_UNITS.map(escapeRegex).join("|"); var STRICT_PATTERN = STRICT_UNITS.map(escapeRegex).join("|"); var SAFE_UNIT_REGEX = new RegExp( `(\\d)[ \\t]+(?=(?:${SAFE_PATTERN})(?![\\p{L}\\p{N}]))`, "gu" ); var STRICT_UNIT_REGEX = new RegExp( `(\\d)[ \\t]+(?=(?:${STRICT_PATTERN})(?:[.,;:!?)\\]/\\u00A0]|$))`, "gu" ); function applyUnits(text) { return text.replace(SAFE_UNIT_REGEX, "$1\xA0").replace(STRICT_UNIT_REGEX, "$1\xA0"); } // src/fixCzech.js var DEFAULT_OPTIONS = Object.freeze({ prepositions: true, units: true, initials: true, dates: true, ordinals: true, roman: true, thousands: true }); function fixCzech(text, options) { if (typeof text !== "string" || text.length === 0) { return text; } const opts = options ? { ...DEFAULT_OPTIONS, ...options } : DEFAULT_OPTIONS; let result = text; if (opts.thousands) result = applyThousands(result); if (opts.dates) result = applyDates(result); if (opts.ordinals) result = applyOrdinals(result); if (opts.units) result = applyUnits(result); if (opts.initials) result = applyInitials(result); if (opts.roman) result = applyRoman(result); if (opts.prepositions) result = applyPrepositions(result); return result; } var fixCzech_default = fixCzech; // src/fixCzechHtml.js var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style", "pre", "code", "textarea"]); var TOKEN_REGEX = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<![A-Za-z][^>]*>|<\?[\s\S]*?\?>|<\/?([A-Za-z][A-Za-z0-9-]*)\b[^>]*>|[^<]+/g; var SENTINEL = ""; function isTextToken(match) { return match.charCodeAt(0) !== 60; } function isTagToken(match) { return match.charCodeAt(0) === 60 && !match.startsWith("<!--") && !match.startsWith("<![") && !match.startsWith("<!") && !match.startsWith("<?"); } function fixWithLookahead(text, options, nextChar) { if (nextChar && /\S/.test(nextChar)) { const padded = text + nextChar + SENTINEL; const fixed = fixCzech(padded, options); if (fixed.endsWith(nextChar + SENTINEL)) { return fixed.slice(0, -(nextChar.length + SENTINEL.length)); } return fixed.endsWith(SENTINEL) ? fixed.slice(0, -SENTINEL.length) : fixed; } return fixCzech(text, options); } function transformOne(match, tagName, rawTextStack, options, nextChar) { if (!isTagToken(match) && match.charCodeAt(0) === 60) { return match; } if (isTagToken(match)) { const isClosing = match.charCodeAt(1) === 47; const tag = tagName ? tagName.toLowerCase() : ""; if (isClosing) { const top = rawTextStack[rawTextStack.length - 1]; if (top && top === tag) rawTextStack.pop(); } else if (RAW_TEXT_TAGS.has(tag) && !/\/\s*>$/.test(match)) { rawTextStack.push(tag); } return match; } if (rawTextStack.length > 0) return match; return fixWithLookahead(match, options, nextChar); } function tokenize(html) { const tokens = []; TOKEN_REGEX.lastIndex = 0; let m; while (m = TOKEN_REGEX.exec(html)) { tokens.push({ match: m[0], tagName: m[1], end: m.index + m[0].length }); } return tokens; } function lookaheadOf(tokens, index, fallback) { const next = tokens[index + 1]; if (!next) return fallback || ""; if (isTextToken(next.match)) return next.match.charAt(0); return "<"; } function transformTokens(tokens, rawTextStack, options, trailingChar) { let output = ""; for (let i = 0; i < tokens.length; i++) { const { match, tagName } = tokens[i]; const nextChar = lookaheadOf(tokens, i, trailingChar); output += transformOne(match, tagName, rawTextStack, options, nextChar); } return output; } function fixCzechHtml(html, options) { if (typeof html !== "string" || html.length === 0) return html; return transformTokens(tokenize(html), [], options, ""); } var STREAM_TAIL = 64; function createFixCzechProcessor(options) { let buffer = ""; const rawTextStack = []; function flushUpTo(cutPoint, trailingChar) { if (cutPoint === 0) return ""; const processable = buffer.slice(0, cutPoint); const tokens = tokenize(processable); const output = transformTokens(tokens, rawTextStack, options, trailingChar); buffer = buffer.slice(cutPoint); return output; } return { feed(chunk) { if (typeof chunk !== "string") chunk = String(chunk); buffer += chunk; if (buffer.length === 0) return ""; let cutPoint = Math.max(0, buffer.length - STREAM_TAIL); const lastLt = buffer.lastIndexOf("<"); const lastGt = buffer.lastIndexOf(">"); if (lastLt > lastGt) cutPoint = Math.min(cutPoint, lastLt); while (cutPoint > 0 && !/\s/.test(buffer[cutPoint - 1])) cutPoint--; while (cutPoint > 0 && /\s/.test(buffer[cutPoint - 1])) cutPoint--; while (cutPoint > 0 && !/\s/.test(buffer[cutPoint - 1])) cutPoint--; if (cutPoint === 0) return ""; const trailingChar = buffer.charAt(cutPoint); return flushUpTo(cutPoint, trailingChar); }, flush() { if (buffer.length === 0) return ""; return flushUpTo(buffer.length, ""); } }; } function createFixCzechStream(options) { if (typeof TransformStream === "undefined") { throw new Error( "createFixCzechStream requires the Web Streams API. Use Node 18+, Deno, Bun, or run in a browser/Edge runtime." ); } const decoder = new TextDecoder("utf-8"); const encoder = new TextEncoder(); const processor = createFixCzechProcessor(options); return new TransformStream({ transform(chunk, controller) { const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true }); const out = processor.feed(text); if (out.length > 0) controller.enqueue(encoder.encode(out)); }, flush(controller) { const tail = decoder.decode(); if (tail.length > 0) { const out = processor.feed(tail); if (out.length > 0) controller.enqueue(encoder.encode(out)); } const final = processor.flush(); if (final.length > 0) controller.enqueue(encoder.encode(final)); } }); } exports.PREPOSITIONS = PREPOSITIONS; exports.SAFE_UNITS = SAFE_UNITS; exports.STRICT_UNITS = STRICT_UNITS; exports.applyDates = applyDates; exports.applyInitials = applyInitials; exports.applyOrdinals = applyOrdinals; exports.applyPrepositions = applyPrepositions; exports.applyRoman = applyRoman; exports.applyThousands = applyThousands; exports.applyUnits = applyUnits; exports.createFixCzechProcessor = createFixCzechProcessor; exports.createFixCzechStream = createFixCzechStream; exports.default = fixCzech_default; exports.fixCzech = fixCzech; exports.fixCzechHtml = fixCzechHtml; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map