@rohal12/spindle 0.56.0 → 0.58.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/pkg/format.js +1 -1
- package/dist/pkg/headless.js +638 -414
- package/dist/pkg/macro-registry.json +2 -1
- package/dist/pkg/story-variables.js +611 -157
- package/dist/pkg/tooling.js +39 -2
- package/package.json +1 -1
- package/src/code-check.ts +129 -21
- package/src/components/macros/Include.tsx +2 -6
- package/src/components/macros/MacroLink.tsx +33 -7
- package/src/components/macros/arg-utils.ts +37 -0
- package/src/components/macros/include-args.ts +23 -0
- package/src/components/macros/macro-args.ts +61 -5
- package/src/components/macros/option-utils.ts +17 -6
- package/src/define-macro.ts +22 -2
- package/src/expression.ts +4 -54
- package/src/markup/grammar-shared.ts +8 -0
- package/src/markup/parse.ts +89 -2
- package/src/markup/spindle.d.peggy.ts +5 -2
- package/src/markup/spindle.peggy +6 -7
- package/src/markup/validate.ts +16 -3
- package/src/registry.ts +7 -0
- package/src/story-variables.ts +48 -10
- package/src/tooling.ts +59 -0
- package/src/transform.ts +65 -0
- package/types/tooling.d.ts +311 -0
|
@@ -1,12 +1,39 @@
|
|
|
1
|
-
//#region src/markup/
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var SIGIL_SCOPES$1 = {
|
|
1
|
+
//#region src/markup/tokens.ts
|
|
2
|
+
/** Variable sigils: story ($), temporary (_), local (@), transient (%). */
|
|
3
|
+
var SIGIL_SCOPES = {
|
|
5
4
|
$: "variable",
|
|
6
5
|
_: "temporary",
|
|
7
6
|
"@": "local",
|
|
8
7
|
"%": "transient"
|
|
9
8
|
};
|
|
9
|
+
Object.fromEntries(Object.entries(SIGIL_SCOPES).map(([sigil, scope]) => [scope, sigil]));
|
|
10
|
+
var SIGIL_CHARS = new Set(Object.keys(SIGIL_SCOPES));
|
|
11
|
+
/** Whether `c` is a variable sigil. */
|
|
12
|
+
function isSigil(c) {
|
|
13
|
+
return c !== void 0 && SIGIL_CHARS.has(c);
|
|
14
|
+
}
|
|
15
|
+
/** Copy the selectors set in `from` onto `target`, and return it. */
|
|
16
|
+
function withSelectors$1(target, from) {
|
|
17
|
+
if (from.className) target.className = from.className;
|
|
18
|
+
if (from.id) target.id = from.id;
|
|
19
|
+
return target;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/markup/grammar-shared.ts
|
|
23
|
+
/**
|
|
24
|
+
* What the grammar (spindle.peggy) imports from the rest of Spindle, so that
|
|
25
|
+
* it holds no second copy: the generated parser imports this module's
|
|
26
|
+
* default export as `shared` (see scripts/peggy.ts).
|
|
27
|
+
*/
|
|
28
|
+
var grammar_shared_default = {
|
|
29
|
+
SIGIL_SCOPES,
|
|
30
|
+
withSelectors: withSelectors$1
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/markup/spindle.peggy
|
|
34
|
+
var LB = String.fromCharCode(123);
|
|
35
|
+
var RB = String.fromCharCode(125);
|
|
36
|
+
var { SIGIL_SCOPES: SIGIL_SCOPES$1, withSelectors } = grammar_shared_default;
|
|
10
37
|
var VOID_TAGS = new Set([
|
|
11
38
|
"area",
|
|
12
39
|
"base",
|
|
@@ -64,11 +91,6 @@ function parseLink(inner) {
|
|
|
64
91
|
function quoteArg(value) {
|
|
65
92
|
return "\"" + value.replace(/[\\"]/g, "\\$&") + "\"";
|
|
66
93
|
}
|
|
67
|
-
function withSelectors(target, from) {
|
|
68
|
-
if (from.className) target.className = from.className;
|
|
69
|
-
if (from.id) target.id = from.id;
|
|
70
|
-
return target;
|
|
71
|
-
}
|
|
72
94
|
function selectorsOf(parts) {
|
|
73
95
|
const classes = [];
|
|
74
96
|
let id = "";
|
|
@@ -288,7 +310,8 @@ function peg$parse(input, options) {
|
|
|
288
310
|
const peg$source = options.grammarSource;
|
|
289
311
|
const peg$startRuleFunctions = {
|
|
290
312
|
Markup: peg$parseMarkup,
|
|
291
|
-
Tokens: peg$parseTokens
|
|
313
|
+
Tokens: peg$parseTokens,
|
|
314
|
+
SelectorsPrefix: peg$parseSelectorsPrefix
|
|
292
315
|
};
|
|
293
316
|
let peg$startRuleFunction = peg$parseMarkup;
|
|
294
317
|
const peg$c0 = "\\";
|
|
@@ -690,40 +713,46 @@ function peg$parse(input, options) {
|
|
|
690
713
|
function peg$f53() {
|
|
691
714
|
return NO_SELECTORS;
|
|
692
715
|
}
|
|
693
|
-
function peg$f54(
|
|
716
|
+
function peg$f54(s, end) {
|
|
717
|
+
return {
|
|
718
|
+
...s,
|
|
719
|
+
end
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function peg$f55(prefix, name) {
|
|
694
723
|
return [prefix, name];
|
|
695
724
|
}
|
|
696
|
-
function peg$
|
|
725
|
+
function peg$f56(s, sigil, name) {
|
|
697
726
|
return withSelectors(lexeme("variable", location(), {
|
|
698
727
|
name,
|
|
699
728
|
scope: SIGIL_SCOPES$1[sigil]
|
|
700
729
|
}), s);
|
|
701
730
|
}
|
|
702
|
-
function peg$
|
|
731
|
+
function peg$f57(s, expression) {
|
|
703
732
|
return withSelectors(lexeme("expression", location(), { expression }), s);
|
|
704
733
|
}
|
|
705
|
-
function peg$
|
|
734
|
+
function peg$f58() {
|
|
706
735
|
fail("Unclosed " + LB + input.substr(location().end.offset, 1) + "…: no } ends it", location());
|
|
707
736
|
}
|
|
708
|
-
function peg$
|
|
737
|
+
function peg$f59(start) {
|
|
709
738
|
return skipTo(hooks.closeBrace(input, start, start));
|
|
710
739
|
}
|
|
711
|
-
function peg$
|
|
740
|
+
function peg$f60(start) {
|
|
712
741
|
return skipEnd - offset();
|
|
713
742
|
}
|
|
714
|
-
function peg$
|
|
743
|
+
function peg$f61(start, code) {
|
|
715
744
|
return code;
|
|
716
745
|
}
|
|
717
|
-
function peg$
|
|
746
|
+
function peg$f62(s, start, close, name, nameEnd) {
|
|
718
747
|
return skipTo(hooks.closeBrace(input, nameEnd, start));
|
|
719
748
|
}
|
|
720
|
-
function peg$
|
|
749
|
+
function peg$f63(s, start, close, name, nameEnd) {
|
|
721
750
|
return skipEnd - offset();
|
|
722
751
|
}
|
|
723
|
-
function peg$
|
|
752
|
+
function peg$f64(s, start, close, name, nameEnd, args) {
|
|
724
753
|
return args;
|
|
725
754
|
}
|
|
726
|
-
function peg$
|
|
755
|
+
function peg$f65(s, start, close, name, nameEnd, tail) {
|
|
727
756
|
const loc = location();
|
|
728
757
|
const lower = name.toLowerCase();
|
|
729
758
|
const tag = lexeme("macro", loc, {
|
|
@@ -744,19 +773,19 @@ function peg$parse(input, options) {
|
|
|
744
773
|
}
|
|
745
774
|
return tag;
|
|
746
775
|
}
|
|
747
|
-
function peg$
|
|
776
|
+
function peg$f66(s) {
|
|
748
777
|
fail("A closing tag starts with a letter after " + LB + "/", location());
|
|
749
778
|
}
|
|
750
|
-
function peg$
|
|
779
|
+
function peg$f67() {
|
|
751
780
|
return !textMode;
|
|
752
781
|
}
|
|
753
|
-
function peg$
|
|
782
|
+
function peg$f68(tag) {
|
|
754
783
|
return VOID_TAGS.has(tag.lower);
|
|
755
784
|
}
|
|
756
|
-
function peg$
|
|
785
|
+
function peg$f69(tag) {
|
|
757
786
|
return null;
|
|
758
787
|
}
|
|
759
|
-
function peg$
|
|
788
|
+
function peg$f70(tag, list, slash) {
|
|
760
789
|
const lower = tag.toLowerCase();
|
|
761
790
|
return lexeme("html", location(), {
|
|
762
791
|
tag,
|
|
@@ -766,7 +795,7 @@ function peg$parse(input, options) {
|
|
|
766
795
|
isSelfClose: !!slash || VOID_TAGS.has(lower)
|
|
767
796
|
});
|
|
768
797
|
}
|
|
769
|
-
function peg$
|
|
798
|
+
function peg$f71(tag, bad) {
|
|
770
799
|
const c = input.charAt(bad);
|
|
771
800
|
if (!c) fail("Unclosed tag <" + tag + ": no > ends it (write < for a < that starts no tag)", location());
|
|
772
801
|
const at = {
|
|
@@ -784,7 +813,7 @@ function peg$parse(input, options) {
|
|
|
784
813
|
};
|
|
785
814
|
fail("Unexpected " + JSON.stringify(c) + " in the tag <" + tag + ">: expected an attribute, or > to end the tag", at);
|
|
786
815
|
}
|
|
787
|
-
function peg$
|
|
816
|
+
function peg$f72(tag) {
|
|
788
817
|
return lexeme("html", location(), {
|
|
789
818
|
tag,
|
|
790
819
|
lower: tag.toLowerCase(),
|
|
@@ -793,44 +822,44 @@ function peg$parse(input, options) {
|
|
|
793
822
|
isSelfClose: false
|
|
794
823
|
});
|
|
795
824
|
}
|
|
796
|
-
function peg$
|
|
825
|
+
function peg$f73(name) {
|
|
797
826
|
attrCode = hooks.isCodeAttribute(name);
|
|
798
827
|
return true;
|
|
799
828
|
}
|
|
800
|
-
function peg$
|
|
829
|
+
function peg$f74(name, v) {
|
|
801
830
|
return v;
|
|
802
831
|
}
|
|
803
|
-
function peg$
|
|
832
|
+
function peg$f75(name, value) {
|
|
804
833
|
return [name, value ?? ""];
|
|
805
834
|
}
|
|
806
|
-
function peg$
|
|
835
|
+
function peg$f76(v) {
|
|
807
836
|
return v;
|
|
808
837
|
}
|
|
809
|
-
function peg$
|
|
838
|
+
function peg$f77(v) {
|
|
810
839
|
return v;
|
|
811
840
|
}
|
|
812
|
-
function peg$
|
|
841
|
+
function peg$f78(q) {
|
|
813
842
|
fail("Unclosed attribute value: the " + q + " here has no closing " + q, location());
|
|
814
843
|
}
|
|
815
|
-
function peg$
|
|
844
|
+
function peg$f79() {
|
|
816
845
|
return !attrCode;
|
|
817
846
|
}
|
|
818
|
-
function peg$
|
|
847
|
+
function peg$f80(bs) {
|
|
819
848
|
return bs.length % 2 === 1;
|
|
820
849
|
}
|
|
821
|
-
function peg$
|
|
850
|
+
function peg$f81() {
|
|
822
851
|
return !attrCode;
|
|
823
852
|
}
|
|
824
|
-
function peg$
|
|
853
|
+
function peg$f82(start, nameEnd) {
|
|
825
854
|
return skipTo(hooks.closeBrace(input, nameEnd, start));
|
|
826
855
|
}
|
|
827
|
-
function peg$
|
|
856
|
+
function peg$f83(start, nameEnd) {
|
|
828
857
|
return skipEnd - offset();
|
|
829
858
|
}
|
|
830
|
-
function peg$
|
|
859
|
+
function peg$f84() {
|
|
831
860
|
return !attrCode;
|
|
832
861
|
}
|
|
833
|
-
function peg$
|
|
862
|
+
function peg$f85() {
|
|
834
863
|
return attrCode;
|
|
835
864
|
}
|
|
836
865
|
let peg$currPos = options.peg$currPos | 0;
|
|
@@ -1919,6 +1948,38 @@ function peg$parse(input, options) {
|
|
|
1919
1948
|
}
|
|
1920
1949
|
return s0;
|
|
1921
1950
|
}
|
|
1951
|
+
function peg$parseSelectorsPrefix() {
|
|
1952
|
+
let s0, s1, s2, s3, s4;
|
|
1953
|
+
s0 = peg$currPos;
|
|
1954
|
+
s1 = peg$parseSelectors();
|
|
1955
|
+
if (s1 !== peg$FAILED) {
|
|
1956
|
+
s2 = peg$parseHere();
|
|
1957
|
+
s3 = [];
|
|
1958
|
+
if (input.length > peg$currPos) {
|
|
1959
|
+
s4 = input.charAt(peg$currPos);
|
|
1960
|
+
peg$currPos++;
|
|
1961
|
+
} else {
|
|
1962
|
+
s4 = peg$FAILED;
|
|
1963
|
+
if (peg$silentFails === 0) peg$fail(peg$e0);
|
|
1964
|
+
}
|
|
1965
|
+
while (s4 !== peg$FAILED) {
|
|
1966
|
+
s3.push(s4);
|
|
1967
|
+
if (input.length > peg$currPos) {
|
|
1968
|
+
s4 = input.charAt(peg$currPos);
|
|
1969
|
+
peg$currPos++;
|
|
1970
|
+
} else {
|
|
1971
|
+
s4 = peg$FAILED;
|
|
1972
|
+
if (peg$silentFails === 0) peg$fail(peg$e0);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
peg$savedPos = s0;
|
|
1976
|
+
s0 = peg$f54(s1, s2);
|
|
1977
|
+
} else {
|
|
1978
|
+
peg$currPos = s0;
|
|
1979
|
+
s0 = peg$FAILED;
|
|
1980
|
+
}
|
|
1981
|
+
return s0;
|
|
1982
|
+
}
|
|
1922
1983
|
function peg$parseSelectorPart() {
|
|
1923
1984
|
let s0, s1, s2, s3, s4;
|
|
1924
1985
|
s0 = peg$currPos;
|
|
@@ -1938,7 +1999,7 @@ function peg$parse(input, options) {
|
|
|
1938
1999
|
}
|
|
1939
2000
|
s2 = input.substring(s2, peg$currPos);
|
|
1940
2001
|
peg$savedPos = s0;
|
|
1941
|
-
s0 = peg$
|
|
2002
|
+
s0 = peg$f55(s1, s2);
|
|
1942
2003
|
} else {
|
|
1943
2004
|
peg$currPos = s0;
|
|
1944
2005
|
s0 = peg$FAILED;
|
|
@@ -2063,7 +2124,7 @@ function peg$parse(input, options) {
|
|
|
2063
2124
|
}
|
|
2064
2125
|
if (s5 !== peg$FAILED) {
|
|
2065
2126
|
peg$savedPos = s0;
|
|
2066
|
-
s0 = peg$
|
|
2127
|
+
s0 = peg$f56(s2, s3, s4);
|
|
2067
2128
|
} else {
|
|
2068
2129
|
peg$currPos = s0;
|
|
2069
2130
|
s0 = peg$FAILED;
|
|
@@ -2117,7 +2178,7 @@ function peg$parse(input, options) {
|
|
|
2117
2178
|
}
|
|
2118
2179
|
if (s5 !== peg$FAILED) {
|
|
2119
2180
|
peg$savedPos = s0;
|
|
2120
|
-
s0 = peg$
|
|
2181
|
+
s0 = peg$f57(s2, s4);
|
|
2121
2182
|
} else {
|
|
2122
2183
|
peg$currPos = s0;
|
|
2123
2184
|
s0 = peg$FAILED;
|
|
@@ -2165,7 +2226,7 @@ function peg$parse(input, options) {
|
|
|
2165
2226
|
} else s3 = peg$FAILED;
|
|
2166
2227
|
if (s3 !== peg$FAILED) {
|
|
2167
2228
|
peg$savedPos = s0;
|
|
2168
|
-
s0 = peg$
|
|
2229
|
+
s0 = peg$f58();
|
|
2169
2230
|
} else {
|
|
2170
2231
|
peg$currPos = s0;
|
|
2171
2232
|
s0 = peg$FAILED;
|
|
@@ -2187,12 +2248,12 @@ function peg$parse(input, options) {
|
|
|
2187
2248
|
s0 = peg$currPos;
|
|
2188
2249
|
s1 = peg$parseHere();
|
|
2189
2250
|
peg$savedPos = peg$currPos;
|
|
2190
|
-
s2 = peg$
|
|
2251
|
+
s2 = peg$f59(s1);
|
|
2191
2252
|
if (s2) s2 = void 0;
|
|
2192
2253
|
else s2 = peg$FAILED;
|
|
2193
2254
|
if (s2 !== peg$FAILED) {
|
|
2194
2255
|
s3 = peg$currPos;
|
|
2195
|
-
s4 = peg$
|
|
2256
|
+
s4 = peg$f60(s1);
|
|
2196
2257
|
s5 = peg$currPos;
|
|
2197
2258
|
s6 = [];
|
|
2198
2259
|
if (s6.length >= (s4 | 0)) s7 = peg$FAILED;
|
|
@@ -2223,7 +2284,7 @@ function peg$parse(input, options) {
|
|
|
2223
2284
|
else s3 = s4;
|
|
2224
2285
|
if (s3 !== peg$FAILED) {
|
|
2225
2286
|
peg$savedPos = s0;
|
|
2226
|
-
s0 = peg$
|
|
2287
|
+
s0 = peg$f61(s1, s3);
|
|
2227
2288
|
} else {
|
|
2228
2289
|
peg$currPos = s0;
|
|
2229
2290
|
s0 = peg$FAILED;
|
|
@@ -2261,12 +2322,12 @@ function peg$parse(input, options) {
|
|
|
2261
2322
|
s6 = peg$parseHere();
|
|
2262
2323
|
s7 = peg$currPos;
|
|
2263
2324
|
peg$savedPos = peg$currPos;
|
|
2264
|
-
s8 = peg$
|
|
2325
|
+
s8 = peg$f62(s2, s3, s4, s5, s6);
|
|
2265
2326
|
if (s8) s8 = void 0;
|
|
2266
2327
|
else s8 = peg$FAILED;
|
|
2267
2328
|
if (s8 !== peg$FAILED) {
|
|
2268
2329
|
s9 = peg$currPos;
|
|
2269
|
-
s10 = peg$
|
|
2330
|
+
s10 = peg$f63(s2, s3, s4, s5, s6);
|
|
2270
2331
|
s11 = peg$currPos;
|
|
2271
2332
|
s12 = [];
|
|
2272
2333
|
if (s12.length >= (s10 | 0)) s13 = peg$FAILED;
|
|
@@ -2305,7 +2366,7 @@ function peg$parse(input, options) {
|
|
|
2305
2366
|
}
|
|
2306
2367
|
if (s10 !== peg$FAILED) {
|
|
2307
2368
|
peg$savedPos = s7;
|
|
2308
|
-
s7 = peg$
|
|
2369
|
+
s7 = peg$f64(s2, s3, s4, s5, s6, s9);
|
|
2309
2370
|
} else {
|
|
2310
2371
|
peg$currPos = s7;
|
|
2311
2372
|
s7 = peg$FAILED;
|
|
@@ -2320,7 +2381,7 @@ function peg$parse(input, options) {
|
|
|
2320
2381
|
}
|
|
2321
2382
|
if (s7 === peg$FAILED) s7 = null;
|
|
2322
2383
|
peg$savedPos = s0;
|
|
2323
|
-
s0 = peg$
|
|
2384
|
+
s0 = peg$f65(s2, s3, s4, s5, s6, s7);
|
|
2324
2385
|
} else {
|
|
2325
2386
|
peg$currPos = s0;
|
|
2326
2387
|
s0 = peg$FAILED;
|
|
@@ -2354,7 +2415,7 @@ function peg$parse(input, options) {
|
|
|
2354
2415
|
}
|
|
2355
2416
|
if (s3 !== peg$FAILED) {
|
|
2356
2417
|
peg$savedPos = s0;
|
|
2357
|
-
s0 = peg$
|
|
2418
|
+
s0 = peg$f66(s2);
|
|
2358
2419
|
} else {
|
|
2359
2420
|
peg$currPos = s0;
|
|
2360
2421
|
s0 = peg$FAILED;
|
|
@@ -2411,19 +2472,19 @@ function peg$parse(input, options) {
|
|
|
2411
2472
|
let s0, s1, s2, s3;
|
|
2412
2473
|
s0 = peg$currPos;
|
|
2413
2474
|
peg$savedPos = peg$currPos;
|
|
2414
|
-
s1 = peg$
|
|
2475
|
+
s1 = peg$f67();
|
|
2415
2476
|
if (s1) s1 = void 0;
|
|
2416
2477
|
else s1 = peg$FAILED;
|
|
2417
2478
|
if (s1 !== peg$FAILED) {
|
|
2418
2479
|
s2 = peg$parseHtmlClose();
|
|
2419
2480
|
if (s2 !== peg$FAILED) {
|
|
2420
2481
|
peg$savedPos = peg$currPos;
|
|
2421
|
-
s3 = peg$
|
|
2482
|
+
s3 = peg$f68(s2);
|
|
2422
2483
|
if (s3) s3 = void 0;
|
|
2423
2484
|
else s3 = peg$FAILED;
|
|
2424
2485
|
if (s3 !== peg$FAILED) {
|
|
2425
2486
|
peg$savedPos = s0;
|
|
2426
|
-
s0 = peg$
|
|
2487
|
+
s0 = peg$f69(s2);
|
|
2427
2488
|
} else {
|
|
2428
2489
|
peg$currPos = s0;
|
|
2429
2490
|
s0 = peg$FAILED;
|
|
@@ -2475,7 +2536,7 @@ function peg$parse(input, options) {
|
|
|
2475
2536
|
}
|
|
2476
2537
|
if (s6 !== peg$FAILED) {
|
|
2477
2538
|
peg$savedPos = s0;
|
|
2478
|
-
s0 = peg$
|
|
2539
|
+
s0 = peg$f70(s2, s3, s5);
|
|
2479
2540
|
} else {
|
|
2480
2541
|
peg$currPos = s0;
|
|
2481
2542
|
s0 = peg$FAILED;
|
|
@@ -2540,7 +2601,7 @@ function peg$parse(input, options) {
|
|
|
2540
2601
|
s5 = peg$parse_();
|
|
2541
2602
|
s6 = peg$parseHere();
|
|
2542
2603
|
peg$savedPos = s0;
|
|
2543
|
-
s0 = peg$
|
|
2604
|
+
s0 = peg$f71(s2, s6);
|
|
2544
2605
|
} else {
|
|
2545
2606
|
peg$currPos = s0;
|
|
2546
2607
|
s0 = peg$FAILED;
|
|
@@ -2579,7 +2640,7 @@ function peg$parse(input, options) {
|
|
|
2579
2640
|
}
|
|
2580
2641
|
if (s4 !== peg$FAILED) {
|
|
2581
2642
|
peg$savedPos = s0;
|
|
2582
|
-
s0 = peg$
|
|
2643
|
+
s0 = peg$f72(s2);
|
|
2583
2644
|
} else {
|
|
2584
2645
|
peg$currPos = s0;
|
|
2585
2646
|
s0 = peg$FAILED;
|
|
@@ -2713,7 +2774,7 @@ function peg$parse(input, options) {
|
|
|
2713
2774
|
else s2 = s3;
|
|
2714
2775
|
if (s2 !== peg$FAILED) {
|
|
2715
2776
|
peg$savedPos = peg$currPos;
|
|
2716
|
-
s3 = peg$
|
|
2777
|
+
s3 = peg$f73(s2);
|
|
2717
2778
|
if (s3) s3 = void 0;
|
|
2718
2779
|
else s3 = peg$FAILED;
|
|
2719
2780
|
if (s3 !== peg$FAILED) {
|
|
@@ -2731,7 +2792,7 @@ function peg$parse(input, options) {
|
|
|
2731
2792
|
s8 = peg$parseAttrValue();
|
|
2732
2793
|
if (s8 !== peg$FAILED) {
|
|
2733
2794
|
peg$savedPos = s4;
|
|
2734
|
-
s4 = peg$
|
|
2795
|
+
s4 = peg$f74(s2, s8);
|
|
2735
2796
|
} else {
|
|
2736
2797
|
peg$currPos = s4;
|
|
2737
2798
|
s4 = peg$FAILED;
|
|
@@ -2742,7 +2803,7 @@ function peg$parse(input, options) {
|
|
|
2742
2803
|
}
|
|
2743
2804
|
if (s4 === peg$FAILED) s4 = null;
|
|
2744
2805
|
peg$savedPos = s0;
|
|
2745
|
-
s0 = peg$
|
|
2806
|
+
s0 = peg$f75(s2, s4);
|
|
2746
2807
|
} else {
|
|
2747
2808
|
peg$currPos = s0;
|
|
2748
2809
|
s0 = peg$FAILED;
|
|
@@ -2797,7 +2858,7 @@ function peg$parse(input, options) {
|
|
|
2797
2858
|
}
|
|
2798
2859
|
if (s3 !== peg$FAILED) {
|
|
2799
2860
|
peg$savedPos = s0;
|
|
2800
|
-
s0 = peg$
|
|
2861
|
+
s0 = peg$f76(s2);
|
|
2801
2862
|
} else {
|
|
2802
2863
|
peg$currPos = s0;
|
|
2803
2864
|
s0 = peg$FAILED;
|
|
@@ -2849,7 +2910,7 @@ function peg$parse(input, options) {
|
|
|
2849
2910
|
}
|
|
2850
2911
|
if (s3 !== peg$FAILED) {
|
|
2851
2912
|
peg$savedPos = s0;
|
|
2852
|
-
s0 = peg$
|
|
2913
|
+
s0 = peg$f77(s2);
|
|
2853
2914
|
} else {
|
|
2854
2915
|
peg$currPos = s0;
|
|
2855
2916
|
s0 = peg$FAILED;
|
|
@@ -2868,7 +2929,7 @@ function peg$parse(input, options) {
|
|
|
2868
2929
|
}
|
|
2869
2930
|
if (s1 !== peg$FAILED) {
|
|
2870
2931
|
peg$savedPos = s0;
|
|
2871
|
-
s1 = peg$
|
|
2932
|
+
s1 = peg$f78(s1);
|
|
2872
2933
|
}
|
|
2873
2934
|
s0 = s1;
|
|
2874
2935
|
if (s0 === peg$FAILED) {
|
|
@@ -2899,7 +2960,7 @@ function peg$parse(input, options) {
|
|
|
2899
2960
|
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
|
|
2900
2961
|
s0 = peg$currPos;
|
|
2901
2962
|
peg$savedPos = peg$currPos;
|
|
2902
|
-
s1 = peg$
|
|
2963
|
+
s1 = peg$f79();
|
|
2903
2964
|
if (s1) s1 = void 0;
|
|
2904
2965
|
else s1 = peg$FAILED;
|
|
2905
2966
|
if (s1 !== peg$FAILED) {
|
|
@@ -2928,7 +2989,7 @@ function peg$parse(input, options) {
|
|
|
2928
2989
|
if (s2 !== peg$FAILED) {
|
|
2929
2990
|
s3 = peg$currPos;
|
|
2930
2991
|
peg$savedPos = peg$currPos;
|
|
2931
|
-
s4 = peg$
|
|
2992
|
+
s4 = peg$f80(s2);
|
|
2932
2993
|
if (s4) s4 = void 0;
|
|
2933
2994
|
else s4 = peg$FAILED;
|
|
2934
2995
|
if (s4 !== peg$FAILED) {
|
|
@@ -2967,7 +3028,7 @@ function peg$parse(input, options) {
|
|
|
2967
3028
|
if (s0 === peg$FAILED) {
|
|
2968
3029
|
s0 = peg$currPos;
|
|
2969
3030
|
peg$savedPos = peg$currPos;
|
|
2970
|
-
s1 = peg$
|
|
3031
|
+
s1 = peg$f81();
|
|
2971
3032
|
if (s1) s1 = void 0;
|
|
2972
3033
|
else s1 = peg$FAILED;
|
|
2973
3034
|
if (s1 !== peg$FAILED) {
|
|
@@ -2994,12 +3055,12 @@ function peg$parse(input, options) {
|
|
|
2994
3055
|
if (s6 !== peg$FAILED) {
|
|
2995
3056
|
s7 = peg$parseHere();
|
|
2996
3057
|
peg$savedPos = peg$currPos;
|
|
2997
|
-
s8 = peg$
|
|
3058
|
+
s8 = peg$f82(s4, s7);
|
|
2998
3059
|
if (s8) s8 = void 0;
|
|
2999
3060
|
else s8 = peg$FAILED;
|
|
3000
3061
|
if (s8 !== peg$FAILED) {
|
|
3001
3062
|
s9 = peg$currPos;
|
|
3002
|
-
s10 = peg$
|
|
3063
|
+
s10 = peg$f83(s4, s7);
|
|
3003
3064
|
s11 = peg$currPos;
|
|
3004
3065
|
s12 = [];
|
|
3005
3066
|
if (s12.length >= (s10 | 0)) s13 = peg$FAILED;
|
|
@@ -3081,7 +3142,7 @@ function peg$parse(input, options) {
|
|
|
3081
3142
|
if (s0 === peg$FAILED) {
|
|
3082
3143
|
s0 = peg$currPos;
|
|
3083
3144
|
peg$savedPos = peg$currPos;
|
|
3084
|
-
s1 = peg$
|
|
3145
|
+
s1 = peg$f84();
|
|
3085
3146
|
if (s1) s1 = void 0;
|
|
3086
3147
|
else s1 = peg$FAILED;
|
|
3087
3148
|
if (s1 !== peg$FAILED) {
|
|
@@ -3172,7 +3233,7 @@ function peg$parse(input, options) {
|
|
|
3172
3233
|
if (s0 === peg$FAILED) {
|
|
3173
3234
|
s0 = peg$currPos;
|
|
3174
3235
|
peg$savedPos = peg$currPos;
|
|
3175
|
-
s1 = peg$
|
|
3236
|
+
s1 = peg$f85();
|
|
3176
3237
|
if (s1) s1 = void 0;
|
|
3177
3238
|
else s1 = peg$FAILED;
|
|
3178
3239
|
if (s1 !== peg$FAILED) {
|
|
@@ -3403,21 +3464,6 @@ function isBlockMacro(name) {
|
|
|
3403
3464
|
return BLOCK_MACROS.has(name);
|
|
3404
3465
|
}
|
|
3405
3466
|
//#endregion
|
|
3406
|
-
//#region src/markup/tokens.ts
|
|
3407
|
-
/** Variable sigils: story ($), temporary (_), local (@), transient (%). */
|
|
3408
|
-
var SIGIL_SCOPES = {
|
|
3409
|
-
$: "variable",
|
|
3410
|
-
_: "temporary",
|
|
3411
|
-
"@": "local",
|
|
3412
|
-
"%": "transient"
|
|
3413
|
-
};
|
|
3414
|
-
Object.fromEntries(Object.entries(SIGIL_SCOPES).map(([sigil, scope]) => [scope, sigil]));
|
|
3415
|
-
var SIGIL_CHARS = new Set(Object.keys(SIGIL_SCOPES));
|
|
3416
|
-
/** Whether `c` is a variable sigil. */
|
|
3417
|
-
function isSigil(c) {
|
|
3418
|
-
return c !== void 0 && SIGIL_CHARS.has(c);
|
|
3419
|
-
}
|
|
3420
|
-
//#endregion
|
|
3421
3467
|
//#region node_modules/acorn/dist/acorn.mjs
|
|
3422
3468
|
var astralIdentifierCodes = [
|
|
3423
3469
|
509,
|
|
@@ -9239,6 +9285,14 @@ function* tokens(src, start, goal, options = {}) {
|
|
|
9239
9285
|
function lexJs(src, handlers, goal = "expression") {
|
|
9240
9286
|
return walkTokens(src, 0, handlers, 0, goal, false);
|
|
9241
9287
|
}
|
|
9288
|
+
/**
|
|
9289
|
+
* Lex the template literal opening at `start` (a backtick) as `lexJs` does,
|
|
9290
|
+
* its interpolations one nesting level deeper. Returns the index just past
|
|
9291
|
+
* its closing backtick, or `src.length` if it is unterminated.
|
|
9292
|
+
*/
|
|
9293
|
+
function lexTemplate(src, start, handlers = {}, nesting = 0) {
|
|
9294
|
+
return walkTokens(src, start, handlers, nesting, "expression", true);
|
|
9295
|
+
}
|
|
9242
9296
|
function walkTokens(src, from, handlers, nesting, goal, oneTemplate) {
|
|
9243
9297
|
const comments = [];
|
|
9244
9298
|
const onComment = (_block, _text, s, e) => {
|
|
@@ -9591,6 +9645,80 @@ function parseMarkup(source, options = {}) {
|
|
|
9591
9645
|
function tokenizeMarkup(source, options = {}) {
|
|
9592
9646
|
return run(source, "Tokens", options);
|
|
9593
9647
|
}
|
|
9648
|
+
/**
|
|
9649
|
+
* The `.class#id` selectors that start `source` at `at`, with the index just
|
|
9650
|
+
* past them and the one space that may follow (`end`; `at` if there are
|
|
9651
|
+
* none). A selector name may hold `{$name}` interpolations, as in markup.
|
|
9652
|
+
*/
|
|
9653
|
+
function parseSelectors(source, at = 0) {
|
|
9654
|
+
const found = run(source.slice(at), "SelectorsPrefix", {});
|
|
9655
|
+
return {
|
|
9656
|
+
...found,
|
|
9657
|
+
end: at + found.end
|
|
9658
|
+
};
|
|
9659
|
+
}
|
|
9660
|
+
/**
|
|
9661
|
+
* `tokenizeMarkup` for half-typed markup: the tokens it can read, and every
|
|
9662
|
+
* error, instead of throwing at the first. At a malformed tag, the tokens
|
|
9663
|
+
* before it are kept, its first character is read as text and tokenizing
|
|
9664
|
+
* resumes after it, so what follows is read as if the tag had not been
|
|
9665
|
+
* started. Tokens and errors have offsets in `source`; for well-formed
|
|
9666
|
+
* markup the tokens are those of `tokenizeMarkup` and there are no errors.
|
|
9667
|
+
*/
|
|
9668
|
+
function tokenizeMarkupTolerant(source, options = {}) {
|
|
9669
|
+
const tokens = [];
|
|
9670
|
+
const errors = [];
|
|
9671
|
+
/** `text` shifted by `base`, tokenized: the tokens or the error. */
|
|
9672
|
+
const attempt = (text, base) => {
|
|
9673
|
+
try {
|
|
9674
|
+
return tokenizeMarkup(text, options).map((token) => shift(token, base));
|
|
9675
|
+
} catch (error) {
|
|
9676
|
+
if (!(error instanceof MarkupError)) throw error;
|
|
9677
|
+
return error;
|
|
9678
|
+
}
|
|
9679
|
+
};
|
|
9680
|
+
let base = 0;
|
|
9681
|
+
while (base <= source.length) {
|
|
9682
|
+
const rest = source.slice(base);
|
|
9683
|
+
const result = attempt(rest, base);
|
|
9684
|
+
if (Array.isArray(result)) {
|
|
9685
|
+
tokens.push(...result);
|
|
9686
|
+
break;
|
|
9687
|
+
}
|
|
9688
|
+
const at = base + result.offset;
|
|
9689
|
+
const { line, column } = lineColumn(source, at);
|
|
9690
|
+
errors.push(new MarkupError(result.reason, at, line, column));
|
|
9691
|
+
let cut = result.offset;
|
|
9692
|
+
let prefix = attempt(rest.slice(0, cut), base);
|
|
9693
|
+
while (!Array.isArray(prefix)) {
|
|
9694
|
+
cut = prefix.offset;
|
|
9695
|
+
prefix = attempt(rest.slice(0, cut), base);
|
|
9696
|
+
}
|
|
9697
|
+
tokens.push(...prefix);
|
|
9698
|
+
if (cut < rest.length) {
|
|
9699
|
+
const start = base + cut;
|
|
9700
|
+
tokens.push({
|
|
9701
|
+
type: "text",
|
|
9702
|
+
value: rest[cut],
|
|
9703
|
+
start,
|
|
9704
|
+
end: start + 1
|
|
9705
|
+
});
|
|
9706
|
+
}
|
|
9707
|
+
base += cut + 1;
|
|
9708
|
+
}
|
|
9709
|
+
return {
|
|
9710
|
+
tokens,
|
|
9711
|
+
errors
|
|
9712
|
+
};
|
|
9713
|
+
}
|
|
9714
|
+
/** `token` with its offsets moved `by` on. */
|
|
9715
|
+
function shift(token, by) {
|
|
9716
|
+
return by === 0 ? token : {
|
|
9717
|
+
...token,
|
|
9718
|
+
start: token.start + by,
|
|
9719
|
+
end: token.end + by
|
|
9720
|
+
};
|
|
9721
|
+
}
|
|
9594
9722
|
//#endregion
|
|
9595
9723
|
//#region src/utils/parse-delay.ts
|
|
9596
9724
|
/**
|
|
@@ -9618,6 +9746,15 @@ function errorMessage(err, fallback) {
|
|
|
9618
9746
|
return fallback ?? String(err);
|
|
9619
9747
|
}
|
|
9620
9748
|
//#endregion
|
|
9749
|
+
//#region src/runtime-errors.ts
|
|
9750
|
+
/**
|
|
9751
|
+
* The error for `name`, a passage name that names no passage, used in the
|
|
9752
|
+
* passage `from`.
|
|
9753
|
+
*/
|
|
9754
|
+
function noPassageError(name, from) {
|
|
9755
|
+
return /* @__PURE__ */ new Error(`No passage named ${JSON.stringify(name)} (in passage ${JSON.stringify(from)})`);
|
|
9756
|
+
}
|
|
9757
|
+
//#endregion
|
|
9621
9758
|
//#region src/components/macros/arg-utils.ts
|
|
9622
9759
|
/**
|
|
9623
9760
|
* Shared helpers for macros that split or read their raw argument text by
|
|
@@ -9668,6 +9805,37 @@ function readWholeQuoted(src) {
|
|
|
9668
9805
|
const quoted = readQuoted(src, 0);
|
|
9669
9806
|
return quoted && quoted.end === src.length ? quoted.value : null;
|
|
9670
9807
|
}
|
|
9808
|
+
var JS_SIMPLE_ESCAPES = {
|
|
9809
|
+
n: "\n",
|
|
9810
|
+
t: " ",
|
|
9811
|
+
r: "\r",
|
|
9812
|
+
b: "\b",
|
|
9813
|
+
f: "\f",
|
|
9814
|
+
v: "\v",
|
|
9815
|
+
"0": "\0"
|
|
9816
|
+
};
|
|
9817
|
+
/** Undo every escape of a JavaScript string literal's body. */
|
|
9818
|
+
function decodeJsEscapes(body) {
|
|
9819
|
+
return body.replace(/\\(?:u\{([0-9a-fA-F]+)\}|u([0-9a-fA-F]{4})|x([0-9a-fA-F]{2})|(\r\n|[\n\r\u2028\u2029])|([\s\S]))/g, (_, braced, unicode, hex, lineBreak, other) => {
|
|
9820
|
+
if (lineBreak !== void 0) return "";
|
|
9821
|
+
const code = braced ?? unicode ?? hex;
|
|
9822
|
+
if (code !== void 0) {
|
|
9823
|
+
const point = parseInt(code, 16);
|
|
9824
|
+
return point <= 1114111 ? String.fromCodePoint(point) : "";
|
|
9825
|
+
}
|
|
9826
|
+
return JS_SIMPLE_ESCAPES[other] ?? other;
|
|
9827
|
+
});
|
|
9828
|
+
}
|
|
9829
|
+
/**
|
|
9830
|
+
* The value of `src` when it is exactly one `"…"` or `'…'` string, read as
|
|
9831
|
+
* the JavaScript literal it is (`"\u0048all"` is `Hall`), otherwise `null`.
|
|
9832
|
+
* `readWholeQuoted` keeps other escapes as written, for labels and code.
|
|
9833
|
+
*/
|
|
9834
|
+
function readWholeJsString(src) {
|
|
9835
|
+
const quoted = readQuoted(src, 0);
|
|
9836
|
+
if (!quoted || quoted.end !== src.length) return null;
|
|
9837
|
+
return decodeJsEscapes(src.slice(1, -1));
|
|
9838
|
+
}
|
|
9671
9839
|
/**
|
|
9672
9840
|
* Strip an optional quote from each end of an unquoted-or-loosely-quoted
|
|
9673
9841
|
* argument (`"Red`, `Red'`), leaving at least one character. This is the
|
|
@@ -9720,6 +9888,67 @@ function topLevelIndices(src, isSeparator) {
|
|
|
9720
9888
|
} });
|
|
9721
9889
|
return indices;
|
|
9722
9890
|
}
|
|
9891
|
+
/**
|
|
9892
|
+
* Split `src` at every depth-0 separator (see `topLevelIndices`). Segments
|
|
9893
|
+
* are returned as written, untrimmed, and empty segments are kept, so
|
|
9894
|
+
* `n` separators always give `n + 1` segments.
|
|
9895
|
+
*/
|
|
9896
|
+
function splitTopLevel(src, isSeparator) {
|
|
9897
|
+
const segments = [];
|
|
9898
|
+
let from = 0;
|
|
9899
|
+
for (const i of topLevelIndices(src, isSeparator)) {
|
|
9900
|
+
segments.push(src.slice(from, i));
|
|
9901
|
+
from = i + 1;
|
|
9902
|
+
}
|
|
9903
|
+
segments.push(src.slice(from));
|
|
9904
|
+
return segments;
|
|
9905
|
+
}
|
|
9906
|
+
/**
|
|
9907
|
+
* Check whether a whitespace-delimited token looks like a standalone value
|
|
9908
|
+
* (not an operator or partial expression): it starts like a value and does
|
|
9909
|
+
* not end with an operator still waiting for its operand.
|
|
9910
|
+
*/
|
|
9911
|
+
function isStandaloneValue(token) {
|
|
9912
|
+
return startsLikeValue(token) && !endsWithOperator(token);
|
|
9913
|
+
}
|
|
9914
|
+
function startsLikeValue(token) {
|
|
9915
|
+
const first = token[0];
|
|
9916
|
+
if (first === "\"" || first === "'" || first === "`") return true;
|
|
9917
|
+
if (/^[$_@]\w|^%[A-Za-z_]/.test(token)) return true;
|
|
9918
|
+
if (/\d/.test(first)) return true;
|
|
9919
|
+
if ((first === "-" || first === "+") && token.length > 1 && /\d/.test(token[1])) return true;
|
|
9920
|
+
if (first === "(" || first === "[" || first === "{") return true;
|
|
9921
|
+
if (/^(?:true|false|null|undefined)(?![\w$])/.test(token)) return true;
|
|
9922
|
+
if (first === "!" && token.length > 1 && token[1] !== "=") return true;
|
|
9923
|
+
return false;
|
|
9924
|
+
}
|
|
9925
|
+
/**
|
|
9926
|
+
* Try to split a raw string on whitespace at depth 0 (respecting strings,
|
|
9927
|
+
* template literals, parentheses, brackets, and braces). Each resulting token
|
|
9928
|
+
* must pass `isStandaloneValue()` or the split is rejected and `null` is
|
|
9929
|
+
* returned.
|
|
9930
|
+
*/
|
|
9931
|
+
function trySplitOnWhitespace(raw) {
|
|
9932
|
+
const args = splitTopLevel(raw, isWhitespace).filter(Boolean);
|
|
9933
|
+
if (args.length < 2) return null;
|
|
9934
|
+
for (const arg of args) if (!isStandaloneValue(arg)) return null;
|
|
9935
|
+
return args;
|
|
9936
|
+
}
|
|
9937
|
+
/**
|
|
9938
|
+
* Split rawArgs by commas, respecting parentheses, brackets, braces, and
|
|
9939
|
+
* strings. When no top-level commas are present, also supports adjacent quoted
|
|
9940
|
+
* string literals separated by whitespace (e.g. `"Label" "target"`).
|
|
9941
|
+
*/
|
|
9942
|
+
function splitArgs(raw) {
|
|
9943
|
+
const args = splitTopLevel(raw, (ch) => ch === ",").map((a) => a.trim());
|
|
9944
|
+
const hasComma = args.length > 1;
|
|
9945
|
+
if (args[args.length - 1] === "") args.pop();
|
|
9946
|
+
if (!hasComma && args.length === 1) {
|
|
9947
|
+
const split = trySplitOnWhitespace(args[0]);
|
|
9948
|
+
if (split) return split;
|
|
9949
|
+
}
|
|
9950
|
+
return args;
|
|
9951
|
+
}
|
|
9723
9952
|
//#endregion
|
|
9724
9953
|
//#region src/components/macros/macro-args.ts
|
|
9725
9954
|
/**
|
|
@@ -9756,9 +9985,23 @@ var slice = (src, [start, end]) => src.slice(start, end);
|
|
|
9756
9985
|
function readText(src) {
|
|
9757
9986
|
return readWholeQuoted(src) ?? stripLooseQuotes(src);
|
|
9758
9987
|
}
|
|
9988
|
+
/**
|
|
9989
|
+
* An argument that doesn't have its parameter's form: a `string` parameter
|
|
9990
|
+
* given anything but one quoted string.
|
|
9991
|
+
*/
|
|
9992
|
+
var MacroArgumentError = class extends Error {
|
|
9993
|
+
constructor(param, src) {
|
|
9994
|
+
super(`The ${param.name} must be a quoted string ("…" or '…'), ${src ? `not ${src}` : "but it has no value"}`);
|
|
9995
|
+
this.name = "MacroArgumentError";
|
|
9996
|
+
}
|
|
9997
|
+
};
|
|
9759
9998
|
function readValue(param, src) {
|
|
9760
9999
|
switch (param.type) {
|
|
9761
|
-
case "string":
|
|
10000
|
+
case "string": {
|
|
10001
|
+
const value = readWholeQuoted(src);
|
|
10002
|
+
if (value === null) throw new MacroArgumentError(param, src);
|
|
10003
|
+
return value;
|
|
10004
|
+
}
|
|
9762
10005
|
case "text": return readText(src);
|
|
9763
10006
|
case "names": return src.split(",").map((name) => name.trim());
|
|
9764
10007
|
case "delay": return parseDelay(src);
|
|
@@ -9773,6 +10016,7 @@ function unset(param) {
|
|
|
9773
10016
|
}
|
|
9774
10017
|
var WORD_RE = /\w+/y;
|
|
9775
10018
|
var DIGITS_RE = /\d+/y;
|
|
10019
|
+
var WORD_OR_PUNCT_RE = /\S+/y;
|
|
9776
10020
|
/** Match the sticky regex `re` at `pos`, returning the matched text. */
|
|
9777
10021
|
function matchAt(re, src, pos) {
|
|
9778
10022
|
re.lastIndex = pos;
|
|
@@ -9781,7 +10025,8 @@ function matchAt(re, src, pos) {
|
|
|
9781
10025
|
/**
|
|
9782
10026
|
* Read keyword options (`goto "X" priority 5 once`). A keyword takes the
|
|
9783
10027
|
* quoted string or digit run after it as its value; keywords that aren't
|
|
9784
|
-
* declared, and their values, are skipped.
|
|
10028
|
+
* declared, and their values, are skipped. A keyword of a `string` parameter
|
|
10029
|
+
* must be followed by a quoted string.
|
|
9785
10030
|
*/
|
|
9786
10031
|
function readOptions(params, src) {
|
|
9787
10032
|
const options = {};
|
|
@@ -9794,10 +10039,11 @@ function readOptions(params, src) {
|
|
|
9794
10039
|
}
|
|
9795
10040
|
i += key.length;
|
|
9796
10041
|
let val;
|
|
10042
|
+
let quoted = null;
|
|
9797
10043
|
let j = i;
|
|
9798
10044
|
while (j < src.length && isWhitespace(src[j])) j++;
|
|
9799
10045
|
if (j > i) {
|
|
9800
|
-
|
|
10046
|
+
quoted = readQuoted(src, j);
|
|
9801
10047
|
const digits = quoted ? null : matchAt(DIGITS_RE, src, j);
|
|
9802
10048
|
if (quoted) {
|
|
9803
10049
|
val = quoted.value;
|
|
@@ -9808,6 +10054,7 @@ function readOptions(params, src) {
|
|
|
9808
10054
|
}
|
|
9809
10055
|
}
|
|
9810
10056
|
const param = params.find((p) => p.name === key);
|
|
10057
|
+
if (param?.type === "string" && (!quoted || val === void 0)) throw new MacroArgumentError(param, matchAt(WORD_OR_PUNCT_RE, src, j) ?? "");
|
|
9811
10058
|
if (param?.type === "flag") options[key] = true;
|
|
9812
10059
|
else if (param?.type === "number") options[key] = Number(val ?? 0);
|
|
9813
10060
|
else if (param) options[key] = val;
|
|
@@ -9880,8 +10127,16 @@ function readGroup(params, src, args) {
|
|
|
9880
10127
|
let from = 0;
|
|
9881
10128
|
let to = spans.length;
|
|
9882
10129
|
for (const param of positional.slice(0, restIndex)) args[param.name] = from < to ? read(param, slice(text, spans[from++])) : unset(param);
|
|
10130
|
+
const readTrailing = (param, value) => {
|
|
10131
|
+
try {
|
|
10132
|
+
return read(param, value);
|
|
10133
|
+
} catch (error) {
|
|
10134
|
+
if (error instanceof MacroArgumentError) return void 0;
|
|
10135
|
+
throw error;
|
|
10136
|
+
}
|
|
10137
|
+
};
|
|
9883
10138
|
for (const param of positional.slice(restIndex + 1).reverse()) {
|
|
9884
|
-
const value = from < to && !endsWithOperator(text.slice(0, spans[to - 1][0])) ?
|
|
10139
|
+
const value = from < to && !endsWithOperator(text.slice(0, spans[to - 1][0])) ? readTrailing(param, slice(text, spans[to - 1])) : void 0;
|
|
9885
10140
|
args[param.name] = value ?? unset(param);
|
|
9886
10141
|
if (value !== void 0) to--;
|
|
9887
10142
|
}
|
|
@@ -9905,6 +10160,34 @@ function parseMacroArgs(rawArgs, parameters) {
|
|
|
9905
10160
|
readGroup(parameters.slice(groupStart), rest, args);
|
|
9906
10161
|
return args;
|
|
9907
10162
|
}
|
|
10163
|
+
/**
|
|
10164
|
+
* Read a `passage` argument as written (`{goto "Hall"}`, `{goto $room}`): a
|
|
10165
|
+
* quoted string is the name its JavaScript literal has (`"\u0048all"` is
|
|
10166
|
+
* `Hall`), anything else an expression, whose value is the name when it
|
|
10167
|
+
* runs (see evaluatePassageName).
|
|
10168
|
+
*/
|
|
10169
|
+
function passageTarget(arg) {
|
|
10170
|
+
const expression = arg.trim();
|
|
10171
|
+
const name = readWholeJsString(expression);
|
|
10172
|
+
return name === null ? {
|
|
10173
|
+
kind: "expression",
|
|
10174
|
+
expression
|
|
10175
|
+
} : {
|
|
10176
|
+
kind: "name",
|
|
10177
|
+
name
|
|
10178
|
+
};
|
|
10179
|
+
}
|
|
10180
|
+
/**
|
|
10181
|
+
* The passage a `passage` argument names (`{goto "Hall"}`, `{goto $room}`):
|
|
10182
|
+
* the value of its expression. Throws what evaluating it throws, and if no
|
|
10183
|
+
* passage of the story `state` holds has that name, an error naming it and
|
|
10184
|
+
* the current passage.
|
|
10185
|
+
*/
|
|
10186
|
+
function evaluatePassageName(expr, evaluate, { storyData, currentPassage }) {
|
|
10187
|
+
const name = String(evaluate(expr ?? ""));
|
|
10188
|
+
if (storyData && !storyData.passages.has(name)) throw noPassageError(name, currentPassage);
|
|
10189
|
+
return name;
|
|
10190
|
+
}
|
|
9908
10191
|
//#endregion
|
|
9909
10192
|
//#region src/widgets/widget-def.ts
|
|
9910
10193
|
/**
|
|
@@ -9988,15 +10271,21 @@ var CONDITION_MACROS = new Set([
|
|
|
9988
10271
|
"case"
|
|
9989
10272
|
]);
|
|
9990
10273
|
/** Code inside the quoted strings of a macro's arguments, by parameter. */
|
|
9991
|
-
var CODE_IN_STRINGS =
|
|
10274
|
+
var CODE_IN_STRINGS = new Map([["watch", {
|
|
9992
10275
|
condition: "expression",
|
|
9993
10276
|
run: "statements"
|
|
9994
|
-
}
|
|
10277
|
+
}]]);
|
|
9995
10278
|
/** The `string` parameters of a macro that name a passage. */
|
|
9996
|
-
var PASSAGE_STRINGS =
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10279
|
+
var PASSAGE_STRINGS = new Map([["watch", ["goto", "dialog"]]]);
|
|
10280
|
+
/**
|
|
10281
|
+
* The `text` and `string` parameters of a macro that the runtime keeps as
|
|
10282
|
+
* written, not as markup: an option's value, the name of a watcher.
|
|
10283
|
+
*/
|
|
10284
|
+
var LITERAL_STRINGS = new Map([
|
|
10285
|
+
["option", ["value"]],
|
|
10286
|
+
["watch", ["name"]],
|
|
10287
|
+
["unwatch", ["name"]]
|
|
10288
|
+
]);
|
|
10000
10289
|
/** Block macros whose body is the name of a passage. */
|
|
10001
10290
|
var PASSAGE_BODIES = new Set(["dialog"]);
|
|
10002
10291
|
/** The goal of the code an argument of this type holds, if it is code. */
|
|
@@ -10017,6 +10306,8 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10017
10306
|
kind: "passage",
|
|
10018
10307
|
name: token.target,
|
|
10019
10308
|
offset: locate(src, token.target, token.start),
|
|
10309
|
+
length: token.target.length,
|
|
10310
|
+
macro: "link",
|
|
10020
10311
|
label: src.slice(token.start, token.end)
|
|
10021
10312
|
};
|
|
10022
10313
|
else if (token.type === "expression") yield {
|
|
@@ -10076,6 +10367,8 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10076
10367
|
kind: "passage",
|
|
10077
10368
|
name: passage,
|
|
10078
10369
|
offset: locate(src, passage, token.end),
|
|
10370
|
+
length: passage.length,
|
|
10371
|
+
macro: name,
|
|
10079
10372
|
label: src.slice(token.start, token.end)
|
|
10080
10373
|
};
|
|
10081
10374
|
}
|
|
@@ -10100,9 +10393,23 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10100
10393
|
}
|
|
10101
10394
|
/** The code and text in the arguments `args` (at `offset`) of `macro`. */
|
|
10102
10395
|
function* argPieces(args, offset, params, macro, label) {
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10396
|
+
let values;
|
|
10397
|
+
try {
|
|
10398
|
+
values = parseMacroArgs(args, params);
|
|
10399
|
+
} catch (error) {
|
|
10400
|
+
if (!(error instanceof MacroArgumentError)) throw error;
|
|
10401
|
+
yield {
|
|
10402
|
+
kind: "argument-error",
|
|
10403
|
+
message: error.message,
|
|
10404
|
+
offset,
|
|
10405
|
+
label
|
|
10406
|
+
};
|
|
10407
|
+
return;
|
|
10408
|
+
}
|
|
10409
|
+
const key = macro.toLowerCase();
|
|
10410
|
+
const inStrings = CODE_IN_STRINGS.get(key) ?? {};
|
|
10411
|
+
const passageStrings = PASSAGE_STRINGS.get(key) ?? [];
|
|
10412
|
+
const literalStrings = LITERAL_STRINGS.get(key) ?? [];
|
|
10106
10413
|
let cursor = 0;
|
|
10107
10414
|
/** The pieces of the parameters `list`, with their values in `from`. */
|
|
10108
10415
|
function* visit(list, from) {
|
|
@@ -10124,17 +10431,27 @@ function* argPieces(args, offset, params, macro, label) {
|
|
|
10124
10431
|
goal,
|
|
10125
10432
|
label
|
|
10126
10433
|
};
|
|
10127
|
-
if (param.type === "passage")
|
|
10434
|
+
if (param.type === "passage") {
|
|
10435
|
+
piece.passage = true;
|
|
10436
|
+
piece.macro = macro.toLowerCase();
|
|
10437
|
+
}
|
|
10438
|
+
if (!codeGoal(param)) piece.inString = true;
|
|
10128
10439
|
yield piece;
|
|
10129
10440
|
}
|
|
10130
|
-
const
|
|
10131
|
-
|
|
10441
|
+
const written = param.type === "passage" ? value.trim() : value;
|
|
10442
|
+
const target = param.type === "passage" ? passageTarget(value) : passageStrings.includes(param.name) ? {
|
|
10443
|
+
kind: "name",
|
|
10444
|
+
name: value
|
|
10445
|
+
} : null;
|
|
10446
|
+
if (target?.kind === "name") yield {
|
|
10132
10447
|
kind: "passage",
|
|
10133
|
-
name,
|
|
10134
|
-
offset: offset + at,
|
|
10448
|
+
name: target.name,
|
|
10449
|
+
offset: offset + at + value.indexOf(written),
|
|
10450
|
+
length: written.length,
|
|
10451
|
+
macro: macro.toLowerCase(),
|
|
10135
10452
|
label
|
|
10136
10453
|
};
|
|
10137
|
-
if (!goal && (param.type === "text" || param.type === "string")) yield {
|
|
10454
|
+
if (!goal && (param.type === "text" || param.type === "string") && !literalStrings.includes(param.name)) yield {
|
|
10138
10455
|
kind: "text",
|
|
10139
10456
|
text: value,
|
|
10140
10457
|
offset: offset + at,
|
|
@@ -10154,6 +10471,43 @@ function locate(text, part, from) {
|
|
|
10154
10471
|
const anywhere = text.indexOf(part);
|
|
10155
10472
|
return anywhere >= 0 ? anywhere : from;
|
|
10156
10473
|
}
|
|
10474
|
+
/**
|
|
10475
|
+
* The passages the markup `src` (with the `tokens` it tokenizes to) names:
|
|
10476
|
+
* `[[…]]` links, the passage argument of `{goto}`, `{include}` and `{link}`
|
|
10477
|
+
* (and of macros that declare one), the `goto` and `dialog` actions of
|
|
10478
|
+
* `{watch}` and the body of `{dialog}`, in source order. A name written out
|
|
10479
|
+
* is a literal; the others are expressions, which name a passage when they
|
|
10480
|
+
* run.
|
|
10481
|
+
*/
|
|
10482
|
+
function collectPassageReferences(src, tokens, parametersOf) {
|
|
10483
|
+
const refs = [];
|
|
10484
|
+
for (const piece of codeAndText(src, tokens, parametersOf)) if (piece.kind === "passage") refs.push({
|
|
10485
|
+
macro: piece.macro,
|
|
10486
|
+
target: {
|
|
10487
|
+
kind: "name",
|
|
10488
|
+
name: piece.name
|
|
10489
|
+
},
|
|
10490
|
+
start: piece.offset,
|
|
10491
|
+
end: piece.offset + piece.length
|
|
10492
|
+
});
|
|
10493
|
+
else if (piece.kind === "code" && piece.passage) refs.push({
|
|
10494
|
+
macro: piece.macro,
|
|
10495
|
+
target: passageTarget(piece.code),
|
|
10496
|
+
start: piece.offset,
|
|
10497
|
+
end: piece.offset + piece.code.length
|
|
10498
|
+
});
|
|
10499
|
+
return refs.filter((ref, i) => refs.findIndex((r) => r.start === ref.start && r.macro === ref.macro) === i);
|
|
10500
|
+
}
|
|
10501
|
+
/** The parameters of the built-in sub-macros that take arguments. */
|
|
10502
|
+
var SUB_MACRO_PARAMETERS = new Map([["option", [{
|
|
10503
|
+
name: "value",
|
|
10504
|
+
type: "string",
|
|
10505
|
+
required: true
|
|
10506
|
+
}]]]);
|
|
10507
|
+
/** The parameters of the built-in sub-macro `name`, if it takes any. */
|
|
10508
|
+
function subMacroParameters(name) {
|
|
10509
|
+
return SUB_MACRO_PARAMETERS.get(name.toLowerCase());
|
|
10510
|
+
}
|
|
10157
10511
|
//#endregion
|
|
10158
10512
|
//#region src/markup/validate.ts
|
|
10159
10513
|
/**
|
|
@@ -10163,8 +10517,15 @@ function locate(text, part, from) {
|
|
|
10163
10517
|
* line and column they are at, instead of failing when the passage renders.
|
|
10164
10518
|
* Tooling runs it too (see src/tooling.ts).
|
|
10165
10519
|
*/
|
|
10166
|
-
/**
|
|
10167
|
-
|
|
10520
|
+
/**
|
|
10521
|
+
* Passages that hold no markup: declarations, and the JavaScript function
|
|
10522
|
+
* body of SaveTitle.
|
|
10523
|
+
*/
|
|
10524
|
+
var NOT_MARKUP = new Set([
|
|
10525
|
+
"StoryVariables",
|
|
10526
|
+
"StoryTransients",
|
|
10527
|
+
"SaveTitle"
|
|
10528
|
+
]);
|
|
10168
10529
|
var NOT_MARKUP_TAGS = ["script", "stylesheet"];
|
|
10169
10530
|
/** Branch macros, which their parent macro renders. */
|
|
10170
10531
|
var BRANCH_MACROS = new Set([
|
|
@@ -10275,7 +10636,7 @@ function validateMarkup(passages, options) {
|
|
|
10275
10636
|
report(passage, base + token.start, `${where}Unknown macro {${token.name}}.${didYouMean(token.name, names, (n) => `{${n}}`)}`);
|
|
10276
10637
|
}
|
|
10277
10638
|
};
|
|
10278
|
-
const parametersOf = options.parametersOf ?? (
|
|
10639
|
+
const parametersOf = (name) => options.parametersOf?.(name) ?? subMacroParameters(name);
|
|
10279
10640
|
/**
|
|
10280
10641
|
* Check `tokens`, the tokens of `src`, which starts at `base` in the
|
|
10281
10642
|
* passage: their macros, the code in them and the markup in their text.
|
|
@@ -10284,6 +10645,10 @@ function validateMarkup(passages, options) {
|
|
|
10284
10645
|
checkMacros(passage, tokens, base, where);
|
|
10285
10646
|
for (const piece of codeAndText(src, tokens, parametersOf)) {
|
|
10286
10647
|
const at = base + piece.offset;
|
|
10648
|
+
if (piece.kind === "argument-error") {
|
|
10649
|
+
report(passage, at, `${where}${piece.message} in ${piece.label}`);
|
|
10650
|
+
continue;
|
|
10651
|
+
}
|
|
10287
10652
|
if (piece.kind === "passage") {
|
|
10288
10653
|
if (options.checkPassageNames !== false && !passageNames.has(piece.name)) {
|
|
10289
10654
|
const hint = didYouMean(piece.name, passageNames, JSON.stringify);
|
|
@@ -10317,6 +10682,58 @@ function validateMarkup(passages, options) {
|
|
|
10317
10682
|
for (const [passage, tokens] of parsed) withParseCache(() => checkTokens(passage, passage.content, tokens, 0, ""));
|
|
10318
10683
|
return diagnostics;
|
|
10319
10684
|
}
|
|
10685
|
+
new NameMap();
|
|
10686
|
+
new NameMap();
|
|
10687
|
+
new NameSet();
|
|
10688
|
+
/**
|
|
10689
|
+
* How a macro argument is read (see components/macros/macro-args.ts). Every
|
|
10690
|
+
* declared parameter names one: there is no default.
|
|
10691
|
+
* Quoted strings accept `\"`, `\'` and `\\` escapes.
|
|
10692
|
+
* - `expression`: code, as written.
|
|
10693
|
+
* - `statements`: code run as statements (`{set}`), as written.
|
|
10694
|
+
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
10695
|
+
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
10696
|
+
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
10697
|
+
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
10698
|
+
* - `names`: a comma-separated list of names (`@item, @i`).
|
|
10699
|
+
* - `delay`: a duration (`2s`, `500ms`, `300`) in milliseconds.
|
|
10700
|
+
* - `number`: a number.
|
|
10701
|
+
* - `flag`: a keyword, the parameter's name, at the start or end; a boolean.
|
|
10702
|
+
* - `separator`: a word (`of`) or `=` separating the parameters before it
|
|
10703
|
+
* from those after it; a boolean.
|
|
10704
|
+
* - `options`: keywords, the names of its `parameters`, each followed by a
|
|
10705
|
+
* quoted string or a number unless it is a flag.
|
|
10706
|
+
*/
|
|
10707
|
+
var PARAMETER_TYPES = [
|
|
10708
|
+
"expression",
|
|
10709
|
+
"statements",
|
|
10710
|
+
"passage",
|
|
10711
|
+
"variable",
|
|
10712
|
+
"string",
|
|
10713
|
+
"text",
|
|
10714
|
+
"names",
|
|
10715
|
+
"delay",
|
|
10716
|
+
"number",
|
|
10717
|
+
"flag",
|
|
10718
|
+
"separator",
|
|
10719
|
+
"options"
|
|
10720
|
+
];
|
|
10721
|
+
/**
|
|
10722
|
+
* Throw if a parameter `macro` declares (or an option of one) has no type,
|
|
10723
|
+
* or one that isn't a ParameterType: arguments are read by their type, and
|
|
10724
|
+
* there is no default.
|
|
10725
|
+
*/
|
|
10726
|
+
function checkParameterTypes(macro, parameters) {
|
|
10727
|
+
for (const param of parameters) {
|
|
10728
|
+
const type = param.type;
|
|
10729
|
+
if (!PARAMETER_TYPES.includes(type)) {
|
|
10730
|
+
const problem = type === void 0 ? "has no type" : `has the unknown type ${JSON.stringify(type)}`;
|
|
10731
|
+
throw new Error(`spindle: The parameter "${param.name}" of the macro {${macro}} ${problem}. Give it one of the types ${PARAMETER_TYPES.join(", ")} (see docs/custom-macros.md#parameter-types), or declare no parameters and read props.rawArgs.`);
|
|
10732
|
+
}
|
|
10733
|
+
if (param.parameters) checkParameterTypes(macro, param.parameters);
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
new NameMap();
|
|
10320
10737
|
//#endregion
|
|
10321
10738
|
//#region src/utils/namespace.ts
|
|
10322
10739
|
/** The variable name no namespace can hold. */
|
|
@@ -10407,58 +10824,84 @@ function parseStoryVariables(content, sigil = "$") {
|
|
|
10407
10824
|
}
|
|
10408
10825
|
return schema;
|
|
10409
10826
|
}
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
new NameSet();
|
|
10827
|
+
//#endregion
|
|
10828
|
+
//#region src/components/macros/include-args.ts
|
|
10413
10829
|
/**
|
|
10414
|
-
*
|
|
10415
|
-
*
|
|
10416
|
-
*
|
|
10417
|
-
* - `expression`: code, as written.
|
|
10418
|
-
* - `statements`: code run as statements (`{set}`), as written.
|
|
10419
|
-
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
10420
|
-
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
10421
|
-
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
10422
|
-
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
10423
|
-
* - `names`: a comma-separated list of names (`@item, @i`).
|
|
10424
|
-
* - `delay`: a duration (`2s`, `500ms`, `300`) in milliseconds.
|
|
10425
|
-
* - `number`: a number.
|
|
10426
|
-
* - `flag`: a keyword, the parameter's name, at the start or end; a boolean.
|
|
10427
|
-
* - `separator`: a word (`of`) or `=` separating the parameters before it
|
|
10428
|
-
* from those after it; a boolean.
|
|
10429
|
-
* - `options`: keywords, the names of its `parameters`, each followed by a
|
|
10430
|
-
* quoted string or a number unless it is a flag.
|
|
10830
|
+
* The parameters of `{include}`. A standalone `inline` flag only counts
|
|
10831
|
+
* outside quotes and brackets, so passage names and expressions containing
|
|
10832
|
+
* the word stay intact (#201).
|
|
10431
10833
|
*/
|
|
10432
|
-
var
|
|
10433
|
-
"
|
|
10434
|
-
"
|
|
10435
|
-
|
|
10436
|
-
"
|
|
10437
|
-
"
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
"delay",
|
|
10441
|
-
"number",
|
|
10442
|
-
"flag",
|
|
10443
|
-
"separator",
|
|
10444
|
-
"options"
|
|
10445
|
-
];
|
|
10834
|
+
var INCLUDE_PARAMETERS = [{
|
|
10835
|
+
name: "inline",
|
|
10836
|
+
type: "flag"
|
|
10837
|
+
}, {
|
|
10838
|
+
name: "passage",
|
|
10839
|
+
type: "passage",
|
|
10840
|
+
required: true
|
|
10841
|
+
}];
|
|
10446
10842
|
/**
|
|
10447
|
-
*
|
|
10448
|
-
* or
|
|
10449
|
-
* there is no default.
|
|
10843
|
+
* The arguments of `{include}`: whether it has the `inline` flag, as its
|
|
10844
|
+
* first or last word, and the passage expression that is left.
|
|
10450
10845
|
*/
|
|
10451
|
-
function
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
}
|
|
10458
|
-
if (param.parameters) checkParameterTypes(macro, param.parameters);
|
|
10459
|
-
}
|
|
10846
|
+
function splitIncludeFlag(rawArgs) {
|
|
10847
|
+
const { inline, passage } = parseMacroArgs(rawArgs, INCLUDE_PARAMETERS);
|
|
10848
|
+
return {
|
|
10849
|
+
inline,
|
|
10850
|
+
passage
|
|
10851
|
+
};
|
|
10460
10852
|
}
|
|
10461
|
-
|
|
10853
|
+
//#endregion
|
|
10854
|
+
//#region src/transform.ts
|
|
10855
|
+
/**
|
|
10856
|
+
* The sigil transform: the story's code with its variable references
|
|
10857
|
+
* (`$var`, `_var`, `@var`, `%var`) turned into the namespace lookups the
|
|
10858
|
+
* expression engine runs. Pure string-to-string, so tooling can use it
|
|
10859
|
+
* (see src/tooling.ts).
|
|
10860
|
+
*/
|
|
10861
|
+
var TRANSFORM_CACHE_MAX = 500;
|
|
10862
|
+
var NAMESPACES = {
|
|
10863
|
+
$: "variables",
|
|
10864
|
+
_: "temporary",
|
|
10865
|
+
"@": "locals",
|
|
10866
|
+
"%": "transient"
|
|
10867
|
+
};
|
|
10868
|
+
/** Ends with an identifier character. */
|
|
10869
|
+
var IDENT_END_RE = /[\p{ID_Continue}$\u200c\u200d]$/u;
|
|
10870
|
+
/**
|
|
10871
|
+
* Transform expression: $var → variables["var"], _var → temporary["var"],
|
|
10872
|
+
* @var → locals["var"], %var → transient["var"].
|
|
10873
|
+
* The lexer (`lexJs`) finds the references: only in code (string, template
|
|
10874
|
+
* and regex literal text and comments are left untouched), only where an
|
|
10875
|
+
* identifier starts (not in `a$b`, nor as the property name in `obj._x`),
|
|
10876
|
+
* and `%var` only where an operand is expected, since `%` is also the
|
|
10877
|
+
* modulo operator. `goal` tells whether `expr` is an expression or a list of
|
|
10878
|
+
* statements, as that decides whether a leading `{` opens an object literal
|
|
10879
|
+
* or a block. A reference to a variable named `__proto__` throws a
|
|
10880
|
+
* SyntaxError: no namespace can hold one (see utils/namespace.ts).
|
|
10881
|
+
* */
|
|
10882
|
+
function transform(expr, goal = "expression") {
|
|
10883
|
+
const key = goal === "statements" ? "s" + expr : "e" + expr;
|
|
10884
|
+
const cached = transformCache.get(key);
|
|
10885
|
+
if (cached !== void 0) return cached;
|
|
10886
|
+
const { refs } = parseCode(expr, goal);
|
|
10887
|
+
let result = "";
|
|
10888
|
+
let at = 0;
|
|
10889
|
+
for (const ref of refs) {
|
|
10890
|
+
const error = variableNameError(ref.name, ref.sigil + ref.name);
|
|
10891
|
+
if (error) throw new SyntaxError(`spindle: ${error}`);
|
|
10892
|
+
result += expr.slice(at, ref.start);
|
|
10893
|
+
if (ref.shorthand) result += `${ref.name}: `;
|
|
10894
|
+
else if (IDENT_END_RE.test(result.slice(-2))) result += " ";
|
|
10895
|
+
result += `${NAMESPACES[ref.sigil]}["${ref.name}"]`;
|
|
10896
|
+
at = ref.end;
|
|
10897
|
+
}
|
|
10898
|
+
result += expr.slice(at);
|
|
10899
|
+
if (transformCache.size >= TRANSFORM_CACHE_MAX) transformCache.delete(transformCache.keys().next().value);
|
|
10900
|
+
transformCache.set(key, result);
|
|
10901
|
+
return result;
|
|
10902
|
+
}
|
|
10903
|
+
/** Transformed code by goal and source. */
|
|
10904
|
+
var transformCache = /* @__PURE__ */ new Map();
|
|
10462
10905
|
//#endregion
|
|
10463
10906
|
//#region src/tooling.ts
|
|
10464
10907
|
/**
|
|
@@ -10493,5 +10936,16 @@ function validateStoryMarkup(passages, macros, options = {}) {
|
|
|
10493
10936
|
checkPassageNames: options.checkPassageNames
|
|
10494
10937
|
});
|
|
10495
10938
|
}
|
|
10939
|
+
/**
|
|
10940
|
+
* The passages the markup of `source` names (see PassageReference), against
|
|
10941
|
+
* the given macros: what the story-start check looks up. Malformed tags are
|
|
10942
|
+
* skipped, so it reads half-typed markup.
|
|
10943
|
+
*/
|
|
10944
|
+
function collectStoryPassageReferences(source, macros) {
|
|
10945
|
+
const parameters = /* @__PURE__ */ new Map();
|
|
10946
|
+
for (const macro of macros) parameters.set(macro.name.toLowerCase(), macro.parameters);
|
|
10947
|
+
const { tokens } = tokenizeMarkupTolerant(source);
|
|
10948
|
+
return collectPassageReferences(source, tokens, (name) => parameters.get(name.toLowerCase()) ?? subMacroParameters(name));
|
|
10949
|
+
}
|
|
10496
10950
|
//#endregion
|
|
10497
|
-
export { checkParameterTypes, formatDiagnostic, parseStoryVariables, validateStoryMarkup };
|
|
10951
|
+
export { MarkupError, SIGIL_SCOPES, checkParameterTypes, collectStoryPassageReferences, endsWithOperator, evaluatePassageName, findCodeEnd, formatDiagnostic, isSigil, lexJs, lexTemplate, parseSelectors, parseStoryVariables, passageTarget, readQuoted, scanStringLiteral, splitArgs, splitIncludeFlag, splitTopLevel, stripLooseQuotes, tokenizeMarkup, tokenizeMarkupTolerant, transform, unescapeQuoted, validateStoryMarkup };
|