@spintax/core 0.3.0 → 0.3.2
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 +92 -0
- package/README.md +1 -1
- package/dist/index.cjs +49 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +49 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -81,7 +81,7 @@ interface PluralIssue {
|
|
|
81
81
|
* (which injects a fixture's `rng` strategy). Keeping the rng a parameter is what
|
|
82
82
|
* lets the deterministic parity fixtures assert exact output.
|
|
83
83
|
*
|
|
84
|
-
* Order: strip stray sentinels →
|
|
84
|
+
* Order: strip stray sentinels → build vars, roll #def, tree-walk (+ #include) →
|
|
85
85
|
* cosmetic post-process (if on) → mandatory neutralize safety-restore (always).
|
|
86
86
|
*/
|
|
87
87
|
|
package/dist/index.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ interface PluralIssue {
|
|
|
81
81
|
* (which injects a fixture's `rng` strategy). Keeping the rng a parameter is what
|
|
82
82
|
* lets the deterministic parity fixtures assert exact output.
|
|
83
83
|
*
|
|
84
|
-
* Order: strip stray sentinels →
|
|
84
|
+
* Order: strip stray sentinels → build vars, roll #def, tree-walk (+ #include) →
|
|
85
85
|
* cosmetic post-process (if on) → mandatory neutralize safety-restore (always).
|
|
86
86
|
*/
|
|
87
87
|
|
package/dist/index.js
CHANGED
|
@@ -21,13 +21,38 @@ function isParsedAst(value) {
|
|
|
21
21
|
return typeof value === "object" && value !== null && value.astVersion === AST_VERSION && typeof value.source === "string" && Array.isArray(value.nodes);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// src/internal/neutralize.ts
|
|
25
|
+
var STRUCTURAL = ["{", "}", "[", "]", "%", "#"];
|
|
26
|
+
var SENTINEL_BASE = 57344;
|
|
27
|
+
var SHIELD = /* @__PURE__ */ new Map();
|
|
28
|
+
var RESTORE = /* @__PURE__ */ new Map();
|
|
29
|
+
STRUCTURAL.forEach((ch, i) => {
|
|
30
|
+
const sentinel = String.fromCharCode(SENTINEL_BASE + i);
|
|
31
|
+
SHIELD.set(ch, sentinel);
|
|
32
|
+
RESTORE.set(sentinel, ch);
|
|
33
|
+
});
|
|
34
|
+
var SHIELD_RE = /[{}[\]%#]/gu;
|
|
35
|
+
var RESTORE_RE = new RegExp(
|
|
36
|
+
`[${String.fromCharCode(SENTINEL_BASE)}-${String.fromCharCode(SENTINEL_BASE + STRUCTURAL.length - 1)}]`,
|
|
37
|
+
"gu"
|
|
38
|
+
);
|
|
39
|
+
function neutralize(value) {
|
|
40
|
+
return value.replace(SHIELD_RE, (ch) => SHIELD.get(ch) ?? ch);
|
|
41
|
+
}
|
|
42
|
+
function safetyRestore(text) {
|
|
43
|
+
return text.replace(RESTORE_RE, (ch) => RESTORE.get(ch) ?? ch);
|
|
44
|
+
}
|
|
45
|
+
function stripSentinels(text) {
|
|
46
|
+
return text.replace(RESTORE_RE, "");
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
// src/internal/parser.ts
|
|
25
50
|
var VARIABLE_RE = /^%(\w+)%/;
|
|
26
51
|
var DIRECTIVE_RE = /^[ \t]*#(set|def)[ \t]+%(\w+)%[ \t]*=[ \t]*(.*?)[ \t]*\r?$/gmu;
|
|
27
52
|
var CONDITIONAL_NAME_RE = /^[A-Za-z_]\w*/;
|
|
28
53
|
var PLURAL_PREFIX = "plural ";
|
|
29
54
|
function parseTemplate(src) {
|
|
30
|
-
const { body, setDefs, defDefs } = extractDirectives(stripComments(src));
|
|
55
|
+
const { body, setDefs, defDefs } = extractDirectives(stripComments(stripSentinels(src)));
|
|
31
56
|
return { astVersion: AST_VERSION, source: src, setDefs, defDefs, nodes: parseSequence(body) };
|
|
32
57
|
}
|
|
33
58
|
function extractDirectives(text) {
|
|
@@ -700,31 +725,6 @@ var NotImplementedError = class extends SpintaxError {
|
|
|
700
725
|
}
|
|
701
726
|
};
|
|
702
727
|
|
|
703
|
-
// src/internal/neutralize.ts
|
|
704
|
-
var STRUCTURAL = ["{", "}", "[", "]", "%", "#"];
|
|
705
|
-
var SENTINEL_BASE = 57344;
|
|
706
|
-
var SHIELD = /* @__PURE__ */ new Map();
|
|
707
|
-
var RESTORE = /* @__PURE__ */ new Map();
|
|
708
|
-
STRUCTURAL.forEach((ch, i) => {
|
|
709
|
-
const sentinel = String.fromCharCode(SENTINEL_BASE + i);
|
|
710
|
-
SHIELD.set(ch, sentinel);
|
|
711
|
-
RESTORE.set(sentinel, ch);
|
|
712
|
-
});
|
|
713
|
-
var SHIELD_RE = /[{}[\]%#]/gu;
|
|
714
|
-
var RESTORE_RE = new RegExp(
|
|
715
|
-
`[${String.fromCharCode(SENTINEL_BASE)}-${String.fromCharCode(SENTINEL_BASE + STRUCTURAL.length - 1)}]`,
|
|
716
|
-
"gu"
|
|
717
|
-
);
|
|
718
|
-
function neutralize(value) {
|
|
719
|
-
return value.replace(SHIELD_RE, (ch) => SHIELD.get(ch) ?? ch);
|
|
720
|
-
}
|
|
721
|
-
function safetyRestore(text) {
|
|
722
|
-
return text.replace(RESTORE_RE, (ch) => RESTORE.get(ch) ?? ch);
|
|
723
|
-
}
|
|
724
|
-
function stripSentinels(text) {
|
|
725
|
-
return text.replace(RESTORE_RE, "");
|
|
726
|
-
}
|
|
727
|
-
|
|
728
728
|
// src/internal/render.ts
|
|
729
729
|
var MAX_VARIABLE_DEPTH = 50;
|
|
730
730
|
var INCLUDE_LINE_RE = /^[ \t]*#include[ \t\n\r\f\x0B]+"([^"]+)"[ \t\n\r\f\x0B]*$/gmu;
|
|
@@ -751,7 +751,7 @@ function resolveIncludes(text, ctx) {
|
|
|
751
751
|
throw new IncludeResolverError(`includeResolver threw for "${ref}"`, { cause });
|
|
752
752
|
}
|
|
753
753
|
if (included === null) return "";
|
|
754
|
-
return renderAst(parseTemplate(
|
|
754
|
+
return renderAst(parseTemplate(included), {
|
|
755
755
|
...ctx,
|
|
756
756
|
runtimeContext: ctx.runtimeContext,
|
|
757
757
|
// child inherits runtime, not parent #set
|
|
@@ -877,7 +877,7 @@ function renderPlural(node, opts) {
|
|
|
877
877
|
if (/[{}[\]]/u.test(formsRaw)) {
|
|
878
878
|
report({
|
|
879
879
|
code: "plural.nested-brackets",
|
|
880
|
-
message: "Plural form slot contains nested spintax brackets; extract via #set
|
|
880
|
+
message: "Plural form slot contains nested spintax brackets; extract via #def first \u2014 a #set is substituted verbatim and would put the brackets straight back.",
|
|
881
881
|
construct: rawConstruct(countRaw, formsRaw),
|
|
882
882
|
locale: base
|
|
883
883
|
});
|
|
@@ -1036,8 +1036,9 @@ var SINGLE_ABBREVS = [
|
|
|
1036
1036
|
var WS = " \\t\\r\\n\\f\\x0B";
|
|
1037
1037
|
var S = `[${WS}]`;
|
|
1038
1038
|
var DOMAIN_PART = "(?:(?:(?:xn--)?[\\p{L}\\p{N}]+(?:-[\\p{L}\\p{N}]+)*)\\.)+(?:xn--[a-z0-9\\-]{2,59}|[\\p{L}][\\p{L}\\p{N}-]{1,62})";
|
|
1039
|
-
var
|
|
1040
|
-
var
|
|
1039
|
+
var URI_BODY = `[^\\x00${WS}<>"')\\]]`;
|
|
1040
|
+
var URI_RE = new RegExp(`(?:(?:https?|ftp):\\/\\/|(?:mailto|tel):)${URI_BODY}+`, "giu");
|
|
1041
|
+
var MAILTEL_PREFIX_RE = /^(?:mailto|tel):/iu;
|
|
1041
1042
|
var EMAIL_RE = new RegExp(`[a-z0-9._%+\\-]+@${DOMAIN_PART}\\b`, "giu");
|
|
1042
1043
|
var DOMAIN_RE = new RegExp(`\\b${DOMAIN_PART}\\b`, "giu");
|
|
1043
1044
|
var DECIMAL_RE = /\b\d+\.\d+\b/gu;
|
|
@@ -1058,6 +1059,18 @@ var CAP_AFTER_BLOCK_RE = new RegExp(
|
|
|
1058
1059
|
);
|
|
1059
1060
|
var CAP_AFTER_BREAK_RE = new RegExp(`(\\n${LEAD})(\\p{Ll})`, "gu");
|
|
1060
1061
|
var up = (ch) => ch.toUpperCase();
|
|
1062
|
+
var SHIELD_PREFIXES = ["URL", "URI", "EMAIL", "DOM", "NUM", "ABBR"];
|
|
1063
|
+
var RESTORE_RE2 = new RegExp(`\\x00(?:${SHIELD_PREFIXES.join("|")})_\\d+\\x00`, "gu");
|
|
1064
|
+
function restore(text, input, placeholders) {
|
|
1065
|
+
if (!input.includes("\0")) {
|
|
1066
|
+
return text.replace(RESTORE_RE2, (key) => placeholders.get(key) ?? key);
|
|
1067
|
+
}
|
|
1068
|
+
let out = text;
|
|
1069
|
+
for (const [key, value] of placeholders) {
|
|
1070
|
+
out = out.split(key).join(value);
|
|
1071
|
+
}
|
|
1072
|
+
return out;
|
|
1073
|
+
}
|
|
1061
1074
|
function postProcess(input) {
|
|
1062
1075
|
const placeholders = /* @__PURE__ */ new Map();
|
|
1063
1076
|
let counter = 0;
|
|
@@ -1077,8 +1090,10 @@ function postProcess(input) {
|
|
|
1077
1090
|
return store(value, prefix);
|
|
1078
1091
|
};
|
|
1079
1092
|
let text = input;
|
|
1080
|
-
text = text.replace(
|
|
1081
|
-
|
|
1093
|
+
text = text.replace(
|
|
1094
|
+
URI_RE,
|
|
1095
|
+
(m) => storeWithTrailingPunct(m, MAILTEL_PREFIX_RE.test(m) ? "URI" : "URL")
|
|
1096
|
+
);
|
|
1082
1097
|
text = text.replace(EMAIL_RE, (m) => store(m, "EMAIL"));
|
|
1083
1098
|
text = text.replace(DOMAIN_RE, (m) => store(m, "DOM"));
|
|
1084
1099
|
text = text.replace(DECIMAL_RE, (m) => store(m, "NUM"));
|
|
@@ -1093,10 +1108,7 @@ function postProcess(input) {
|
|
|
1093
1108
|
text = text.replace(CAP_AFTER_SENTENCE_RE, (_m, p, gap, ch) => p + gap + up(ch));
|
|
1094
1109
|
text = text.replace(CAP_AFTER_BLOCK_RE, (_m, tag, ch) => tag + up(ch));
|
|
1095
1110
|
text = text.replace(CAP_AFTER_BREAK_RE, (_m, br, ch) => br + up(ch));
|
|
1096
|
-
|
|
1097
|
-
text = text.split(key).join(value);
|
|
1098
|
-
}
|
|
1099
|
-
return text.trim();
|
|
1111
|
+
return restore(text, input, placeholders).trim();
|
|
1100
1112
|
}
|
|
1101
1113
|
|
|
1102
1114
|
// src/internal/pipeline.ts
|
|
@@ -1111,7 +1123,7 @@ function renderWith(input, rng, opts = {}) {
|
|
|
1111
1123
|
includeStack: [],
|
|
1112
1124
|
onPluralError: opts.onPluralError
|
|
1113
1125
|
};
|
|
1114
|
-
const ast = resolveAst(
|
|
1126
|
+
const ast = resolveAst(input);
|
|
1115
1127
|
let out = renderAst(ast, ctx);
|
|
1116
1128
|
if (opts.postProcess !== false) out = postProcess(out);
|
|
1117
1129
|
return safetyRestore(out);
|