@rohal12/spindle 0.57.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 +584 -426
- package/dist/pkg/story-variables.js +567 -154
- package/dist/pkg/tooling.js +39 -2
- package/package.json +1 -1
- package/src/code-check.ts +107 -17
- package/src/components/macros/Include.tsx +2 -6
- package/src/components/macros/MacroLink.tsx +3 -0
- 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 +35 -3
- package/src/components/macros/option-utils.ts +9 -3
- 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 +11 -4
- 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
|
/**
|
|
@@ -9762,7 +9991,7 @@ function readText(src) {
|
|
|
9762
9991
|
*/
|
|
9763
9992
|
var MacroArgumentError = class extends Error {
|
|
9764
9993
|
constructor(param, src) {
|
|
9765
|
-
super(`The ${param.name} must be a quoted string ("…" or '…'), not ${src}`);
|
|
9994
|
+
super(`The ${param.name} must be a quoted string ("…" or '…'), ${src ? `not ${src}` : "but it has no value"}`);
|
|
9766
9995
|
this.name = "MacroArgumentError";
|
|
9767
9996
|
}
|
|
9768
9997
|
};
|
|
@@ -9787,6 +10016,7 @@ function unset(param) {
|
|
|
9787
10016
|
}
|
|
9788
10017
|
var WORD_RE = /\w+/y;
|
|
9789
10018
|
var DIGITS_RE = /\d+/y;
|
|
10019
|
+
var WORD_OR_PUNCT_RE = /\S+/y;
|
|
9790
10020
|
/** Match the sticky regex `re` at `pos`, returning the matched text. */
|
|
9791
10021
|
function matchAt(re, src, pos) {
|
|
9792
10022
|
re.lastIndex = pos;
|
|
@@ -9795,7 +10025,8 @@ function matchAt(re, src, pos) {
|
|
|
9795
10025
|
/**
|
|
9796
10026
|
* Read keyword options (`goto "X" priority 5 once`). A keyword takes the
|
|
9797
10027
|
* quoted string or digit run after it as its value; keywords that aren't
|
|
9798
|
-
* 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.
|
|
9799
10030
|
*/
|
|
9800
10031
|
function readOptions(params, src) {
|
|
9801
10032
|
const options = {};
|
|
@@ -9808,10 +10039,11 @@ function readOptions(params, src) {
|
|
|
9808
10039
|
}
|
|
9809
10040
|
i += key.length;
|
|
9810
10041
|
let val;
|
|
10042
|
+
let quoted = null;
|
|
9811
10043
|
let j = i;
|
|
9812
10044
|
while (j < src.length && isWhitespace(src[j])) j++;
|
|
9813
10045
|
if (j > i) {
|
|
9814
|
-
|
|
10046
|
+
quoted = readQuoted(src, j);
|
|
9815
10047
|
const digits = quoted ? null : matchAt(DIGITS_RE, src, j);
|
|
9816
10048
|
if (quoted) {
|
|
9817
10049
|
val = quoted.value;
|
|
@@ -9822,6 +10054,7 @@ function readOptions(params, src) {
|
|
|
9822
10054
|
}
|
|
9823
10055
|
}
|
|
9824
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) ?? "");
|
|
9825
10058
|
if (param?.type === "flag") options[key] = true;
|
|
9826
10059
|
else if (param?.type === "number") options[key] = Number(val ?? 0);
|
|
9827
10060
|
else if (param) options[key] = val;
|
|
@@ -9927,6 +10160,34 @@ function parseMacroArgs(rawArgs, parameters) {
|
|
|
9927
10160
|
readGroup(parameters.slice(groupStart), rest, args);
|
|
9928
10161
|
return args;
|
|
9929
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
|
+
}
|
|
9930
10191
|
//#endregion
|
|
9931
10192
|
//#region src/widgets/widget-def.ts
|
|
9932
10193
|
/**
|
|
@@ -10010,12 +10271,21 @@ var CONDITION_MACROS = new Set([
|
|
|
10010
10271
|
"case"
|
|
10011
10272
|
]);
|
|
10012
10273
|
/** Code inside the quoted strings of a macro's arguments, by parameter. */
|
|
10013
|
-
var CODE_IN_STRINGS =
|
|
10274
|
+
var CODE_IN_STRINGS = new Map([["watch", {
|
|
10014
10275
|
condition: "expression",
|
|
10015
10276
|
run: "statements"
|
|
10016
|
-
}
|
|
10277
|
+
}]]);
|
|
10017
10278
|
/** The `string` parameters of a macro that name a passage. */
|
|
10018
|
-
var PASSAGE_STRINGS =
|
|
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
|
+
]);
|
|
10019
10289
|
/** Block macros whose body is the name of a passage. */
|
|
10020
10290
|
var PASSAGE_BODIES = new Set(["dialog"]);
|
|
10021
10291
|
/** The goal of the code an argument of this type holds, if it is code. */
|
|
@@ -10036,6 +10306,8 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10036
10306
|
kind: "passage",
|
|
10037
10307
|
name: token.target,
|
|
10038
10308
|
offset: locate(src, token.target, token.start),
|
|
10309
|
+
length: token.target.length,
|
|
10310
|
+
macro: "link",
|
|
10039
10311
|
label: src.slice(token.start, token.end)
|
|
10040
10312
|
};
|
|
10041
10313
|
else if (token.type === "expression") yield {
|
|
@@ -10095,6 +10367,8 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10095
10367
|
kind: "passage",
|
|
10096
10368
|
name: passage,
|
|
10097
10369
|
offset: locate(src, passage, token.end),
|
|
10370
|
+
length: passage.length,
|
|
10371
|
+
macro: name,
|
|
10098
10372
|
label: src.slice(token.start, token.end)
|
|
10099
10373
|
};
|
|
10100
10374
|
}
|
|
@@ -10132,8 +10406,10 @@ function* argPieces(args, offset, params, macro, label) {
|
|
|
10132
10406
|
};
|
|
10133
10407
|
return;
|
|
10134
10408
|
}
|
|
10135
|
-
const
|
|
10136
|
-
const
|
|
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) ?? [];
|
|
10137
10413
|
let cursor = 0;
|
|
10138
10414
|
/** The pieces of the parameters `list`, with their values in `from`. */
|
|
10139
10415
|
function* visit(list, from) {
|
|
@@ -10155,17 +10431,27 @@ function* argPieces(args, offset, params, macro, label) {
|
|
|
10155
10431
|
goal,
|
|
10156
10432
|
label
|
|
10157
10433
|
};
|
|
10158
|
-
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;
|
|
10159
10439
|
yield piece;
|
|
10160
10440
|
}
|
|
10161
|
-
const
|
|
10162
|
-
|
|
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 {
|
|
10163
10447
|
kind: "passage",
|
|
10164
|
-
name,
|
|
10165
|
-
offset: offset + at,
|
|
10448
|
+
name: target.name,
|
|
10449
|
+
offset: offset + at + value.indexOf(written),
|
|
10450
|
+
length: written.length,
|
|
10451
|
+
macro: macro.toLowerCase(),
|
|
10166
10452
|
label
|
|
10167
10453
|
};
|
|
10168
|
-
if (!goal && (param.type === "text" || param.type === "string")) yield {
|
|
10454
|
+
if (!goal && (param.type === "text" || param.type === "string") && !literalStrings.includes(param.name)) yield {
|
|
10169
10455
|
kind: "text",
|
|
10170
10456
|
text: value,
|
|
10171
10457
|
offset: offset + at,
|
|
@@ -10185,12 +10471,43 @@ function locate(text, part, from) {
|
|
|
10185
10471
|
const anywhere = text.indexOf(part);
|
|
10186
10472
|
return anywhere >= 0 ? anywhere : from;
|
|
10187
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
|
+
}
|
|
10188
10501
|
/** The parameters of the built-in sub-macros that take arguments. */
|
|
10189
|
-
var SUB_MACRO_PARAMETERS =
|
|
10502
|
+
var SUB_MACRO_PARAMETERS = new Map([["option", [{
|
|
10190
10503
|
name: "value",
|
|
10191
10504
|
type: "string",
|
|
10192
10505
|
required: true
|
|
10193
|
-
}]
|
|
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
|
+
}
|
|
10194
10511
|
//#endregion
|
|
10195
10512
|
//#region src/markup/validate.ts
|
|
10196
10513
|
/**
|
|
@@ -10200,8 +10517,15 @@ var SUB_MACRO_PARAMETERS = { option: [{
|
|
|
10200
10517
|
* line and column they are at, instead of failing when the passage renders.
|
|
10201
10518
|
* Tooling runs it too (see src/tooling.ts).
|
|
10202
10519
|
*/
|
|
10203
|
-
/**
|
|
10204
|
-
|
|
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
|
+
]);
|
|
10205
10529
|
var NOT_MARKUP_TAGS = ["script", "stylesheet"];
|
|
10206
10530
|
/** Branch macros, which their parent macro renders. */
|
|
10207
10531
|
var BRANCH_MACROS = new Set([
|
|
@@ -10312,7 +10636,7 @@ function validateMarkup(passages, options) {
|
|
|
10312
10636
|
report(passage, base + token.start, `${where}Unknown macro {${token.name}}.${didYouMean(token.name, names, (n) => `{${n}}`)}`);
|
|
10313
10637
|
}
|
|
10314
10638
|
};
|
|
10315
|
-
const parametersOf = (name) => options.parametersOf?.(name) ??
|
|
10639
|
+
const parametersOf = (name) => options.parametersOf?.(name) ?? subMacroParameters(name);
|
|
10316
10640
|
/**
|
|
10317
10641
|
* Check `tokens`, the tokens of `src`, which starts at `base` in the
|
|
10318
10642
|
* passage: their macros, the code in them and the markup in their text.
|
|
@@ -10358,6 +10682,58 @@ function validateMarkup(passages, options) {
|
|
|
10358
10682
|
for (const [passage, tokens] of parsed) withParseCache(() => checkTokens(passage, passage.content, tokens, 0, ""));
|
|
10359
10683
|
return diagnostics;
|
|
10360
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();
|
|
10361
10737
|
//#endregion
|
|
10362
10738
|
//#region src/utils/namespace.ts
|
|
10363
10739
|
/** The variable name no namespace can hold. */
|
|
@@ -10448,58 +10824,84 @@ function parseStoryVariables(content, sigil = "$") {
|
|
|
10448
10824
|
}
|
|
10449
10825
|
return schema;
|
|
10450
10826
|
}
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
new NameSet();
|
|
10827
|
+
//#endregion
|
|
10828
|
+
//#region src/components/macros/include-args.ts
|
|
10454
10829
|
/**
|
|
10455
|
-
*
|
|
10456
|
-
*
|
|
10457
|
-
*
|
|
10458
|
-
* - `expression`: code, as written.
|
|
10459
|
-
* - `statements`: code run as statements (`{set}`), as written.
|
|
10460
|
-
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
10461
|
-
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
10462
|
-
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
10463
|
-
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
10464
|
-
* - `names`: a comma-separated list of names (`@item, @i`).
|
|
10465
|
-
* - `delay`: a duration (`2s`, `500ms`, `300`) in milliseconds.
|
|
10466
|
-
* - `number`: a number.
|
|
10467
|
-
* - `flag`: a keyword, the parameter's name, at the start or end; a boolean.
|
|
10468
|
-
* - `separator`: a word (`of`) or `=` separating the parameters before it
|
|
10469
|
-
* from those after it; a boolean.
|
|
10470
|
-
* - `options`: keywords, the names of its `parameters`, each followed by a
|
|
10471
|
-
* 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).
|
|
10472
10833
|
*/
|
|
10473
|
-
var
|
|
10474
|
-
"
|
|
10475
|
-
"
|
|
10476
|
-
|
|
10477
|
-
"
|
|
10478
|
-
"
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
"delay",
|
|
10482
|
-
"number",
|
|
10483
|
-
"flag",
|
|
10484
|
-
"separator",
|
|
10485
|
-
"options"
|
|
10486
|
-
];
|
|
10834
|
+
var INCLUDE_PARAMETERS = [{
|
|
10835
|
+
name: "inline",
|
|
10836
|
+
type: "flag"
|
|
10837
|
+
}, {
|
|
10838
|
+
name: "passage",
|
|
10839
|
+
type: "passage",
|
|
10840
|
+
required: true
|
|
10841
|
+
}];
|
|
10487
10842
|
/**
|
|
10488
|
-
*
|
|
10489
|
-
* or
|
|
10490
|
-
* 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.
|
|
10491
10845
|
*/
|
|
10492
|
-
function
|
|
10493
|
-
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10497
|
-
|
|
10498
|
-
}
|
|
10499
|
-
if (param.parameters) checkParameterTypes(macro, param.parameters);
|
|
10500
|
-
}
|
|
10846
|
+
function splitIncludeFlag(rawArgs) {
|
|
10847
|
+
const { inline, passage } = parseMacroArgs(rawArgs, INCLUDE_PARAMETERS);
|
|
10848
|
+
return {
|
|
10849
|
+
inline,
|
|
10850
|
+
passage
|
|
10851
|
+
};
|
|
10501
10852
|
}
|
|
10502
|
-
|
|
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();
|
|
10503
10905
|
//#endregion
|
|
10504
10906
|
//#region src/tooling.ts
|
|
10505
10907
|
/**
|
|
@@ -10534,5 +10936,16 @@ function validateStoryMarkup(passages, macros, options = {}) {
|
|
|
10534
10936
|
checkPassageNames: options.checkPassageNames
|
|
10535
10937
|
});
|
|
10536
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
|
+
}
|
|
10537
10950
|
//#endregion
|
|
10538
|
-
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 };
|