@wdprlib/parser 2.0.9 → 2.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 +221 -190
- package/dist/index.d.cts +38 -8
- package/dist/index.d.ts +38 -8
- package/dist/index.js +221 -190
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(exports_src, {
|
|
|
34
34
|
text: () => import_ast5.text,
|
|
35
35
|
resolveModules: () => resolveModules,
|
|
36
36
|
resolveListUsers: () => resolveListUsers,
|
|
37
|
+
resolveIncludesAsync: () => resolveIncludesAsync,
|
|
37
38
|
resolveIncludes: () => resolveIncludes,
|
|
38
39
|
parseTags: () => parseTags,
|
|
39
40
|
parseParent: () => parseParent,
|
|
@@ -646,7 +647,139 @@ var BLOCK_START_TOKENS = [
|
|
|
646
647
|
"CLEAR_FLOAT_RIGHT"
|
|
647
648
|
];
|
|
648
649
|
|
|
650
|
+
// packages/parser/src/parser/rules/utils.ts
|
|
651
|
+
var SAFE_ATTRIBUTES = new Set([
|
|
652
|
+
"accept",
|
|
653
|
+
"align",
|
|
654
|
+
"alt",
|
|
655
|
+
"autocapitalize",
|
|
656
|
+
"autoplay",
|
|
657
|
+
"background",
|
|
658
|
+
"bgcolor",
|
|
659
|
+
"border",
|
|
660
|
+
"buffered",
|
|
661
|
+
"checked",
|
|
662
|
+
"cite",
|
|
663
|
+
"class",
|
|
664
|
+
"cols",
|
|
665
|
+
"colspan",
|
|
666
|
+
"contenteditable",
|
|
667
|
+
"controls",
|
|
668
|
+
"coords",
|
|
669
|
+
"datetime",
|
|
670
|
+
"decoding",
|
|
671
|
+
"default",
|
|
672
|
+
"dir",
|
|
673
|
+
"dirname",
|
|
674
|
+
"disabled",
|
|
675
|
+
"download",
|
|
676
|
+
"draggable",
|
|
677
|
+
"for",
|
|
678
|
+
"form",
|
|
679
|
+
"headers",
|
|
680
|
+
"height",
|
|
681
|
+
"hidden",
|
|
682
|
+
"high",
|
|
683
|
+
"href",
|
|
684
|
+
"hreflang",
|
|
685
|
+
"id",
|
|
686
|
+
"inputmode",
|
|
687
|
+
"ismap",
|
|
688
|
+
"itemprop",
|
|
689
|
+
"kind",
|
|
690
|
+
"label",
|
|
691
|
+
"lang",
|
|
692
|
+
"list",
|
|
693
|
+
"loop",
|
|
694
|
+
"low",
|
|
695
|
+
"max",
|
|
696
|
+
"maxlength",
|
|
697
|
+
"min",
|
|
698
|
+
"minlength",
|
|
699
|
+
"multiple",
|
|
700
|
+
"muted",
|
|
701
|
+
"name",
|
|
702
|
+
"optimum",
|
|
703
|
+
"pattern",
|
|
704
|
+
"placeholder",
|
|
705
|
+
"poster",
|
|
706
|
+
"preload",
|
|
707
|
+
"readonly",
|
|
708
|
+
"required",
|
|
709
|
+
"reversed",
|
|
710
|
+
"role",
|
|
711
|
+
"rows",
|
|
712
|
+
"rowspan",
|
|
713
|
+
"scope",
|
|
714
|
+
"selected",
|
|
715
|
+
"shape",
|
|
716
|
+
"size",
|
|
717
|
+
"sizes",
|
|
718
|
+
"span",
|
|
719
|
+
"spellcheck",
|
|
720
|
+
"src",
|
|
721
|
+
"srclang",
|
|
722
|
+
"srcset",
|
|
723
|
+
"start",
|
|
724
|
+
"step",
|
|
725
|
+
"style",
|
|
726
|
+
"tabindex",
|
|
727
|
+
"target",
|
|
728
|
+
"title",
|
|
729
|
+
"translate",
|
|
730
|
+
"type",
|
|
731
|
+
"usemap",
|
|
732
|
+
"value",
|
|
733
|
+
"width",
|
|
734
|
+
"wrap"
|
|
735
|
+
]);
|
|
736
|
+
function filterUnsafeAttributes(attrs) {
|
|
737
|
+
const result = {};
|
|
738
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
739
|
+
const lower = key.toLowerCase();
|
|
740
|
+
if (lower.startsWith("on"))
|
|
741
|
+
continue;
|
|
742
|
+
if (lower.startsWith("aria-") || lower.startsWith("data-")) {
|
|
743
|
+
result[key] = value;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (!SAFE_ATTRIBUTES.has(lower))
|
|
747
|
+
continue;
|
|
748
|
+
if (lower === "id") {
|
|
749
|
+
result[key] = value.startsWith("u-") ? value : `u-${value}`;
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
result[key] = value;
|
|
753
|
+
}
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
function parseBlockName(ctx, startPos) {
|
|
757
|
+
let pos = startPos;
|
|
758
|
+
let consumed = 0;
|
|
759
|
+
const token = ctx.tokens[pos];
|
|
760
|
+
if (!token || token.type !== "TEXT" && token.type !== "IDENTIFIER") {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
let name = token.value.toLowerCase();
|
|
764
|
+
consumed++;
|
|
765
|
+
pos++;
|
|
766
|
+
if (ctx.tokens[pos]?.type === "UNDERSCORE") {
|
|
767
|
+
name += "_";
|
|
768
|
+
consumed++;
|
|
769
|
+
}
|
|
770
|
+
return { name, consumed };
|
|
771
|
+
}
|
|
772
|
+
|
|
649
773
|
// packages/parser/src/parser/rules/inline/utils.ts
|
|
774
|
+
function isExcludedBlockToken(ctx, tokenPos) {
|
|
775
|
+
if (!ctx.excludedBlockNames?.size)
|
|
776
|
+
return false;
|
|
777
|
+
const token = ctx.tokens[tokenPos];
|
|
778
|
+
if (token?.type !== "BLOCK_OPEN" && token?.type !== "BLOCK_END_OPEN")
|
|
779
|
+
return false;
|
|
780
|
+
const nameResult = parseBlockName(ctx, tokenPos + 1);
|
|
781
|
+
return nameResult !== null && ctx.excludedBlockNames.has(nameResult.name);
|
|
782
|
+
}
|
|
650
783
|
function canApplyInlineRule(rule, token) {
|
|
651
784
|
if (rule.startTokens.length === 0) {
|
|
652
785
|
return true;
|
|
@@ -738,7 +871,8 @@ function parseInlineUntil(ctx, endType) {
|
|
|
738
871
|
isInvalidHeading = true;
|
|
739
872
|
}
|
|
740
873
|
}
|
|
741
|
-
const
|
|
874
|
+
const isExcludedBlock = (nextMeaningfulToken?.type === "BLOCK_OPEN" || nextMeaningfulToken?.type === "BLOCK_END_OPEN") && isExcludedBlockToken(ctx, pos + lookAhead);
|
|
875
|
+
const isBlockStart = nextMeaningfulToken && BLOCK_START_TOKENS.includes(nextMeaningfulToken.type) && nextMeaningfulToken.lineStart && !isOrphanCloseSpan && !isAnchorName && !isInvalidBlockOpen && !isInvalidHeading && !isExcludedBlock;
|
|
742
876
|
if (!nextMeaningfulToken || nextMeaningfulToken.type === "NEWLINE" || nextMeaningfulToken.type === "EOF" || isBlockStart) {
|
|
743
877
|
if (isBlockStart && nodes.length > 0) {
|
|
744
878
|
const nextPos = pos + lookAhead;
|
|
@@ -1076,129 +1210,6 @@ function buildListData(topLtype, list) {
|
|
|
1076
1210
|
};
|
|
1077
1211
|
}
|
|
1078
1212
|
|
|
1079
|
-
// packages/parser/src/parser/rules/utils.ts
|
|
1080
|
-
var SAFE_ATTRIBUTES = new Set([
|
|
1081
|
-
"accept",
|
|
1082
|
-
"align",
|
|
1083
|
-
"alt",
|
|
1084
|
-
"autocapitalize",
|
|
1085
|
-
"autoplay",
|
|
1086
|
-
"background",
|
|
1087
|
-
"bgcolor",
|
|
1088
|
-
"border",
|
|
1089
|
-
"buffered",
|
|
1090
|
-
"checked",
|
|
1091
|
-
"cite",
|
|
1092
|
-
"class",
|
|
1093
|
-
"cols",
|
|
1094
|
-
"colspan",
|
|
1095
|
-
"contenteditable",
|
|
1096
|
-
"controls",
|
|
1097
|
-
"coords",
|
|
1098
|
-
"datetime",
|
|
1099
|
-
"decoding",
|
|
1100
|
-
"default",
|
|
1101
|
-
"dir",
|
|
1102
|
-
"dirname",
|
|
1103
|
-
"disabled",
|
|
1104
|
-
"download",
|
|
1105
|
-
"draggable",
|
|
1106
|
-
"for",
|
|
1107
|
-
"form",
|
|
1108
|
-
"headers",
|
|
1109
|
-
"height",
|
|
1110
|
-
"hidden",
|
|
1111
|
-
"high",
|
|
1112
|
-
"href",
|
|
1113
|
-
"hreflang",
|
|
1114
|
-
"id",
|
|
1115
|
-
"inputmode",
|
|
1116
|
-
"ismap",
|
|
1117
|
-
"itemprop",
|
|
1118
|
-
"kind",
|
|
1119
|
-
"label",
|
|
1120
|
-
"lang",
|
|
1121
|
-
"list",
|
|
1122
|
-
"loop",
|
|
1123
|
-
"low",
|
|
1124
|
-
"max",
|
|
1125
|
-
"maxlength",
|
|
1126
|
-
"min",
|
|
1127
|
-
"minlength",
|
|
1128
|
-
"multiple",
|
|
1129
|
-
"muted",
|
|
1130
|
-
"name",
|
|
1131
|
-
"optimum",
|
|
1132
|
-
"pattern",
|
|
1133
|
-
"placeholder",
|
|
1134
|
-
"poster",
|
|
1135
|
-
"preload",
|
|
1136
|
-
"readonly",
|
|
1137
|
-
"required",
|
|
1138
|
-
"reversed",
|
|
1139
|
-
"role",
|
|
1140
|
-
"rows",
|
|
1141
|
-
"rowspan",
|
|
1142
|
-
"scope",
|
|
1143
|
-
"selected",
|
|
1144
|
-
"shape",
|
|
1145
|
-
"size",
|
|
1146
|
-
"sizes",
|
|
1147
|
-
"span",
|
|
1148
|
-
"spellcheck",
|
|
1149
|
-
"src",
|
|
1150
|
-
"srclang",
|
|
1151
|
-
"srcset",
|
|
1152
|
-
"start",
|
|
1153
|
-
"step",
|
|
1154
|
-
"style",
|
|
1155
|
-
"tabindex",
|
|
1156
|
-
"target",
|
|
1157
|
-
"title",
|
|
1158
|
-
"translate",
|
|
1159
|
-
"type",
|
|
1160
|
-
"usemap",
|
|
1161
|
-
"value",
|
|
1162
|
-
"width",
|
|
1163
|
-
"wrap"
|
|
1164
|
-
]);
|
|
1165
|
-
function filterUnsafeAttributes(attrs) {
|
|
1166
|
-
const result = {};
|
|
1167
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
1168
|
-
const lower = key.toLowerCase();
|
|
1169
|
-
if (lower.startsWith("on"))
|
|
1170
|
-
continue;
|
|
1171
|
-
if (lower.startsWith("aria-") || lower.startsWith("data-")) {
|
|
1172
|
-
result[key] = value;
|
|
1173
|
-
continue;
|
|
1174
|
-
}
|
|
1175
|
-
if (!SAFE_ATTRIBUTES.has(lower))
|
|
1176
|
-
continue;
|
|
1177
|
-
if (lower === "id") {
|
|
1178
|
-
result[key] = value.startsWith("u-") ? value : `u-${value}`;
|
|
1179
|
-
continue;
|
|
1180
|
-
}
|
|
1181
|
-
result[key] = value;
|
|
1182
|
-
}
|
|
1183
|
-
return result;
|
|
1184
|
-
}
|
|
1185
|
-
function parseBlockName(ctx, startPos) {
|
|
1186
|
-
let pos = startPos;
|
|
1187
|
-
let consumed = 0;
|
|
1188
|
-
const token = ctx.tokens[pos];
|
|
1189
|
-
if (!token || token.type !== "TEXT" && token.type !== "IDENTIFIER") {
|
|
1190
|
-
return null;
|
|
1191
|
-
}
|
|
1192
|
-
let name = token.value.toLowerCase();
|
|
1193
|
-
consumed++;
|
|
1194
|
-
pos++;
|
|
1195
|
-
if (ctx.tokens[pos]?.type === "UNDERSCORE") {
|
|
1196
|
-
name += "_";
|
|
1197
|
-
consumed++;
|
|
1198
|
-
}
|
|
1199
|
-
return { name, consumed };
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
1213
|
// packages/parser/src/parser/rules/block/utils.ts
|
|
1203
1214
|
function canApplyBlockRule(rule, token) {
|
|
1204
1215
|
if (rule.requiresLineStart && !token.lineStart) {
|
|
@@ -1209,11 +1220,13 @@ function canApplyBlockRule(rule, token) {
|
|
|
1209
1220
|
}
|
|
1210
1221
|
return rule.startTokens.includes(token.type);
|
|
1211
1222
|
}
|
|
1212
|
-
function parseBlocksUntil(ctx, closeCondition) {
|
|
1223
|
+
function parseBlocksUntil(ctx, closeCondition, options) {
|
|
1213
1224
|
const elements = [];
|
|
1214
1225
|
let consumed = 0;
|
|
1215
1226
|
let pos = ctx.pos;
|
|
1216
|
-
const
|
|
1227
|
+
const excluded = options?.excludedBlockNames;
|
|
1228
|
+
const blockRules = excluded ? ctx.blockRules.filter((r) => !excluded.has(r.name)) : ctx.blockRules;
|
|
1229
|
+
const { blockFallbackRule } = ctx;
|
|
1217
1230
|
while (pos < ctx.tokens.length) {
|
|
1218
1231
|
const token = ctx.tokens[pos];
|
|
1219
1232
|
if (!token || token.type === "EOF") {
|
|
@@ -1234,7 +1247,13 @@ function parseBlocksUntil(ctx, closeCondition) {
|
|
|
1234
1247
|
continue;
|
|
1235
1248
|
}
|
|
1236
1249
|
let matched = false;
|
|
1237
|
-
const blockCtx = {
|
|
1250
|
+
const blockCtx = {
|
|
1251
|
+
...ctx,
|
|
1252
|
+
pos,
|
|
1253
|
+
blockRules,
|
|
1254
|
+
blockCloseCondition: closeCondition,
|
|
1255
|
+
excludedBlockNames: excluded
|
|
1256
|
+
};
|
|
1238
1257
|
for (const rule of blockRules) {
|
|
1239
1258
|
if (canApplyBlockRule(rule, token)) {
|
|
1240
1259
|
const result = rule.parse(blockCtx);
|
|
@@ -2619,44 +2638,7 @@ function consumeCloseTag2(ctx, pos) {
|
|
|
2619
2638
|
closeConsumed++;
|
|
2620
2639
|
return closeConsumed;
|
|
2621
2640
|
}
|
|
2622
|
-
|
|
2623
|
-
const result = [];
|
|
2624
|
-
let mergedElements = [];
|
|
2625
|
-
for (const elem of elements) {
|
|
2626
|
-
if (elem.element === "container" && elem.data && typeof elem.data === "object" && "type" in elem.data && elem.data.type === "paragraph") {
|
|
2627
|
-
if (mergedElements.length > 0) {
|
|
2628
|
-
mergedElements.push({ element: "line-break" });
|
|
2629
|
-
}
|
|
2630
|
-
if ("elements" in elem.data && Array.isArray(elem.data.elements)) {
|
|
2631
|
-
mergedElements.push(...elem.data.elements);
|
|
2632
|
-
}
|
|
2633
|
-
} else {
|
|
2634
|
-
if (mergedElements.length > 0) {
|
|
2635
|
-
result.push({
|
|
2636
|
-
element: "container",
|
|
2637
|
-
data: {
|
|
2638
|
-
type: "paragraph",
|
|
2639
|
-
attributes: {},
|
|
2640
|
-
elements: mergedElements
|
|
2641
|
-
}
|
|
2642
|
-
});
|
|
2643
|
-
mergedElements = [];
|
|
2644
|
-
}
|
|
2645
|
-
result.push(elem);
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2648
|
-
if (mergedElements.length > 0) {
|
|
2649
|
-
result.push({
|
|
2650
|
-
element: "container",
|
|
2651
|
-
data: {
|
|
2652
|
-
type: "paragraph",
|
|
2653
|
-
attributes: {},
|
|
2654
|
-
elements: mergedElements
|
|
2655
|
-
}
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
2658
|
-
return result;
|
|
2659
|
-
}
|
|
2641
|
+
var EXCLUDED_BLOCKS = new Set(["collapsible"]);
|
|
2660
2642
|
var collapsibleRule = {
|
|
2661
2643
|
name: "collapsible",
|
|
2662
2644
|
startTokens: ["BLOCK_OPEN"],
|
|
@@ -2720,18 +2702,16 @@ var collapsibleRule = {
|
|
|
2720
2702
|
bodyElements = [];
|
|
2721
2703
|
}
|
|
2722
2704
|
} else {
|
|
2723
|
-
const bodyCtx = {
|
|
2724
|
-
...ctx,
|
|
2725
|
-
pos,
|
|
2726
|
-
blockRules: ctx.blockRules.filter((r) => r.name !== "collapsible")
|
|
2727
|
-
};
|
|
2705
|
+
const bodyCtx = { ...ctx, pos };
|
|
2728
2706
|
const closeCondition = (checkCtx) => {
|
|
2729
2707
|
return isCollapsibleClose(checkCtx, checkCtx.pos);
|
|
2730
2708
|
};
|
|
2731
|
-
const bodyResult = parseBlocksUntil(bodyCtx, closeCondition
|
|
2709
|
+
const bodyResult = parseBlocksUntil(bodyCtx, closeCondition, {
|
|
2710
|
+
excludedBlockNames: EXCLUDED_BLOCKS
|
|
2711
|
+
});
|
|
2732
2712
|
consumed += bodyResult.consumed;
|
|
2733
2713
|
pos += bodyResult.consumed;
|
|
2734
|
-
bodyElements =
|
|
2714
|
+
bodyElements = bodyResult.elements;
|
|
2735
2715
|
}
|
|
2736
2716
|
if (!isCollapsibleClose(ctx, pos)) {
|
|
2737
2717
|
ctx.diagnostics.push({
|
|
@@ -9989,7 +9969,7 @@ function resolveIncludes(source, fetcher, options) {
|
|
|
9989
9969
|
if (options?.settings && !options.settings.enablePageSyntax) {
|
|
9990
9970
|
return source;
|
|
9991
9971
|
}
|
|
9992
|
-
const
|
|
9972
|
+
const maxIterations = options?.maxIterations ?? 10;
|
|
9993
9973
|
const cache = new Map;
|
|
9994
9974
|
const cachedFetcher = (pageRef) => {
|
|
9995
9975
|
const key = normalizePageKey(pageRef);
|
|
@@ -10005,7 +9985,29 @@ function resolveIncludes(source, fetcher, options) {
|
|
|
10005
9985
|
cache.set(key, result);
|
|
10006
9986
|
return result;
|
|
10007
9987
|
};
|
|
10008
|
-
return
|
|
9988
|
+
return expandIterative(source, cachedFetcher, maxIterations);
|
|
9989
|
+
}
|
|
9990
|
+
async function resolveIncludesAsync(source, fetcher, options) {
|
|
9991
|
+
if (options?.settings && !options.settings.enablePageSyntax) {
|
|
9992
|
+
return source;
|
|
9993
|
+
}
|
|
9994
|
+
const maxIterations = options?.maxIterations ?? 10;
|
|
9995
|
+
const cache = new Map;
|
|
9996
|
+
const cachedFetcher = async (pageRef) => {
|
|
9997
|
+
const key = normalizePageKey(pageRef);
|
|
9998
|
+
if (cache.has(key)) {
|
|
9999
|
+
return cache.get(key);
|
|
10000
|
+
}
|
|
10001
|
+
let result;
|
|
10002
|
+
try {
|
|
10003
|
+
result = await fetcher(pageRef);
|
|
10004
|
+
} catch {
|
|
10005
|
+
result = null;
|
|
10006
|
+
}
|
|
10007
|
+
cache.set(key, result);
|
|
10008
|
+
return result;
|
|
10009
|
+
};
|
|
10010
|
+
return expandIterativeAsync(source, cachedFetcher, maxIterations);
|
|
10009
10011
|
}
|
|
10010
10012
|
var INCLUDE_PATTERN = /^\[\[include\s([^\]]*(?:\](?!\])[^\]]*)*)\]\]/gim;
|
|
10011
10013
|
function parseIncludeDirective(inner) {
|
|
@@ -10055,26 +10057,55 @@ function parseIncludeDirective(inner) {
|
|
|
10055
10057
|
}
|
|
10056
10058
|
return { location, variables };
|
|
10057
10059
|
}
|
|
10058
|
-
function
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
if (trace.includes(pageKey)) {
|
|
10065
|
-
return `[[div class="error-block"]]
|
|
10066
|
-
Circular include detected: "${location.page}"
|
|
10060
|
+
function replaceOneInclude(_match, inner, fetcher) {
|
|
10061
|
+
const { location, variables } = parseIncludeDirective(inner);
|
|
10062
|
+
const content = fetcher(location);
|
|
10063
|
+
if (content === null) {
|
|
10064
|
+
return `[[div class="error-block"]]
|
|
10065
|
+
Page to be included "${location.page}" cannot be found!
|
|
10067
10066
|
[[/div]]`;
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10067
|
+
}
|
|
10068
|
+
return substituteVariables(content, variables);
|
|
10069
|
+
}
|
|
10070
|
+
function expandIterative(source, fetcher, maxIterations) {
|
|
10071
|
+
let current = source;
|
|
10072
|
+
for (let i = 0;i < maxIterations; i++) {
|
|
10073
|
+
const previous = current;
|
|
10074
|
+
current = current.replace(INCLUDE_PATTERN, (_match, inner) => replaceOneInclude(_match, inner, fetcher));
|
|
10075
|
+
if (current === previous)
|
|
10076
|
+
break;
|
|
10077
|
+
}
|
|
10078
|
+
return current;
|
|
10079
|
+
}
|
|
10080
|
+
async function expandIterativeAsync(source, fetcher, maxIterations) {
|
|
10081
|
+
let current = source;
|
|
10082
|
+
for (let i = 0;i < maxIterations; i++) {
|
|
10083
|
+
const previous = current;
|
|
10084
|
+
const pattern = new RegExp(INCLUDE_PATTERN.source, INCLUDE_PATTERN.flags);
|
|
10085
|
+
let result = "";
|
|
10086
|
+
let lastPos = 0;
|
|
10087
|
+
let match;
|
|
10088
|
+
while ((match = pattern.exec(current)) !== null) {
|
|
10089
|
+
const fullMatch = match[0];
|
|
10090
|
+
const inner = match[1];
|
|
10091
|
+
result += current.slice(lastPos, match.index);
|
|
10092
|
+
const { location, variables } = parseIncludeDirective(inner);
|
|
10093
|
+
const content = await fetcher(location);
|
|
10094
|
+
if (content === null) {
|
|
10095
|
+
result += `[[div class="error-block"]]
|
|
10072
10096
|
Page to be included "${location.page}" cannot be found!
|
|
10073
10097
|
[[/div]]`;
|
|
10098
|
+
} else {
|
|
10099
|
+
result += substituteVariables(content, variables);
|
|
10100
|
+
}
|
|
10101
|
+
lastPos = match.index + fullMatch.length;
|
|
10074
10102
|
}
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10103
|
+
result += current.slice(lastPos);
|
|
10104
|
+
current = result;
|
|
10105
|
+
if (current === previous)
|
|
10106
|
+
break;
|
|
10107
|
+
}
|
|
10108
|
+
return current;
|
|
10078
10109
|
}
|
|
10079
10110
|
function normalizePageKey(location) {
|
|
10080
10111
|
const site = location.site ?? "";
|
package/dist/index.d.cts
CHANGED
|
@@ -859,21 +859,36 @@ import { PageRef, WikitextSettings as WikitextSettings3 } from "@wdprlib/ast";
|
|
|
859
859
|
*/
|
|
860
860
|
type IncludeFetcher = (pageRef: PageRef) => string | null;
|
|
861
861
|
/**
|
|
862
|
-
*
|
|
862
|
+
* Async callback to fetch page content for include resolution.
|
|
863
|
+
* Returns a promise of the wikitext source, or null if the page does not exist.
|
|
864
|
+
*
|
|
865
|
+
* @security The fetcher is called with user-provided page references.
|
|
866
|
+
* Implementations should validate and sanitize page references before
|
|
867
|
+
* using them in database queries or file system access.
|
|
868
|
+
*/
|
|
869
|
+
type AsyncIncludeFetcher = (pageRef: PageRef) => Promise<string | null>;
|
|
870
|
+
/**
|
|
871
|
+
* Options for resolveIncludes / resolveIncludesAsync
|
|
863
872
|
*/
|
|
864
873
|
interface ResolveIncludesOptions {
|
|
865
|
-
/**
|
|
866
|
-
|
|
874
|
+
/**
|
|
875
|
+
* Maximum number of expansion iterations (default: 10).
|
|
876
|
+
*
|
|
877
|
+
* Each iteration replaces all `[[include]]` directives in the current
|
|
878
|
+
* source with fetched content. Iteration stops when the source is
|
|
879
|
+
* unchanged or this limit is reached.
|
|
880
|
+
*/
|
|
881
|
+
maxIterations?: number;
|
|
867
882
|
/** Wikitext settings. If enablePageSyntax is false, includes are not expanded. */
|
|
868
883
|
settings?: WikitextSettings3;
|
|
869
884
|
}
|
|
870
885
|
/**
|
|
871
886
|
* Expand all [[include]] directives in the source text.
|
|
872
887
|
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
888
|
+
* Uses Wikidot-compatible iterative expansion: each iteration replaces
|
|
889
|
+
* all include directives in the current source with fetched (and
|
|
890
|
+
* variable-substituted) content. Iteration continues until no further
|
|
891
|
+
* changes occur or `maxIterations` is reached.
|
|
877
892
|
*
|
|
878
893
|
* @example
|
|
879
894
|
* ```ts
|
|
@@ -883,6 +898,21 @@ interface ResolveIncludesOptions {
|
|
|
883
898
|
*/
|
|
884
899
|
declare function resolveIncludes(source: string, fetcher: IncludeFetcher, options?: ResolveIncludesOptions): string;
|
|
885
900
|
/**
|
|
901
|
+
* Async version of {@link resolveIncludes}.
|
|
902
|
+
*
|
|
903
|
+
* Expand all [[include]] directives using an async fetcher, allowing
|
|
904
|
+
* page content to be loaded from async sources such as databases.
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```ts
|
|
908
|
+
* const expanded = await resolveIncludesAsync(source, async (ref) => {
|
|
909
|
+
* return await db.getPageContent(ref.page);
|
|
910
|
+
* });
|
|
911
|
+
* const ast = parse(expanded);
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
declare function resolveIncludesAsync(source: string, fetcher: AsyncIncludeFetcher, options?: ResolveIncludesOptions): Promise<string>;
|
|
915
|
+
/**
|
|
886
916
|
* Compile a ListUsers template string into an executable function.
|
|
887
917
|
*
|
|
888
918
|
* The template is split into alternating static strings and dynamic getter
|
|
@@ -981,4 +1011,4 @@ interface ResolveOptions {
|
|
|
981
1011
|
*/
|
|
982
1012
|
declare function resolveModules(ast: SyntaxTree2, dataProvider: DataProvider, options: ResolveOptions): Promise<SyntaxTree2>;
|
|
983
1013
|
import { STYLE_SLOT_PREFIX } from "@wdprlib/ast";
|
|
984
|
-
export { tokenize, text, resolveModules, resolveListUsers, resolveIncludes, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult2 as ParseResult, ParseFunction, PageRef2 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, Module4 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeFetcher, ImageSource, HeadingLevel, Heading, HeaderType, FloatAlignment, ExtractionResult, Embed, Element6 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AnchorTarget, Alignment, AlignType };
|
|
1014
|
+
export { tokenize, text, resolveModules, resolveListUsers, resolveIncludesAsync, resolveIncludes, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult2 as ParseResult, ParseFunction, PageRef2 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, Module4 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeFetcher, ImageSource, HeadingLevel, Heading, HeaderType, FloatAlignment, ExtractionResult, Embed, Element6 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AsyncIncludeFetcher, AnchorTarget, Alignment, AlignType };
|
package/dist/index.d.ts
CHANGED
|
@@ -859,21 +859,36 @@ import { PageRef, WikitextSettings as WikitextSettings3 } from "@wdprlib/ast";
|
|
|
859
859
|
*/
|
|
860
860
|
type IncludeFetcher = (pageRef: PageRef) => string | null;
|
|
861
861
|
/**
|
|
862
|
-
*
|
|
862
|
+
* Async callback to fetch page content for include resolution.
|
|
863
|
+
* Returns a promise of the wikitext source, or null if the page does not exist.
|
|
864
|
+
*
|
|
865
|
+
* @security The fetcher is called with user-provided page references.
|
|
866
|
+
* Implementations should validate and sanitize page references before
|
|
867
|
+
* using them in database queries or file system access.
|
|
868
|
+
*/
|
|
869
|
+
type AsyncIncludeFetcher = (pageRef: PageRef) => Promise<string | null>;
|
|
870
|
+
/**
|
|
871
|
+
* Options for resolveIncludes / resolveIncludesAsync
|
|
863
872
|
*/
|
|
864
873
|
interface ResolveIncludesOptions {
|
|
865
|
-
/**
|
|
866
|
-
|
|
874
|
+
/**
|
|
875
|
+
* Maximum number of expansion iterations (default: 10).
|
|
876
|
+
*
|
|
877
|
+
* Each iteration replaces all `[[include]]` directives in the current
|
|
878
|
+
* source with fetched content. Iteration stops when the source is
|
|
879
|
+
* unchanged or this limit is reached.
|
|
880
|
+
*/
|
|
881
|
+
maxIterations?: number;
|
|
867
882
|
/** Wikitext settings. If enablePageSyntax is false, includes are not expanded. */
|
|
868
883
|
settings?: WikitextSettings3;
|
|
869
884
|
}
|
|
870
885
|
/**
|
|
871
886
|
* Expand all [[include]] directives in the source text.
|
|
872
887
|
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
888
|
+
* Uses Wikidot-compatible iterative expansion: each iteration replaces
|
|
889
|
+
* all include directives in the current source with fetched (and
|
|
890
|
+
* variable-substituted) content. Iteration continues until no further
|
|
891
|
+
* changes occur or `maxIterations` is reached.
|
|
877
892
|
*
|
|
878
893
|
* @example
|
|
879
894
|
* ```ts
|
|
@@ -883,6 +898,21 @@ interface ResolveIncludesOptions {
|
|
|
883
898
|
*/
|
|
884
899
|
declare function resolveIncludes(source: string, fetcher: IncludeFetcher, options?: ResolveIncludesOptions): string;
|
|
885
900
|
/**
|
|
901
|
+
* Async version of {@link resolveIncludes}.
|
|
902
|
+
*
|
|
903
|
+
* Expand all [[include]] directives using an async fetcher, allowing
|
|
904
|
+
* page content to be loaded from async sources such as databases.
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```ts
|
|
908
|
+
* const expanded = await resolveIncludesAsync(source, async (ref) => {
|
|
909
|
+
* return await db.getPageContent(ref.page);
|
|
910
|
+
* });
|
|
911
|
+
* const ast = parse(expanded);
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
declare function resolveIncludesAsync(source: string, fetcher: AsyncIncludeFetcher, options?: ResolveIncludesOptions): Promise<string>;
|
|
915
|
+
/**
|
|
886
916
|
* Compile a ListUsers template string into an executable function.
|
|
887
917
|
*
|
|
888
918
|
* The template is split into alternating static strings and dynamic getter
|
|
@@ -981,4 +1011,4 @@ interface ResolveOptions {
|
|
|
981
1011
|
*/
|
|
982
1012
|
declare function resolveModules(ast: SyntaxTree2, dataProvider: DataProvider, options: ResolveOptions): Promise<SyntaxTree2>;
|
|
983
1013
|
import { STYLE_SLOT_PREFIX } from "@wdprlib/ast";
|
|
984
|
-
export { tokenize, text, resolveModules, resolveListUsers, resolveIncludes, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult2 as ParseResult, ParseFunction, PageRef2 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, Module4 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeFetcher, ImageSource, HeadingLevel, Heading, HeaderType, FloatAlignment, ExtractionResult, Embed, Element6 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AnchorTarget, Alignment, AlignType };
|
|
1014
|
+
export { tokenize, text, resolveModules, resolveListUsers, resolveIncludesAsync, resolveIncludes, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult2 as ParseResult, ParseFunction, PageRef2 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, Module4 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeFetcher, ImageSource, HeadingLevel, Heading, HeaderType, FloatAlignment, ExtractionResult, Embed, Element6 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AsyncIncludeFetcher, AnchorTarget, Alignment, AlignType };
|
package/dist/index.js
CHANGED
|
@@ -591,7 +591,139 @@ var BLOCK_START_TOKENS = [
|
|
|
591
591
|
"CLEAR_FLOAT_RIGHT"
|
|
592
592
|
];
|
|
593
593
|
|
|
594
|
+
// packages/parser/src/parser/rules/utils.ts
|
|
595
|
+
var SAFE_ATTRIBUTES = new Set([
|
|
596
|
+
"accept",
|
|
597
|
+
"align",
|
|
598
|
+
"alt",
|
|
599
|
+
"autocapitalize",
|
|
600
|
+
"autoplay",
|
|
601
|
+
"background",
|
|
602
|
+
"bgcolor",
|
|
603
|
+
"border",
|
|
604
|
+
"buffered",
|
|
605
|
+
"checked",
|
|
606
|
+
"cite",
|
|
607
|
+
"class",
|
|
608
|
+
"cols",
|
|
609
|
+
"colspan",
|
|
610
|
+
"contenteditable",
|
|
611
|
+
"controls",
|
|
612
|
+
"coords",
|
|
613
|
+
"datetime",
|
|
614
|
+
"decoding",
|
|
615
|
+
"default",
|
|
616
|
+
"dir",
|
|
617
|
+
"dirname",
|
|
618
|
+
"disabled",
|
|
619
|
+
"download",
|
|
620
|
+
"draggable",
|
|
621
|
+
"for",
|
|
622
|
+
"form",
|
|
623
|
+
"headers",
|
|
624
|
+
"height",
|
|
625
|
+
"hidden",
|
|
626
|
+
"high",
|
|
627
|
+
"href",
|
|
628
|
+
"hreflang",
|
|
629
|
+
"id",
|
|
630
|
+
"inputmode",
|
|
631
|
+
"ismap",
|
|
632
|
+
"itemprop",
|
|
633
|
+
"kind",
|
|
634
|
+
"label",
|
|
635
|
+
"lang",
|
|
636
|
+
"list",
|
|
637
|
+
"loop",
|
|
638
|
+
"low",
|
|
639
|
+
"max",
|
|
640
|
+
"maxlength",
|
|
641
|
+
"min",
|
|
642
|
+
"minlength",
|
|
643
|
+
"multiple",
|
|
644
|
+
"muted",
|
|
645
|
+
"name",
|
|
646
|
+
"optimum",
|
|
647
|
+
"pattern",
|
|
648
|
+
"placeholder",
|
|
649
|
+
"poster",
|
|
650
|
+
"preload",
|
|
651
|
+
"readonly",
|
|
652
|
+
"required",
|
|
653
|
+
"reversed",
|
|
654
|
+
"role",
|
|
655
|
+
"rows",
|
|
656
|
+
"rowspan",
|
|
657
|
+
"scope",
|
|
658
|
+
"selected",
|
|
659
|
+
"shape",
|
|
660
|
+
"size",
|
|
661
|
+
"sizes",
|
|
662
|
+
"span",
|
|
663
|
+
"spellcheck",
|
|
664
|
+
"src",
|
|
665
|
+
"srclang",
|
|
666
|
+
"srcset",
|
|
667
|
+
"start",
|
|
668
|
+
"step",
|
|
669
|
+
"style",
|
|
670
|
+
"tabindex",
|
|
671
|
+
"target",
|
|
672
|
+
"title",
|
|
673
|
+
"translate",
|
|
674
|
+
"type",
|
|
675
|
+
"usemap",
|
|
676
|
+
"value",
|
|
677
|
+
"width",
|
|
678
|
+
"wrap"
|
|
679
|
+
]);
|
|
680
|
+
function filterUnsafeAttributes(attrs) {
|
|
681
|
+
const result = {};
|
|
682
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
683
|
+
const lower = key.toLowerCase();
|
|
684
|
+
if (lower.startsWith("on"))
|
|
685
|
+
continue;
|
|
686
|
+
if (lower.startsWith("aria-") || lower.startsWith("data-")) {
|
|
687
|
+
result[key] = value;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (!SAFE_ATTRIBUTES.has(lower))
|
|
691
|
+
continue;
|
|
692
|
+
if (lower === "id") {
|
|
693
|
+
result[key] = value.startsWith("u-") ? value : `u-${value}`;
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
result[key] = value;
|
|
697
|
+
}
|
|
698
|
+
return result;
|
|
699
|
+
}
|
|
700
|
+
function parseBlockName(ctx, startPos) {
|
|
701
|
+
let pos = startPos;
|
|
702
|
+
let consumed = 0;
|
|
703
|
+
const token = ctx.tokens[pos];
|
|
704
|
+
if (!token || token.type !== "TEXT" && token.type !== "IDENTIFIER") {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
let name = token.value.toLowerCase();
|
|
708
|
+
consumed++;
|
|
709
|
+
pos++;
|
|
710
|
+
if (ctx.tokens[pos]?.type === "UNDERSCORE") {
|
|
711
|
+
name += "_";
|
|
712
|
+
consumed++;
|
|
713
|
+
}
|
|
714
|
+
return { name, consumed };
|
|
715
|
+
}
|
|
716
|
+
|
|
594
717
|
// packages/parser/src/parser/rules/inline/utils.ts
|
|
718
|
+
function isExcludedBlockToken(ctx, tokenPos) {
|
|
719
|
+
if (!ctx.excludedBlockNames?.size)
|
|
720
|
+
return false;
|
|
721
|
+
const token = ctx.tokens[tokenPos];
|
|
722
|
+
if (token?.type !== "BLOCK_OPEN" && token?.type !== "BLOCK_END_OPEN")
|
|
723
|
+
return false;
|
|
724
|
+
const nameResult = parseBlockName(ctx, tokenPos + 1);
|
|
725
|
+
return nameResult !== null && ctx.excludedBlockNames.has(nameResult.name);
|
|
726
|
+
}
|
|
595
727
|
function canApplyInlineRule(rule, token) {
|
|
596
728
|
if (rule.startTokens.length === 0) {
|
|
597
729
|
return true;
|
|
@@ -683,7 +815,8 @@ function parseInlineUntil(ctx, endType) {
|
|
|
683
815
|
isInvalidHeading = true;
|
|
684
816
|
}
|
|
685
817
|
}
|
|
686
|
-
const
|
|
818
|
+
const isExcludedBlock = (nextMeaningfulToken?.type === "BLOCK_OPEN" || nextMeaningfulToken?.type === "BLOCK_END_OPEN") && isExcludedBlockToken(ctx, pos + lookAhead);
|
|
819
|
+
const isBlockStart = nextMeaningfulToken && BLOCK_START_TOKENS.includes(nextMeaningfulToken.type) && nextMeaningfulToken.lineStart && !isOrphanCloseSpan && !isAnchorName && !isInvalidBlockOpen && !isInvalidHeading && !isExcludedBlock;
|
|
687
820
|
if (!nextMeaningfulToken || nextMeaningfulToken.type === "NEWLINE" || nextMeaningfulToken.type === "EOF" || isBlockStart) {
|
|
688
821
|
if (isBlockStart && nodes.length > 0) {
|
|
689
822
|
const nextPos = pos + lookAhead;
|
|
@@ -1021,129 +1154,6 @@ function buildListData(topLtype, list) {
|
|
|
1021
1154
|
};
|
|
1022
1155
|
}
|
|
1023
1156
|
|
|
1024
|
-
// packages/parser/src/parser/rules/utils.ts
|
|
1025
|
-
var SAFE_ATTRIBUTES = new Set([
|
|
1026
|
-
"accept",
|
|
1027
|
-
"align",
|
|
1028
|
-
"alt",
|
|
1029
|
-
"autocapitalize",
|
|
1030
|
-
"autoplay",
|
|
1031
|
-
"background",
|
|
1032
|
-
"bgcolor",
|
|
1033
|
-
"border",
|
|
1034
|
-
"buffered",
|
|
1035
|
-
"checked",
|
|
1036
|
-
"cite",
|
|
1037
|
-
"class",
|
|
1038
|
-
"cols",
|
|
1039
|
-
"colspan",
|
|
1040
|
-
"contenteditable",
|
|
1041
|
-
"controls",
|
|
1042
|
-
"coords",
|
|
1043
|
-
"datetime",
|
|
1044
|
-
"decoding",
|
|
1045
|
-
"default",
|
|
1046
|
-
"dir",
|
|
1047
|
-
"dirname",
|
|
1048
|
-
"disabled",
|
|
1049
|
-
"download",
|
|
1050
|
-
"draggable",
|
|
1051
|
-
"for",
|
|
1052
|
-
"form",
|
|
1053
|
-
"headers",
|
|
1054
|
-
"height",
|
|
1055
|
-
"hidden",
|
|
1056
|
-
"high",
|
|
1057
|
-
"href",
|
|
1058
|
-
"hreflang",
|
|
1059
|
-
"id",
|
|
1060
|
-
"inputmode",
|
|
1061
|
-
"ismap",
|
|
1062
|
-
"itemprop",
|
|
1063
|
-
"kind",
|
|
1064
|
-
"label",
|
|
1065
|
-
"lang",
|
|
1066
|
-
"list",
|
|
1067
|
-
"loop",
|
|
1068
|
-
"low",
|
|
1069
|
-
"max",
|
|
1070
|
-
"maxlength",
|
|
1071
|
-
"min",
|
|
1072
|
-
"minlength",
|
|
1073
|
-
"multiple",
|
|
1074
|
-
"muted",
|
|
1075
|
-
"name",
|
|
1076
|
-
"optimum",
|
|
1077
|
-
"pattern",
|
|
1078
|
-
"placeholder",
|
|
1079
|
-
"poster",
|
|
1080
|
-
"preload",
|
|
1081
|
-
"readonly",
|
|
1082
|
-
"required",
|
|
1083
|
-
"reversed",
|
|
1084
|
-
"role",
|
|
1085
|
-
"rows",
|
|
1086
|
-
"rowspan",
|
|
1087
|
-
"scope",
|
|
1088
|
-
"selected",
|
|
1089
|
-
"shape",
|
|
1090
|
-
"size",
|
|
1091
|
-
"sizes",
|
|
1092
|
-
"span",
|
|
1093
|
-
"spellcheck",
|
|
1094
|
-
"src",
|
|
1095
|
-
"srclang",
|
|
1096
|
-
"srcset",
|
|
1097
|
-
"start",
|
|
1098
|
-
"step",
|
|
1099
|
-
"style",
|
|
1100
|
-
"tabindex",
|
|
1101
|
-
"target",
|
|
1102
|
-
"title",
|
|
1103
|
-
"translate",
|
|
1104
|
-
"type",
|
|
1105
|
-
"usemap",
|
|
1106
|
-
"value",
|
|
1107
|
-
"width",
|
|
1108
|
-
"wrap"
|
|
1109
|
-
]);
|
|
1110
|
-
function filterUnsafeAttributes(attrs) {
|
|
1111
|
-
const result = {};
|
|
1112
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
1113
|
-
const lower = key.toLowerCase();
|
|
1114
|
-
if (lower.startsWith("on"))
|
|
1115
|
-
continue;
|
|
1116
|
-
if (lower.startsWith("aria-") || lower.startsWith("data-")) {
|
|
1117
|
-
result[key] = value;
|
|
1118
|
-
continue;
|
|
1119
|
-
}
|
|
1120
|
-
if (!SAFE_ATTRIBUTES.has(lower))
|
|
1121
|
-
continue;
|
|
1122
|
-
if (lower === "id") {
|
|
1123
|
-
result[key] = value.startsWith("u-") ? value : `u-${value}`;
|
|
1124
|
-
continue;
|
|
1125
|
-
}
|
|
1126
|
-
result[key] = value;
|
|
1127
|
-
}
|
|
1128
|
-
return result;
|
|
1129
|
-
}
|
|
1130
|
-
function parseBlockName(ctx, startPos) {
|
|
1131
|
-
let pos = startPos;
|
|
1132
|
-
let consumed = 0;
|
|
1133
|
-
const token = ctx.tokens[pos];
|
|
1134
|
-
if (!token || token.type !== "TEXT" && token.type !== "IDENTIFIER") {
|
|
1135
|
-
return null;
|
|
1136
|
-
}
|
|
1137
|
-
let name = token.value.toLowerCase();
|
|
1138
|
-
consumed++;
|
|
1139
|
-
pos++;
|
|
1140
|
-
if (ctx.tokens[pos]?.type === "UNDERSCORE") {
|
|
1141
|
-
name += "_";
|
|
1142
|
-
consumed++;
|
|
1143
|
-
}
|
|
1144
|
-
return { name, consumed };
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
1157
|
// packages/parser/src/parser/rules/block/utils.ts
|
|
1148
1158
|
function canApplyBlockRule(rule, token) {
|
|
1149
1159
|
if (rule.requiresLineStart && !token.lineStart) {
|
|
@@ -1154,11 +1164,13 @@ function canApplyBlockRule(rule, token) {
|
|
|
1154
1164
|
}
|
|
1155
1165
|
return rule.startTokens.includes(token.type);
|
|
1156
1166
|
}
|
|
1157
|
-
function parseBlocksUntil(ctx, closeCondition) {
|
|
1167
|
+
function parseBlocksUntil(ctx, closeCondition, options) {
|
|
1158
1168
|
const elements = [];
|
|
1159
1169
|
let consumed = 0;
|
|
1160
1170
|
let pos = ctx.pos;
|
|
1161
|
-
const
|
|
1171
|
+
const excluded = options?.excludedBlockNames;
|
|
1172
|
+
const blockRules = excluded ? ctx.blockRules.filter((r) => !excluded.has(r.name)) : ctx.blockRules;
|
|
1173
|
+
const { blockFallbackRule } = ctx;
|
|
1162
1174
|
while (pos < ctx.tokens.length) {
|
|
1163
1175
|
const token = ctx.tokens[pos];
|
|
1164
1176
|
if (!token || token.type === "EOF") {
|
|
@@ -1179,7 +1191,13 @@ function parseBlocksUntil(ctx, closeCondition) {
|
|
|
1179
1191
|
continue;
|
|
1180
1192
|
}
|
|
1181
1193
|
let matched = false;
|
|
1182
|
-
const blockCtx = {
|
|
1194
|
+
const blockCtx = {
|
|
1195
|
+
...ctx,
|
|
1196
|
+
pos,
|
|
1197
|
+
blockRules,
|
|
1198
|
+
blockCloseCondition: closeCondition,
|
|
1199
|
+
excludedBlockNames: excluded
|
|
1200
|
+
};
|
|
1183
1201
|
for (const rule of blockRules) {
|
|
1184
1202
|
if (canApplyBlockRule(rule, token)) {
|
|
1185
1203
|
const result = rule.parse(blockCtx);
|
|
@@ -2564,44 +2582,7 @@ function consumeCloseTag2(ctx, pos) {
|
|
|
2564
2582
|
closeConsumed++;
|
|
2565
2583
|
return closeConsumed;
|
|
2566
2584
|
}
|
|
2567
|
-
|
|
2568
|
-
const result = [];
|
|
2569
|
-
let mergedElements = [];
|
|
2570
|
-
for (const elem of elements) {
|
|
2571
|
-
if (elem.element === "container" && elem.data && typeof elem.data === "object" && "type" in elem.data && elem.data.type === "paragraph") {
|
|
2572
|
-
if (mergedElements.length > 0) {
|
|
2573
|
-
mergedElements.push({ element: "line-break" });
|
|
2574
|
-
}
|
|
2575
|
-
if ("elements" in elem.data && Array.isArray(elem.data.elements)) {
|
|
2576
|
-
mergedElements.push(...elem.data.elements);
|
|
2577
|
-
}
|
|
2578
|
-
} else {
|
|
2579
|
-
if (mergedElements.length > 0) {
|
|
2580
|
-
result.push({
|
|
2581
|
-
element: "container",
|
|
2582
|
-
data: {
|
|
2583
|
-
type: "paragraph",
|
|
2584
|
-
attributes: {},
|
|
2585
|
-
elements: mergedElements
|
|
2586
|
-
}
|
|
2587
|
-
});
|
|
2588
|
-
mergedElements = [];
|
|
2589
|
-
}
|
|
2590
|
-
result.push(elem);
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
if (mergedElements.length > 0) {
|
|
2594
|
-
result.push({
|
|
2595
|
-
element: "container",
|
|
2596
|
-
data: {
|
|
2597
|
-
type: "paragraph",
|
|
2598
|
-
attributes: {},
|
|
2599
|
-
elements: mergedElements
|
|
2600
|
-
}
|
|
2601
|
-
});
|
|
2602
|
-
}
|
|
2603
|
-
return result;
|
|
2604
|
-
}
|
|
2585
|
+
var EXCLUDED_BLOCKS = new Set(["collapsible"]);
|
|
2605
2586
|
var collapsibleRule = {
|
|
2606
2587
|
name: "collapsible",
|
|
2607
2588
|
startTokens: ["BLOCK_OPEN"],
|
|
@@ -2665,18 +2646,16 @@ var collapsibleRule = {
|
|
|
2665
2646
|
bodyElements = [];
|
|
2666
2647
|
}
|
|
2667
2648
|
} else {
|
|
2668
|
-
const bodyCtx = {
|
|
2669
|
-
...ctx,
|
|
2670
|
-
pos,
|
|
2671
|
-
blockRules: ctx.blockRules.filter((r) => r.name !== "collapsible")
|
|
2672
|
-
};
|
|
2649
|
+
const bodyCtx = { ...ctx, pos };
|
|
2673
2650
|
const closeCondition = (checkCtx) => {
|
|
2674
2651
|
return isCollapsibleClose(checkCtx, checkCtx.pos);
|
|
2675
2652
|
};
|
|
2676
|
-
const bodyResult = parseBlocksUntil(bodyCtx, closeCondition
|
|
2653
|
+
const bodyResult = parseBlocksUntil(bodyCtx, closeCondition, {
|
|
2654
|
+
excludedBlockNames: EXCLUDED_BLOCKS
|
|
2655
|
+
});
|
|
2677
2656
|
consumed += bodyResult.consumed;
|
|
2678
2657
|
pos += bodyResult.consumed;
|
|
2679
|
-
bodyElements =
|
|
2658
|
+
bodyElements = bodyResult.elements;
|
|
2680
2659
|
}
|
|
2681
2660
|
if (!isCollapsibleClose(ctx, pos)) {
|
|
2682
2661
|
ctx.diagnostics.push({
|
|
@@ -9934,7 +9913,7 @@ function resolveIncludes(source, fetcher, options) {
|
|
|
9934
9913
|
if (options?.settings && !options.settings.enablePageSyntax) {
|
|
9935
9914
|
return source;
|
|
9936
9915
|
}
|
|
9937
|
-
const
|
|
9916
|
+
const maxIterations = options?.maxIterations ?? 10;
|
|
9938
9917
|
const cache = new Map;
|
|
9939
9918
|
const cachedFetcher = (pageRef) => {
|
|
9940
9919
|
const key = normalizePageKey(pageRef);
|
|
@@ -9950,7 +9929,29 @@ function resolveIncludes(source, fetcher, options) {
|
|
|
9950
9929
|
cache.set(key, result);
|
|
9951
9930
|
return result;
|
|
9952
9931
|
};
|
|
9953
|
-
return
|
|
9932
|
+
return expandIterative(source, cachedFetcher, maxIterations);
|
|
9933
|
+
}
|
|
9934
|
+
async function resolveIncludesAsync(source, fetcher, options) {
|
|
9935
|
+
if (options?.settings && !options.settings.enablePageSyntax) {
|
|
9936
|
+
return source;
|
|
9937
|
+
}
|
|
9938
|
+
const maxIterations = options?.maxIterations ?? 10;
|
|
9939
|
+
const cache = new Map;
|
|
9940
|
+
const cachedFetcher = async (pageRef) => {
|
|
9941
|
+
const key = normalizePageKey(pageRef);
|
|
9942
|
+
if (cache.has(key)) {
|
|
9943
|
+
return cache.get(key);
|
|
9944
|
+
}
|
|
9945
|
+
let result;
|
|
9946
|
+
try {
|
|
9947
|
+
result = await fetcher(pageRef);
|
|
9948
|
+
} catch {
|
|
9949
|
+
result = null;
|
|
9950
|
+
}
|
|
9951
|
+
cache.set(key, result);
|
|
9952
|
+
return result;
|
|
9953
|
+
};
|
|
9954
|
+
return expandIterativeAsync(source, cachedFetcher, maxIterations);
|
|
9954
9955
|
}
|
|
9955
9956
|
var INCLUDE_PATTERN = /^\[\[include\s([^\]]*(?:\](?!\])[^\]]*)*)\]\]/gim;
|
|
9956
9957
|
function parseIncludeDirective(inner) {
|
|
@@ -10000,26 +10001,55 @@ function parseIncludeDirective(inner) {
|
|
|
10000
10001
|
}
|
|
10001
10002
|
return { location, variables };
|
|
10002
10003
|
}
|
|
10003
|
-
function
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
if (trace.includes(pageKey)) {
|
|
10010
|
-
return `[[div class="error-block"]]
|
|
10011
|
-
Circular include detected: "${location.page}"
|
|
10004
|
+
function replaceOneInclude(_match, inner, fetcher) {
|
|
10005
|
+
const { location, variables } = parseIncludeDirective(inner);
|
|
10006
|
+
const content = fetcher(location);
|
|
10007
|
+
if (content === null) {
|
|
10008
|
+
return `[[div class="error-block"]]
|
|
10009
|
+
Page to be included "${location.page}" cannot be found!
|
|
10012
10010
|
[[/div]]`;
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10011
|
+
}
|
|
10012
|
+
return substituteVariables(content, variables);
|
|
10013
|
+
}
|
|
10014
|
+
function expandIterative(source, fetcher, maxIterations) {
|
|
10015
|
+
let current = source;
|
|
10016
|
+
for (let i = 0;i < maxIterations; i++) {
|
|
10017
|
+
const previous = current;
|
|
10018
|
+
current = current.replace(INCLUDE_PATTERN, (_match, inner) => replaceOneInclude(_match, inner, fetcher));
|
|
10019
|
+
if (current === previous)
|
|
10020
|
+
break;
|
|
10021
|
+
}
|
|
10022
|
+
return current;
|
|
10023
|
+
}
|
|
10024
|
+
async function expandIterativeAsync(source, fetcher, maxIterations) {
|
|
10025
|
+
let current = source;
|
|
10026
|
+
for (let i = 0;i < maxIterations; i++) {
|
|
10027
|
+
const previous = current;
|
|
10028
|
+
const pattern = new RegExp(INCLUDE_PATTERN.source, INCLUDE_PATTERN.flags);
|
|
10029
|
+
let result = "";
|
|
10030
|
+
let lastPos = 0;
|
|
10031
|
+
let match;
|
|
10032
|
+
while ((match = pattern.exec(current)) !== null) {
|
|
10033
|
+
const fullMatch = match[0];
|
|
10034
|
+
const inner = match[1];
|
|
10035
|
+
result += current.slice(lastPos, match.index);
|
|
10036
|
+
const { location, variables } = parseIncludeDirective(inner);
|
|
10037
|
+
const content = await fetcher(location);
|
|
10038
|
+
if (content === null) {
|
|
10039
|
+
result += `[[div class="error-block"]]
|
|
10017
10040
|
Page to be included "${location.page}" cannot be found!
|
|
10018
10041
|
[[/div]]`;
|
|
10042
|
+
} else {
|
|
10043
|
+
result += substituteVariables(content, variables);
|
|
10044
|
+
}
|
|
10045
|
+
lastPos = match.index + fullMatch.length;
|
|
10019
10046
|
}
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10047
|
+
result += current.slice(lastPos);
|
|
10048
|
+
current = result;
|
|
10049
|
+
if (current === previous)
|
|
10050
|
+
break;
|
|
10051
|
+
}
|
|
10052
|
+
return current;
|
|
10023
10053
|
}
|
|
10024
10054
|
function normalizePageKey(location) {
|
|
10025
10055
|
const site = location.site ?? "";
|
|
@@ -10248,6 +10278,7 @@ export {
|
|
|
10248
10278
|
text,
|
|
10249
10279
|
resolveModules,
|
|
10250
10280
|
resolveListUsers,
|
|
10281
|
+
resolveIncludesAsync,
|
|
10251
10282
|
resolveIncludes,
|
|
10252
10283
|
parseTags,
|
|
10253
10284
|
parseParent,
|