@wdprlib/parser 2.2.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +921 -471
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +921 -471
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ class Lexer {
|
|
|
27
27
|
state;
|
|
28
28
|
options;
|
|
29
29
|
splitBlockClosePositions = new Set;
|
|
30
|
+
blockOpenerDepth = 0;
|
|
30
31
|
constructor(source, options = {}) {
|
|
31
32
|
this.options = {
|
|
32
33
|
trackPositions: options.trackPositions ?? true
|
|
@@ -126,6 +127,11 @@ class Lexer {
|
|
|
126
127
|
const position = this.options.trackPositions ? createPosition(startPos, endPos) : createPosition(createPoint(0, 0, 0), createPoint(0, 0, 0));
|
|
127
128
|
const lineStart = this.state.tokens.length === 0 || this.state.tokens[this.state.tokens.length - 1]?.type === "NEWLINE";
|
|
128
129
|
this.state.tokens.push(createToken(type, value, position, lineStart));
|
|
130
|
+
if (type === "BLOCK_OPEN" || type === "BLOCK_END_OPEN") {
|
|
131
|
+
this.blockOpenerDepth++;
|
|
132
|
+
} else if (type === "BLOCK_CLOSE" && this.blockOpenerDepth > 0) {
|
|
133
|
+
this.blockOpenerDepth--;
|
|
134
|
+
}
|
|
129
135
|
}
|
|
130
136
|
scanToken() {
|
|
131
137
|
const char = this.current();
|
|
@@ -390,7 +396,7 @@ class Lexer {
|
|
|
390
396
|
}
|
|
391
397
|
if (char === '"') {
|
|
392
398
|
const lastNonWs = this.lastNonWhitespaceTokenType();
|
|
393
|
-
if (lastNonWs === "EQUALS") {
|
|
399
|
+
if (this.blockOpenerDepth > 0 && lastNonWs === "EQUALS") {
|
|
394
400
|
let quoted = this.advance();
|
|
395
401
|
while (!this.isAtEnd() && this.current() !== '"' && this.current() !== `
|
|
396
402
|
`) {
|
|
@@ -635,6 +641,28 @@ var KNOWN_BLOCK_NAMES = new Set([
|
|
|
635
641
|
"gallery",
|
|
636
642
|
"file"
|
|
637
643
|
]);
|
|
644
|
+
var INDENT_ACCEPTING_BLOCK_NAMES = new Set([
|
|
645
|
+
"bibliography",
|
|
646
|
+
"ul",
|
|
647
|
+
"ol",
|
|
648
|
+
"li",
|
|
649
|
+
"code",
|
|
650
|
+
"collapsible",
|
|
651
|
+
"div",
|
|
652
|
+
"div_",
|
|
653
|
+
"embed",
|
|
654
|
+
"embedvideo",
|
|
655
|
+
"embedaudio",
|
|
656
|
+
"html",
|
|
657
|
+
"iframe",
|
|
658
|
+
"iftags",
|
|
659
|
+
"math",
|
|
660
|
+
"module",
|
|
661
|
+
"module654",
|
|
662
|
+
"table",
|
|
663
|
+
"tabview",
|
|
664
|
+
"tabs"
|
|
665
|
+
]);
|
|
638
666
|
|
|
639
667
|
// packages/parser/src/parser/rules/utils.ts
|
|
640
668
|
var SAFE_ATTRIBUTES = new Set([
|
|
@@ -761,13 +789,14 @@ function parseBlockName(ctx, startPos) {
|
|
|
761
789
|
|
|
762
790
|
// packages/parser/src/parser/rules/inline/utils.ts
|
|
763
791
|
function isExcludedBlockToken(ctx, tokenPos) {
|
|
764
|
-
|
|
792
|
+
const excluded = ctx.scope.excludedBlockNames;
|
|
793
|
+
if (!excluded?.size)
|
|
765
794
|
return false;
|
|
766
795
|
const token = ctx.tokens[tokenPos];
|
|
767
796
|
if (token?.type !== "BLOCK_OPEN" && token?.type !== "BLOCK_END_OPEN")
|
|
768
797
|
return false;
|
|
769
798
|
const nameResult = parseBlockName(ctx, tokenPos + 1);
|
|
770
|
-
return nameResult !== null &&
|
|
799
|
+
return nameResult !== null && excluded.has(nameResult.name);
|
|
771
800
|
}
|
|
772
801
|
function isUnknownBlockToken(ctx, tokenPos) {
|
|
773
802
|
const token = ctx.tokens[tokenPos];
|
|
@@ -782,6 +811,15 @@ function isUnknownBlockToken(ctx, tokenPos) {
|
|
|
782
811
|
}
|
|
783
812
|
return !KNOWN_BLOCK_NAMES.has(nameResult.name);
|
|
784
813
|
}
|
|
814
|
+
function isIndentAcceptingBlock(ctx, tokenPos) {
|
|
815
|
+
const token = ctx.tokens[tokenPos];
|
|
816
|
+
if (token?.type !== "BLOCK_OPEN" && token?.type !== "BLOCK_END_OPEN")
|
|
817
|
+
return false;
|
|
818
|
+
const nameResult = parseBlockName(ctx, tokenPos + 1);
|
|
819
|
+
if (nameResult === null)
|
|
820
|
+
return false;
|
|
821
|
+
return INDENT_ACCEPTING_BLOCK_NAMES.has(nameResult.name);
|
|
822
|
+
}
|
|
785
823
|
function canApplyInlineRule(rule, token) {
|
|
786
824
|
if (rule.startTokens.length === 0) {
|
|
787
825
|
return true;
|
|
@@ -799,9 +837,9 @@ function parseInlineUntil(ctx, endType) {
|
|
|
799
837
|
if (!token || token.type === "EOF") {
|
|
800
838
|
break;
|
|
801
839
|
}
|
|
802
|
-
if (paragraphMode && ctx.blockCloseCondition) {
|
|
840
|
+
if (paragraphMode && ctx.scope.blockCloseCondition) {
|
|
803
841
|
const checkCtx = { ...ctx, pos };
|
|
804
|
-
if (ctx.blockCloseCondition(checkCtx)) {
|
|
842
|
+
if (ctx.scope.blockCloseCondition(checkCtx)) {
|
|
805
843
|
break;
|
|
806
844
|
}
|
|
807
845
|
}
|
|
@@ -853,7 +891,7 @@ function parseInlineUntil(ctx, endType) {
|
|
|
853
891
|
skipWhitespace++;
|
|
854
892
|
}
|
|
855
893
|
const blockNameToken = ctx.tokens[afterOpen + skipWhitespace];
|
|
856
|
-
if (blockNameToken && (blockNameToken.type === "TEXT" || blockNameToken.type === "IDENTIFIER") && blockNameToken.value.toLowerCase() === "footnoteblock" && ctx.footnoteBlockParsed) {
|
|
894
|
+
if (blockNameToken && (blockNameToken.type === "TEXT" || blockNameToken.type === "IDENTIFIER") && blockNameToken.value.toLowerCase() === "footnoteblock" && ctx.scope.footnoteBlockParsed) {
|
|
857
895
|
isInvalidBlockOpen = true;
|
|
858
896
|
}
|
|
859
897
|
}
|
|
@@ -875,7 +913,8 @@ function parseInlineUntil(ctx, endType) {
|
|
|
875
913
|
}
|
|
876
914
|
const isExcludedBlock = (nextMeaningfulToken?.type === "BLOCK_OPEN" || nextMeaningfulToken?.type === "BLOCK_END_OPEN") && isExcludedBlockToken(ctx, pos + lookAhead);
|
|
877
915
|
const isUnknownBlock = (nextMeaningfulToken?.type === "BLOCK_OPEN" || nextMeaningfulToken?.type === "BLOCK_END_OPEN") && isUnknownBlockToken(ctx, pos + lookAhead);
|
|
878
|
-
const
|
|
916
|
+
const isIndentedBlockOpener = nextMeaningfulToken && (nextMeaningfulToken.type === "BLOCK_OPEN" || nextMeaningfulToken.type === "BLOCK_END_OPEN") && isIndentAcceptingBlock(ctx, pos + lookAhead);
|
|
917
|
+
const isBlockStart = nextMeaningfulToken && BLOCK_START_TOKENS.includes(nextMeaningfulToken.type) && (nextMeaningfulToken.lineStart || isIndentedBlockOpener) && !isOrphanCloseSpan && !isAnchorName && !isInvalidBlockOpen && !isInvalidHeading && !isExcludedBlock && !isUnknownBlock;
|
|
879
918
|
if (!nextMeaningfulToken || nextMeaningfulToken.type === "NEWLINE" || nextMeaningfulToken.type === "EOF" || isBlockStart) {
|
|
880
919
|
if (isBlockStart && nodes.length > 0) {
|
|
881
920
|
const nextPos = pos + lookAhead;
|
|
@@ -1253,8 +1292,11 @@ function parseBlocksUntil(ctx, closeCondition, options) {
|
|
|
1253
1292
|
...ctx,
|
|
1254
1293
|
pos,
|
|
1255
1294
|
blockRules,
|
|
1256
|
-
|
|
1257
|
-
|
|
1295
|
+
scope: {
|
|
1296
|
+
...ctx.scope,
|
|
1297
|
+
blockCloseCondition: closeCondition,
|
|
1298
|
+
excludedBlockNames: excluded
|
|
1299
|
+
}
|
|
1258
1300
|
};
|
|
1259
1301
|
for (const rule of blockRules) {
|
|
1260
1302
|
if (canApplyBlockRule(rule, token)) {
|
|
@@ -2242,15 +2284,15 @@ var divRule = {
|
|
|
2242
2284
|
if (ctx.tokens[pos]?.type !== "NEWLINE") {
|
|
2243
2285
|
return consumeFailedDiv(ctx);
|
|
2244
2286
|
}
|
|
2245
|
-
if (ctx.divClosesBudget === 0) {
|
|
2287
|
+
if (ctx.scope.divClosesBudget === 0) {
|
|
2246
2288
|
return { success: false };
|
|
2247
2289
|
}
|
|
2248
2290
|
pos++;
|
|
2249
2291
|
consumed++;
|
|
2250
2292
|
const openPosition = openToken.position;
|
|
2251
2293
|
let bodyBudget;
|
|
2252
|
-
if (ctx.divClosesBudget !== undefined) {
|
|
2253
|
-
bodyBudget = ctx.divClosesBudget - 1;
|
|
2294
|
+
if (ctx.scope.divClosesBudget !== undefined) {
|
|
2295
|
+
bodyBudget = ctx.scope.divClosesBudget - 1;
|
|
2254
2296
|
} else {
|
|
2255
2297
|
const closesInScope = countDivCloses(ctx, pos);
|
|
2256
2298
|
bodyBudget = closesInScope > 0 ? closesInScope - 1 : 0;
|
|
@@ -2265,7 +2307,11 @@ var divRule = {
|
|
|
2265
2307
|
}
|
|
2266
2308
|
return false;
|
|
2267
2309
|
};
|
|
2268
|
-
const bodyCtx = {
|
|
2310
|
+
const bodyCtx = {
|
|
2311
|
+
...ctx,
|
|
2312
|
+
pos,
|
|
2313
|
+
scope: { ...ctx.scope, divClosesBudget: bodyBudget }
|
|
2314
|
+
};
|
|
2269
2315
|
let children;
|
|
2270
2316
|
if (paragraphStrip) {
|
|
2271
2317
|
const bodyResult = parseBlocksUntil(bodyCtx, closeCondition);
|
|
@@ -3908,10 +3954,10 @@ var footnoteBlockRule = {
|
|
|
3908
3954
|
}
|
|
3909
3955
|
pos++;
|
|
3910
3956
|
consumed++;
|
|
3911
|
-
if (ctx.footnoteBlockParsed) {
|
|
3957
|
+
if (ctx.scope.footnoteBlockParsed) {
|
|
3912
3958
|
return { success: false };
|
|
3913
3959
|
}
|
|
3914
|
-
ctx.
|
|
3960
|
+
ctx.scope = { ...ctx.scope, footnoteBlockParsed: true };
|
|
3915
3961
|
const title = attrs.title !== undefined ? attrs.title : null;
|
|
3916
3962
|
const hide = attrs.hide === "true" || attrs.hide === "yes";
|
|
3917
3963
|
return {
|
|
@@ -4667,6 +4713,24 @@ var mathBlockRule = {
|
|
|
4667
4713
|
};
|
|
4668
4714
|
|
|
4669
4715
|
// packages/parser/src/parser/rules/block/html.ts
|
|
4716
|
+
function lookaheadHasHtmlClose(ctx, from) {
|
|
4717
|
+
for (let i = from;i < ctx.tokens.length; i++) {
|
|
4718
|
+
const t = ctx.tokens[i];
|
|
4719
|
+
if (!t || t.type === "EOF")
|
|
4720
|
+
return false;
|
|
4721
|
+
if (t.type !== "BLOCK_END_OPEN")
|
|
4722
|
+
continue;
|
|
4723
|
+
const closeName = parseBlockName(ctx, i + 1);
|
|
4724
|
+
if (closeName?.name.toLowerCase() !== "html")
|
|
4725
|
+
continue;
|
|
4726
|
+
let cp = i + 1 + closeName.consumed;
|
|
4727
|
+
while (ctx.tokens[cp]?.type === "WHITESPACE")
|
|
4728
|
+
cp++;
|
|
4729
|
+
if (ctx.tokens[cp]?.type === "BLOCK_CLOSE")
|
|
4730
|
+
return true;
|
|
4731
|
+
}
|
|
4732
|
+
return false;
|
|
4733
|
+
}
|
|
4670
4734
|
var htmlBlockRule = {
|
|
4671
4735
|
name: "html",
|
|
4672
4736
|
startTokens: ["BLOCK_OPEN"],
|
|
@@ -4693,20 +4757,32 @@ var htmlBlockRule = {
|
|
|
4693
4757
|
}
|
|
4694
4758
|
pos++;
|
|
4695
4759
|
consumed++;
|
|
4760
|
+
const disabled = ctx.settings.allowHtmlBlocks === false;
|
|
4761
|
+
const hasCloseAhead = disabled && lookaheadHasHtmlClose(ctx, pos);
|
|
4696
4762
|
let contents = "";
|
|
4697
4763
|
let foundClose = false;
|
|
4698
4764
|
while (pos < ctx.tokens.length) {
|
|
4699
4765
|
const token = ctx.tokens[pos];
|
|
4700
4766
|
if (!token || token.type === "EOF")
|
|
4701
4767
|
break;
|
|
4768
|
+
if (disabled && !hasCloseAhead && token.type === "NEWLINE" && ctx.tokens[pos + 1]?.type === "NEWLINE") {
|
|
4769
|
+
break;
|
|
4770
|
+
}
|
|
4702
4771
|
if (token.type === "BLOCK_END_OPEN") {
|
|
4703
4772
|
const closeNameResult = parseBlockName(ctx, pos + 1);
|
|
4704
4773
|
if (closeNameResult?.name.toLowerCase() === "html") {
|
|
4705
|
-
|
|
4706
|
-
|
|
4774
|
+
let checkPos = pos + 1 + closeNameResult.consumed;
|
|
4775
|
+
while (ctx.tokens[checkPos]?.type === "WHITESPACE")
|
|
4776
|
+
checkPos++;
|
|
4777
|
+
if (ctx.tokens[checkPos]?.type === "BLOCK_CLOSE") {
|
|
4778
|
+
foundClose = true;
|
|
4779
|
+
break;
|
|
4780
|
+
}
|
|
4707
4781
|
}
|
|
4708
4782
|
}
|
|
4709
|
-
|
|
4783
|
+
if (!disabled) {
|
|
4784
|
+
contents += token.value;
|
|
4785
|
+
}
|
|
4710
4786
|
pos++;
|
|
4711
4787
|
consumed++;
|
|
4712
4788
|
}
|
|
@@ -4717,7 +4793,16 @@ var htmlBlockRule = {
|
|
|
4717
4793
|
message: "Missing closing tag [[/html]] for [[html]]",
|
|
4718
4794
|
position: openToken.position
|
|
4719
4795
|
});
|
|
4720
|
-
|
|
4796
|
+
if (!disabled) {
|
|
4797
|
+
return { success: false };
|
|
4798
|
+
}
|
|
4799
|
+
ctx.diagnostics.push({
|
|
4800
|
+
severity: "info",
|
|
4801
|
+
code: "html-block-disabled",
|
|
4802
|
+
message: "[[html]] block ignored: disabled by settings",
|
|
4803
|
+
position: openToken.position
|
|
4804
|
+
});
|
|
4805
|
+
return { success: true, elements: [], consumed };
|
|
4721
4806
|
}
|
|
4722
4807
|
if (ctx.tokens[pos]?.type === "BLOCK_END_OPEN") {
|
|
4723
4808
|
pos++;
|
|
@@ -4727,6 +4812,10 @@ var htmlBlockRule = {
|
|
|
4727
4812
|
pos += closeNameResult.consumed;
|
|
4728
4813
|
consumed += closeNameResult.consumed;
|
|
4729
4814
|
}
|
|
4815
|
+
while (ctx.tokens[pos]?.type === "WHITESPACE") {
|
|
4816
|
+
pos++;
|
|
4817
|
+
consumed++;
|
|
4818
|
+
}
|
|
4730
4819
|
if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
|
|
4731
4820
|
pos++;
|
|
4732
4821
|
consumed++;
|
|
@@ -4736,6 +4825,15 @@ var htmlBlockRule = {
|
|
|
4736
4825
|
consumed++;
|
|
4737
4826
|
}
|
|
4738
4827
|
}
|
|
4828
|
+
if (disabled) {
|
|
4829
|
+
ctx.diagnostics.push({
|
|
4830
|
+
severity: "info",
|
|
4831
|
+
code: "html-block-disabled",
|
|
4832
|
+
message: "[[html]] block ignored: disabled by settings",
|
|
4833
|
+
position: openToken.position
|
|
4834
|
+
});
|
|
4835
|
+
return { success: true, elements: [], consumed };
|
|
4836
|
+
}
|
|
4739
4837
|
contents = contents.trim();
|
|
4740
4838
|
ctx.htmlBlocks.push(contents);
|
|
4741
4839
|
return {
|
|
@@ -6421,6 +6519,82 @@ var commentRule = {
|
|
|
6421
6519
|
}
|
|
6422
6520
|
};
|
|
6423
6521
|
|
|
6522
|
+
// packages/parser/src/parser/rules/inline/html.ts
|
|
6523
|
+
var htmlInlineRule = {
|
|
6524
|
+
name: "html",
|
|
6525
|
+
startTokens: ["BLOCK_OPEN"],
|
|
6526
|
+
parse(ctx) {
|
|
6527
|
+
const openToken = currentToken(ctx);
|
|
6528
|
+
if (openToken.type !== "BLOCK_OPEN") {
|
|
6529
|
+
return { success: false };
|
|
6530
|
+
}
|
|
6531
|
+
let pos = ctx.pos + 1;
|
|
6532
|
+
let consumed = 1;
|
|
6533
|
+
const nameResult = parseBlockName(ctx, pos);
|
|
6534
|
+
if (!nameResult || nameResult.name.toLowerCase() !== "html") {
|
|
6535
|
+
return { success: false };
|
|
6536
|
+
}
|
|
6537
|
+
pos += nameResult.consumed;
|
|
6538
|
+
consumed += nameResult.consumed;
|
|
6539
|
+
const attrResult = parseAttributesRaw(ctx, pos);
|
|
6540
|
+
pos += attrResult.consumed;
|
|
6541
|
+
consumed += attrResult.consumed;
|
|
6542
|
+
if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
|
|
6543
|
+
return { success: false };
|
|
6544
|
+
}
|
|
6545
|
+
pos++;
|
|
6546
|
+
consumed++;
|
|
6547
|
+
if (ctx.settings.allowHtmlBlocks !== false) {
|
|
6548
|
+
return { success: false };
|
|
6549
|
+
}
|
|
6550
|
+
const hasCloseAhead = lookaheadHasHtmlClose(ctx, pos);
|
|
6551
|
+
let foundClose = false;
|
|
6552
|
+
while (pos < ctx.tokens.length) {
|
|
6553
|
+
const token = ctx.tokens[pos];
|
|
6554
|
+
if (!token || token.type === "EOF")
|
|
6555
|
+
break;
|
|
6556
|
+
if (!hasCloseAhead && token.type === "NEWLINE" && ctx.tokens[pos + 1]?.type === "NEWLINE") {
|
|
6557
|
+
break;
|
|
6558
|
+
}
|
|
6559
|
+
if (token.type === "BLOCK_END_OPEN") {
|
|
6560
|
+
const closeNameResult = parseBlockName(ctx, pos + 1);
|
|
6561
|
+
if (closeNameResult?.name.toLowerCase() === "html") {
|
|
6562
|
+
let checkPos = pos + 1 + closeNameResult.consumed;
|
|
6563
|
+
while (ctx.tokens[checkPos]?.type === "WHITESPACE")
|
|
6564
|
+
checkPos++;
|
|
6565
|
+
if (ctx.tokens[checkPos]?.type === "BLOCK_CLOSE") {
|
|
6566
|
+
foundClose = true;
|
|
6567
|
+
consumed += checkPos - pos + 1;
|
|
6568
|
+
pos = checkPos + 1;
|
|
6569
|
+
if (ctx.tokens[pos]?.type === "NEWLINE") {
|
|
6570
|
+
pos++;
|
|
6571
|
+
consumed++;
|
|
6572
|
+
}
|
|
6573
|
+
break;
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6576
|
+
}
|
|
6577
|
+
pos++;
|
|
6578
|
+
consumed++;
|
|
6579
|
+
}
|
|
6580
|
+
if (!foundClose) {
|
|
6581
|
+
ctx.diagnostics.push({
|
|
6582
|
+
severity: "warning",
|
|
6583
|
+
code: "unclosed-block",
|
|
6584
|
+
message: "Missing closing tag [[/html]] for [[html]]",
|
|
6585
|
+
position: openToken.position
|
|
6586
|
+
});
|
|
6587
|
+
}
|
|
6588
|
+
ctx.diagnostics.push({
|
|
6589
|
+
severity: "info",
|
|
6590
|
+
code: "html-block-disabled",
|
|
6591
|
+
message: "[[html]] block ignored: disabled by settings",
|
|
6592
|
+
position: openToken.position
|
|
6593
|
+
});
|
|
6594
|
+
return { success: true, elements: [], consumed };
|
|
6595
|
+
}
|
|
6596
|
+
};
|
|
6597
|
+
|
|
6424
6598
|
// packages/parser/src/parser/rules/inline/raw.ts
|
|
6425
6599
|
var rawRule = {
|
|
6426
6600
|
name: "raw",
|
|
@@ -8131,6 +8305,7 @@ var inlineRules = [
|
|
|
8131
8305
|
underscoreLineBreakRule,
|
|
8132
8306
|
newlineLineBreakRule,
|
|
8133
8307
|
commentRule,
|
|
8308
|
+
htmlInlineRule,
|
|
8134
8309
|
rawRule,
|
|
8135
8310
|
imageRule,
|
|
8136
8311
|
sizeRule,
|
|
@@ -8648,83 +8823,564 @@ function buildTableOfContents(entries) {
|
|
|
8648
8823
|
return trees.map((tree) => buildTocList(indexer, tree.list));
|
|
8649
8824
|
}
|
|
8650
8825
|
|
|
8651
|
-
// packages/parser/src/parser/
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
footnoteBlockParsed: false,
|
|
8666
|
-
bibcites: [],
|
|
8667
|
-
diagnostics: [],
|
|
8668
|
-
blockRules,
|
|
8669
|
-
blockFallbackRule: paragraphRule,
|
|
8670
|
-
inlineRules
|
|
8671
|
-
};
|
|
8672
|
-
}
|
|
8673
|
-
parse() {
|
|
8674
|
-
const children = [];
|
|
8675
|
-
while (!this.isAtEnd()) {
|
|
8676
|
-
const blocks = this.parseBlock();
|
|
8677
|
-
children.push(...blocks);
|
|
8678
|
-
}
|
|
8679
|
-
const mergedChildren = mergeSpanStripParagraphs(children);
|
|
8680
|
-
const divProcessed = suppressDivAdjacentParagraphs(mergedChildren);
|
|
8681
|
-
const cleanedChildren = cleanInternalFlags(divProcessed);
|
|
8682
|
-
const hasFootnoteBlock = cleanedChildren.some((el) => el.element === "footnote-block");
|
|
8683
|
-
if (!hasFootnoteBlock) {
|
|
8684
|
-
cleanedChildren.push({
|
|
8685
|
-
element: "footnote-block",
|
|
8686
|
-
data: { title: null, hide: false }
|
|
8687
|
-
});
|
|
8826
|
+
// packages/parser/src/parser/rules/block/module/walk.ts
|
|
8827
|
+
function walkElements(elements, callback) {
|
|
8828
|
+
for (const element of elements) {
|
|
8829
|
+
callback(element);
|
|
8830
|
+
if (element.element === "list") {
|
|
8831
|
+
const listData = element.data;
|
|
8832
|
+
for (const item of listData.items) {
|
|
8833
|
+
if (item["item-type"] === "elements") {
|
|
8834
|
+
walkElements(item.elements, callback);
|
|
8835
|
+
} else if (item["item-type"] === "sub-list") {
|
|
8836
|
+
walkElements([{ element: "list", data: item.data }], callback);
|
|
8837
|
+
}
|
|
8838
|
+
}
|
|
8839
|
+
continue;
|
|
8688
8840
|
}
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8841
|
+
if (element.element === "table") {
|
|
8842
|
+
const tableData = element.data;
|
|
8843
|
+
for (const row of tableData.rows) {
|
|
8844
|
+
for (const cell of row.cells) {
|
|
8845
|
+
walkElements(cell.elements, callback);
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
continue;
|
|
8695
8849
|
}
|
|
8696
|
-
if (
|
|
8697
|
-
|
|
8850
|
+
if (element.element === "definition-list") {
|
|
8851
|
+
const defListData = element.data;
|
|
8852
|
+
for (const item of defListData) {
|
|
8853
|
+
walkElements(item.key, callback);
|
|
8854
|
+
walkElements(item.value, callback);
|
|
8855
|
+
}
|
|
8856
|
+
continue;
|
|
8698
8857
|
}
|
|
8699
|
-
if (
|
|
8700
|
-
|
|
8858
|
+
if (element.element === "tab-view") {
|
|
8859
|
+
const tabData = element.data;
|
|
8860
|
+
for (const tab of tabData) {
|
|
8861
|
+
walkElements(tab.elements, callback);
|
|
8862
|
+
}
|
|
8863
|
+
continue;
|
|
8701
8864
|
}
|
|
8702
|
-
if (
|
|
8703
|
-
|
|
8865
|
+
if ("data" in element && element.data && typeof element.data === "object") {
|
|
8866
|
+
const data = element.data;
|
|
8867
|
+
if ("elements" in data && Array.isArray(data.elements)) {
|
|
8868
|
+
walkElements(data.elements, callback);
|
|
8869
|
+
}
|
|
8704
8870
|
}
|
|
8705
|
-
return { ast: result, diagnostics: this.ctx.diagnostics };
|
|
8706
|
-
}
|
|
8707
|
-
isAtEnd() {
|
|
8708
|
-
return this.ctx.pos >= this.ctx.tokens.length || this.currentToken().type === "EOF";
|
|
8709
|
-
}
|
|
8710
|
-
currentToken() {
|
|
8711
|
-
return this.ctx.tokens[this.ctx.pos] ?? this.eofToken();
|
|
8712
8871
|
}
|
|
8713
|
-
|
|
8872
|
+
}
|
|
8873
|
+
function mapElementChildren(element, transform) {
|
|
8874
|
+
if (element.element === "list") {
|
|
8875
|
+
const listData = element.data;
|
|
8876
|
+
const newItems = [];
|
|
8877
|
+
for (const item of listData.items) {
|
|
8878
|
+
if (item["item-type"] === "elements") {
|
|
8879
|
+
newItems.push({
|
|
8880
|
+
"item-type": "elements",
|
|
8881
|
+
attributes: item.attributes,
|
|
8882
|
+
elements: transform(item.elements)
|
|
8883
|
+
});
|
|
8884
|
+
} else if (item["item-type"] === "sub-list") {
|
|
8885
|
+
const subListResult = transform([{ element: "list", data: item.data }]);
|
|
8886
|
+
const resolvedList = subListResult[0];
|
|
8887
|
+
if (resolvedList?.element === "list") {
|
|
8888
|
+
newItems.push({
|
|
8889
|
+
"item-type": "sub-list",
|
|
8890
|
+
element: "list",
|
|
8891
|
+
data: resolvedList.data
|
|
8892
|
+
});
|
|
8893
|
+
} else {
|
|
8894
|
+
newItems.push(item);
|
|
8895
|
+
}
|
|
8896
|
+
}
|
|
8897
|
+
}
|
|
8714
8898
|
return {
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
position: {
|
|
8718
|
-
start: { line: 0, column: 0, offset: 0 },
|
|
8719
|
-
end: { line: 0, column: 0, offset: 0 }
|
|
8720
|
-
},
|
|
8721
|
-
lineStart: false
|
|
8899
|
+
element: "list",
|
|
8900
|
+
data: { ...listData, items: newItems }
|
|
8722
8901
|
};
|
|
8723
8902
|
}
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8903
|
+
if (element.element === "table") {
|
|
8904
|
+
const tableData = element.data;
|
|
8905
|
+
const newRows = [];
|
|
8906
|
+
for (const row of tableData.rows) {
|
|
8907
|
+
const newCells = [];
|
|
8908
|
+
for (const cell of row.cells) {
|
|
8909
|
+
newCells.push({ ...cell, elements: transform(cell.elements) });
|
|
8910
|
+
}
|
|
8911
|
+
newRows.push({ ...row, cells: newCells });
|
|
8912
|
+
}
|
|
8913
|
+
return {
|
|
8914
|
+
element: "table",
|
|
8915
|
+
data: { ...tableData, rows: newRows }
|
|
8916
|
+
};
|
|
8917
|
+
}
|
|
8918
|
+
if (element.element === "definition-list") {
|
|
8919
|
+
const defListData = element.data;
|
|
8920
|
+
const newItems = [];
|
|
8921
|
+
for (const item of defListData) {
|
|
8922
|
+
newItems.push({
|
|
8923
|
+
key_string: item.key_string,
|
|
8924
|
+
key: transform(item.key),
|
|
8925
|
+
value: transform(item.value)
|
|
8926
|
+
});
|
|
8927
|
+
}
|
|
8928
|
+
return {
|
|
8929
|
+
element: "definition-list",
|
|
8930
|
+
data: newItems
|
|
8931
|
+
};
|
|
8932
|
+
}
|
|
8933
|
+
if (element.element === "tab-view") {
|
|
8934
|
+
const tabData = element.data;
|
|
8935
|
+
const newTabs = [];
|
|
8936
|
+
for (const tab of tabData) {
|
|
8937
|
+
newTabs.push({ ...tab, elements: transform(tab.elements) });
|
|
8938
|
+
}
|
|
8939
|
+
return {
|
|
8940
|
+
element: "tab-view",
|
|
8941
|
+
data: newTabs
|
|
8942
|
+
};
|
|
8943
|
+
}
|
|
8944
|
+
if ("data" in element && element.data && typeof element.data === "object") {
|
|
8945
|
+
const data = element.data;
|
|
8946
|
+
if ("elements" in data && Array.isArray(data.elements)) {
|
|
8947
|
+
return {
|
|
8948
|
+
...element,
|
|
8949
|
+
data: {
|
|
8950
|
+
...data,
|
|
8951
|
+
elements: transform(data.elements)
|
|
8952
|
+
}
|
|
8953
|
+
};
|
|
8954
|
+
}
|
|
8955
|
+
}
|
|
8956
|
+
return element;
|
|
8957
|
+
}
|
|
8958
|
+
function mapElementChildrenWithState(element, state, transform) {
|
|
8959
|
+
if (element.element === "list") {
|
|
8960
|
+
const listData = element.data;
|
|
8961
|
+
const newItems = [];
|
|
8962
|
+
let currentState = state;
|
|
8963
|
+
for (const item of listData.items) {
|
|
8964
|
+
if (item["item-type"] === "elements") {
|
|
8965
|
+
const result = transform(item.elements, currentState);
|
|
8966
|
+
newItems.push({
|
|
8967
|
+
"item-type": "elements",
|
|
8968
|
+
attributes: item.attributes,
|
|
8969
|
+
elements: result.elements
|
|
8970
|
+
});
|
|
8971
|
+
currentState = result.state;
|
|
8972
|
+
} else if (item["item-type"] === "sub-list") {
|
|
8973
|
+
const result = transform([{ element: "list", data: item.data }], currentState);
|
|
8974
|
+
const resolvedList = result.elements[0];
|
|
8975
|
+
if (resolvedList?.element === "list") {
|
|
8976
|
+
newItems.push({
|
|
8977
|
+
"item-type": "sub-list",
|
|
8978
|
+
element: "list",
|
|
8979
|
+
data: resolvedList.data
|
|
8980
|
+
});
|
|
8981
|
+
} else {
|
|
8982
|
+
newItems.push(item);
|
|
8983
|
+
}
|
|
8984
|
+
currentState = result.state;
|
|
8985
|
+
}
|
|
8986
|
+
}
|
|
8987
|
+
return {
|
|
8988
|
+
element: {
|
|
8989
|
+
element: "list",
|
|
8990
|
+
data: { ...listData, items: newItems }
|
|
8991
|
+
},
|
|
8992
|
+
state: currentState
|
|
8993
|
+
};
|
|
8994
|
+
}
|
|
8995
|
+
if (element.element === "table") {
|
|
8996
|
+
const tableData = element.data;
|
|
8997
|
+
const newRows = [];
|
|
8998
|
+
let currentState = state;
|
|
8999
|
+
for (const row of tableData.rows) {
|
|
9000
|
+
const newCells = [];
|
|
9001
|
+
for (const cell of row.cells) {
|
|
9002
|
+
const result = transform(cell.elements, currentState);
|
|
9003
|
+
newCells.push({ ...cell, elements: result.elements });
|
|
9004
|
+
currentState = result.state;
|
|
9005
|
+
}
|
|
9006
|
+
newRows.push({ ...row, cells: newCells });
|
|
9007
|
+
}
|
|
9008
|
+
return {
|
|
9009
|
+
element: {
|
|
9010
|
+
element: "table",
|
|
9011
|
+
data: { ...tableData, rows: newRows }
|
|
9012
|
+
},
|
|
9013
|
+
state: currentState
|
|
9014
|
+
};
|
|
9015
|
+
}
|
|
9016
|
+
if (element.element === "definition-list") {
|
|
9017
|
+
const defListData = element.data;
|
|
9018
|
+
const newItems = [];
|
|
9019
|
+
let currentState = state;
|
|
9020
|
+
for (const item of defListData) {
|
|
9021
|
+
const keyResult = transform(item.key, currentState);
|
|
9022
|
+
currentState = keyResult.state;
|
|
9023
|
+
const valueResult = transform(item.value, currentState);
|
|
9024
|
+
currentState = valueResult.state;
|
|
9025
|
+
newItems.push({
|
|
9026
|
+
key_string: item.key_string,
|
|
9027
|
+
key: keyResult.elements,
|
|
9028
|
+
value: valueResult.elements
|
|
9029
|
+
});
|
|
9030
|
+
}
|
|
9031
|
+
return {
|
|
9032
|
+
element: {
|
|
9033
|
+
element: "definition-list",
|
|
9034
|
+
data: newItems
|
|
9035
|
+
},
|
|
9036
|
+
state: currentState
|
|
9037
|
+
};
|
|
9038
|
+
}
|
|
9039
|
+
if (element.element === "tab-view") {
|
|
9040
|
+
const tabData = element.data;
|
|
9041
|
+
const newTabs = [];
|
|
9042
|
+
let currentState = state;
|
|
9043
|
+
for (const tab of tabData) {
|
|
9044
|
+
const result = transform(tab.elements, currentState);
|
|
9045
|
+
newTabs.push({ ...tab, elements: result.elements });
|
|
9046
|
+
currentState = result.state;
|
|
9047
|
+
}
|
|
9048
|
+
return {
|
|
9049
|
+
element: {
|
|
9050
|
+
element: "tab-view",
|
|
9051
|
+
data: newTabs
|
|
9052
|
+
},
|
|
9053
|
+
state: currentState
|
|
9054
|
+
};
|
|
9055
|
+
}
|
|
9056
|
+
if ("data" in element && element.data && typeof element.data === "object") {
|
|
9057
|
+
const data = element.data;
|
|
9058
|
+
if ("elements" in data && Array.isArray(data.elements)) {
|
|
9059
|
+
const result = transform(data.elements, state);
|
|
9060
|
+
return {
|
|
9061
|
+
element: {
|
|
9062
|
+
...element,
|
|
9063
|
+
data: {
|
|
9064
|
+
...data,
|
|
9065
|
+
elements: result.elements
|
|
9066
|
+
}
|
|
9067
|
+
},
|
|
9068
|
+
state: result.state
|
|
9069
|
+
};
|
|
9070
|
+
}
|
|
9071
|
+
}
|
|
9072
|
+
return { element, state };
|
|
9073
|
+
}
|
|
9074
|
+
|
|
9075
|
+
// packages/parser/src/parser/rules/block/module/iftags/condition.ts
|
|
9076
|
+
function parseTagCondition(condition) {
|
|
9077
|
+
const required = [];
|
|
9078
|
+
const forbidden = [];
|
|
9079
|
+
const optional = [];
|
|
9080
|
+
const parts = condition.trim().split(/\s+/);
|
|
9081
|
+
for (const part of parts) {
|
|
9082
|
+
if (!part)
|
|
9083
|
+
continue;
|
|
9084
|
+
if (part.startsWith("+")) {
|
|
9085
|
+
const tag = part.slice(1);
|
|
9086
|
+
if (tag)
|
|
9087
|
+
required.push(tag);
|
|
9088
|
+
} else if (part.startsWith("-")) {
|
|
9089
|
+
const tag = part.slice(1);
|
|
9090
|
+
if (tag)
|
|
9091
|
+
forbidden.push(tag);
|
|
9092
|
+
} else {
|
|
9093
|
+
optional.push(part);
|
|
9094
|
+
}
|
|
9095
|
+
}
|
|
9096
|
+
return { required, forbidden, optional };
|
|
9097
|
+
}
|
|
9098
|
+
function evaluateTagCondition(condition, pageTags) {
|
|
9099
|
+
if (condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0) {
|
|
9100
|
+
return false;
|
|
9101
|
+
}
|
|
9102
|
+
const tagSet = new Set(pageTags);
|
|
9103
|
+
for (const tag of condition.required) {
|
|
9104
|
+
if (!tagSet.has(tag)) {
|
|
9105
|
+
return false;
|
|
9106
|
+
}
|
|
9107
|
+
}
|
|
9108
|
+
for (const tag of condition.forbidden) {
|
|
9109
|
+
if (tagSet.has(tag)) {
|
|
9110
|
+
return false;
|
|
9111
|
+
}
|
|
9112
|
+
}
|
|
9113
|
+
if (condition.optional.length > 0) {
|
|
9114
|
+
if (!condition.optional.some((tag) => tagSet.has(tag))) {
|
|
9115
|
+
return false;
|
|
9116
|
+
}
|
|
9117
|
+
}
|
|
9118
|
+
return true;
|
|
9119
|
+
}
|
|
9120
|
+
|
|
9121
|
+
// packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
|
|
9122
|
+
var BASE_PLACEHOLDER_OPEN = "";
|
|
9123
|
+
var BASE_PLACEHOLDER_CLOSE = "";
|
|
9124
|
+
var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
|
|
9125
|
+
var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
|
|
9126
|
+
function preprocessIftags(source, pageTags) {
|
|
9127
|
+
if (!source.includes("[["))
|
|
9128
|
+
return source;
|
|
9129
|
+
const sentinels = makeUniqueSentinels(source);
|
|
9130
|
+
const { masked, placeholders } = maskRawRegions(source, sentinels);
|
|
9131
|
+
const reduced = reduceIftags(masked, pageTags);
|
|
9132
|
+
return restorePlaceholders(reduced, placeholders, sentinels);
|
|
9133
|
+
}
|
|
9134
|
+
function makeUniqueSentinels(source) {
|
|
9135
|
+
let open = BASE_PLACEHOLDER_OPEN;
|
|
9136
|
+
let close = BASE_PLACEHOLDER_CLOSE;
|
|
9137
|
+
while (source.includes(open) || source.includes(close)) {
|
|
9138
|
+
open += BASE_PLACEHOLDER_OPEN;
|
|
9139
|
+
close += BASE_PLACEHOLDER_CLOSE;
|
|
9140
|
+
}
|
|
9141
|
+
return { open, close };
|
|
9142
|
+
}
|
|
9143
|
+
function reduceIftags(source, pageTags) {
|
|
9144
|
+
let current = source;
|
|
9145
|
+
const maxIterations = source.length + 1;
|
|
9146
|
+
const tagSet = pageTags ?? [];
|
|
9147
|
+
for (let i = 0;i < maxIterations; i++) {
|
|
9148
|
+
const depths = pageTags === null ? computeBracketDepths(current) : null;
|
|
9149
|
+
let changed = false;
|
|
9150
|
+
const next = current.replace(INNERMOST_IFTAGS_PATTERN, (match, cond, body, offset) => {
|
|
9151
|
+
if (depths !== null && depths[offset] === 0) {
|
|
9152
|
+
return match;
|
|
9153
|
+
}
|
|
9154
|
+
changed = true;
|
|
9155
|
+
const condition = parseTagCondition(cond);
|
|
9156
|
+
return evaluateTagCondition(condition, tagSet) ? body : "";
|
|
9157
|
+
});
|
|
9158
|
+
if (!changed)
|
|
9159
|
+
return current;
|
|
9160
|
+
current = next;
|
|
9161
|
+
}
|
|
9162
|
+
return current;
|
|
9163
|
+
}
|
|
9164
|
+
function computeBracketDepths(masked) {
|
|
9165
|
+
const n = masked.length;
|
|
9166
|
+
const depths = new Int32Array(n + 1);
|
|
9167
|
+
let depth = 0;
|
|
9168
|
+
let i = 0;
|
|
9169
|
+
while (i < n) {
|
|
9170
|
+
depths[i] = depth;
|
|
9171
|
+
const c = masked.charCodeAt(i);
|
|
9172
|
+
const c1 = i + 1 < n ? masked.charCodeAt(i + 1) : -1;
|
|
9173
|
+
const c2 = i + 2 < n ? masked.charCodeAt(i + 2) : -1;
|
|
9174
|
+
if (depth > 0 && c === 34 && precededByEqualsAttr(masked, i)) {
|
|
9175
|
+
const end = findQuoteEnd(masked, i + 1);
|
|
9176
|
+
for (let k = i;k <= end; k++)
|
|
9177
|
+
depths[k] = depth;
|
|
9178
|
+
i = end + 1;
|
|
9179
|
+
continue;
|
|
9180
|
+
}
|
|
9181
|
+
if (c === 91 && c1 === 91 && c2 === 91) {
|
|
9182
|
+
const end = findTripleLinkEnd(masked, i + 3);
|
|
9183
|
+
for (let k = i;k <= end; k++)
|
|
9184
|
+
depths[k] = depth;
|
|
9185
|
+
i = end + 1;
|
|
9186
|
+
continue;
|
|
9187
|
+
}
|
|
9188
|
+
if (c === 91 && c1 === 91) {
|
|
9189
|
+
depth++;
|
|
9190
|
+
depths[i + 1] = depth;
|
|
9191
|
+
i += 2;
|
|
9192
|
+
continue;
|
|
9193
|
+
}
|
|
9194
|
+
if (c === 93 && c1 === 93) {
|
|
9195
|
+
depth = Math.max(0, depth - 1);
|
|
9196
|
+
depths[i + 1] = depth;
|
|
9197
|
+
i += 2;
|
|
9198
|
+
continue;
|
|
9199
|
+
}
|
|
9200
|
+
if (c === 10) {
|
|
9201
|
+
depth = 0;
|
|
9202
|
+
}
|
|
9203
|
+
i++;
|
|
9204
|
+
}
|
|
9205
|
+
depths[n] = depth;
|
|
9206
|
+
return depths;
|
|
9207
|
+
}
|
|
9208
|
+
function precededByEqualsAttr(s, i) {
|
|
9209
|
+
let j = i - 1;
|
|
9210
|
+
while (j >= 0) {
|
|
9211
|
+
const ch = s.charCodeAt(j);
|
|
9212
|
+
if (ch === 32 || ch === 9) {
|
|
9213
|
+
j--;
|
|
9214
|
+
continue;
|
|
9215
|
+
}
|
|
9216
|
+
return ch === 61;
|
|
9217
|
+
}
|
|
9218
|
+
return false;
|
|
9219
|
+
}
|
|
9220
|
+
function findQuoteEnd(s, from) {
|
|
9221
|
+
for (let i = from;i < s.length; i++) {
|
|
9222
|
+
const ch = s.charCodeAt(i);
|
|
9223
|
+
if (ch === 34 || ch === 10)
|
|
9224
|
+
return i;
|
|
9225
|
+
}
|
|
9226
|
+
return s.length - 1;
|
|
9227
|
+
}
|
|
9228
|
+
function findTripleLinkEnd(s, from) {
|
|
9229
|
+
for (let i = from;i < s.length; i++) {
|
|
9230
|
+
if (s.charCodeAt(i) === 93 && i + 2 < s.length && s.charCodeAt(i + 1) === 93 && s.charCodeAt(i + 2) === 93) {
|
|
9231
|
+
return i + 2;
|
|
9232
|
+
}
|
|
9233
|
+
if (s.charCodeAt(i) === 10 && i + 1 < s.length && s.charCodeAt(i + 1) === 10) {
|
|
9234
|
+
return i;
|
|
9235
|
+
}
|
|
9236
|
+
}
|
|
9237
|
+
return s.length - 1;
|
|
9238
|
+
}
|
|
9239
|
+
function maskRawRegions(source, sentinels) {
|
|
9240
|
+
const placeholders = [];
|
|
9241
|
+
let masked = "";
|
|
9242
|
+
let i = 0;
|
|
9243
|
+
while (i < source.length) {
|
|
9244
|
+
if (source[i] === "[" && source[i + 1] === "[") {
|
|
9245
|
+
RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
|
|
9246
|
+
const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
|
|
9247
|
+
if (openMatch) {
|
|
9248
|
+
const name = openMatch[1].toLowerCase();
|
|
9249
|
+
const openLen = openMatch[0].length;
|
|
9250
|
+
const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
|
|
9251
|
+
closePattern.lastIndex = i + openLen;
|
|
9252
|
+
const closeMatch = closePattern.exec(source);
|
|
9253
|
+
if (closeMatch) {
|
|
9254
|
+
const regionEnd = closeMatch.index + closeMatch[0].length;
|
|
9255
|
+
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
9256
|
+
i = regionEnd;
|
|
9257
|
+
continue;
|
|
9258
|
+
}
|
|
9259
|
+
if (name === "code") {
|
|
9260
|
+
masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
|
|
9261
|
+
i = source.length;
|
|
9262
|
+
continue;
|
|
9263
|
+
}
|
|
9264
|
+
}
|
|
9265
|
+
}
|
|
9266
|
+
if (source[i] === "@" && source[i + 1] === "<") {
|
|
9267
|
+
const close = source.indexOf(">@", i + 2);
|
|
9268
|
+
const newline = source.indexOf(`
|
|
9269
|
+
`, i + 2);
|
|
9270
|
+
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
9271
|
+
const regionEnd = close + 2;
|
|
9272
|
+
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
9273
|
+
i = regionEnd;
|
|
9274
|
+
continue;
|
|
9275
|
+
}
|
|
9276
|
+
}
|
|
9277
|
+
if (source[i] === "@" && source[i + 1] === "@") {
|
|
9278
|
+
const close = source.indexOf("@@", i + 2);
|
|
9279
|
+
const newline = source.indexOf(`
|
|
9280
|
+
`, i + 2);
|
|
9281
|
+
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
9282
|
+
const regionEnd = close + 2;
|
|
9283
|
+
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
9284
|
+
i = regionEnd;
|
|
9285
|
+
continue;
|
|
9286
|
+
}
|
|
9287
|
+
}
|
|
9288
|
+
masked += source[i];
|
|
9289
|
+
i++;
|
|
9290
|
+
}
|
|
9291
|
+
return { masked, placeholders };
|
|
9292
|
+
}
|
|
9293
|
+
function pushPlaceholder(placeholders, text, sentinels) {
|
|
9294
|
+
const idx = placeholders.length;
|
|
9295
|
+
placeholders.push(text);
|
|
9296
|
+
return `${sentinels.open}${idx}${sentinels.close}`;
|
|
9297
|
+
}
|
|
9298
|
+
function escapeRegex(str) {
|
|
9299
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9300
|
+
}
|
|
9301
|
+
function restorePlaceholders(source, placeholders, sentinels) {
|
|
9302
|
+
const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
|
|
9303
|
+
return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
|
|
9304
|
+
}
|
|
9305
|
+
|
|
9306
|
+
// packages/parser/src/parser/parse.ts
|
|
9307
|
+
class Parser {
|
|
9308
|
+
ctx;
|
|
9309
|
+
constructor(tokens, options = {}) {
|
|
9310
|
+
this.ctx = {
|
|
9311
|
+
tokens,
|
|
9312
|
+
pos: 0,
|
|
9313
|
+
version: options.version ?? "wikidot",
|
|
9314
|
+
trackPositions: options.trackPositions ?? true,
|
|
9315
|
+
settings: options.settings ?? DEFAULT_SETTINGS,
|
|
9316
|
+
footnotes: [],
|
|
9317
|
+
tocEntries: [],
|
|
9318
|
+
codeBlocks: [],
|
|
9319
|
+
htmlBlocks: [],
|
|
9320
|
+
bibcites: [],
|
|
9321
|
+
diagnostics: [],
|
|
9322
|
+
blockRules,
|
|
9323
|
+
blockFallbackRule: paragraphRule,
|
|
9324
|
+
inlineRules,
|
|
9325
|
+
scope: {
|
|
9326
|
+
footnoteBlockParsed: false
|
|
9327
|
+
}
|
|
9328
|
+
};
|
|
9329
|
+
}
|
|
9330
|
+
parse() {
|
|
9331
|
+
const children = [];
|
|
9332
|
+
while (!this.isAtEnd()) {
|
|
9333
|
+
const blocks = this.parseBlock();
|
|
9334
|
+
children.push(...blocks);
|
|
9335
|
+
}
|
|
9336
|
+
const mergedChildren = mergeSpanStripParagraphs(children);
|
|
9337
|
+
const divProcessed = suppressDivAdjacentParagraphs(mergedChildren);
|
|
9338
|
+
const cleanedChildren = cleanInternalFlags(divProcessed);
|
|
9339
|
+
if (!containsFootnoteBlock(cleanedChildren)) {
|
|
9340
|
+
cleanedChildren.push({
|
|
9341
|
+
element: "footnote-block",
|
|
9342
|
+
data: { title: null, hide: false }
|
|
9343
|
+
});
|
|
9344
|
+
}
|
|
9345
|
+
const tableOfContents = buildTableOfContents(this.ctx.tocEntries);
|
|
9346
|
+
const result = {
|
|
9347
|
+
elements: cleanedChildren
|
|
9348
|
+
};
|
|
9349
|
+
if (tableOfContents.length > 0) {
|
|
9350
|
+
result["table-of-contents"] = tableOfContents;
|
|
9351
|
+
}
|
|
9352
|
+
if (this.ctx.footnotes.length > 0) {
|
|
9353
|
+
result.footnotes = this.ctx.footnotes;
|
|
9354
|
+
}
|
|
9355
|
+
if (this.ctx.codeBlocks.length > 0) {
|
|
9356
|
+
result["code-blocks"] = this.ctx.codeBlocks;
|
|
9357
|
+
}
|
|
9358
|
+
if (this.ctx.htmlBlocks.length > 0) {
|
|
9359
|
+
result["html-blocks"] = this.ctx.htmlBlocks;
|
|
9360
|
+
}
|
|
9361
|
+
return { ast: result, diagnostics: this.ctx.diagnostics };
|
|
9362
|
+
}
|
|
9363
|
+
isAtEnd() {
|
|
9364
|
+
return this.ctx.pos >= this.ctx.tokens.length || this.currentToken().type === "EOF";
|
|
9365
|
+
}
|
|
9366
|
+
currentToken() {
|
|
9367
|
+
return this.ctx.tokens[this.ctx.pos] ?? this.eofToken();
|
|
9368
|
+
}
|
|
9369
|
+
eofToken() {
|
|
9370
|
+
return {
|
|
9371
|
+
type: "EOF",
|
|
9372
|
+
value: "",
|
|
9373
|
+
position: {
|
|
9374
|
+
start: { line: 0, column: 0, offset: 0 },
|
|
9375
|
+
end: { line: 0, column: 0, offset: 0 }
|
|
9376
|
+
},
|
|
9377
|
+
lineStart: false
|
|
9378
|
+
};
|
|
9379
|
+
}
|
|
9380
|
+
skipWhitespace() {
|
|
9381
|
+
while (this.currentToken().type === "WHITESPACE") {
|
|
9382
|
+
this.ctx.pos++;
|
|
9383
|
+
}
|
|
8728
9384
|
}
|
|
8729
9385
|
parseBlock() {
|
|
8730
9386
|
this.skipWhitespace();
|
|
@@ -8755,10 +9411,19 @@ class Parser {
|
|
|
8755
9411
|
}
|
|
8756
9412
|
}
|
|
8757
9413
|
function parse(source, options) {
|
|
8758
|
-
const
|
|
9414
|
+
const iftagsProcessed = options?.pageTags !== undefined ? preprocessIftags(source, options.pageTags) : source;
|
|
9415
|
+
const preprocessed = preprocess(iftagsProcessed);
|
|
8759
9416
|
const tokens = tokenize(preprocessed, { trackPositions: options?.trackPositions });
|
|
8760
9417
|
return new Parser(tokens, options).parse();
|
|
8761
9418
|
}
|
|
9419
|
+
function containsFootnoteBlock(elements) {
|
|
9420
|
+
let found = false;
|
|
9421
|
+
walkElements(elements, (el) => {
|
|
9422
|
+
if (el.element === "footnote-block")
|
|
9423
|
+
found = true;
|
|
9424
|
+
});
|
|
9425
|
+
return found;
|
|
9426
|
+
}
|
|
8762
9427
|
// packages/parser/src/parser/rules/block/module/listpages/compiler.ts
|
|
8763
9428
|
var DEFAULT_PREVIEW_LENGTH = 200;
|
|
8764
9429
|
var VARIABLE_REGEX = /%%([a-z_]+)(?:\{([^}]+)\})?(?:\((\d+)\))?(?:\|([^%]*(?:%(?!%)[^%]*)*))?%%/gi;
|
|
@@ -8980,334 +9645,85 @@ function strftime(date, format) {
|
|
|
8980
9645
|
return MONTHS[date.getMonth()] ?? "";
|
|
8981
9646
|
case "a":
|
|
8982
9647
|
return DAYS_SHORT[date.getDay()] ?? "";
|
|
8983
|
-
case "A":
|
|
8984
|
-
return DAYS[date.getDay()] ?? "";
|
|
8985
|
-
case "w":
|
|
8986
|
-
return String(date.getDay());
|
|
8987
|
-
case "j":
|
|
8988
|
-
return pad(getDayOfYear(date), 3);
|
|
8989
|
-
case "Z":
|
|
8990
|
-
return "UTC";
|
|
8991
|
-
case "z":
|
|
8992
|
-
return "+0000";
|
|
8993
|
-
case "%":
|
|
8994
|
-
return "%";
|
|
8995
|
-
default:
|
|
8996
|
-
return `%${token}`;
|
|
8997
|
-
}
|
|
8998
|
-
});
|
|
8999
|
-
}
|
|
9000
|
-
function getDayOfYear(date) {
|
|
9001
|
-
const start = new Date(date.getFullYear(), 0, 0);
|
|
9002
|
-
const diff = date.getTime() - start.getTime();
|
|
9003
|
-
const oneDay = 1000 * 60 * 60 * 24;
|
|
9004
|
-
return Math.floor(diff / oneDay);
|
|
9005
|
-
}
|
|
9006
|
-
|
|
9007
|
-
// packages/parser/src/parser/rules/block/module/listusers/compiler.ts
|
|
9008
|
-
var VARIABLE_REGEX2 = /%%([a-z_]+)%%/gi;
|
|
9009
|
-
function compileListUsersTemplate(template) {
|
|
9010
|
-
const parts = [];
|
|
9011
|
-
let lastIndex = 0;
|
|
9012
|
-
for (const match of template.matchAll(VARIABLE_REGEX2)) {
|
|
9013
|
-
if (match.index !== undefined && match.index > lastIndex) {
|
|
9014
|
-
parts.push(template.slice(lastIndex, match.index));
|
|
9015
|
-
}
|
|
9016
|
-
const [, varName] = match;
|
|
9017
|
-
if (!varName)
|
|
9018
|
-
continue;
|
|
9019
|
-
const getter = createVariableGetter2(varName.toLowerCase());
|
|
9020
|
-
parts.push(getter);
|
|
9021
|
-
lastIndex = match.index !== undefined ? match.index + match[0].length : lastIndex;
|
|
9022
|
-
}
|
|
9023
|
-
if (lastIndex < template.length) {
|
|
9024
|
-
parts.push(template.slice(lastIndex));
|
|
9025
|
-
}
|
|
9026
|
-
return (ctx) => {
|
|
9027
|
-
let result = "";
|
|
9028
|
-
for (const part of parts) {
|
|
9029
|
-
result += typeof part === "string" ? part : part(ctx);
|
|
9030
|
-
}
|
|
9031
|
-
return result;
|
|
9032
|
-
};
|
|
9033
|
-
}
|
|
9034
|
-
function createVariableGetter2(name) {
|
|
9035
|
-
switch (name) {
|
|
9036
|
-
case "number":
|
|
9037
|
-
return (ctx) => String(ctx.user.number);
|
|
9038
|
-
case "title":
|
|
9039
|
-
return (ctx) => ctx.user.title;
|
|
9040
|
-
case "name":
|
|
9041
|
-
return (ctx) => ctx.user.name;
|
|
9042
|
-
default:
|
|
9043
|
-
return () => "";
|
|
9044
|
-
}
|
|
9045
|
-
}
|
|
9046
|
-
|
|
9047
|
-
// packages/parser/src/parser/rules/block/module/listusers/extract.ts
|
|
9048
|
-
var VARIABLE_REGEX3 = /%%([a-z_]+)%%/gi;
|
|
9049
|
-
var KNOWN_VARIABLES = ["number", "title", "name"];
|
|
9050
|
-
function extractListUsersVariables(template) {
|
|
9051
|
-
const variables = new Set;
|
|
9052
|
-
for (const match of template.matchAll(VARIABLE_REGEX3)) {
|
|
9053
|
-
const [, varName] = match;
|
|
9054
|
-
if (!varName)
|
|
9055
|
-
continue;
|
|
9056
|
-
const normalized = varName.toLowerCase();
|
|
9057
|
-
if (KNOWN_VARIABLES.includes(normalized)) {
|
|
9058
|
-
variables.add(normalized);
|
|
9059
|
-
}
|
|
9060
|
-
}
|
|
9061
|
-
return Array.from(variables);
|
|
9062
|
-
}
|
|
9063
|
-
|
|
9064
|
-
// packages/parser/src/parser/rules/block/module/walk.ts
|
|
9065
|
-
function walkElements(elements, callback) {
|
|
9066
|
-
for (const element of elements) {
|
|
9067
|
-
callback(element);
|
|
9068
|
-
if (element.element === "list") {
|
|
9069
|
-
const listData = element.data;
|
|
9070
|
-
for (const item of listData.items) {
|
|
9071
|
-
if (item["item-type"] === "elements") {
|
|
9072
|
-
walkElements(item.elements, callback);
|
|
9073
|
-
} else if (item["item-type"] === "sub-list") {
|
|
9074
|
-
walkElements([{ element: "list", data: item.data }], callback);
|
|
9075
|
-
}
|
|
9076
|
-
}
|
|
9077
|
-
continue;
|
|
9078
|
-
}
|
|
9079
|
-
if (element.element === "table") {
|
|
9080
|
-
const tableData = element.data;
|
|
9081
|
-
for (const row of tableData.rows) {
|
|
9082
|
-
for (const cell of row.cells) {
|
|
9083
|
-
walkElements(cell.elements, callback);
|
|
9084
|
-
}
|
|
9085
|
-
}
|
|
9086
|
-
continue;
|
|
9087
|
-
}
|
|
9088
|
-
if (element.element === "definition-list") {
|
|
9089
|
-
const defListData = element.data;
|
|
9090
|
-
for (const item of defListData) {
|
|
9091
|
-
walkElements(item.key, callback);
|
|
9092
|
-
walkElements(item.value, callback);
|
|
9093
|
-
}
|
|
9094
|
-
continue;
|
|
9095
|
-
}
|
|
9096
|
-
if (element.element === "tab-view") {
|
|
9097
|
-
const tabData = element.data;
|
|
9098
|
-
for (const tab of tabData) {
|
|
9099
|
-
walkElements(tab.elements, callback);
|
|
9100
|
-
}
|
|
9101
|
-
continue;
|
|
9102
|
-
}
|
|
9103
|
-
if ("data" in element && element.data && typeof element.data === "object") {
|
|
9104
|
-
const data = element.data;
|
|
9105
|
-
if ("elements" in data && Array.isArray(data.elements)) {
|
|
9106
|
-
walkElements(data.elements, callback);
|
|
9107
|
-
}
|
|
9108
|
-
}
|
|
9109
|
-
}
|
|
9110
|
-
}
|
|
9111
|
-
function mapElementChildren(element, transform) {
|
|
9112
|
-
if (element.element === "list") {
|
|
9113
|
-
const listData = element.data;
|
|
9114
|
-
const newItems = [];
|
|
9115
|
-
for (const item of listData.items) {
|
|
9116
|
-
if (item["item-type"] === "elements") {
|
|
9117
|
-
newItems.push({
|
|
9118
|
-
"item-type": "elements",
|
|
9119
|
-
attributes: item.attributes,
|
|
9120
|
-
elements: transform(item.elements)
|
|
9121
|
-
});
|
|
9122
|
-
} else if (item["item-type"] === "sub-list") {
|
|
9123
|
-
const subListResult = transform([{ element: "list", data: item.data }]);
|
|
9124
|
-
const resolvedList = subListResult[0];
|
|
9125
|
-
if (resolvedList?.element === "list") {
|
|
9126
|
-
newItems.push({
|
|
9127
|
-
"item-type": "sub-list",
|
|
9128
|
-
element: "list",
|
|
9129
|
-
data: resolvedList.data
|
|
9130
|
-
});
|
|
9131
|
-
} else {
|
|
9132
|
-
newItems.push(item);
|
|
9133
|
-
}
|
|
9134
|
-
}
|
|
9135
|
-
}
|
|
9136
|
-
return {
|
|
9137
|
-
element: "list",
|
|
9138
|
-
data: { ...listData, items: newItems }
|
|
9139
|
-
};
|
|
9140
|
-
}
|
|
9141
|
-
if (element.element === "table") {
|
|
9142
|
-
const tableData = element.data;
|
|
9143
|
-
const newRows = [];
|
|
9144
|
-
for (const row of tableData.rows) {
|
|
9145
|
-
const newCells = [];
|
|
9146
|
-
for (const cell of row.cells) {
|
|
9147
|
-
newCells.push({ ...cell, elements: transform(cell.elements) });
|
|
9148
|
-
}
|
|
9149
|
-
newRows.push({ ...row, cells: newCells });
|
|
9150
|
-
}
|
|
9151
|
-
return {
|
|
9152
|
-
element: "table",
|
|
9153
|
-
data: { ...tableData, rows: newRows }
|
|
9154
|
-
};
|
|
9155
|
-
}
|
|
9156
|
-
if (element.element === "definition-list") {
|
|
9157
|
-
const defListData = element.data;
|
|
9158
|
-
const newItems = [];
|
|
9159
|
-
for (const item of defListData) {
|
|
9160
|
-
newItems.push({
|
|
9161
|
-
key_string: item.key_string,
|
|
9162
|
-
key: transform(item.key),
|
|
9163
|
-
value: transform(item.value)
|
|
9164
|
-
});
|
|
9165
|
-
}
|
|
9166
|
-
return {
|
|
9167
|
-
element: "definition-list",
|
|
9168
|
-
data: newItems
|
|
9169
|
-
};
|
|
9170
|
-
}
|
|
9171
|
-
if (element.element === "tab-view") {
|
|
9172
|
-
const tabData = element.data;
|
|
9173
|
-
const newTabs = [];
|
|
9174
|
-
for (const tab of tabData) {
|
|
9175
|
-
newTabs.push({ ...tab, elements: transform(tab.elements) });
|
|
9176
|
-
}
|
|
9177
|
-
return {
|
|
9178
|
-
element: "tab-view",
|
|
9179
|
-
data: newTabs
|
|
9180
|
-
};
|
|
9181
|
-
}
|
|
9182
|
-
if ("data" in element && element.data && typeof element.data === "object") {
|
|
9183
|
-
const data = element.data;
|
|
9184
|
-
if ("elements" in data && Array.isArray(data.elements)) {
|
|
9185
|
-
return {
|
|
9186
|
-
...element,
|
|
9187
|
-
data: {
|
|
9188
|
-
...data,
|
|
9189
|
-
elements: transform(data.elements)
|
|
9190
|
-
}
|
|
9191
|
-
};
|
|
9648
|
+
case "A":
|
|
9649
|
+
return DAYS[date.getDay()] ?? "";
|
|
9650
|
+
case "w":
|
|
9651
|
+
return String(date.getDay());
|
|
9652
|
+
case "j":
|
|
9653
|
+
return pad(getDayOfYear(date), 3);
|
|
9654
|
+
case "Z":
|
|
9655
|
+
return "UTC";
|
|
9656
|
+
case "z":
|
|
9657
|
+
return "+0000";
|
|
9658
|
+
case "%":
|
|
9659
|
+
return "%";
|
|
9660
|
+
default:
|
|
9661
|
+
return `%${token}`;
|
|
9192
9662
|
}
|
|
9193
|
-
}
|
|
9194
|
-
return element;
|
|
9663
|
+
});
|
|
9195
9664
|
}
|
|
9196
|
-
function
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
const result = transform([{ element: "list", data: item.data }], currentState);
|
|
9212
|
-
const resolvedList = result.elements[0];
|
|
9213
|
-
if (resolvedList?.element === "list") {
|
|
9214
|
-
newItems.push({
|
|
9215
|
-
"item-type": "sub-list",
|
|
9216
|
-
element: "list",
|
|
9217
|
-
data: resolvedList.data
|
|
9218
|
-
});
|
|
9219
|
-
} else {
|
|
9220
|
-
newItems.push(item);
|
|
9221
|
-
}
|
|
9222
|
-
currentState = result.state;
|
|
9223
|
-
}
|
|
9224
|
-
}
|
|
9225
|
-
return {
|
|
9226
|
-
element: {
|
|
9227
|
-
element: "list",
|
|
9228
|
-
data: { ...listData, items: newItems }
|
|
9229
|
-
},
|
|
9230
|
-
state: currentState
|
|
9231
|
-
};
|
|
9232
|
-
}
|
|
9233
|
-
if (element.element === "table") {
|
|
9234
|
-
const tableData = element.data;
|
|
9235
|
-
const newRows = [];
|
|
9236
|
-
let currentState = state;
|
|
9237
|
-
for (const row of tableData.rows) {
|
|
9238
|
-
const newCells = [];
|
|
9239
|
-
for (const cell of row.cells) {
|
|
9240
|
-
const result = transform(cell.elements, currentState);
|
|
9241
|
-
newCells.push({ ...cell, elements: result.elements });
|
|
9242
|
-
currentState = result.state;
|
|
9243
|
-
}
|
|
9244
|
-
newRows.push({ ...row, cells: newCells });
|
|
9665
|
+
function getDayOfYear(date) {
|
|
9666
|
+
const start = new Date(date.getFullYear(), 0, 0);
|
|
9667
|
+
const diff = date.getTime() - start.getTime();
|
|
9668
|
+
const oneDay = 1000 * 60 * 60 * 24;
|
|
9669
|
+
return Math.floor(diff / oneDay);
|
|
9670
|
+
}
|
|
9671
|
+
|
|
9672
|
+
// packages/parser/src/parser/rules/block/module/listusers/compiler.ts
|
|
9673
|
+
var VARIABLE_REGEX2 = /%%([a-z_]+)%%/gi;
|
|
9674
|
+
function compileListUsersTemplate(template) {
|
|
9675
|
+
const parts = [];
|
|
9676
|
+
let lastIndex = 0;
|
|
9677
|
+
for (const match of template.matchAll(VARIABLE_REGEX2)) {
|
|
9678
|
+
if (match.index !== undefined && match.index > lastIndex) {
|
|
9679
|
+
parts.push(template.slice(lastIndex, match.index));
|
|
9245
9680
|
}
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
};
|
|
9681
|
+
const [, varName] = match;
|
|
9682
|
+
if (!varName)
|
|
9683
|
+
continue;
|
|
9684
|
+
const getter = createVariableGetter2(varName.toLowerCase());
|
|
9685
|
+
parts.push(getter);
|
|
9686
|
+
lastIndex = match.index !== undefined ? match.index + match[0].length : lastIndex;
|
|
9253
9687
|
}
|
|
9254
|
-
if (
|
|
9255
|
-
|
|
9256
|
-
const newItems = [];
|
|
9257
|
-
let currentState = state;
|
|
9258
|
-
for (const item of defListData) {
|
|
9259
|
-
const keyResult = transform(item.key, currentState);
|
|
9260
|
-
currentState = keyResult.state;
|
|
9261
|
-
const valueResult = transform(item.value, currentState);
|
|
9262
|
-
currentState = valueResult.state;
|
|
9263
|
-
newItems.push({
|
|
9264
|
-
key_string: item.key_string,
|
|
9265
|
-
key: keyResult.elements,
|
|
9266
|
-
value: valueResult.elements
|
|
9267
|
-
});
|
|
9268
|
-
}
|
|
9269
|
-
return {
|
|
9270
|
-
element: {
|
|
9271
|
-
element: "definition-list",
|
|
9272
|
-
data: newItems
|
|
9273
|
-
},
|
|
9274
|
-
state: currentState
|
|
9275
|
-
};
|
|
9688
|
+
if (lastIndex < template.length) {
|
|
9689
|
+
parts.push(template.slice(lastIndex));
|
|
9276
9690
|
}
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
const
|
|
9280
|
-
|
|
9281
|
-
for (const tab of tabData) {
|
|
9282
|
-
const result = transform(tab.elements, currentState);
|
|
9283
|
-
newTabs.push({ ...tab, elements: result.elements });
|
|
9284
|
-
currentState = result.state;
|
|
9691
|
+
return (ctx) => {
|
|
9692
|
+
let result = "";
|
|
9693
|
+
for (const part of parts) {
|
|
9694
|
+
result += typeof part === "string" ? part : part(ctx);
|
|
9285
9695
|
}
|
|
9286
|
-
return
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9696
|
+
return result;
|
|
9697
|
+
};
|
|
9698
|
+
}
|
|
9699
|
+
function createVariableGetter2(name) {
|
|
9700
|
+
switch (name) {
|
|
9701
|
+
case "number":
|
|
9702
|
+
return (ctx) => String(ctx.user.number);
|
|
9703
|
+
case "title":
|
|
9704
|
+
return (ctx) => ctx.user.title;
|
|
9705
|
+
case "name":
|
|
9706
|
+
return (ctx) => ctx.user.name;
|
|
9707
|
+
default:
|
|
9708
|
+
return () => "";
|
|
9293
9709
|
}
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9710
|
+
}
|
|
9711
|
+
|
|
9712
|
+
// packages/parser/src/parser/rules/block/module/listusers/extract.ts
|
|
9713
|
+
var VARIABLE_REGEX3 = /%%([a-z_]+)%%/gi;
|
|
9714
|
+
var KNOWN_VARIABLES = ["number", "title", "name"];
|
|
9715
|
+
function extractListUsersVariables(template) {
|
|
9716
|
+
const variables = new Set;
|
|
9717
|
+
for (const match of template.matchAll(VARIABLE_REGEX3)) {
|
|
9718
|
+
const [, varName] = match;
|
|
9719
|
+
if (!varName)
|
|
9720
|
+
continue;
|
|
9721
|
+
const normalized = varName.toLowerCase();
|
|
9722
|
+
if (KNOWN_VARIABLES.includes(normalized)) {
|
|
9723
|
+
variables.add(normalized);
|
|
9308
9724
|
}
|
|
9309
9725
|
}
|
|
9310
|
-
return
|
|
9726
|
+
return Array.from(variables);
|
|
9311
9727
|
}
|
|
9312
9728
|
|
|
9313
9729
|
// packages/parser/src/parser/rules/block/module/listpages/extract.ts
|
|
@@ -9909,51 +10325,6 @@ function resolveAndNormalizeQuery(requirement, urlParams) {
|
|
|
9909
10325
|
const resolved = resolveQuery(requirement, urlParams);
|
|
9910
10326
|
return normalizeQuery(resolved);
|
|
9911
10327
|
}
|
|
9912
|
-
// packages/parser/src/parser/rules/block/module/iftags/condition.ts
|
|
9913
|
-
function parseTagCondition(condition) {
|
|
9914
|
-
const required = [];
|
|
9915
|
-
const forbidden = [];
|
|
9916
|
-
const optional = [];
|
|
9917
|
-
const parts = condition.trim().split(/\s+/);
|
|
9918
|
-
for (const part of parts) {
|
|
9919
|
-
if (!part)
|
|
9920
|
-
continue;
|
|
9921
|
-
if (part.startsWith("+")) {
|
|
9922
|
-
const tag = part.slice(1);
|
|
9923
|
-
if (tag)
|
|
9924
|
-
required.push(tag);
|
|
9925
|
-
} else if (part.startsWith("-")) {
|
|
9926
|
-
const tag = part.slice(1);
|
|
9927
|
-
if (tag)
|
|
9928
|
-
forbidden.push(tag);
|
|
9929
|
-
} else {
|
|
9930
|
-
optional.push(part);
|
|
9931
|
-
}
|
|
9932
|
-
}
|
|
9933
|
-
return { required, forbidden, optional };
|
|
9934
|
-
}
|
|
9935
|
-
function evaluateTagCondition(condition, pageTags) {
|
|
9936
|
-
if (condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0) {
|
|
9937
|
-
return false;
|
|
9938
|
-
}
|
|
9939
|
-
const tagSet = new Set(pageTags);
|
|
9940
|
-
for (const tag of condition.required) {
|
|
9941
|
-
if (!tagSet.has(tag)) {
|
|
9942
|
-
return false;
|
|
9943
|
-
}
|
|
9944
|
-
}
|
|
9945
|
-
for (const tag of condition.forbidden) {
|
|
9946
|
-
if (tagSet.has(tag)) {
|
|
9947
|
-
return false;
|
|
9948
|
-
}
|
|
9949
|
-
}
|
|
9950
|
-
if (condition.optional.length > 0) {
|
|
9951
|
-
if (!condition.optional.some((tag) => tagSet.has(tag))) {
|
|
9952
|
-
return false;
|
|
9953
|
-
}
|
|
9954
|
-
}
|
|
9955
|
-
return true;
|
|
9956
|
-
}
|
|
9957
10328
|
// packages/parser/src/parser/rules/block/module/iftags/resolve.ts
|
|
9958
10329
|
function isIfTagsElement(element) {
|
|
9959
10330
|
return element.element === "if-tags";
|
|
@@ -10011,7 +10382,67 @@ async function resolveIncludesAsync(source, fetcher, options) {
|
|
|
10011
10382
|
};
|
|
10012
10383
|
return expandIterativeAsync(source, cachedFetcher, maxIterations);
|
|
10013
10384
|
}
|
|
10014
|
-
var
|
|
10385
|
+
var INCLUDE_OPEN_PATTERN = /^\[\[include\s/gim;
|
|
10386
|
+
function isRestOfLineBlank(source, pos) {
|
|
10387
|
+
for (let i = pos;i < source.length; i++) {
|
|
10388
|
+
const ch = source[i];
|
|
10389
|
+
if (ch === `
|
|
10390
|
+
`)
|
|
10391
|
+
return true;
|
|
10392
|
+
if (ch !== " " && ch !== "\t" && ch !== "\r")
|
|
10393
|
+
return false;
|
|
10394
|
+
}
|
|
10395
|
+
return true;
|
|
10396
|
+
}
|
|
10397
|
+
function scanIncludeDirectives(source) {
|
|
10398
|
+
const matches = [];
|
|
10399
|
+
const opener = new RegExp(INCLUDE_OPEN_PATTERN.source, INCLUDE_OPEN_PATTERN.flags);
|
|
10400
|
+
let m;
|
|
10401
|
+
while ((m = opener.exec(source)) !== null) {
|
|
10402
|
+
const start = m.index;
|
|
10403
|
+
const contentStart = start + m[0].length;
|
|
10404
|
+
const firstNewline = source.indexOf(`
|
|
10405
|
+
`, start);
|
|
10406
|
+
let depth = 0;
|
|
10407
|
+
let linkDepth = 0;
|
|
10408
|
+
let i = start;
|
|
10409
|
+
let closeEnd = -1;
|
|
10410
|
+
while (i < source.length) {
|
|
10411
|
+
if (source.startsWith("[[[", i)) {
|
|
10412
|
+
linkDepth++;
|
|
10413
|
+
i += 3;
|
|
10414
|
+
} else if (linkDepth > 0 && source.startsWith("]]]", i)) {
|
|
10415
|
+
linkDepth--;
|
|
10416
|
+
i += 3;
|
|
10417
|
+
} else if (linkDepth > 0) {
|
|
10418
|
+
i++;
|
|
10419
|
+
} else if (source.startsWith("[[", i)) {
|
|
10420
|
+
depth++;
|
|
10421
|
+
i += 2;
|
|
10422
|
+
} else if (source.startsWith("]]", i)) {
|
|
10423
|
+
const closeStart = i;
|
|
10424
|
+
depth--;
|
|
10425
|
+
i += 2;
|
|
10426
|
+
if (depth <= 0) {
|
|
10427
|
+
const onOpenerLine = firstNewline === -1 || closeStart < firstNewline;
|
|
10428
|
+
if (onOpenerLine || isRestOfLineBlank(source, i)) {
|
|
10429
|
+
closeEnd = i;
|
|
10430
|
+
break;
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
} else {
|
|
10434
|
+
i++;
|
|
10435
|
+
}
|
|
10436
|
+
}
|
|
10437
|
+
if (closeEnd === -1) {
|
|
10438
|
+
opener.lastIndex = start + 2;
|
|
10439
|
+
continue;
|
|
10440
|
+
}
|
|
10441
|
+
matches.push({ start, end: closeEnd, inner: source.slice(contentStart, closeEnd - 2) });
|
|
10442
|
+
opener.lastIndex = closeEnd;
|
|
10443
|
+
}
|
|
10444
|
+
return matches;
|
|
10445
|
+
}
|
|
10015
10446
|
function parseIncludeDirective(inner) {
|
|
10016
10447
|
const normalized = inner.replace(/\n/g, " ");
|
|
10017
10448
|
const parts = normalized.split("|");
|
|
@@ -10035,14 +10466,24 @@ function parseIncludeDirective(inner) {
|
|
|
10035
10466
|
}
|
|
10036
10467
|
}
|
|
10037
10468
|
const variables = {};
|
|
10469
|
+
const hasConcrete = new Set;
|
|
10038
10470
|
for (const segment of varSegments) {
|
|
10039
10471
|
const eqIndex = segment.indexOf("=");
|
|
10040
|
-
if (eqIndex
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10472
|
+
if (eqIndex === -1)
|
|
10473
|
+
continue;
|
|
10474
|
+
const key = segment.slice(0, eqIndex).trim();
|
|
10475
|
+
if (!key)
|
|
10476
|
+
continue;
|
|
10477
|
+
const value = segment.slice(eqIndex + 1).trim();
|
|
10478
|
+
const isPlaceholder = /^\{\$[^}]*\}$/.test(value);
|
|
10479
|
+
const isConcrete = value !== "" && !isPlaceholder;
|
|
10480
|
+
if (isConcrete) {
|
|
10481
|
+
if (!hasConcrete.has(key)) {
|
|
10044
10482
|
variables[key] = value;
|
|
10483
|
+
hasConcrete.add(key);
|
|
10045
10484
|
}
|
|
10485
|
+
} else if (!Object.hasOwn(variables, key)) {
|
|
10486
|
+
variables[key] = value;
|
|
10046
10487
|
}
|
|
10047
10488
|
}
|
|
10048
10489
|
let location;
|
|
@@ -10059,7 +10500,7 @@ function parseIncludeDirective(inner) {
|
|
|
10059
10500
|
}
|
|
10060
10501
|
return { location, variables };
|
|
10061
10502
|
}
|
|
10062
|
-
function replaceOneInclude(
|
|
10503
|
+
function replaceOneInclude(inner, fetcher) {
|
|
10063
10504
|
const { location, variables } = parseIncludeDirective(inner);
|
|
10064
10505
|
const content = fetcher(location);
|
|
10065
10506
|
if (content === null) {
|
|
@@ -10072,25 +10513,33 @@ Page to be included "${location.page}" cannot be found!
|
|
|
10072
10513
|
function expandIterative(source, fetcher, maxIterations) {
|
|
10073
10514
|
let current = source;
|
|
10074
10515
|
for (let i = 0;i < maxIterations; i++) {
|
|
10075
|
-
const
|
|
10076
|
-
|
|
10077
|
-
|
|
10516
|
+
const directives = scanIncludeDirectives(current);
|
|
10517
|
+
if (directives.length === 0)
|
|
10518
|
+
break;
|
|
10519
|
+
let result = "";
|
|
10520
|
+
let lastPos = 0;
|
|
10521
|
+
for (const { start, end, inner } of directives) {
|
|
10522
|
+
result += current.slice(lastPos, start);
|
|
10523
|
+
result += replaceOneInclude(inner, fetcher);
|
|
10524
|
+
lastPos = end;
|
|
10525
|
+
}
|
|
10526
|
+
result += current.slice(lastPos);
|
|
10527
|
+
if (result === current)
|
|
10078
10528
|
break;
|
|
10529
|
+
current = result;
|
|
10079
10530
|
}
|
|
10080
10531
|
return current;
|
|
10081
10532
|
}
|
|
10082
10533
|
async function expandIterativeAsync(source, fetcher, maxIterations) {
|
|
10083
10534
|
let current = source;
|
|
10084
10535
|
for (let i = 0;i < maxIterations; i++) {
|
|
10085
|
-
const
|
|
10086
|
-
|
|
10536
|
+
const directives = scanIncludeDirectives(current);
|
|
10537
|
+
if (directives.length === 0)
|
|
10538
|
+
break;
|
|
10087
10539
|
let result = "";
|
|
10088
10540
|
let lastPos = 0;
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
const fullMatch = match[0];
|
|
10092
|
-
const inner = match[1];
|
|
10093
|
-
result += current.slice(lastPos, match.index);
|
|
10541
|
+
for (const { start, end, inner } of directives) {
|
|
10542
|
+
result += current.slice(lastPos, start);
|
|
10094
10543
|
const { location, variables } = parseIncludeDirective(inner);
|
|
10095
10544
|
const content = await fetcher(location);
|
|
10096
10545
|
if (content === null) {
|
|
@@ -10100,12 +10549,12 @@ Page to be included "${location.page}" cannot be found!
|
|
|
10100
10549
|
} else {
|
|
10101
10550
|
result += substituteVariables(content, variables);
|
|
10102
10551
|
}
|
|
10103
|
-
lastPos =
|
|
10552
|
+
lastPos = end;
|
|
10104
10553
|
}
|
|
10105
10554
|
result += current.slice(lastPos);
|
|
10106
|
-
|
|
10107
|
-
if (current === previous)
|
|
10555
|
+
if (result === current)
|
|
10108
10556
|
break;
|
|
10557
|
+
current = result;
|
|
10109
10558
|
}
|
|
10110
10559
|
return current;
|
|
10111
10560
|
}
|
|
@@ -10338,6 +10787,7 @@ export {
|
|
|
10338
10787
|
resolveListUsers,
|
|
10339
10788
|
resolveIncludesAsync,
|
|
10340
10789
|
resolveIncludes,
|
|
10790
|
+
preprocessIftags,
|
|
10341
10791
|
parseTags,
|
|
10342
10792
|
parseParent,
|
|
10343
10793
|
parseOrder,
|