@wdprlib/parser 5.3.0 → 6.0.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 +2429 -1199
- package/dist/index.d.cts +95 -8
- package/dist/index.d.ts +95 -8
- package/dist/index.js +2385 -1152
- package/package.json +3 -2
- package/src/build-info.generated.ts +2 -2
- package/src/index.ts +3 -0
- package/src/lexer/lexer.ts +34 -5
- package/src/lexer/quoted-string.ts +4 -4
- package/src/lexer/syntax-actions.ts +6 -1
- package/src/lexer/text-actions.ts +15 -1
- package/src/lexer/url-schemes.ts +78 -0
- package/src/parser/constants.ts +4 -0
- package/src/parser/parse/block.ts +1 -1
- package/src/parser/postprocess/divAdjacentParagraph.ts +23 -21
- package/src/parser/postprocess/spanStrip/merge.ts +8 -2
- package/src/parser/preprocess/typography.ts +25 -3
- package/src/parser/preprocess/utils/raw-regions.ts +59 -14
- package/src/parser/preprocess/whitespace/index.ts +8 -1
- package/src/parser/rules/block/bibliography/entry-content.ts +1 -1
- package/src/parser/rules/block/block-list/bare-content.ts +3 -1
- package/src/parser/rules/block/block-list/bare-paragraph.ts +7 -2
- package/src/parser/rules/block/block-list/item-content.ts +7 -3
- package/src/parser/rules/block/block-list/li-content.ts +8 -3
- package/src/parser/rules/block/block-list/li-item.ts +1 -1
- package/src/parser/rules/block/blockquote/build.ts +1 -1
- package/src/parser/rules/block/code/boundary.ts +76 -0
- package/src/parser/rules/block/code/content.ts +11 -40
- package/src/parser/rules/block/code/index.ts +8 -31
- package/src/parser/rules/block/code/open.ts +46 -0
- package/src/parser/rules/block/definition-list/item-key.ts +1 -1
- package/src/parser/rules/block/definition-list/item-value.ts +1 -1
- package/src/parser/rules/block/div/failed.ts +2 -0
- package/src/parser/rules/block/index.ts +3 -0
- package/src/parser/rules/block/list/line.ts +6 -3
- package/src/parser/rules/block/math/boundary.ts +104 -0
- package/src/parser/rules/block/math/index.ts +17 -57
- package/src/parser/rules/block/module/index.ts +3 -2
- package/src/parser/rules/block/module/listpages/extraction/listpages.ts +5 -0
- package/src/parser/rules/block/module/listpages/extraction/query.ts +1 -0
- package/src/parser/rules/block/module/listpages/extraction/template.ts +16 -0
- package/src/parser/rules/block/module/listpages/extraction/variables.ts +1 -0
- package/src/parser/rules/block/module/listpages/index.ts +1 -0
- package/src/parser/rules/block/module/listpages/normalization/order-parent.ts +9 -1
- package/src/parser/rules/block/module/listpages/normalize.ts +1 -0
- package/src/parser/rules/block/module/listpages/parser.ts +1 -0
- package/src/parser/rules/block/module/listpages/resolve.ts +4 -2
- package/src/parser/rules/block/module/listpages/template/excerpt.ts +63 -0
- package/src/parser/rules/block/module/listpages/template/format/content.ts +0 -32
- package/src/parser/rules/block/module/listpages/template/format/index.ts +1 -1
- package/src/parser/rules/block/module/listpages/template/getters/index.ts +14 -0
- package/src/parser/rules/block/module/listpages/template/getters/parameterized.ts +3 -1
- package/src/parser/rules/block/module/listpages/template/getters/registered.ts +52 -0
- package/src/parser/rules/block/module/listpages/template/getters/simple.ts +15 -5
- package/src/parser/rules/block/module/listpages/template/literal.ts +6 -0
- package/src/parser/rules/block/module/listpages/template/syntax.ts +10 -1
- package/src/parser/rules/block/module/listpages/types/data-requirements.ts +6 -0
- package/src/parser/rules/block/module/listpages/types/external-data.ts +36 -2
- package/src/parser/rules/block/module/listpages/types/normalized-query.ts +14 -4
- package/src/parser/rules/block/module/listpages/types/query.ts +2 -0
- package/src/parser/rules/block/module/listpages/types/variables.ts +5 -0
- package/src/parser/rules/block/module/listpages/url-resolution/fields.ts +2 -0
- package/src/parser/rules/block/module/listusers/resolve.ts +2 -1
- package/src/parser/rules/block/module/mapping.ts +2 -1
- package/src/parser/rules/block/module/rate/index.ts +16 -19
- package/src/parser/rules/block/module/rate/resolve.ts +108 -0
- package/src/parser/rules/block/module/rate/types.ts +6 -16
- package/src/parser/rules/block/module/resolution/resolve-async.ts +2 -1
- package/src/parser/rules/block/module/resolve.ts +10 -8
- package/src/parser/rules/block/module/types-common.ts +4 -0
- package/src/parser/rules/block/note/boundary.ts +88 -0
- package/src/parser/rules/block/note/index.ts +37 -0
- package/src/parser/rules/block/paragraph/index.ts +12 -4
- package/src/parser/rules/block/paragraph/normalize.ts +6 -1
- package/src/parser/rules/block/parsing/block-item.ts +1 -1
- package/src/parser/rules/block/parsing/content.ts +1 -1
- package/src/parser/rules/block/parsing/inline-content.ts +6 -3
- package/src/parser/rules/block/table/pipe/cell.ts +3 -1
- package/src/parser/rules/block/table-block/cell-content/index.ts +1 -1
- package/src/parser/rules/block/table-block/cell-content/segments.ts +5 -2
- package/src/parser/rules/block/table-block/cell-newline.ts +2 -1
- package/src/parser/rules/contracts/rule.ts +4 -2
- package/src/parser/rules/contracts/scope.ts +2 -0
- package/src/parser/rules/inline/anchor/child.ts +8 -2
- package/src/parser/rules/inline/anchor/content.ts +3 -1
- package/src/parser/rules/inline/anchor/index.ts +4 -1
- package/src/parser/rules/inline/anchor/newline.ts +2 -1
- package/src/parser/rules/inline/autolink.ts +153 -0
- package/src/parser/rules/inline/button/attributes.ts +17 -0
- package/src/parser/rules/inline/button/index.ts +17 -0
- package/src/parser/rules/inline/button/syntax.ts +56 -0
- package/src/parser/rules/inline/date/index.ts +17 -0
- package/src/parser/rules/inline/date/syntax.ts +46 -0
- package/src/parser/rules/inline/email/candidates.ts +134 -0
- package/src/parser/rules/inline/email/index.ts +36 -0
- package/src/parser/rules/inline/email/scan.ts +76 -0
- package/src/parser/rules/inline/expr/branch.ts +3 -1
- package/src/parser/rules/inline/footnote/boundary.ts +56 -0
- package/src/parser/rules/inline/footnote/content.ts +29 -41
- package/src/parser/rules/inline/footnote/elements.ts +9 -34
- package/src/parser/rules/inline/footnote/index.ts +4 -1
- package/src/parser/rules/inline/formatting/close.ts +12 -0
- package/src/parser/rules/inline/index.ts +14 -0
- package/src/parser/rules/inline/line-break/newline.ts +8 -1
- package/src/parser/rules/inline/link-bracket/direct-url.ts +11 -3
- package/src/parser/rules/inline/link-bracket/parsed.ts +9 -4
- package/src/parser/rules/inline/link-bracket/parts.ts +14 -36
- package/src/parser/rules/inline/link-bracket/special-target.ts +9 -0
- package/src/parser/rules/inline/link-single.ts +9 -7
- package/src/parser/rules/inline/link-triple/index.ts +1 -0
- package/src/parser/rules/inline/link-triple/label.ts +7 -1
- package/src/parser/rules/inline/parsing/automatic-line-break.ts +35 -0
- package/src/parser/rules/inline/parsing/block-boundary.ts +2 -0
- package/src/parser/rules/inline/parsing/block-start-predicates.ts +10 -0
- package/src/parser/rules/inline/parsing/inline-content.ts +46 -7
- package/src/parser/rules/inline/parsing/plain-text.ts +7 -2
- package/src/parser/rules/inline/parsing/preserved-line-break.ts +13 -0
- package/src/parser/rules/inline/parsing/raw-tag.ts +19 -0
- package/src/parser/rules/inline/raw/angle.ts +2 -1
- package/src/parser/rules/inline/raw/end.ts +21 -1
- package/src/parser/rules/inline/size/content.ts +32 -5
- package/src/parser/rules/inline/size/value.ts +11 -0
- package/src/parser/rules/inline/social/index.ts +17 -0
- package/src/parser/rules/inline/social/syntax.ts +40 -0
- package/src/parser/rules/inline/span/content.ts +3 -1
- package/src/parser/rules/inline/span/newline.ts +2 -1
- package/src/parser/rules/opaque-probe.ts +58 -0
- package/src/pipeline/process.ts +13 -0
- package/src/pipeline/types.ts +19 -0
- package/src/parser/rules/block/math/content.ts +0 -54
- package/src/parser/rules/block/math/name.ts +0 -35
- package/src/parser/rules/inline/footnote/child.ts +0 -22
- package/src/parser/rules/inline/footnote/newline.ts +0 -27
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { getEmailCandidate } from "../email/candidates";
|
|
1
2
|
import type { TokenType } from "../../../../lexer";
|
|
3
|
+
import { URL_SCHEME_NAMES } from "../../../../lexer/url-schemes";
|
|
2
4
|
import type { ParseContext } from "../../types";
|
|
3
5
|
|
|
4
6
|
const MIN_INLINE_TEXT_RUN_LENGTH = 32;
|
|
@@ -18,6 +20,7 @@ export function collectLongPlainTextRun(
|
|
|
18
20
|
): PlainTextRun | null {
|
|
19
21
|
const firstToken = ctx.tokens[startPos];
|
|
20
22
|
if (
|
|
23
|
+
!getEmailCandidate(ctx.tokens, startPos) &&
|
|
21
24
|
firstToken?.type === "TEXT" &&
|
|
22
25
|
firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH &&
|
|
23
26
|
firstToken.value !== "("
|
|
@@ -50,10 +53,12 @@ export function collectLongPlainTextRun(
|
|
|
50
53
|
|
|
51
54
|
function isPlainTextRunToken(ctx: ParseContext, pos: number): boolean {
|
|
52
55
|
const token = ctx.tokens[pos];
|
|
53
|
-
if (!token) return false;
|
|
56
|
+
if (!token || getEmailCandidate(ctx.tokens, pos)) return false;
|
|
54
57
|
|
|
55
58
|
if (token.type === "IDENTIFIER") {
|
|
56
|
-
|
|
59
|
+
// 連続平文の一括テキスト化がURL先頭のスキーム名を取り込むと
|
|
60
|
+
// autolinkルールに到達しなくなるため、スキーム名の手前で止める
|
|
61
|
+
return !(URL_SCHEME_NAMES.has(token.value) && ctx.tokens[pos + 1]?.type === "COLON");
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
if (token.type === "WHITESPACE") {
|
|
@@ -2,6 +2,7 @@ import type { Element } from "@wdprlib/ast";
|
|
|
2
2
|
|
|
3
3
|
type PreservedLineBreak = Extract<Element, { element: "line-break" }> & {
|
|
4
4
|
_preservedTrailingBreak?: boolean;
|
|
5
|
+
_preservedLeadingBreak?: boolean;
|
|
5
6
|
};
|
|
6
7
|
|
|
7
8
|
export function createPreservedTrailingLineBreak(): Element {
|
|
@@ -9,3 +10,15 @@ export function createPreservedTrailingLineBreak(): Element {
|
|
|
9
10
|
lineBreak._preservedTrailingBreak = true;
|
|
10
11
|
return lineBreak;
|
|
11
12
|
}
|
|
13
|
+
|
|
14
|
+
export function createPreservedLeadingLineBreak(): Element {
|
|
15
|
+
const lineBreak: PreservedLineBreak = { element: "line-break", _preservedLeadingBreak: true };
|
|
16
|
+
return lineBreak;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isPreservedLeadingLineBreak(element: Element | undefined): boolean {
|
|
20
|
+
return (
|
|
21
|
+
element?.element === "line-break" &&
|
|
22
|
+
(element as PreservedLineBreak)._preservedLeadingBreak === true
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Token } from "../../../../lexer";
|
|
2
|
+
|
|
3
|
+
const unclosedRanges = new WeakMap<readonly Token[], { start: number; end: number }>();
|
|
4
|
+
|
|
5
|
+
/** Raw directives end at the first ]], even inside a quoted value. */
|
|
6
|
+
export function findRawTagClose(
|
|
7
|
+
tokens: readonly Token[],
|
|
8
|
+
start: number,
|
|
9
|
+
end: number,
|
|
10
|
+
): number | null {
|
|
11
|
+
const unclosed = unclosedRanges.get(tokens);
|
|
12
|
+
if (unclosed && start >= unclosed.start && end <= unclosed.end) return null;
|
|
13
|
+
for (let close = start; close < end; close++) {
|
|
14
|
+
if (tokens[close]?.type === "BLOCK_CLOSE") return close;
|
|
15
|
+
if (!tokens[close] || tokens[close]?.type === "EOF") break;
|
|
16
|
+
}
|
|
17
|
+
unclosedRanges.set(tokens, { start, end });
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Element } from "@wdprlib/ast";
|
|
2
|
+
import { decodeHTML } from "entities";
|
|
2
3
|
import type { ParseContext, RuleResult } from "../../types";
|
|
3
4
|
import { currentToken, hasClosingMarkerBeforeNewline } from "../../types";
|
|
4
5
|
import { rawElement, textElement } from "./result";
|
|
@@ -36,5 +37,5 @@ export function parseAngleRaw(ctx: ParseContext): RuleResult<Element> {
|
|
|
36
37
|
consumed++;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
return rawElement(value, consumed);
|
|
40
|
+
return rawElement(decodeHTML(value), consumed);
|
|
40
41
|
}
|
|
@@ -1,15 +1,35 @@
|
|
|
1
1
|
import type { Token } from "../../../../lexer";
|
|
2
2
|
|
|
3
|
+
// A failed angle-raw search also rules out later openers within the same immutable token range.
|
|
4
|
+
const unclosedAngleRanges = new WeakMap<
|
|
5
|
+
readonly Token[],
|
|
6
|
+
{ start: number; end: number; lineEnd: boolean }
|
|
7
|
+
>();
|
|
8
|
+
|
|
3
9
|
/** Exclusive end of a complete single-line raw region, or the original position. */
|
|
4
10
|
export function rawRegionEnd(tokens: readonly Token[], start: number, end: number): number {
|
|
5
11
|
const type = tokens[start]?.type;
|
|
6
12
|
const close =
|
|
7
13
|
type === "RAW_OPEN" ? "RAW_OPEN" : type === "RAW_BLOCK_OPEN" ? "RAW_BLOCK_CLOSE" : null;
|
|
8
14
|
if (!close) return start;
|
|
15
|
+
const cached = type === "RAW_BLOCK_OPEN" ? unclosedAngleRanges.get(tokens) : undefined;
|
|
16
|
+
if (
|
|
17
|
+
cached &&
|
|
18
|
+
start >= cached.start &&
|
|
19
|
+
start < cached.end &&
|
|
20
|
+
(cached.lineEnd || end <= cached.end)
|
|
21
|
+
) {
|
|
22
|
+
return start;
|
|
23
|
+
}
|
|
9
24
|
for (let pos = start + 1; pos < end; pos++) {
|
|
10
|
-
if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF")
|
|
25
|
+
if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF") {
|
|
26
|
+
if (type === "RAW_BLOCK_OPEN")
|
|
27
|
+
unclosedAngleRanges.set(tokens, { start, end: pos, lineEnd: true });
|
|
28
|
+
return start;
|
|
29
|
+
}
|
|
11
30
|
if (tokens[pos]?.type === close) return pos + 1;
|
|
12
31
|
}
|
|
32
|
+
if (type === "RAW_BLOCK_OPEN") unclosedAngleRanges.set(tokens, { start, end, lineEnd: false });
|
|
13
33
|
return start;
|
|
14
34
|
}
|
|
15
35
|
|
|
@@ -2,6 +2,8 @@ import type { Element } from "@wdprlib/ast";
|
|
|
2
2
|
import type { ParseContext } from "../../types";
|
|
3
3
|
import { parseBlockName } from "../../common";
|
|
4
4
|
import { parseInlineUntil } from "../utils";
|
|
5
|
+
import { protectedInlineRegionEnd } from "../raw/end";
|
|
6
|
+
import { parseSizeOpen } from "./open";
|
|
5
7
|
|
|
6
8
|
export interface SizeContentResult {
|
|
7
9
|
children: Element[];
|
|
@@ -13,8 +15,9 @@ export function parseSizeContent(ctx: ParseContext, startPos: number): SizeConte
|
|
|
13
15
|
const children: Element[] = [];
|
|
14
16
|
let pos = startPos;
|
|
15
17
|
let consumed = 0;
|
|
18
|
+
const inlineEnd = findSizeClose(ctx, startPos);
|
|
16
19
|
|
|
17
|
-
while (pos < ctx.tokens.length) {
|
|
20
|
+
while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
|
|
18
21
|
const token = ctx.tokens[pos];
|
|
19
22
|
if (!token || token.type === "EOF") {
|
|
20
23
|
break;
|
|
@@ -29,10 +32,10 @@ export function parseSizeContent(ctx: ParseContext, startPos: number): SizeConte
|
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
const inlineCtx: ParseContext = { ...ctx, pos };
|
|
33
|
-
const inlineResult = parseInlineUntil(inlineCtx, "
|
|
34
|
-
if (inlineResult.
|
|
35
|
-
|
|
35
|
+
const inlineCtx: ParseContext = { ...ctx, pos, scope: { ...ctx.scope, inlineEnd } };
|
|
36
|
+
const inlineResult = parseInlineUntil(inlineCtx, "EOF");
|
|
37
|
+
if (inlineResult.consumed > 0) {
|
|
38
|
+
for (const element of inlineResult.elements) children.push(element);
|
|
36
39
|
pos += inlineResult.consumed;
|
|
37
40
|
consumed += inlineResult.consumed;
|
|
38
41
|
} else {
|
|
@@ -45,6 +48,30 @@ export function parseSizeContent(ctx: ParseContext, startPos: number): SizeConte
|
|
|
45
48
|
return { children, consumed, foundClose: false };
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
function findSizeClose(ctx: ParseContext, startPos: number): number {
|
|
52
|
+
const end = ctx.scope.inlineEnd ?? ctx.tokens.length;
|
|
53
|
+
let depth = 0;
|
|
54
|
+
for (let pos = startPos; pos < end; pos++) {
|
|
55
|
+
const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, end);
|
|
56
|
+
if (protectedEnd > pos) {
|
|
57
|
+
pos = protectedEnd - 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const open = ctx.tokens[pos]?.type === "BLOCK_OPEN" ? parseSizeOpen({ ...ctx, pos }) : null;
|
|
61
|
+
if (open) {
|
|
62
|
+
depth++;
|
|
63
|
+
pos = open.bodyStart - 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const close = tryConsumeSizeClose(ctx, pos);
|
|
67
|
+
if (!close) continue;
|
|
68
|
+
if (depth === 0) return pos;
|
|
69
|
+
depth--;
|
|
70
|
+
pos += close.consumed - 1;
|
|
71
|
+
}
|
|
72
|
+
return end;
|
|
73
|
+
}
|
|
74
|
+
|
|
48
75
|
function tryConsumeSizeClose(ctx: ParseContext, pos: number): { consumed: number } | null {
|
|
49
76
|
if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
|
|
50
77
|
return null;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { ParseContext } from "../../types";
|
|
2
2
|
|
|
3
3
|
const VALID_SIZE_UNITS = ["px", "em", "rem", "ex", "%", "cm", "mm", "in", "pc"];
|
|
4
|
+
const VALID_SIZE_KEYWORDS = [
|
|
5
|
+
"smaller",
|
|
6
|
+
"larger",
|
|
7
|
+
"xx-small",
|
|
8
|
+
"x-small",
|
|
9
|
+
"small",
|
|
10
|
+
"large",
|
|
11
|
+
"x-large",
|
|
12
|
+
"xx-large",
|
|
13
|
+
];
|
|
4
14
|
|
|
5
15
|
export function parseSizeValue(
|
|
6
16
|
ctx: ParseContext,
|
|
@@ -40,6 +50,7 @@ export function parseSizeValue(
|
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
function isValidSizeValue(size: string): boolean {
|
|
53
|
+
if (VALID_SIZE_KEYWORDS.includes(size)) return true;
|
|
43
54
|
const unitPattern = VALID_SIZE_UNITS.join("|");
|
|
44
55
|
return new RegExp(`^(\\d+(?:\\.\\d+)?)(${unitPattern})$`, "i").test(size);
|
|
45
56
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { InlineRule } from "../../types";
|
|
2
|
+
import { parseSocialSyntax } from "./syntax";
|
|
3
|
+
|
|
4
|
+
export const socialRule: InlineRule = {
|
|
5
|
+
name: "social",
|
|
6
|
+
startTokens: ["BLOCK_OPEN"],
|
|
7
|
+
parse(ctx) {
|
|
8
|
+
const result = parseSocialSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
|
|
9
|
+
return result
|
|
10
|
+
? {
|
|
11
|
+
success: true,
|
|
12
|
+
consumed: result.end - ctx.pos,
|
|
13
|
+
elements: [{ element: "social", data: result.data }],
|
|
14
|
+
}
|
|
15
|
+
: { success: false };
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SocialData } from "@wdprlib/ast";
|
|
2
|
+
import type { Token } from "../../../../lexer";
|
|
3
|
+
import type { ParseContext } from "../../types";
|
|
4
|
+
import { findRawTagClose } from "../parsing/raw-tag";
|
|
5
|
+
|
|
6
|
+
const invalidBodies = new WeakMap<readonly Token[], { start: number; bracket: number }>();
|
|
7
|
+
|
|
8
|
+
export function parseSocialSyntax(
|
|
9
|
+
ctx: ParseContext,
|
|
10
|
+
start: number,
|
|
11
|
+
end: number,
|
|
12
|
+
): { data: SocialData; end: number } | null {
|
|
13
|
+
const tokens = ctx.tokens;
|
|
14
|
+
if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "social")
|
|
15
|
+
return null;
|
|
16
|
+
const bodyStart = start + 2;
|
|
17
|
+
if (tokens[bodyStart]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[bodyStart]?.value ?? ""))
|
|
18
|
+
return null;
|
|
19
|
+
const invalid = invalidBodies.get(tokens);
|
|
20
|
+
if (invalid && bodyStart >= invalid.start && bodyStart <= invalid.bracket) return null;
|
|
21
|
+
const close = findRawTagClose(tokens, bodyStart, end);
|
|
22
|
+
if (close === null) return null;
|
|
23
|
+
for (let pos = bodyStart; pos < close; pos++) {
|
|
24
|
+
if (tokens[pos]!.value.includes("]")) {
|
|
25
|
+
invalidBodies.set(tokens, { start: bodyStart, bracket: pos });
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const body = tokens
|
|
30
|
+
.slice(bodyStart, close)
|
|
31
|
+
.map((token) => token.value)
|
|
32
|
+
.join("");
|
|
33
|
+
const sites = body.trim()
|
|
34
|
+
? body
|
|
35
|
+
.split(",")
|
|
36
|
+
.map((site) => site.trim().toLowerCase())
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
: null;
|
|
39
|
+
return { data: { sites }, end: close + 1 };
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripAutomaticLineBreak } from "../parsing/automatic-line-break";
|
|
1
2
|
import { rawRegionEnd } from "../raw/end";
|
|
2
3
|
import type { Element } from "@wdprlib/ast";
|
|
3
4
|
import type { ParseContext } from "../../types";
|
|
@@ -118,7 +119,8 @@ function parseOneSpanChild(
|
|
|
118
119
|
for (const rule of getCandidateInlineRules(inlineRules, token.type)) {
|
|
119
120
|
const result = rule.parse(inlineCtx);
|
|
120
121
|
if (result.success) {
|
|
121
|
-
targetChildren
|
|
122
|
+
stripAutomaticLineBreak(targetChildren, result.stripLeadingLineBreak);
|
|
123
|
+
for (const element of result.elements) targetChildren.push(element);
|
|
122
124
|
return { consumed: result.consumed };
|
|
123
125
|
}
|
|
124
126
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createAutomaticLineBreak } from "../parsing/automatic-line-break";
|
|
1
2
|
import type { Element } from "@wdprlib/ast";
|
|
2
3
|
import type { ParseContext } from "../../types";
|
|
3
4
|
|
|
@@ -38,7 +39,7 @@ export function consumeSpanNewline(
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const targetChildren = paragraphStrip && afterBlankLine ? escapedChildren : children;
|
|
41
|
-
targetChildren.push(
|
|
42
|
+
targetChildren.push(createAutomaticLineBreak(ctx.tokens[pos]!));
|
|
42
43
|
let consumed = 1;
|
|
43
44
|
let nextPos = pos + 1;
|
|
44
45
|
while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { BlockRule, InlineRule, ParseContext } from "./types";
|
|
2
|
+
import { findCodeOpen } from "./block/code/open";
|
|
3
|
+
import { findCodeBodyBounds } from "./block/code/boundary";
|
|
4
|
+
import { findMathOpen, findMathBodyBounds } from "./block/math/boundary";
|
|
5
|
+
|
|
6
|
+
/** Probe opaque syntax without registering its code, footnotes or diagnostics. */
|
|
7
|
+
export function createOpaqueProbe(ctx: ParseContext): ParseContext {
|
|
8
|
+
return {
|
|
9
|
+
...ctx,
|
|
10
|
+
diagnostics: [],
|
|
11
|
+
footnotes: [],
|
|
12
|
+
tocEntries: [],
|
|
13
|
+
codeBlocks: [],
|
|
14
|
+
htmlBlocks: [],
|
|
15
|
+
bibcites: [],
|
|
16
|
+
scope: {
|
|
17
|
+
...ctx.scope,
|
|
18
|
+
inlineEnd: undefined,
|
|
19
|
+
tableFormatting: undefined,
|
|
20
|
+
blockCloseCondition: () => true,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function opaqueRuleEnd(
|
|
26
|
+
probe: ParseContext,
|
|
27
|
+
pos: number,
|
|
28
|
+
rules: readonly (BlockRule | InlineRule)[],
|
|
29
|
+
requireClosed: boolean = false,
|
|
30
|
+
): number {
|
|
31
|
+
const token = probe.tokens[pos];
|
|
32
|
+
if (!token) return pos;
|
|
33
|
+
probe.pos = pos;
|
|
34
|
+
for (const rule of rules) {
|
|
35
|
+
if (!rule.startTokens.includes(token.type)) continue;
|
|
36
|
+
if (rule.name === "code") {
|
|
37
|
+
const open = findCodeOpen(probe.tokens, pos);
|
|
38
|
+
if (!open) continue;
|
|
39
|
+
if (open.closingSwallowed) {
|
|
40
|
+
if (!requireClosed) return open.bodyStart;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const bounds = findCodeBodyBounds(probe.tokens, open.bodyStart);
|
|
44
|
+
if (!requireClosed || bounds.foundClose) return bounds.end;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (rule.name === "math") {
|
|
48
|
+
const open = findMathOpen(probe.tokens, pos);
|
|
49
|
+
if (!open) continue;
|
|
50
|
+
const bounds = findMathBodyBounds(probe.tokens, open.bodyStart);
|
|
51
|
+
if (bounds.hasContent && (!requireClosed || bounds.foundClose)) return bounds.end;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const result = rule.parse(probe);
|
|
55
|
+
if (result.success) return pos + result.consumed;
|
|
56
|
+
}
|
|
57
|
+
return pos;
|
|
58
|
+
}
|
package/src/pipeline/process.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_SETTINGS,
|
|
3
|
+
extractReadableText,
|
|
4
|
+
extractFirstParagraph,
|
|
5
|
+
countCharacters,
|
|
3
6
|
type Diagnostic,
|
|
4
7
|
type PageRef,
|
|
5
8
|
type WikitextPageContext,
|
|
@@ -10,6 +13,7 @@ import type { AsyncIncludeFetcher, IncludeDependency } from "../parser/rules/blo
|
|
|
10
13
|
import { resolveIncludesAsyncWithTraceSelective } from "../parser/rules/block/module/include/resolve";
|
|
11
14
|
import type { DataProvider } from "../parser/rules/block/module/types-common";
|
|
12
15
|
import { resolveModulesWithAsyncParse } from "../parser/rules/block/module/resolution/resolve-async";
|
|
16
|
+
import { resolveRatings } from "../parser/rules/block/module/rate/resolve";
|
|
13
17
|
import type {
|
|
14
18
|
ProcessedWikitextDocument,
|
|
15
19
|
ProcessWikitextCallbackContext,
|
|
@@ -117,8 +121,14 @@ export async function processWikitext<TPage extends WikitextPageContext>(
|
|
|
117
121
|
);
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
ast = await resolveRatings(ast, dataProvider.fetchRatings);
|
|
125
|
+
const readableText = extractReadableText(ast, options.readableText);
|
|
126
|
+
|
|
120
127
|
return {
|
|
121
128
|
ast,
|
|
129
|
+
readableText,
|
|
130
|
+
firstParagraph: extractFirstParagraph(ast, options.readableText),
|
|
131
|
+
characterCount: countCharacters(readableText),
|
|
122
132
|
page: options.page,
|
|
123
133
|
settings,
|
|
124
134
|
diagnostics,
|
|
@@ -167,6 +177,9 @@ function createModuleDataProvider<TPage extends WikitextPageContext>(
|
|
|
167
177
|
): DataProvider {
|
|
168
178
|
const provider = options.dataProvider;
|
|
169
179
|
return {
|
|
180
|
+
fetchRatings: provider?.fetchRatings
|
|
181
|
+
? (refs) => provider.fetchRatings!(refs, context)
|
|
182
|
+
: undefined,
|
|
170
183
|
fetchListPages: provider?.fetchListPages
|
|
171
184
|
? (query, requirement) => provider.fetchListPages!(query, requirement, context)
|
|
172
185
|
: undefined,
|
package/src/pipeline/types.ts
CHANGED
|
@@ -4,6 +4,9 @@ import type {
|
|
|
4
4
|
SyntaxTree,
|
|
5
5
|
WikitextPageContext,
|
|
6
6
|
WikitextSettings,
|
|
7
|
+
RatingRef,
|
|
8
|
+
RatingState,
|
|
9
|
+
ReadableTextOptions,
|
|
7
10
|
} from "@wdprlib/ast";
|
|
8
11
|
import type { IncludeDependency } from "../parser/rules/block/module/include";
|
|
9
12
|
import type {
|
|
@@ -26,6 +29,17 @@ export interface ProcessWikitextCallbackContext<TPage extends WikitextPageContex
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export interface ProcessWikitextDataProvider<TPage extends WikitextPageContext> {
|
|
32
|
+
/**
|
|
33
|
+
* Read only authorized, registered ratings in context.page, including declarations
|
|
34
|
+
* from its includes. Keys match exactly. Omit no-rate and inaccessible references.
|
|
35
|
+
* Main category policy and custom policies are independent. Votes belong to page + ref.
|
|
36
|
+
* ListPages/ListUsers output cannot declare ratings, including its nested includes.
|
|
37
|
+
* The host owns registration, aggregation and invalidation when included declarations change.
|
|
38
|
+
*/
|
|
39
|
+
fetchRatings?: (
|
|
40
|
+
refs: readonly RatingRef[],
|
|
41
|
+
context: ProcessWikitextCallbackContext<TPage>,
|
|
42
|
+
) => Promise<readonly RatingState[]>;
|
|
29
43
|
fetchInclude?: (
|
|
30
44
|
pageRef: PageRef,
|
|
31
45
|
context: ProcessWikitextCallbackContext<TPage>,
|
|
@@ -50,12 +64,17 @@ export interface ProcessWikitextOptions<TPage extends WikitextPageContext> {
|
|
|
50
64
|
settings?: WikitextSettings;
|
|
51
65
|
dataProvider?: ProcessWikitextDataProvider<TPage>;
|
|
52
66
|
includeMaxIterations?: number;
|
|
67
|
+
readableText?: ReadableTextOptions;
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
export interface ProcessedWikitextDocument<
|
|
56
71
|
TPage extends WikitextPageContext = WikitextPageContext,
|
|
57
72
|
> {
|
|
58
73
|
ast: SyntaxTree;
|
|
74
|
+
readableText: string;
|
|
75
|
+
/** First nonempty body paragraph, excluding headings and appended footnotes. */
|
|
76
|
+
firstParagraph: string;
|
|
77
|
+
characterCount: number;
|
|
59
78
|
page: TPage;
|
|
60
79
|
settings: WikitextSettings;
|
|
61
80
|
diagnostics: Diagnostic[];
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { ParseContext } from "../../types";
|
|
2
|
-
import { parseBlockName } from "../utils";
|
|
3
|
-
|
|
4
|
-
export interface MathContentResult {
|
|
5
|
-
latexSource: string;
|
|
6
|
-
consumed: number;
|
|
7
|
-
foundClose: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function collectMathContent(ctx: ParseContext, startPos: number): MathContentResult {
|
|
11
|
-
let latexSource = "";
|
|
12
|
-
let pos = startPos;
|
|
13
|
-
let consumed = 0;
|
|
14
|
-
let foundClose = false;
|
|
15
|
-
|
|
16
|
-
while (pos < ctx.tokens.length) {
|
|
17
|
-
const token = ctx.tokens[pos];
|
|
18
|
-
if (!token) break;
|
|
19
|
-
|
|
20
|
-
if (token.type === "BLOCK_END_OPEN") {
|
|
21
|
-
const closeNameResult = parseBlockName(ctx, pos + 1);
|
|
22
|
-
if (closeNameResult?.name === "math") {
|
|
23
|
-
foundClose = true;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
latexSource += token.type === "BACKSLASH_BREAK" ? "\\\n" : token.value;
|
|
29
|
-
pos++;
|
|
30
|
-
consumed++;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return { latexSource, consumed, foundClose };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function consumeMathClose(ctx: ParseContext, startPos: number): number {
|
|
37
|
-
let pos = startPos + 1;
|
|
38
|
-
let consumed = 1;
|
|
39
|
-
|
|
40
|
-
const closeNameResult = parseBlockName(ctx, pos);
|
|
41
|
-
if (closeNameResult) {
|
|
42
|
-
pos += closeNameResult.consumed;
|
|
43
|
-
consumed += closeNameResult.consumed;
|
|
44
|
-
}
|
|
45
|
-
if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
|
|
46
|
-
pos++;
|
|
47
|
-
consumed++;
|
|
48
|
-
}
|
|
49
|
-
if (ctx.tokens[pos]?.type === "NEWLINE") {
|
|
50
|
-
consumed++;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return consumed;
|
|
54
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ParseContext } from "../../types";
|
|
2
|
-
|
|
3
|
-
export interface MathNameResult {
|
|
4
|
-
name: string | null;
|
|
5
|
-
consumed: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function parseMathName(ctx: ParseContext, startPos: number): MathNameResult {
|
|
9
|
-
let pos = startPos;
|
|
10
|
-
let consumed = 0;
|
|
11
|
-
let name = "";
|
|
12
|
-
|
|
13
|
-
const first = ctx.tokens[pos];
|
|
14
|
-
if (first?.type !== "IDENTIFIER" && first?.type !== "TEXT") {
|
|
15
|
-
return { name: null, consumed: 0 };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
while (pos < ctx.tokens.length) {
|
|
19
|
-
const token = ctx.tokens[pos];
|
|
20
|
-
if (
|
|
21
|
-
!token ||
|
|
22
|
-
token.type === "BLOCK_CLOSE" ||
|
|
23
|
-
token.type === "WHITESPACE" ||
|
|
24
|
-
token.type === "NEWLINE"
|
|
25
|
-
) {
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
name += token.value;
|
|
30
|
-
pos++;
|
|
31
|
-
consumed++;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return { name: name === "" ? null : name, consumed };
|
|
35
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { Element } from "@wdprlib/ast";
|
|
2
|
-
import type { ParseContext } from "../../types";
|
|
3
|
-
import { parseInlineUntil } from "../utils";
|
|
4
|
-
|
|
5
|
-
export interface FootnoteChildResult {
|
|
6
|
-
elements: Element[];
|
|
7
|
-
consumed: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function parseFootnoteChild(ctx: ParseContext, pos: number): FootnoteChildResult {
|
|
11
|
-
const token = ctx.tokens[pos];
|
|
12
|
-
if (!token) {
|
|
13
|
-
return { elements: [], consumed: 0 };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const inlineResult = parseInlineUntil({ ...ctx, pos }, "BLOCK_END_OPEN");
|
|
17
|
-
if (inlineResult.elements.length > 0) {
|
|
18
|
-
return { elements: inlineResult.elements, consumed: inlineResult.consumed };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return { elements: [{ element: "text", data: token.value }], consumed: 1 };
|
|
22
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { ParseContext } from "../../types";
|
|
2
|
-
|
|
3
|
-
export interface FootnoteNewlineResult {
|
|
4
|
-
consumed: number;
|
|
5
|
-
paragraphBreak: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function consumeFootnoteNewline(ctx: ParseContext, pos: number): FootnoteNewlineResult {
|
|
9
|
-
let nextPos = pos + 1;
|
|
10
|
-
let consumed = 1;
|
|
11
|
-
|
|
12
|
-
while (ctx.tokens[nextPos]?.type === "WHITESPACE") {
|
|
13
|
-
nextPos++;
|
|
14
|
-
consumed++;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (ctx.tokens[nextPos]?.type !== "NEWLINE") {
|
|
18
|
-
return { consumed, paragraphBreak: false };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
while (ctx.tokens[nextPos]?.type === "NEWLINE") {
|
|
22
|
-
nextPos++;
|
|
23
|
-
consumed++;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return { consumed, paragraphBreak: true };
|
|
27
|
-
}
|