@spintax/core 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1387,6 +1387,37 @@ function trimFragment(fragment) {
1387
1387
  if (fragment.lead === 0 && fragment.trail === 0) return fragment;
1388
1388
  return new Window(fragment, fragment.lead, fragment.length - fragment.trail);
1389
1389
  }
1390
+ function firstCodePointOf(fragment) {
1391
+ const hi = charCodeAtOf(fragment, 0);
1392
+ if (hi >= 55296 && hi <= 56319) {
1393
+ const lo = charCodeAtOf(fragment, 1);
1394
+ if (lo >= 56320 && lo <= 57343) return (hi - 55296) * 1024 + (lo - 56320) + 65536;
1395
+ }
1396
+ return hi;
1397
+ }
1398
+ function charCodeAtOf(fragment, index) {
1399
+ let piece = fragment;
1400
+ let at = index;
1401
+ for (; ; ) {
1402
+ if (at < 0 || at >= lengthOf(piece)) return -1;
1403
+ if (typeof piece === "string") return piece.charCodeAt(at);
1404
+ if (piece instanceof Window) {
1405
+ at += piece.from;
1406
+ piece = piece.base;
1407
+ continue;
1408
+ }
1409
+ let offset = 0;
1410
+ for (const child of piece.pieces) {
1411
+ const length = lengthOf(child);
1412
+ if (at < offset + length) {
1413
+ piece = child;
1414
+ at -= offset;
1415
+ break;
1416
+ }
1417
+ offset += length;
1418
+ }
1419
+ }
1420
+ }
1390
1421
  function flatten(fragment) {
1391
1422
  if (typeof fragment === "string") return fragment;
1392
1423
  const out = [];
@@ -1780,7 +1811,12 @@ function assemblePermutation(node, rendered, opts) {
1780
1811
  max = Math.max(min, Math.min(max, total));
1781
1812
  const pick = randomInt(opts.rng, min, max);
1782
1813
  shuffle(elements, opts.rng);
1783
- return joinWithSeparators(elements.slice(0, pick), config.sep, config.lastsep ?? config.sep);
1814
+ return joinWithSeparators(
1815
+ elements.slice(0, pick),
1816
+ config.sep,
1817
+ config.lastsep ?? config.sep,
1818
+ normalizeBaseLang(opts.locale)
1819
+ );
1784
1820
  }
1785
1821
  function shuffle(arr, rng) {
1786
1822
  for (let i = arr.length - 1; i > 0; i -= 1) {
@@ -1790,7 +1826,7 @@ function shuffle(arr, rng) {
1790
1826
  arr[j] = tmp;
1791
1827
  }
1792
1828
  }
1793
- function joinWithSeparators(elements, globalSep, globalLastsep) {
1829
+ function joinWithSeparators(elements, globalSep, globalLastsep, lang) {
1794
1830
  const count = elements.length;
1795
1831
  if (count === 0) return "";
1796
1832
  if (count === 1) return elements[0].text;
@@ -1798,14 +1834,23 @@ function joinWithSeparators(elements, globalSep, globalLastsep) {
1798
1834
  for (let i = 1; i < count; i += 1) {
1799
1835
  const el = elements[i];
1800
1836
  const sep = el.sep ?? (i === count - 1 ? globalLastsep : globalSep);
1801
- pieces.push(padSeparator(sep), el.text);
1837
+ pieces.push(padSeparator(sep, lang, el.text), el.text);
1802
1838
  }
1803
1839
  return joinFragments(pieces);
1804
1840
  }
1805
- var UNSPACED_SCRIPT_RE = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ーー]+$/u;
1806
- function padSeparator(sep) {
1841
+ var UNSPACED_SCRIPT_RE = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ーー\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]+$/u;
1842
+ var PROCLITICS = /* @__PURE__ */ new Map([
1843
+ ["ar", { words: /* @__PURE__ */ new Set(["\u0648", "\u0641"]), letter: /^(?=\p{L})\p{Script=Arabic}$/u }],
1844
+ ["he", { words: /* @__PURE__ */ new Set(["\u05D5"]), letter: /^(?=\p{L})\p{Script=Hebrew}$/u }]
1845
+ ]);
1846
+ function padSeparator(sep, lang, next) {
1807
1847
  const trimmed = phpTrim(sep);
1808
1848
  if (trimmed === "") return sep;
1849
+ const proclitic = PROCLITICS.get(lang);
1850
+ if (proclitic !== void 0 && proclitic.words.has(trimmed)) {
1851
+ const first = firstCodePointOf(next);
1852
+ return first >= 0 && proclitic.letter.test(String.fromCodePoint(first)) ? ` ${trimmed}` : ` ${trimmed} `;
1853
+ }
1809
1854
  if (/^\p{L}+$/u.test(trimmed) && !UNSPACED_SCRIPT_RE.test(trimmed)) return ` ${trimmed} `;
1810
1855
  return sep;
1811
1856
  }
@@ -1872,7 +1917,7 @@ var TLD_UPPER = "\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}";
1872
1917
  var TLD = `(?:${XN}[a-zA-Z0-9\\-\\u017F\\u212A]{2,59}|[${TLD_LOWER}][${TLD_LOWER}\\p{N}-]{1,62}|[${TLD_UPPER}][${TLD_UPPER}\\p{N}-]{1,62})`;
1873
1918
  var DOMAIN_PART = `(?:${LABEL}\\.)+${TLD}`;
1874
1919
  var URI_BODY = `[^\\x00${WS}<>"')\\]]`;
1875
- var URI_RE = new RegExp(`(?:(?:https?|ftp):\\/\\/|(?:mailto|tel):)${URI_BODY}+`, "giu");
1920
+ var URI_RE = new RegExp(`(?:(?:https?|ftp):\\/\\/${URI_BODY}*|(?:mailto|tel):${URI_BODY}+)`, "giu");
1876
1921
  var MAILTEL_PREFIX_RE = /^(?:mailto|tel):/iu;
1877
1922
  var DOMAIN_AT_RE = new RegExp(`${DOMAIN_PART}${WORD_BOUNDARY}`, "uy");
1878
1923
  var DOMAIN_SCAN_RE = new RegExp(`${AFTER_NON_WORD}(?:(${DOMAIN_PART}${WORD_BOUNDARY})|(?:${LABEL}\\.)*${LABEL})`, "gu");
@@ -1921,12 +1966,15 @@ var SENTENCE_OPENERS = "\xBF\xA1";
1921
1966
  var LEAD = `(?:<[^>]+>|[${SENTENCE_OPENERS}]|${S})*`;
1922
1967
  var QUOTES = `"'\xAB\xBB\u2039\u203A\u201C\u201D\u2018\u2019`;
1923
1968
  var CLOSER = `[)\\]]|[${QUOTES}]+(?=${S}|$|<|\\/?>|[.,;:!?\u2026)\\]\u2014\u2013])`;
1969
+ var DOT_TOKEN_RE = new RegExp(`(?<=^|${S}|[(\\[{>/\\\\${QUOTES}])\\.[\\p{L}\\p{N}][\\p{L}\\p{N}_.\\-]*`, "gu");
1924
1970
  var SPACE_BEFORE_PUNCT_RE = new RegExp(`(?<!${S})${S}+([,;:!?.])`, "gu");
1925
1971
  var SPACE_AFTER_COMMA_RE = new RegExp(`([,;:])(?!\\p{Nd})(?!${S}|$|<|${CLOSER})`, "gu");
1926
1972
  var SPACE_AFTER_SENTENCE_RE = new RegExp(`(?<![.!?])([.!?]+)(?![.!?])(?!\\p{Nd})(?!${S}|$|<|${CLOSER})`, "gu");
1927
1973
  var SPACE_AFTER_OPENER_RE = new RegExp(`([${SENTENCE_OPENERS}])${S}+`, "gu");
1928
1974
  var CAP_FIRST_RE = new RegExp(`^(${LEAD})(\\p{Ll})`, "u");
1929
1975
  var up = (ch) => ch.toUpperCase();
1976
+ var upTurkic = (ch) => ch === "i" ? "\u0130" : ch.toUpperCase();
1977
+ var TURKIC_LANGS = /* @__PURE__ */ new Set(["tr", "az"]);
1930
1978
  function indexLeads(text) {
1931
1979
  const n = text.length;
1932
1980
  const leadEnd = new Int32Array(n + 1);
@@ -1959,7 +2007,7 @@ function leadEndFrom(text, i, leads) {
1959
2007
  }
1960
2008
  return j < text.length ? leads().leadEnd[j] : j;
1961
2009
  }
1962
- function capitalizeAfter(text, lower, next, context) {
2010
+ function capitalizeAfter(text, lower, next, context, upper) {
1963
2011
  const leads = () => {
1964
2012
  if (context.leads === null || context.leads.leadEnd.length !== text.length + 1) context.leads = indexLeads(text);
1965
2013
  return context.leads;
@@ -1974,7 +2022,7 @@ function capitalizeAfter(text, lower, next, context) {
1974
2022
  if (cp === void 0) continue;
1975
2023
  const ch = String.fromCodePoint(cp);
1976
2024
  if (!lower.test(ch)) continue;
1977
- out += text.slice(emitted, at) + up(ch);
2025
+ out += text.slice(emitted, at) + upper(ch);
1978
2026
  emitted = at + ch.length;
1979
2027
  from = emitted;
1980
2028
  }
@@ -2007,7 +2055,7 @@ var afterLineBreak = (text, from) => {
2007
2055
  const at = text.indexOf("\n", from);
2008
2056
  return at === -1 ? null : { leadStart: at + 1, resume: at + 1 };
2009
2057
  };
2010
- var SHIELD_PREFIXES = ["URL", "URI", "EMAIL", "DOM", "NUM", "ABBR"];
2058
+ var SHIELD_PREFIXES = ["URL", "URI", "EMAIL", "DOTTOK", "DOM", "NUM", "ABBR"];
2011
2059
  var RESTORE_RE2 = new RegExp(`\\x00(?:${SHIELD_PREFIXES.join("|")})_\\d+\\x00`, "gu");
2012
2060
  function restore(text, input, placeholders) {
2013
2061
  if (!input.includes("\0")) {
@@ -2042,7 +2090,8 @@ function restoreAsTheLoopDoes(text, placeholders) {
2042
2090
  }
2043
2091
  return out;
2044
2092
  }
2045
- function postProcess(input) {
2093
+ function postProcess(input, locale = "") {
2094
+ const upper = TURKIC_LANGS.has(normalizeBaseLang(locale)) ? upTurkic : up;
2046
2095
  const placeholders = /* @__PURE__ */ new Map();
2047
2096
  let counter = 0;
2048
2097
  const store = (value, prefix) => {
@@ -2062,6 +2111,7 @@ function postProcess(input) {
2062
2111
  (m) => storeWithTrailingPunct(m, MAILTEL_PREFIX_RE.test(m) ? "URI" : "URL")
2063
2112
  );
2064
2113
  text = shieldEmails(text, (m) => store(m, "EMAIL"));
2114
+ text = text.replace(DOT_TOKEN_RE, (m) => storeWithTrailingPunct(m, "DOTTOK"));
2065
2115
  text = shieldDomains(text, (m) => store(m, "DOM"));
2066
2116
  text = text.replace(DECIMAL_RE, (m) => store(m, "NUM"));
2067
2117
  text = text.replace(MULTI_ABBR_RE, (m) => store(m, "ABBR"));
@@ -2071,11 +2121,11 @@ function postProcess(input) {
2071
2121
  text = text.replace(SPACE_AFTER_COMMA_RE, "$1 ");
2072
2122
  text = text.replace(SPACE_AFTER_SENTENCE_RE, "$1 ");
2073
2123
  text = text.replace(SPACE_AFTER_OPENER_RE, "$1");
2074
- text = text.replace(CAP_FIRST_RE, (_m, lead, ch) => lead + up(ch));
2124
+ text = text.replace(CAP_FIRST_RE, (_m, lead, ch) => lead + upper(ch));
2075
2125
  const leadContext = { leads: null };
2076
- text = capitalizeAfter(text, LOWER_RE, afterSentenceEnd, leadContext);
2077
- text = capitalizeAfter(text, LOWER_RE, afterBlockTag(), leadContext);
2078
- text = capitalizeAfter(text, LOWER_RE, afterLineBreak, leadContext);
2126
+ text = capitalizeAfter(text, LOWER_RE, afterSentenceEnd, leadContext, upper);
2127
+ text = capitalizeAfter(text, LOWER_RE, afterBlockTag(), leadContext, upper);
2128
+ text = capitalizeAfter(text, LOWER_RE, afterLineBreak, leadContext, upper);
2079
2129
  return restore(text, input, placeholders).trim();
2080
2130
  }
2081
2131
 
@@ -2096,7 +2146,7 @@ function renderWith(input, rng, opts = {}) {
2096
2146
  };
2097
2147
  const ast = resolveAst(input);
2098
2148
  let out = renderAst(ast, ctx);
2099
- if (opts.postProcess !== false) out = postProcess(out);
2149
+ if (opts.postProcess !== false) out = postProcess(out, ctx.locale);
2100
2150
  return safetyRestore(out);
2101
2151
  }
2102
2152
  function resolveAst(input) {