@wdprlib/parser 3.0.0 → 3.1.1
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 +236 -152
- package/dist/index.d.cts +25 -6
- package/dist/index.d.ts +25 -6
- package/dist/index.js +236 -152
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9129,6 +9129,237 @@ function mapElementChildrenWithState(element, state, transform) {
|
|
|
9129
9129
|
return { element, state };
|
|
9130
9130
|
}
|
|
9131
9131
|
|
|
9132
|
+
// packages/parser/src/parser/rules/block/module/iftags/condition.ts
|
|
9133
|
+
function parseTagCondition(condition) {
|
|
9134
|
+
const required = [];
|
|
9135
|
+
const forbidden = [];
|
|
9136
|
+
const optional = [];
|
|
9137
|
+
const parts = condition.trim().split(/\s+/);
|
|
9138
|
+
for (const part of parts) {
|
|
9139
|
+
if (!part)
|
|
9140
|
+
continue;
|
|
9141
|
+
if (part.startsWith("+")) {
|
|
9142
|
+
const tag = part.slice(1);
|
|
9143
|
+
if (tag)
|
|
9144
|
+
required.push(tag);
|
|
9145
|
+
} else if (part.startsWith("-")) {
|
|
9146
|
+
const tag = part.slice(1);
|
|
9147
|
+
if (tag)
|
|
9148
|
+
forbidden.push(tag);
|
|
9149
|
+
} else {
|
|
9150
|
+
optional.push(part);
|
|
9151
|
+
}
|
|
9152
|
+
}
|
|
9153
|
+
return { required, forbidden, optional };
|
|
9154
|
+
}
|
|
9155
|
+
function evaluateTagCondition(condition, pageTags) {
|
|
9156
|
+
if (condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0) {
|
|
9157
|
+
return false;
|
|
9158
|
+
}
|
|
9159
|
+
const tagSet = new Set(pageTags);
|
|
9160
|
+
for (const tag of condition.required) {
|
|
9161
|
+
if (!tagSet.has(tag)) {
|
|
9162
|
+
return false;
|
|
9163
|
+
}
|
|
9164
|
+
}
|
|
9165
|
+
for (const tag of condition.forbidden) {
|
|
9166
|
+
if (tagSet.has(tag)) {
|
|
9167
|
+
return false;
|
|
9168
|
+
}
|
|
9169
|
+
}
|
|
9170
|
+
if (condition.optional.length > 0) {
|
|
9171
|
+
if (!condition.optional.some((tag) => tagSet.has(tag))) {
|
|
9172
|
+
return false;
|
|
9173
|
+
}
|
|
9174
|
+
}
|
|
9175
|
+
return true;
|
|
9176
|
+
}
|
|
9177
|
+
|
|
9178
|
+
// packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
|
|
9179
|
+
var BASE_PLACEHOLDER_OPEN = "";
|
|
9180
|
+
var BASE_PLACEHOLDER_CLOSE = "";
|
|
9181
|
+
var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
|
|
9182
|
+
var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
|
|
9183
|
+
function preprocessIftags(source, pageTags) {
|
|
9184
|
+
if (!source.includes("[["))
|
|
9185
|
+
return source;
|
|
9186
|
+
const sentinels = makeUniqueSentinels(source);
|
|
9187
|
+
const { masked, placeholders } = maskRawRegions(source, sentinels);
|
|
9188
|
+
const reduced = reduceIftags(masked, pageTags);
|
|
9189
|
+
return restorePlaceholders(reduced, placeholders, sentinels);
|
|
9190
|
+
}
|
|
9191
|
+
function makeUniqueSentinels(source) {
|
|
9192
|
+
let open = BASE_PLACEHOLDER_OPEN;
|
|
9193
|
+
let close = BASE_PLACEHOLDER_CLOSE;
|
|
9194
|
+
while (source.includes(open) || source.includes(close)) {
|
|
9195
|
+
open += BASE_PLACEHOLDER_OPEN;
|
|
9196
|
+
close += BASE_PLACEHOLDER_CLOSE;
|
|
9197
|
+
}
|
|
9198
|
+
return { open, close };
|
|
9199
|
+
}
|
|
9200
|
+
function reduceIftags(source, pageTags) {
|
|
9201
|
+
let current = source;
|
|
9202
|
+
const maxIterations = source.length + 1;
|
|
9203
|
+
const tagSet = pageTags ?? [];
|
|
9204
|
+
for (let i = 0;i < maxIterations; i++) {
|
|
9205
|
+
const depths = pageTags === null ? computeBracketDepths(current) : null;
|
|
9206
|
+
let changed = false;
|
|
9207
|
+
const next = current.replace(INNERMOST_IFTAGS_PATTERN, (match, cond, body, offset) => {
|
|
9208
|
+
if (depths !== null && depths[offset] === 0) {
|
|
9209
|
+
return match;
|
|
9210
|
+
}
|
|
9211
|
+
changed = true;
|
|
9212
|
+
const condition = parseTagCondition(cond);
|
|
9213
|
+
return evaluateTagCondition(condition, tagSet) ? body : "";
|
|
9214
|
+
});
|
|
9215
|
+
if (!changed)
|
|
9216
|
+
return current;
|
|
9217
|
+
current = next;
|
|
9218
|
+
}
|
|
9219
|
+
return current;
|
|
9220
|
+
}
|
|
9221
|
+
function computeBracketDepths(masked) {
|
|
9222
|
+
const n = masked.length;
|
|
9223
|
+
const depths = new Int32Array(n + 1);
|
|
9224
|
+
let depth = 0;
|
|
9225
|
+
let i = 0;
|
|
9226
|
+
while (i < n) {
|
|
9227
|
+
depths[i] = depth;
|
|
9228
|
+
const c = masked.charCodeAt(i);
|
|
9229
|
+
const c1 = i + 1 < n ? masked.charCodeAt(i + 1) : -1;
|
|
9230
|
+
const c2 = i + 2 < n ? masked.charCodeAt(i + 2) : -1;
|
|
9231
|
+
if (depth > 0 && c === 34 && precededByEqualsAttr(masked, i)) {
|
|
9232
|
+
const end = findQuoteEnd(masked, i + 1);
|
|
9233
|
+
for (let k = i;k <= end; k++)
|
|
9234
|
+
depths[k] = depth;
|
|
9235
|
+
i = end + 1;
|
|
9236
|
+
continue;
|
|
9237
|
+
}
|
|
9238
|
+
if (c === 91 && c1 === 91 && c2 === 91) {
|
|
9239
|
+
const end = findTripleLinkEnd(masked, i + 3);
|
|
9240
|
+
for (let k = i;k <= end; k++)
|
|
9241
|
+
depths[k] = depth;
|
|
9242
|
+
i = end + 1;
|
|
9243
|
+
continue;
|
|
9244
|
+
}
|
|
9245
|
+
if (c === 91 && c1 === 91) {
|
|
9246
|
+
depth++;
|
|
9247
|
+
depths[i + 1] = depth;
|
|
9248
|
+
i += 2;
|
|
9249
|
+
continue;
|
|
9250
|
+
}
|
|
9251
|
+
if (c === 93 && c1 === 93) {
|
|
9252
|
+
depth = Math.max(0, depth - 1);
|
|
9253
|
+
depths[i + 1] = depth;
|
|
9254
|
+
i += 2;
|
|
9255
|
+
continue;
|
|
9256
|
+
}
|
|
9257
|
+
if (c === 10) {
|
|
9258
|
+
depth = 0;
|
|
9259
|
+
}
|
|
9260
|
+
i++;
|
|
9261
|
+
}
|
|
9262
|
+
depths[n] = depth;
|
|
9263
|
+
return depths;
|
|
9264
|
+
}
|
|
9265
|
+
function precededByEqualsAttr(s, i) {
|
|
9266
|
+
let j = i - 1;
|
|
9267
|
+
while (j >= 0) {
|
|
9268
|
+
const ch = s.charCodeAt(j);
|
|
9269
|
+
if (ch === 32 || ch === 9) {
|
|
9270
|
+
j--;
|
|
9271
|
+
continue;
|
|
9272
|
+
}
|
|
9273
|
+
return ch === 61;
|
|
9274
|
+
}
|
|
9275
|
+
return false;
|
|
9276
|
+
}
|
|
9277
|
+
function findQuoteEnd(s, from) {
|
|
9278
|
+
for (let i = from;i < s.length; i++) {
|
|
9279
|
+
const ch = s.charCodeAt(i);
|
|
9280
|
+
if (ch === 34 || ch === 10)
|
|
9281
|
+
return i;
|
|
9282
|
+
}
|
|
9283
|
+
return s.length - 1;
|
|
9284
|
+
}
|
|
9285
|
+
function findTripleLinkEnd(s, from) {
|
|
9286
|
+
for (let i = from;i < s.length; i++) {
|
|
9287
|
+
if (s.charCodeAt(i) === 93 && i + 2 < s.length && s.charCodeAt(i + 1) === 93 && s.charCodeAt(i + 2) === 93) {
|
|
9288
|
+
return i + 2;
|
|
9289
|
+
}
|
|
9290
|
+
if (s.charCodeAt(i) === 10 && i + 1 < s.length && s.charCodeAt(i + 1) === 10) {
|
|
9291
|
+
return i;
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
return s.length - 1;
|
|
9295
|
+
}
|
|
9296
|
+
function maskRawRegions(source, sentinels) {
|
|
9297
|
+
const placeholders = [];
|
|
9298
|
+
let masked = "";
|
|
9299
|
+
let i = 0;
|
|
9300
|
+
while (i < source.length) {
|
|
9301
|
+
if (source[i] === "[" && source[i + 1] === "[") {
|
|
9302
|
+
RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
|
|
9303
|
+
const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
|
|
9304
|
+
if (openMatch) {
|
|
9305
|
+
const name = openMatch[1].toLowerCase();
|
|
9306
|
+
const openLen = openMatch[0].length;
|
|
9307
|
+
const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
|
|
9308
|
+
closePattern.lastIndex = i + openLen;
|
|
9309
|
+
const closeMatch = closePattern.exec(source);
|
|
9310
|
+
if (closeMatch) {
|
|
9311
|
+
const regionEnd = closeMatch.index + closeMatch[0].length;
|
|
9312
|
+
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
9313
|
+
i = regionEnd;
|
|
9314
|
+
continue;
|
|
9315
|
+
}
|
|
9316
|
+
if (name === "code") {
|
|
9317
|
+
masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
|
|
9318
|
+
i = source.length;
|
|
9319
|
+
continue;
|
|
9320
|
+
}
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
if (source[i] === "@" && source[i + 1] === "<") {
|
|
9324
|
+
const close = source.indexOf(">@", i + 2);
|
|
9325
|
+
const newline = source.indexOf(`
|
|
9326
|
+
`, i + 2);
|
|
9327
|
+
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
9328
|
+
const regionEnd = close + 2;
|
|
9329
|
+
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
9330
|
+
i = regionEnd;
|
|
9331
|
+
continue;
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9334
|
+
if (source[i] === "@" && source[i + 1] === "@") {
|
|
9335
|
+
const close = source.indexOf("@@", i + 2);
|
|
9336
|
+
const newline = source.indexOf(`
|
|
9337
|
+
`, i + 2);
|
|
9338
|
+
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
9339
|
+
const regionEnd = close + 2;
|
|
9340
|
+
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
9341
|
+
i = regionEnd;
|
|
9342
|
+
continue;
|
|
9343
|
+
}
|
|
9344
|
+
}
|
|
9345
|
+
masked += source[i];
|
|
9346
|
+
i++;
|
|
9347
|
+
}
|
|
9348
|
+
return { masked, placeholders };
|
|
9349
|
+
}
|
|
9350
|
+
function pushPlaceholder(placeholders, text, sentinels) {
|
|
9351
|
+
const idx = placeholders.length;
|
|
9352
|
+
placeholders.push(text);
|
|
9353
|
+
return `${sentinels.open}${idx}${sentinels.close}`;
|
|
9354
|
+
}
|
|
9355
|
+
function escapeRegex(str) {
|
|
9356
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9357
|
+
}
|
|
9358
|
+
function restorePlaceholders(source, placeholders, sentinels) {
|
|
9359
|
+
const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
|
|
9360
|
+
return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
|
|
9361
|
+
}
|
|
9362
|
+
|
|
9132
9363
|
// packages/parser/src/parser/parse.ts
|
|
9133
9364
|
class Parser {
|
|
9134
9365
|
ctx;
|
|
@@ -9237,7 +9468,8 @@ class Parser {
|
|
|
9237
9468
|
}
|
|
9238
9469
|
}
|
|
9239
9470
|
function parse(source, options) {
|
|
9240
|
-
const
|
|
9471
|
+
const iftagsProcessed = options?.pageTags !== undefined ? preprocessIftags(source, options.pageTags) : source;
|
|
9472
|
+
const preprocessed = preprocess(iftagsProcessed);
|
|
9241
9473
|
const tokens = tokenize(preprocessed, { trackPositions: options?.trackPositions });
|
|
9242
9474
|
return new Parser(tokens, options).parse();
|
|
9243
9475
|
}
|
|
@@ -10150,51 +10382,6 @@ function resolveAndNormalizeQuery(requirement, urlParams) {
|
|
|
10150
10382
|
const resolved = resolveQuery(requirement, urlParams);
|
|
10151
10383
|
return normalizeQuery(resolved);
|
|
10152
10384
|
}
|
|
10153
|
-
// packages/parser/src/parser/rules/block/module/iftags/condition.ts
|
|
10154
|
-
function parseTagCondition(condition) {
|
|
10155
|
-
const required = [];
|
|
10156
|
-
const forbidden = [];
|
|
10157
|
-
const optional = [];
|
|
10158
|
-
const parts = condition.trim().split(/\s+/);
|
|
10159
|
-
for (const part of parts) {
|
|
10160
|
-
if (!part)
|
|
10161
|
-
continue;
|
|
10162
|
-
if (part.startsWith("+")) {
|
|
10163
|
-
const tag = part.slice(1);
|
|
10164
|
-
if (tag)
|
|
10165
|
-
required.push(tag);
|
|
10166
|
-
} else if (part.startsWith("-")) {
|
|
10167
|
-
const tag = part.slice(1);
|
|
10168
|
-
if (tag)
|
|
10169
|
-
forbidden.push(tag);
|
|
10170
|
-
} else {
|
|
10171
|
-
optional.push(part);
|
|
10172
|
-
}
|
|
10173
|
-
}
|
|
10174
|
-
return { required, forbidden, optional };
|
|
10175
|
-
}
|
|
10176
|
-
function evaluateTagCondition(condition, pageTags) {
|
|
10177
|
-
if (condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0) {
|
|
10178
|
-
return false;
|
|
10179
|
-
}
|
|
10180
|
-
const tagSet = new Set(pageTags);
|
|
10181
|
-
for (const tag of condition.required) {
|
|
10182
|
-
if (!tagSet.has(tag)) {
|
|
10183
|
-
return false;
|
|
10184
|
-
}
|
|
10185
|
-
}
|
|
10186
|
-
for (const tag of condition.forbidden) {
|
|
10187
|
-
if (tagSet.has(tag)) {
|
|
10188
|
-
return false;
|
|
10189
|
-
}
|
|
10190
|
-
}
|
|
10191
|
-
if (condition.optional.length > 0) {
|
|
10192
|
-
if (!condition.optional.some((tag) => tagSet.has(tag))) {
|
|
10193
|
-
return false;
|
|
10194
|
-
}
|
|
10195
|
-
}
|
|
10196
|
-
return true;
|
|
10197
|
-
}
|
|
10198
10385
|
// packages/parser/src/parser/rules/block/module/iftags/resolve.ts
|
|
10199
10386
|
function isIfTagsElement(element) {
|
|
10200
10387
|
return element.element === "if-tags";
|
|
@@ -10207,110 +10394,6 @@ function resolveIfTags(data, pageTags) {
|
|
|
10207
10394
|
const matched = evaluateTagCondition(condition, pageTags);
|
|
10208
10395
|
return { evaluated: true, matched };
|
|
10209
10396
|
}
|
|
10210
|
-
// packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
|
|
10211
|
-
var BASE_PLACEHOLDER_OPEN = "";
|
|
10212
|
-
var BASE_PLACEHOLDER_CLOSE = "";
|
|
10213
|
-
var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
|
|
10214
|
-
var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
|
|
10215
|
-
function preprocessIftags(source, pageTags) {
|
|
10216
|
-
if (pageTags === null)
|
|
10217
|
-
return source;
|
|
10218
|
-
if (!source.includes("[["))
|
|
10219
|
-
return source;
|
|
10220
|
-
const sentinels = makeUniqueSentinels(source);
|
|
10221
|
-
const { masked, placeholders } = maskRawRegions(source, sentinels);
|
|
10222
|
-
const reduced = reduceIftags(masked, pageTags);
|
|
10223
|
-
return restorePlaceholders(reduced, placeholders, sentinels);
|
|
10224
|
-
}
|
|
10225
|
-
function makeUniqueSentinels(source) {
|
|
10226
|
-
let open = BASE_PLACEHOLDER_OPEN;
|
|
10227
|
-
let close = BASE_PLACEHOLDER_CLOSE;
|
|
10228
|
-
while (source.includes(open) || source.includes(close)) {
|
|
10229
|
-
open += BASE_PLACEHOLDER_OPEN;
|
|
10230
|
-
close += BASE_PLACEHOLDER_CLOSE;
|
|
10231
|
-
}
|
|
10232
|
-
return { open, close };
|
|
10233
|
-
}
|
|
10234
|
-
function reduceIftags(source, pageTags) {
|
|
10235
|
-
let current = source;
|
|
10236
|
-
const maxIterations = source.length + 1;
|
|
10237
|
-
for (let i = 0;i < maxIterations; i++) {
|
|
10238
|
-
const next = current.replace(INNERMOST_IFTAGS_PATTERN, (_, cond, body) => {
|
|
10239
|
-
const condition = parseTagCondition(cond);
|
|
10240
|
-
return evaluateTagCondition(condition, pageTags) ? body : "";
|
|
10241
|
-
});
|
|
10242
|
-
if (next === current)
|
|
10243
|
-
return current;
|
|
10244
|
-
current = next;
|
|
10245
|
-
}
|
|
10246
|
-
return current;
|
|
10247
|
-
}
|
|
10248
|
-
function maskRawRegions(source, sentinels) {
|
|
10249
|
-
const placeholders = [];
|
|
10250
|
-
let masked = "";
|
|
10251
|
-
let i = 0;
|
|
10252
|
-
while (i < source.length) {
|
|
10253
|
-
if (source[i] === "[" && source[i + 1] === "[") {
|
|
10254
|
-
RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
|
|
10255
|
-
const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
|
|
10256
|
-
if (openMatch) {
|
|
10257
|
-
const name = openMatch[1].toLowerCase();
|
|
10258
|
-
const openLen = openMatch[0].length;
|
|
10259
|
-
const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
|
|
10260
|
-
closePattern.lastIndex = i + openLen;
|
|
10261
|
-
const closeMatch = closePattern.exec(source);
|
|
10262
|
-
if (closeMatch) {
|
|
10263
|
-
const regionEnd = closeMatch.index + closeMatch[0].length;
|
|
10264
|
-
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
10265
|
-
i = regionEnd;
|
|
10266
|
-
continue;
|
|
10267
|
-
}
|
|
10268
|
-
if (name === "code") {
|
|
10269
|
-
masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
|
|
10270
|
-
i = source.length;
|
|
10271
|
-
continue;
|
|
10272
|
-
}
|
|
10273
|
-
}
|
|
10274
|
-
}
|
|
10275
|
-
if (source[i] === "@" && source[i + 1] === "<") {
|
|
10276
|
-
const close = source.indexOf(">@", i + 2);
|
|
10277
|
-
const newline = source.indexOf(`
|
|
10278
|
-
`, i + 2);
|
|
10279
|
-
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
10280
|
-
const regionEnd = close + 2;
|
|
10281
|
-
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
10282
|
-
i = regionEnd;
|
|
10283
|
-
continue;
|
|
10284
|
-
}
|
|
10285
|
-
}
|
|
10286
|
-
if (source[i] === "@" && source[i + 1] === "@") {
|
|
10287
|
-
const close = source.indexOf("@@", i + 2);
|
|
10288
|
-
const newline = source.indexOf(`
|
|
10289
|
-
`, i + 2);
|
|
10290
|
-
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
10291
|
-
const regionEnd = close + 2;
|
|
10292
|
-
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
10293
|
-
i = regionEnd;
|
|
10294
|
-
continue;
|
|
10295
|
-
}
|
|
10296
|
-
}
|
|
10297
|
-
masked += source[i];
|
|
10298
|
-
i++;
|
|
10299
|
-
}
|
|
10300
|
-
return { masked, placeholders };
|
|
10301
|
-
}
|
|
10302
|
-
function pushPlaceholder(placeholders, text, sentinels) {
|
|
10303
|
-
const idx = placeholders.length;
|
|
10304
|
-
placeholders.push(text);
|
|
10305
|
-
return `${sentinels.open}${idx}${sentinels.close}`;
|
|
10306
|
-
}
|
|
10307
|
-
function escapeRegex(str) {
|
|
10308
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10309
|
-
}
|
|
10310
|
-
function restorePlaceholders(source, placeholders, sentinels) {
|
|
10311
|
-
const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
|
|
10312
|
-
return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
|
|
10313
|
-
}
|
|
10314
10397
|
// packages/parser/src/parser/rules/block/module/include/resolve.ts
|
|
10315
10398
|
function resolveIncludes(source, fetcher, options) {
|
|
10316
10399
|
if (options?.settings && !options.settings.enablePageSyntax) {
|
|
@@ -10363,8 +10446,9 @@ function isRestOfLineBlank(source, pos) {
|
|
|
10363
10446
|
if (ch === `
|
|
10364
10447
|
`)
|
|
10365
10448
|
return true;
|
|
10366
|
-
if (ch
|
|
10367
|
-
|
|
10449
|
+
if (ch === " " || ch === "\t" || ch === "\r" || ch === "]")
|
|
10450
|
+
continue;
|
|
10451
|
+
return false;
|
|
10368
10452
|
}
|
|
10369
10453
|
return true;
|
|
10370
10454
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -169,6 +169,25 @@ interface ParserOptions {
|
|
|
169
169
|
* Defaults to {@link DEFAULT_SETTINGS} (full page mode).
|
|
170
170
|
*/
|
|
171
171
|
settings?: WikitextSettings;
|
|
172
|
+
/**
|
|
173
|
+
* Page tags consulted when expanding `[[iftags]]` directives that are
|
|
174
|
+
* embedded inside another block's opener — e.g.
|
|
175
|
+
* `[[div_ class="x" [[iftags +foo]]style="..."[[/iftags]]]]`. Such
|
|
176
|
+
* iftags must be collapsed at text level *before* tokenization;
|
|
177
|
+
* otherwise the outer opener loses its well-formed structure and the
|
|
178
|
+
* whole `[[div_ ... ]]` line is emitted as raw text.
|
|
179
|
+
*
|
|
180
|
+
* Values:
|
|
181
|
+
* - omitted / `undefined`: no preprocess pass. Existing behaviour;
|
|
182
|
+
* opener-embedded iftags will still break the surrounding opener.
|
|
183
|
+
* - `null`: opener-embedded iftags only, evaluated as if the page has
|
|
184
|
+
* no tags (lossy fallback that keeps tokenization working when real
|
|
185
|
+
* tags are unknown). Block-level iftags remain in the AST for
|
|
186
|
+
* `resolveModules` to evaluate later via `getPageTags`.
|
|
187
|
+
* - `string[]`: every iftags block is evaluated against the given
|
|
188
|
+
* tags eagerly; no `if-tags` nodes survive in the AST.
|
|
189
|
+
*/
|
|
190
|
+
pageTags?: string[] | null;
|
|
172
191
|
}
|
|
173
192
|
/**
|
|
174
193
|
* Converts a token stream into a Wikidot {@link SyntaxTree}.
|
|
@@ -861,19 +880,19 @@ declare function normalizeQuery(query: ListPagesQuery): NormalizedListPagesQuery
|
|
|
861
880
|
* Expand `[[iftags ...]]X[[/iftags]]` directives in `source` against the
|
|
862
881
|
* current page's tags.
|
|
863
882
|
*
|
|
864
|
-
* Returns `source` unchanged when `pageTags` is `null`, which signals
|
|
865
|
-
* that the caller could not resolve tag membership (e.g. rendering a
|
|
866
|
-
* draft preview without a real page). In that case the AST-level
|
|
867
|
-
* resolver remains responsible for evaluation later.
|
|
868
|
-
*
|
|
869
883
|
* Behaviour:
|
|
870
884
|
* - Raw regions (`[[code]]`, `[[html]]`, `@@...@@`, `@<...>@`) are
|
|
871
885
|
* protected: literal `[[iftags]]` tokens inside them are not expanded.
|
|
872
886
|
* - Nested `[[iftags]]` are processed innermost-first, so an outer
|
|
873
887
|
* block can re-process the now-flattened inner body uniformly.
|
|
888
|
+
* - `pageTags === null`: only `[[iftags]]` blocks embedded inside
|
|
889
|
+
* another block's opener are collapsed (using an empty-tag fallback
|
|
890
|
+
* so `+tag` conditions fail and `-tag` conditions pass). Block-level
|
|
891
|
+
* iftags are left intact for the AST-level resolver.
|
|
874
892
|
*
|
|
875
893
|
* @param source Raw wikitext (typically after include expansion).
|
|
876
|
-
* @param pageTags Tags of the page being rendered, or `null`
|
|
894
|
+
* @param pageTags Tags of the page being rendered, or `null` for the
|
|
895
|
+
* opener-embedded-only fallback mode.
|
|
877
896
|
* @returns Source with matching iftags replaced by their bodies and
|
|
878
897
|
* unmatched iftags removed entirely.
|
|
879
898
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -169,6 +169,25 @@ interface ParserOptions {
|
|
|
169
169
|
* Defaults to {@link DEFAULT_SETTINGS} (full page mode).
|
|
170
170
|
*/
|
|
171
171
|
settings?: WikitextSettings;
|
|
172
|
+
/**
|
|
173
|
+
* Page tags consulted when expanding `[[iftags]]` directives that are
|
|
174
|
+
* embedded inside another block's opener — e.g.
|
|
175
|
+
* `[[div_ class="x" [[iftags +foo]]style="..."[[/iftags]]]]`. Such
|
|
176
|
+
* iftags must be collapsed at text level *before* tokenization;
|
|
177
|
+
* otherwise the outer opener loses its well-formed structure and the
|
|
178
|
+
* whole `[[div_ ... ]]` line is emitted as raw text.
|
|
179
|
+
*
|
|
180
|
+
* Values:
|
|
181
|
+
* - omitted / `undefined`: no preprocess pass. Existing behaviour;
|
|
182
|
+
* opener-embedded iftags will still break the surrounding opener.
|
|
183
|
+
* - `null`: opener-embedded iftags only, evaluated as if the page has
|
|
184
|
+
* no tags (lossy fallback that keeps tokenization working when real
|
|
185
|
+
* tags are unknown). Block-level iftags remain in the AST for
|
|
186
|
+
* `resolveModules` to evaluate later via `getPageTags`.
|
|
187
|
+
* - `string[]`: every iftags block is evaluated against the given
|
|
188
|
+
* tags eagerly; no `if-tags` nodes survive in the AST.
|
|
189
|
+
*/
|
|
190
|
+
pageTags?: string[] | null;
|
|
172
191
|
}
|
|
173
192
|
/**
|
|
174
193
|
* Converts a token stream into a Wikidot {@link SyntaxTree}.
|
|
@@ -861,19 +880,19 @@ declare function normalizeQuery(query: ListPagesQuery): NormalizedListPagesQuery
|
|
|
861
880
|
* Expand `[[iftags ...]]X[[/iftags]]` directives in `source` against the
|
|
862
881
|
* current page's tags.
|
|
863
882
|
*
|
|
864
|
-
* Returns `source` unchanged when `pageTags` is `null`, which signals
|
|
865
|
-
* that the caller could not resolve tag membership (e.g. rendering a
|
|
866
|
-
* draft preview without a real page). In that case the AST-level
|
|
867
|
-
* resolver remains responsible for evaluation later.
|
|
868
|
-
*
|
|
869
883
|
* Behaviour:
|
|
870
884
|
* - Raw regions (`[[code]]`, `[[html]]`, `@@...@@`, `@<...>@`) are
|
|
871
885
|
* protected: literal `[[iftags]]` tokens inside them are not expanded.
|
|
872
886
|
* - Nested `[[iftags]]` are processed innermost-first, so an outer
|
|
873
887
|
* block can re-process the now-flattened inner body uniformly.
|
|
888
|
+
* - `pageTags === null`: only `[[iftags]]` blocks embedded inside
|
|
889
|
+
* another block's opener are collapsed (using an empty-tag fallback
|
|
890
|
+
* so `+tag` conditions fail and `-tag` conditions pass). Block-level
|
|
891
|
+
* iftags are left intact for the AST-level resolver.
|
|
874
892
|
*
|
|
875
893
|
* @param source Raw wikitext (typically after include expansion).
|
|
876
|
-
* @param pageTags Tags of the page being rendered, or `null`
|
|
894
|
+
* @param pageTags Tags of the page being rendered, or `null` for the
|
|
895
|
+
* opener-embedded-only fallback mode.
|
|
877
896
|
* @returns Source with matching iftags replaced by their bodies and
|
|
878
897
|
* unmatched iftags removed entirely.
|
|
879
898
|
*/
|
package/dist/index.js
CHANGED
|
@@ -9072,6 +9072,237 @@ function mapElementChildrenWithState(element, state, transform) {
|
|
|
9072
9072
|
return { element, state };
|
|
9073
9073
|
}
|
|
9074
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
|
+
|
|
9075
9306
|
// packages/parser/src/parser/parse.ts
|
|
9076
9307
|
class Parser {
|
|
9077
9308
|
ctx;
|
|
@@ -9180,7 +9411,8 @@ class Parser {
|
|
|
9180
9411
|
}
|
|
9181
9412
|
}
|
|
9182
9413
|
function parse(source, options) {
|
|
9183
|
-
const
|
|
9414
|
+
const iftagsProcessed = options?.pageTags !== undefined ? preprocessIftags(source, options.pageTags) : source;
|
|
9415
|
+
const preprocessed = preprocess(iftagsProcessed);
|
|
9184
9416
|
const tokens = tokenize(preprocessed, { trackPositions: options?.trackPositions });
|
|
9185
9417
|
return new Parser(tokens, options).parse();
|
|
9186
9418
|
}
|
|
@@ -10093,51 +10325,6 @@ function resolveAndNormalizeQuery(requirement, urlParams) {
|
|
|
10093
10325
|
const resolved = resolveQuery(requirement, urlParams);
|
|
10094
10326
|
return normalizeQuery(resolved);
|
|
10095
10327
|
}
|
|
10096
|
-
// packages/parser/src/parser/rules/block/module/iftags/condition.ts
|
|
10097
|
-
function parseTagCondition(condition) {
|
|
10098
|
-
const required = [];
|
|
10099
|
-
const forbidden = [];
|
|
10100
|
-
const optional = [];
|
|
10101
|
-
const parts = condition.trim().split(/\s+/);
|
|
10102
|
-
for (const part of parts) {
|
|
10103
|
-
if (!part)
|
|
10104
|
-
continue;
|
|
10105
|
-
if (part.startsWith("+")) {
|
|
10106
|
-
const tag = part.slice(1);
|
|
10107
|
-
if (tag)
|
|
10108
|
-
required.push(tag);
|
|
10109
|
-
} else if (part.startsWith("-")) {
|
|
10110
|
-
const tag = part.slice(1);
|
|
10111
|
-
if (tag)
|
|
10112
|
-
forbidden.push(tag);
|
|
10113
|
-
} else {
|
|
10114
|
-
optional.push(part);
|
|
10115
|
-
}
|
|
10116
|
-
}
|
|
10117
|
-
return { required, forbidden, optional };
|
|
10118
|
-
}
|
|
10119
|
-
function evaluateTagCondition(condition, pageTags) {
|
|
10120
|
-
if (condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0) {
|
|
10121
|
-
return false;
|
|
10122
|
-
}
|
|
10123
|
-
const tagSet = new Set(pageTags);
|
|
10124
|
-
for (const tag of condition.required) {
|
|
10125
|
-
if (!tagSet.has(tag)) {
|
|
10126
|
-
return false;
|
|
10127
|
-
}
|
|
10128
|
-
}
|
|
10129
|
-
for (const tag of condition.forbidden) {
|
|
10130
|
-
if (tagSet.has(tag)) {
|
|
10131
|
-
return false;
|
|
10132
|
-
}
|
|
10133
|
-
}
|
|
10134
|
-
if (condition.optional.length > 0) {
|
|
10135
|
-
if (!condition.optional.some((tag) => tagSet.has(tag))) {
|
|
10136
|
-
return false;
|
|
10137
|
-
}
|
|
10138
|
-
}
|
|
10139
|
-
return true;
|
|
10140
|
-
}
|
|
10141
10328
|
// packages/parser/src/parser/rules/block/module/iftags/resolve.ts
|
|
10142
10329
|
function isIfTagsElement(element) {
|
|
10143
10330
|
return element.element === "if-tags";
|
|
@@ -10150,110 +10337,6 @@ function resolveIfTags(data, pageTags) {
|
|
|
10150
10337
|
const matched = evaluateTagCondition(condition, pageTags);
|
|
10151
10338
|
return { evaluated: true, matched };
|
|
10152
10339
|
}
|
|
10153
|
-
// packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
|
|
10154
|
-
var BASE_PLACEHOLDER_OPEN = "";
|
|
10155
|
-
var BASE_PLACEHOLDER_CLOSE = "";
|
|
10156
|
-
var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
|
|
10157
|
-
var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
|
|
10158
|
-
function preprocessIftags(source, pageTags) {
|
|
10159
|
-
if (pageTags === null)
|
|
10160
|
-
return source;
|
|
10161
|
-
if (!source.includes("[["))
|
|
10162
|
-
return source;
|
|
10163
|
-
const sentinels = makeUniqueSentinels(source);
|
|
10164
|
-
const { masked, placeholders } = maskRawRegions(source, sentinels);
|
|
10165
|
-
const reduced = reduceIftags(masked, pageTags);
|
|
10166
|
-
return restorePlaceholders(reduced, placeholders, sentinels);
|
|
10167
|
-
}
|
|
10168
|
-
function makeUniqueSentinels(source) {
|
|
10169
|
-
let open = BASE_PLACEHOLDER_OPEN;
|
|
10170
|
-
let close = BASE_PLACEHOLDER_CLOSE;
|
|
10171
|
-
while (source.includes(open) || source.includes(close)) {
|
|
10172
|
-
open += BASE_PLACEHOLDER_OPEN;
|
|
10173
|
-
close += BASE_PLACEHOLDER_CLOSE;
|
|
10174
|
-
}
|
|
10175
|
-
return { open, close };
|
|
10176
|
-
}
|
|
10177
|
-
function reduceIftags(source, pageTags) {
|
|
10178
|
-
let current = source;
|
|
10179
|
-
const maxIterations = source.length + 1;
|
|
10180
|
-
for (let i = 0;i < maxIterations; i++) {
|
|
10181
|
-
const next = current.replace(INNERMOST_IFTAGS_PATTERN, (_, cond, body) => {
|
|
10182
|
-
const condition = parseTagCondition(cond);
|
|
10183
|
-
return evaluateTagCondition(condition, pageTags) ? body : "";
|
|
10184
|
-
});
|
|
10185
|
-
if (next === current)
|
|
10186
|
-
return current;
|
|
10187
|
-
current = next;
|
|
10188
|
-
}
|
|
10189
|
-
return current;
|
|
10190
|
-
}
|
|
10191
|
-
function maskRawRegions(source, sentinels) {
|
|
10192
|
-
const placeholders = [];
|
|
10193
|
-
let masked = "";
|
|
10194
|
-
let i = 0;
|
|
10195
|
-
while (i < source.length) {
|
|
10196
|
-
if (source[i] === "[" && source[i + 1] === "[") {
|
|
10197
|
-
RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
|
|
10198
|
-
const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
|
|
10199
|
-
if (openMatch) {
|
|
10200
|
-
const name = openMatch[1].toLowerCase();
|
|
10201
|
-
const openLen = openMatch[0].length;
|
|
10202
|
-
const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
|
|
10203
|
-
closePattern.lastIndex = i + openLen;
|
|
10204
|
-
const closeMatch = closePattern.exec(source);
|
|
10205
|
-
if (closeMatch) {
|
|
10206
|
-
const regionEnd = closeMatch.index + closeMatch[0].length;
|
|
10207
|
-
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
10208
|
-
i = regionEnd;
|
|
10209
|
-
continue;
|
|
10210
|
-
}
|
|
10211
|
-
if (name === "code") {
|
|
10212
|
-
masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
|
|
10213
|
-
i = source.length;
|
|
10214
|
-
continue;
|
|
10215
|
-
}
|
|
10216
|
-
}
|
|
10217
|
-
}
|
|
10218
|
-
if (source[i] === "@" && source[i + 1] === "<") {
|
|
10219
|
-
const close = source.indexOf(">@", i + 2);
|
|
10220
|
-
const newline = source.indexOf(`
|
|
10221
|
-
`, i + 2);
|
|
10222
|
-
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
10223
|
-
const regionEnd = close + 2;
|
|
10224
|
-
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
10225
|
-
i = regionEnd;
|
|
10226
|
-
continue;
|
|
10227
|
-
}
|
|
10228
|
-
}
|
|
10229
|
-
if (source[i] === "@" && source[i + 1] === "@") {
|
|
10230
|
-
const close = source.indexOf("@@", i + 2);
|
|
10231
|
-
const newline = source.indexOf(`
|
|
10232
|
-
`, i + 2);
|
|
10233
|
-
if (close !== -1 && (newline === -1 || close < newline)) {
|
|
10234
|
-
const regionEnd = close + 2;
|
|
10235
|
-
masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
|
|
10236
|
-
i = regionEnd;
|
|
10237
|
-
continue;
|
|
10238
|
-
}
|
|
10239
|
-
}
|
|
10240
|
-
masked += source[i];
|
|
10241
|
-
i++;
|
|
10242
|
-
}
|
|
10243
|
-
return { masked, placeholders };
|
|
10244
|
-
}
|
|
10245
|
-
function pushPlaceholder(placeholders, text, sentinels) {
|
|
10246
|
-
const idx = placeholders.length;
|
|
10247
|
-
placeholders.push(text);
|
|
10248
|
-
return `${sentinels.open}${idx}${sentinels.close}`;
|
|
10249
|
-
}
|
|
10250
|
-
function escapeRegex(str) {
|
|
10251
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10252
|
-
}
|
|
10253
|
-
function restorePlaceholders(source, placeholders, sentinels) {
|
|
10254
|
-
const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
|
|
10255
|
-
return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
|
|
10256
|
-
}
|
|
10257
10340
|
// packages/parser/src/parser/rules/block/module/include/resolve.ts
|
|
10258
10341
|
function resolveIncludes(source, fetcher, options) {
|
|
10259
10342
|
if (options?.settings && !options.settings.enablePageSyntax) {
|
|
@@ -10306,8 +10389,9 @@ function isRestOfLineBlank(source, pos) {
|
|
|
10306
10389
|
if (ch === `
|
|
10307
10390
|
`)
|
|
10308
10391
|
return true;
|
|
10309
|
-
if (ch
|
|
10310
|
-
|
|
10392
|
+
if (ch === " " || ch === "\t" || ch === "\r" || ch === "]")
|
|
10393
|
+
continue;
|
|
10394
|
+
return false;
|
|
10311
10395
|
}
|
|
10312
10396
|
return true;
|
|
10313
10397
|
}
|