@spintax/core 0.1.6 → 0.3.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/CHANGELOG.md +148 -0
- package/README.md +4 -3
- package/dist/index.cjs +209 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -3
- package/dist/index.d.ts +105 -3
- package/dist/index.js +208 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/internal/ast.ts
|
|
2
|
-
var AST_VERSION =
|
|
2
|
+
var AST_VERSION = 2;
|
|
3
3
|
function walk(nodes, visit) {
|
|
4
4
|
for (const n of nodes) {
|
|
5
5
|
visit(n);
|
|
@@ -23,21 +23,39 @@ function isParsedAst(value) {
|
|
|
23
23
|
|
|
24
24
|
// src/internal/parser.ts
|
|
25
25
|
var VARIABLE_RE = /^%(\w+)%/;
|
|
26
|
-
var
|
|
26
|
+
var DIRECTIVE_RE = /^[ \t]*#(set|def)[ \t]+%(\w+)%[ \t]*=[ \t]*(.*?)[ \t]*\r?$/gmu;
|
|
27
27
|
var CONDITIONAL_NAME_RE = /^[A-Za-z_]\w*/;
|
|
28
28
|
var PLURAL_PREFIX = "plural ";
|
|
29
29
|
function parseTemplate(src) {
|
|
30
|
-
const { body, setDefs } =
|
|
31
|
-
return { astVersion: AST_VERSION, source: src, setDefs, nodes: parseSequence(body) };
|
|
30
|
+
const { body, setDefs, defDefs } = extractDirectives(stripComments(src));
|
|
31
|
+
return { astVersion: AST_VERSION, source: src, setDefs, defDefs, nodes: parseSequence(body) };
|
|
32
32
|
}
|
|
33
|
-
function
|
|
33
|
+
function extractDirectives(text) {
|
|
34
34
|
const setDefs = {};
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
const defDefs = {};
|
|
36
|
+
const occurrences = [];
|
|
37
|
+
DIRECTIVE_RE.lastIndex = 0;
|
|
38
|
+
const stripped = text.replace(
|
|
39
|
+
DIRECTIVE_RE,
|
|
40
|
+
(full, kind, rawName, value, offset) => {
|
|
41
|
+
const name = rawName.toLowerCase();
|
|
42
|
+
occurrences.push({
|
|
43
|
+
kind,
|
|
44
|
+
name,
|
|
45
|
+
value,
|
|
46
|
+
line: countLines(text, offset)
|
|
47
|
+
});
|
|
48
|
+
if (kind === "def") defDefs[name] = value;
|
|
49
|
+
else setDefs[name] = value;
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
return { body: stripped.replace(/\n{3,}/gu, "\n\n"), setDefs, defDefs, occurrences };
|
|
54
|
+
}
|
|
55
|
+
function countLines(text, offset) {
|
|
56
|
+
let line = 1;
|
|
57
|
+
for (let i = 0; i < offset; i += 1) if (text[i] === "\n") line += 1;
|
|
58
|
+
return line;
|
|
41
59
|
}
|
|
42
60
|
function stripComments(text) {
|
|
43
61
|
return text.replace(/\/#[\s\S]*?#\//g, "");
|
|
@@ -342,6 +360,9 @@ function pluralArity(baseLang) {
|
|
|
342
360
|
case "ru":
|
|
343
361
|
case "uk":
|
|
344
362
|
case "be":
|
|
363
|
+
case "sr":
|
|
364
|
+
case "hr":
|
|
365
|
+
case "bs":
|
|
345
366
|
return 3;
|
|
346
367
|
default:
|
|
347
368
|
return 2;
|
|
@@ -355,6 +376,9 @@ function pluralFor(baseLang, n, forms) {
|
|
|
355
376
|
case "ru":
|
|
356
377
|
case "uk":
|
|
357
378
|
case "be":
|
|
379
|
+
case "sr":
|
|
380
|
+
case "hr":
|
|
381
|
+
case "bs":
|
|
358
382
|
if (mod10 === 1 && mod100 !== 11) return forms[0] ?? "";
|
|
359
383
|
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return forms[1] ?? "";
|
|
360
384
|
return forms[2] ?? "";
|
|
@@ -370,7 +394,7 @@ function validateTemplate(src, opts = {}) {
|
|
|
370
394
|
const diagnostics = [];
|
|
371
395
|
const text = stripComments(src);
|
|
372
396
|
checkBrackets(text, diagnostics);
|
|
373
|
-
|
|
397
|
+
checkDirectives(text, diagnostics);
|
|
374
398
|
checkPermutationConfigs(text, diagnostics);
|
|
375
399
|
checkPlurals(text, opts.locale, diagnostics);
|
|
376
400
|
checkVariableReferences(text, opts.knownVariables, diagnostics);
|
|
@@ -429,17 +453,43 @@ function checkBrackets(text, out) {
|
|
|
429
453
|
out.push(err("bracket.unclosed", `Unclosed '${unclosed.char}'.`, { line: unclosed.line, column: unclosed.column, endLine: unclosed.line, endColumn: unclosed.column + 1, data: { bracket: unclosed.char } }));
|
|
430
454
|
}
|
|
431
455
|
}
|
|
432
|
-
function
|
|
456
|
+
function checkDirectives(text, out) {
|
|
433
457
|
const lines = text.split("\n");
|
|
434
458
|
lines.forEach((lineText, idx) => {
|
|
435
459
|
const trimmed = lineText.replace(/^[ \t]+/, "");
|
|
436
|
-
|
|
437
|
-
|
|
460
|
+
const kind = ["#set", "#def"].find(
|
|
461
|
+
(candidate) => trimmed.startsWith(`${candidate} `) || trimmed.startsWith(`${candidate} `)
|
|
462
|
+
);
|
|
463
|
+
if (!kind) return;
|
|
464
|
+
DIRECTIVE_RE.lastIndex = 0;
|
|
465
|
+
if (!DIRECTIVE_RE.test(trimmed)) {
|
|
438
466
|
const column = lineText.length - trimmed.length + 1;
|
|
439
467
|
const line = idx + 1;
|
|
440
|
-
|
|
468
|
+
const code = kind === "#def" ? "def.malformed" : "set.malformed";
|
|
469
|
+
out.push(err(code, `Malformed ${kind}. Expected: ${kind} %name% = value`, { line, column, endLine: line, endColumn: lineText.length + 1 }));
|
|
441
470
|
}
|
|
442
471
|
});
|
|
472
|
+
const { occurrences } = extractDirectives(text);
|
|
473
|
+
const seen = /* @__PURE__ */ new Map();
|
|
474
|
+
for (const occurrence of occurrences) {
|
|
475
|
+
const first = seen.get(occurrence.name);
|
|
476
|
+
if (first !== void 0) {
|
|
477
|
+
out.push(err(
|
|
478
|
+
"definition.duplicate-name",
|
|
479
|
+
`Variable '${occurrence.name}' is defined more than once (first on line ${first}). A name belongs to one directive, once.`,
|
|
480
|
+
{ line: occurrence.line, column: 1 }
|
|
481
|
+
));
|
|
482
|
+
} else {
|
|
483
|
+
seen.set(occurrence.name, occurrence.line);
|
|
484
|
+
}
|
|
485
|
+
if (occurrence.kind === "def" && /#include\b/u.test(occurrence.value)) {
|
|
486
|
+
out.push(err(
|
|
487
|
+
"def.include-in-value",
|
|
488
|
+
`#include cannot appear in a #def value ('${occurrence.name}'): includes resolve after the value is frozen. Use #set, or put the #include in the body.`,
|
|
489
|
+
{ line: occurrence.line, column: 1 }
|
|
490
|
+
));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
443
493
|
}
|
|
444
494
|
function checkPermutationConfigs(text, out) {
|
|
445
495
|
for (const m of text.matchAll(/\[<([^>]*?)>/gu)) {
|
|
@@ -477,10 +527,20 @@ function checkPermutationConfigs(text, out) {
|
|
|
477
527
|
function checkPlurals(text, locale, out) {
|
|
478
528
|
const base = locale && locale !== "" ? normalizeBaseLang(locale) : "";
|
|
479
529
|
const arity = base !== "" ? pluralArity(base) : 0;
|
|
530
|
+
const tainted = macroTaintedNames(text);
|
|
480
531
|
for (const block of findPluralBlocks(text)) {
|
|
481
532
|
const at = span(text, block.start, block.end - block.start);
|
|
533
|
+
for (const m of block.countSlot.matchAll(/%(\w+)%/gu)) {
|
|
534
|
+
const name = (m[1] ?? "").toLowerCase();
|
|
535
|
+
if (!tainted.has(name)) continue;
|
|
536
|
+
out.push(err(
|
|
537
|
+
"plural.count-macro",
|
|
538
|
+
`{plural ...}: the count '${m[1]}' is a #set macro, so it is still unresolved spintax when the plural is decided and the block renders empty. Define it with #def instead.`,
|
|
539
|
+
at
|
|
540
|
+
));
|
|
541
|
+
}
|
|
482
542
|
if (/[{}[\]]/.test(block.formsRaw)) {
|
|
483
|
-
out.push(err("plural.nested-brackets", "{plural ...}: forms must not contain nested spintax brackets.", at));
|
|
543
|
+
out.push(err("plural.nested-brackets", "{plural ...}: forms must not contain nested spintax brackets. Extract via #def first \u2014 a #set is substituted verbatim and would put the brackets straight back.", at));
|
|
484
544
|
continue;
|
|
485
545
|
}
|
|
486
546
|
if (arity > 0) {
|
|
@@ -495,11 +555,33 @@ function checkPlurals(text, locale, out) {
|
|
|
495
555
|
}
|
|
496
556
|
}
|
|
497
557
|
}
|
|
558
|
+
var UNRESOLVED_AT_PLURAL_TIME = /\[|\{(?!\?)/u;
|
|
559
|
+
function macroTaintedNames(text) {
|
|
560
|
+
const macros = extractDirectives(text).setDefs;
|
|
561
|
+
const tainted = /* @__PURE__ */ new Set();
|
|
562
|
+
for (const [name, value] of Object.entries(macros)) {
|
|
563
|
+
if (UNRESOLVED_AT_PLURAL_TIME.test(value)) tainted.add(name);
|
|
564
|
+
}
|
|
565
|
+
let grew = true;
|
|
566
|
+
while (grew) {
|
|
567
|
+
grew = false;
|
|
568
|
+
for (const [name, value] of Object.entries(macros)) {
|
|
569
|
+
if (tainted.has(name)) continue;
|
|
570
|
+
for (const m of value.matchAll(/%(\w+)%/gu)) {
|
|
571
|
+
if (!tainted.has((m[1] ?? "").toLowerCase())) continue;
|
|
572
|
+
tainted.add(name);
|
|
573
|
+
grew = true;
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return tainted;
|
|
579
|
+
}
|
|
498
580
|
function checkVariableReferences(text, known, out) {
|
|
499
581
|
const knownSet = new Set((known ?? []).map((n) => n.toLowerCase()));
|
|
500
582
|
const defs = /* @__PURE__ */ new Map();
|
|
501
583
|
const defPos = /* @__PURE__ */ new Map();
|
|
502
|
-
for (const m of text.matchAll(/^[ \t]*#set[ \t]+%(\w+)%[ \t]*=[ \t]*(.*?)$/gmu)) {
|
|
584
|
+
for (const m of text.matchAll(/^[ \t]*#(?:set|def)[ \t]+%(\w+)%[ \t]*=[ \t]*(.*?)$/gmu)) {
|
|
503
585
|
const name = (m[1] ?? "").toLowerCase();
|
|
504
586
|
defs.set(name, m[2] ?? "");
|
|
505
587
|
const nameOffset = (m.index ?? 0) + m[0].indexOf("%");
|
|
@@ -514,7 +596,7 @@ function checkVariableReferences(text, known, out) {
|
|
|
514
596
|
for (const name of defs.keys()) {
|
|
515
597
|
detectCycle(name, defs, [name], defPos.get(name) ?? somewhere, out);
|
|
516
598
|
}
|
|
517
|
-
const body = text.replace(/^[ \t]*#set[ \t]+%\w+%[ \t]*=[ \t]*.*?$/gmu, (m) => m.replace(/[^\n]/g, " "));
|
|
599
|
+
const body = text.replace(/^[ \t]*#(?:set|def)[ \t]+%\w+%[ \t]*=[ \t]*.*?$/gmu, (m) => m.replace(/[^\n]/g, " "));
|
|
518
600
|
const seen = /* @__PURE__ */ new Set();
|
|
519
601
|
const undefinedAt = (name, offset, length) => {
|
|
520
602
|
const key = name.toLowerCase();
|
|
@@ -561,19 +643,21 @@ function checkIncludeTargets(text, known, out) {
|
|
|
561
643
|
|
|
562
644
|
// src/internal/extract.ts
|
|
563
645
|
var SET_DEF_RE = /^[ \t]*#set[ \t]+%(\w+)%[ \t]*=/gmu;
|
|
564
|
-
var
|
|
646
|
+
var DEF_DEF_RE = /^[ \t]*#def[ \t]+%(\w+)%[ \t]*=/gmu;
|
|
647
|
+
var DEFINITION_LHS_RE = /^[ \t]*#(?:set|def)[ \t]+%\w+%[ \t]*=/gmu;
|
|
565
648
|
var INCLUDE_RE2 = /^[ \t]*#include[ \t\n\r\f\x0B]+"([^"]+)"[ \t\n\r\f\x0B]*$/gmu;
|
|
566
649
|
var VARIABLE_RE2 = /%(\w+)%/gu;
|
|
567
650
|
var CONDITIONAL_REF_RE = /\{\?!?([A-Za-z_]\w*)\?/gu;
|
|
568
651
|
function extractFromSource(src) {
|
|
569
652
|
const text = stripComments(src);
|
|
570
653
|
const sets = collect(text, SET_DEF_RE, true);
|
|
654
|
+
const defs = collect(text, DEF_DEF_RE, true);
|
|
571
655
|
const includes = collect(text, INCLUDE_RE2, false);
|
|
572
|
-
const body = text.replace(
|
|
656
|
+
const body = text.replace(DEFINITION_LHS_RE, "");
|
|
573
657
|
const refs = /* @__PURE__ */ new Set();
|
|
574
658
|
addAll(refs, body, VARIABLE_RE2, true);
|
|
575
659
|
addAll(refs, body, CONDITIONAL_REF_RE, true);
|
|
576
|
-
return { refs: [...refs], sets, includes };
|
|
660
|
+
return { refs: [...refs], sets, defs, includes };
|
|
577
661
|
}
|
|
578
662
|
function collect(text, re, fold) {
|
|
579
663
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -645,8 +729,15 @@ function stripSentinels(text) {
|
|
|
645
729
|
var MAX_VARIABLE_DEPTH = 50;
|
|
646
730
|
var INCLUDE_LINE_RE = /^[ \t]*#include[ \t\n\r\f\x0B]+"([^"]+)"[ \t\n\r\f\x0B]*$/gmu;
|
|
647
731
|
function renderAst(ast, ctx) {
|
|
648
|
-
const
|
|
649
|
-
const
|
|
732
|
+
const base = buildVars(ast.setDefs, ctx.runtimeContext);
|
|
733
|
+
const walkOpts = {
|
|
734
|
+
rng: ctx.rng,
|
|
735
|
+
locale: ctx.locale,
|
|
736
|
+
depth: 0,
|
|
737
|
+
onPluralError: ctx.onPluralError
|
|
738
|
+
};
|
|
739
|
+
const vars = Object.keys(ast.defDefs).length > 0 ? { ...base, ...rollDefinitions(ast.defDefs, base, ctx.runtimeContext, walkOpts) } : base;
|
|
740
|
+
const text = renderNodes(ast.nodes, { ...walkOpts, vars });
|
|
650
741
|
return ctx.resolver ? resolveIncludes(text, ctx) : text;
|
|
651
742
|
}
|
|
652
743
|
function resolveIncludes(text, ctx) {
|
|
@@ -668,33 +759,65 @@ function resolveIncludes(text, ctx) {
|
|
|
668
759
|
});
|
|
669
760
|
});
|
|
670
761
|
}
|
|
671
|
-
function buildVars(setDefs, context
|
|
762
|
+
function buildVars(setDefs, context) {
|
|
672
763
|
const vars = {};
|
|
673
764
|
for (const [name, value] of Object.entries(setDefs)) {
|
|
674
|
-
vars[name] =
|
|
765
|
+
vars[name] = value;
|
|
675
766
|
}
|
|
676
767
|
for (const [name, value] of Object.entries(context)) {
|
|
677
768
|
vars[name.toLowerCase()] = value;
|
|
678
769
|
}
|
|
679
770
|
return vars;
|
|
680
771
|
}
|
|
681
|
-
function
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
772
|
+
function rollDefinitions(defDefs, vars, context, opts) {
|
|
773
|
+
const outranked = new Set(Object.keys(context).map((key) => key.toLowerCase()));
|
|
774
|
+
const rolled = {};
|
|
775
|
+
const aliases = {};
|
|
776
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
777
|
+
if (name in defDefs && !outranked.has(name)) continue;
|
|
778
|
+
aliases[name] = value;
|
|
779
|
+
}
|
|
780
|
+
for (const name of orderDefinitions(defDefs, aliases)) {
|
|
781
|
+
if (outranked.has(name)) continue;
|
|
782
|
+
const value = defDefs[name] ?? "";
|
|
783
|
+
rolled[name] = renderNodes(parseSequence(value), { ...opts, vars: { ...vars, ...rolled } });
|
|
784
|
+
}
|
|
785
|
+
return rolled;
|
|
786
|
+
}
|
|
787
|
+
function orderDefinitions(defDefs, aliases) {
|
|
788
|
+
const names = Object.keys(defDefs);
|
|
789
|
+
const blocked = /* @__PURE__ */ new Map();
|
|
790
|
+
for (const name of names) {
|
|
791
|
+
const reached = referencedNames(defDefs[name] ?? "", aliases);
|
|
792
|
+
blocked.set(name, new Set(names.filter((candidate) => reached.has(candidate))));
|
|
793
|
+
}
|
|
794
|
+
const ordered = [];
|
|
795
|
+
let pending = names;
|
|
796
|
+
while (pending.length > 0) {
|
|
797
|
+
const ready = pending.filter((name) => {
|
|
798
|
+
const deps = blocked.get(name);
|
|
799
|
+
return !deps || ![...deps].some((dep) => dep !== name && pending.includes(dep));
|
|
694
800
|
});
|
|
695
|
-
if (
|
|
801
|
+
if (ready.length === 0) return [...ordered, ...pending];
|
|
802
|
+
ordered.push(...ready);
|
|
803
|
+
pending = pending.filter((name) => !ready.includes(name));
|
|
696
804
|
}
|
|
697
|
-
return
|
|
805
|
+
return ordered;
|
|
806
|
+
}
|
|
807
|
+
function referencedNames(value, aliases) {
|
|
808
|
+
const seen = /* @__PURE__ */ new Set();
|
|
809
|
+
const queue = directReferences(value);
|
|
810
|
+
while (queue.length > 0) {
|
|
811
|
+
const name = queue.shift();
|
|
812
|
+
if (seen.has(name)) continue;
|
|
813
|
+
seen.add(name);
|
|
814
|
+
const alias = aliases[name];
|
|
815
|
+
if (alias !== void 0) queue.push(...directReferences(alias));
|
|
816
|
+
}
|
|
817
|
+
return seen;
|
|
818
|
+
}
|
|
819
|
+
function directReferences(text) {
|
|
820
|
+
return [...text.matchAll(/%(\w+)%/gu)].map((match) => (match[1] ?? "").toLowerCase());
|
|
698
821
|
}
|
|
699
822
|
function renderNodes(nodes, opts) {
|
|
700
823
|
let out = "";
|
|
@@ -749,17 +872,47 @@ function renderConditional(node, opts) {
|
|
|
749
872
|
function renderPlural(node, opts) {
|
|
750
873
|
const countRaw = expandVarsOnly(node.countRaw, opts);
|
|
751
874
|
const formsRaw = expandVarsOnly(node.formsRaw, opts);
|
|
752
|
-
if (/[{}[\]]/u.test(formsRaw)) return fullwidthVerbatim(countRaw, formsRaw);
|
|
753
|
-
const count = phpTrim2(countRaw);
|
|
754
|
-
if (!/^-?\d+$/u.test(count)) return "";
|
|
755
875
|
const base = normalizeBaseLang(opts.locale);
|
|
876
|
+
const report = (issue) => opts.onPluralError?.(issue);
|
|
877
|
+
if (/[{}[\]]/u.test(formsRaw)) {
|
|
878
|
+
report({
|
|
879
|
+
code: "plural.nested-brackets",
|
|
880
|
+
message: "Plural form slot contains nested spintax brackets; extract via #set first.",
|
|
881
|
+
construct: rawConstruct(countRaw, formsRaw),
|
|
882
|
+
locale: base
|
|
883
|
+
});
|
|
884
|
+
return fullwidthVerbatim(countRaw, formsRaw);
|
|
885
|
+
}
|
|
886
|
+
const count = phpTrim2(countRaw);
|
|
887
|
+
if (!/^-?\d+$/u.test(count)) {
|
|
888
|
+
report({
|
|
889
|
+
code: "plural.count",
|
|
890
|
+
message: `Plural count slot is empty or non-numeric (${JSON.stringify(count)}); block erased.`,
|
|
891
|
+
construct: rawConstruct(countRaw, formsRaw),
|
|
892
|
+
locale: base
|
|
893
|
+
});
|
|
894
|
+
return "";
|
|
895
|
+
}
|
|
756
896
|
const forms = formsRaw.split("|").map((f) => phpTrim2(f));
|
|
757
|
-
if (forms.length !== pluralArity(base))
|
|
897
|
+
if (forms.length !== pluralArity(base)) {
|
|
898
|
+
report({
|
|
899
|
+
code: "plural.arity",
|
|
900
|
+
message: `Plural has ${forms.length} form(s); locale "${base}" takes ${pluralArity(base)}.`,
|
|
901
|
+
construct: rawConstruct(countRaw, formsRaw),
|
|
902
|
+
locale: base,
|
|
903
|
+
expected: pluralArity(base),
|
|
904
|
+
got: forms.length
|
|
905
|
+
});
|
|
906
|
+
return fullwidthVerbatim(countRaw, formsRaw);
|
|
907
|
+
}
|
|
758
908
|
const picked = pluralFor(base, Number.parseInt(count, 10), forms);
|
|
759
909
|
return renderNodes(parseSequence(picked), opts);
|
|
760
910
|
}
|
|
911
|
+
function rawConstruct(countRaw, formsRaw) {
|
|
912
|
+
return `{plural ${countRaw}:${formsRaw}}`;
|
|
913
|
+
}
|
|
761
914
|
function fullwidthVerbatim(countRaw, formsRaw) {
|
|
762
|
-
return
|
|
915
|
+
return rawConstruct(countRaw, formsRaw).replace(/\{/gu, "\uFF5B").replace(/\}/gu, "\uFF5D");
|
|
763
916
|
}
|
|
764
917
|
function renderEnumeration(options, opts) {
|
|
765
918
|
if (options.length === 0) return "";
|
|
@@ -823,26 +976,6 @@ function padSeparator(sep) {
|
|
|
823
976
|
if (/^\p{L}+$/u.test(trimmed)) return ` ${trimmed} `;
|
|
824
977
|
return sep;
|
|
825
978
|
}
|
|
826
|
-
function splitTopLevelPipes(inner) {
|
|
827
|
-
const parts = [];
|
|
828
|
-
let brace = 0;
|
|
829
|
-
let bracket = 0;
|
|
830
|
-
let cur = "";
|
|
831
|
-
for (const ch of inner) {
|
|
832
|
-
if (ch === "{") brace += 1;
|
|
833
|
-
else if (ch === "}") brace -= 1;
|
|
834
|
-
else if (ch === "[") bracket += 1;
|
|
835
|
-
else if (ch === "]") bracket -= 1;
|
|
836
|
-
if (ch === "|" && brace === 0 && bracket === 0) {
|
|
837
|
-
parts.push(cur);
|
|
838
|
-
cur = "";
|
|
839
|
-
} else {
|
|
840
|
-
cur += ch;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
parts.push(cur);
|
|
844
|
-
return parts;
|
|
845
|
-
}
|
|
846
979
|
var PHP_LTRIM_RE2 = /^[ \t\n\r\0\x0B]+/u;
|
|
847
980
|
var PHP_RTRIM_RE2 = /[ \t\n\r\0\x0B]+$/u;
|
|
848
981
|
function phpTrim2(s) {
|
|
@@ -975,7 +1108,8 @@ function renderWith(input, rng, opts = {}) {
|
|
|
975
1108
|
locale: opts.locale ?? "",
|
|
976
1109
|
resolver: opts.includeResolver,
|
|
977
1110
|
maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH,
|
|
978
|
-
includeStack: []
|
|
1111
|
+
includeStack: [],
|
|
1112
|
+
onPluralError: opts.onPluralError
|
|
979
1113
|
};
|
|
980
1114
|
const ast = resolveAst(typeof input === "string" ? stripSentinels(input) : input);
|
|
981
1115
|
let out = renderAst(ast, ctx);
|
|
@@ -1012,6 +1146,12 @@ function hashString(s) {
|
|
|
1012
1146
|
}
|
|
1013
1147
|
|
|
1014
1148
|
// src/index.ts
|
|
1149
|
+
function normalizeBaseLang2(locale) {
|
|
1150
|
+
return normalizeBaseLang(locale ?? "");
|
|
1151
|
+
}
|
|
1152
|
+
function pluralArity2(locale) {
|
|
1153
|
+
return pluralArity(normalizeBaseLang2(locale));
|
|
1154
|
+
}
|
|
1015
1155
|
function parse(src) {
|
|
1016
1156
|
return parseTemplate(src);
|
|
1017
1157
|
}
|
|
@@ -1062,6 +1202,6 @@ function neutralize2(value) {
|
|
|
1062
1202
|
return neutralize(value);
|
|
1063
1203
|
}
|
|
1064
1204
|
|
|
1065
|
-
export { AstVersionError, DEFAULT_MAX_DEPTH, IncludeResolverError, NotImplementedError, SpintaxError, analyze, extract, neutralize2 as neutralize, parse, render, validate };
|
|
1205
|
+
export { AstVersionError, DEFAULT_MAX_DEPTH, IncludeResolverError, NotImplementedError, SpintaxError, analyze, extract, neutralize2 as neutralize, normalizeBaseLang2 as normalizeBaseLang, parse, pluralArity2 as pluralArity, render, validate };
|
|
1066
1206
|
//# sourceMappingURL=index.js.map
|
|
1067
1207
|
//# sourceMappingURL=index.js.map
|