wikiparser-node 1.40.0 → 1.42.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/README.md +55 -7
- package/bundle/bundle-es8.min.js +27 -27
- package/bundle/bundle-lsp.min.js +29 -29
- package/bundle/bundle.min.js +19 -19
- package/config/.schema.json +5 -0
- package/config/default.json +1 -1
- package/config/enwiki.json +3 -2
- package/config/jawiki.json +6 -5
- package/config/llwiki.json +2 -1
- package/config/minimum.json +1 -1
- package/config/moegirl.json +2 -1
- package/config/zhwiki.json +3 -2
- package/dist/addon/attribute.js +1 -1
- package/dist/addon/transclude.js +1 -3
- package/dist/base.d.mts +5 -3
- package/dist/base.d.ts +5 -3
- package/dist/bin/config.js +11 -10
- package/dist/index.js +1 -2
- package/dist/lib/element.d.ts +3 -8
- package/dist/lib/element.js +2 -26
- package/dist/lib/lintConfig.js +30 -23
- package/dist/lib/lsp.d.ts +2 -0
- package/dist/lib/lsp.js +8 -20
- package/dist/lib/node.d.ts +2 -0
- package/dist/lib/node.js +0 -7
- package/dist/lib/range.d.ts +0 -7
- package/dist/lib/range.js +5 -14
- package/dist/lib/text.js +4 -6
- package/dist/map.d.ts +66 -0
- package/dist/map.js +2 -0
- package/dist/mixin/attributesParent.d.ts +4 -3
- package/dist/mixin/elementLike.d.ts +13 -0
- package/dist/mixin/elementLike.js +66 -53
- package/dist/parser/selector.js +3 -3
- package/dist/render/extension.js +139 -8
- package/dist/render/html.js +72 -3
- package/dist/render/magicWords.js +6 -3
- package/dist/src/attribute.d.ts +2 -2
- package/dist/src/attribute.js +18 -14
- package/dist/src/attributes.d.ts +3 -3
- package/dist/src/attributes.js +35 -19
- package/dist/src/extLink.js +5 -4
- package/dist/src/heading.js +11 -3
- package/dist/src/index.js +3 -4
- package/dist/src/link/base.js +2 -4
- package/dist/src/link/file.js +5 -5
- package/dist/src/link/galleryImage.js +1 -3
- package/dist/src/magicLink.js +6 -5
- package/dist/src/multiLine/gallery.js +2 -3
- package/dist/src/nowiki/doubleUnderscore.d.ts +1 -0
- package/dist/src/nowiki/doubleUnderscore.js +4 -1
- package/dist/src/nowiki/quote.js +1 -3
- package/dist/src/parameter.js +5 -2
- package/dist/src/table/td.d.ts +2 -2
- package/dist/src/table/td.js +1 -1
- package/dist/src/tag/html.js +29 -12
- package/dist/src/tagPair/ext.js +4 -1
- package/dist/src/tagPair/translate.d.ts +1 -1
- package/dist/src/tagPair/translate.js +1 -1
- package/dist/util/debug.js +5 -7
- package/dist/util/html.js +3 -11
- package/dist/util/sharable.js +2 -0
- package/dist/util/sharable.mjs +3 -1
- package/extensions/dist/base.js +1 -1
- package/extensions/dist/env.js +34 -0
- package/package.json +19 -20
package/dist/lib/text.js
CHANGED
|
@@ -220,7 +220,7 @@ let AstText = (() => {
|
|
|
220
220
|
return [];
|
|
221
221
|
}
|
|
222
222
|
errorRegex.lastIndex = 0;
|
|
223
|
-
const errors = [], nextType = nextSibling?.type, nextName = nextSibling?.name, previousType = previousSibling?.type, root = this.getRootNode(), rootStr = root.toString(), { ext, html, variants } = root.getAttribute('config'), { top, left } = root.posFromIndex(start), { lintConfig } = index_1.default, tagLike = lintConfig.rules['tag-like'], specified =
|
|
223
|
+
const errors = [], nextType = nextSibling?.type, nextName = nextSibling?.name, previousType = previousSibling?.type, root = this.getRootNode(), rootStr = root.toString(), { ext, html, variants } = root.getAttribute('config'), { top, left } = root.posFromIndex(start), { lintConfig } = index_1.default, tagLike = lintConfig.rules['tag-like'], specified = tagLike[1]
|
|
224
224
|
? new Set(Object.keys(tagLike[1]).filter(tag => tag !== 'invalid' && tag !== 'disallowed'))
|
|
225
225
|
: new Set(), tags = new Set([
|
|
226
226
|
'onlyinclude',
|
|
@@ -260,8 +260,7 @@ let AstText = (() => {
|
|
|
260
260
|
// Rule & Severity
|
|
261
261
|
let startIndex = start + index, endIndex = startIndex + length, rule, severity, endLine, endCol;
|
|
262
262
|
const nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], leftBracket = lbrace || lbrack, lConverter = error === '{' && previousChar === '-' && variants.length > 0, rConverter = error === '}' && nextChar === '-' && variants.length > 0, brokenExtLink = lbrack && nextType === 'free-ext-link' && !data.slice(index + 1).trim()
|
|
263
|
-
|| rbrack && previousType === 'free-ext-link'
|
|
264
|
-
&& !data.slice(0, index).includes(']');
|
|
263
|
+
|| rbrack && previousType === 'free-ext-link' && index === 0;
|
|
265
264
|
if (magicLink) {
|
|
266
265
|
rule = 'lonely-http';
|
|
267
266
|
error = error.toUpperCase();
|
|
@@ -571,9 +570,8 @@ let AstText = (() => {
|
|
|
571
570
|
if (/\s$/u.test(this.data)) {
|
|
572
571
|
const spaces = [], mt = /\n[^\S\n]*$/u.exec(this.data);
|
|
573
572
|
let { nextSibling } = this, mt2 = null;
|
|
574
|
-
while (nextSibling
|
|
575
|
-
|| nextSibling.is('category')
|
|
576
|
-
|| nextSibling.type === 'text')) {
|
|
573
|
+
while (nextSibling
|
|
574
|
+
&& (nextSibling.is('comment') || nextSibling.is('category') || nextSibling.type === 'text')) {
|
|
577
575
|
if (nextSibling.type === 'text') {
|
|
578
576
|
mt2 = mt && /^[^\S\n]*(?=\n)/u.exec(nextSibling.data);
|
|
579
577
|
if (mt2 || nextSibling.data.trim()) {
|
package/dist/map.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { RedirectToken, RedirectTargetToken, OnlyincludeToken, IncludeToken, CommentToken, ExtToken, AttributesToken, AttributeToken, AtomToken, ArgToken, TranscludeToken, SyntaxToken, ParameterToken, HeadingToken, HtmlToken, TableToken, TrToken, TdToken, DoubleUnderscoreToken, HrToken, LinkToken, CategoryToken, FileToken, ImageParameterToken, QuoteToken, ExtLinkToken, MagicLinkToken, ListToken, DdToken, ConverterToken, ConverterFlagsToken, ConverterRuleToken, TranslateToken, TvarToken, HiddenToken, GalleryImageToken, ParamLineToken, ImagemapLinkToken, ListRangeToken } from './internal';
|
|
2
|
+
export interface TokenTypeMap {
|
|
3
|
+
redirect: RedirectToken;
|
|
4
|
+
'redirect-syntax': SyntaxToken;
|
|
5
|
+
'redirect-target': RedirectTargetToken;
|
|
6
|
+
translate: TranslateToken;
|
|
7
|
+
'translate-attr': SyntaxToken;
|
|
8
|
+
tvar: TvarToken;
|
|
9
|
+
'tvar-name': SyntaxToken;
|
|
10
|
+
onlyinclude: OnlyincludeToken;
|
|
11
|
+
include: IncludeToken;
|
|
12
|
+
comment: CommentToken;
|
|
13
|
+
ext: ExtToken;
|
|
14
|
+
'ext-attrs': AttributesToken;
|
|
15
|
+
'ext-attr-dirty': AtomToken;
|
|
16
|
+
'ext-attr': AttributeToken;
|
|
17
|
+
'attr-key': AtomToken;
|
|
18
|
+
arg: ArgToken;
|
|
19
|
+
'arg-name': AtomToken;
|
|
20
|
+
hidden: HiddenToken;
|
|
21
|
+
'magic-word': TranscludeToken;
|
|
22
|
+
'magic-word-name': SyntaxToken;
|
|
23
|
+
'invoke-function': AtomToken;
|
|
24
|
+
'invoke-module': AtomToken;
|
|
25
|
+
template: TranscludeToken;
|
|
26
|
+
'template-name': AtomToken;
|
|
27
|
+
parameter: ParameterToken;
|
|
28
|
+
heading: HeadingToken;
|
|
29
|
+
'heading-trail': SyntaxToken;
|
|
30
|
+
html: HtmlToken;
|
|
31
|
+
'html-attrs': AttributesToken;
|
|
32
|
+
'html-attr-dirty': AtomToken;
|
|
33
|
+
'html-attr': AttributeToken;
|
|
34
|
+
table: TableToken;
|
|
35
|
+
tr: TrToken;
|
|
36
|
+
td: TdToken;
|
|
37
|
+
'table-syntax': SyntaxToken;
|
|
38
|
+
'table-attrs': AttributesToken;
|
|
39
|
+
'table-attr-dirty': AtomToken;
|
|
40
|
+
'table-attr': AttributeToken;
|
|
41
|
+
hr: HrToken;
|
|
42
|
+
'double-underscore': DoubleUnderscoreToken;
|
|
43
|
+
link: LinkToken;
|
|
44
|
+
'link-target': AtomToken;
|
|
45
|
+
category: CategoryToken;
|
|
46
|
+
file: FileToken;
|
|
47
|
+
'gallery-image': GalleryImageToken;
|
|
48
|
+
'imagemap-image': GalleryImageToken;
|
|
49
|
+
'image-parameter': ImageParameterToken;
|
|
50
|
+
quote: QuoteToken;
|
|
51
|
+
'ext-link': ExtLinkToken;
|
|
52
|
+
'ext-link-url': MagicLinkToken;
|
|
53
|
+
'free-ext-link': MagicLinkToken;
|
|
54
|
+
'magic-link': MagicLinkToken;
|
|
55
|
+
list: ListToken;
|
|
56
|
+
dd: DdToken;
|
|
57
|
+
converter: ConverterToken;
|
|
58
|
+
'converter-flags': ConverterFlagsToken;
|
|
59
|
+
'converter-flag': AtomToken;
|
|
60
|
+
'converter-rule': ConverterRuleToken;
|
|
61
|
+
'converter-rule-variant': AtomToken;
|
|
62
|
+
'param-line': ParamLineToken;
|
|
63
|
+
'imagemap-link': ImagemapLinkToken;
|
|
64
|
+
'list-range': ListRangeToken;
|
|
65
|
+
}
|
|
66
|
+
export type SelectedTokenTypes = keyof TokenTypeMap;
|
package/dist/map.js
ADDED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface AttributesParentBase {
|
|
2
2
|
/** all attributes / 全部属性 */
|
|
3
|
-
attributes: Record<string, string
|
|
3
|
+
get attributes(): Record<string, string>;
|
|
4
|
+
set attributes(attrs: Record<string, string | true>);
|
|
4
5
|
/** class attribute in string / 以字符串表示的class属性 */
|
|
5
6
|
className: string;
|
|
6
7
|
/** class attribute in Set / 以Set表示的class属性 */
|
|
@@ -20,7 +21,7 @@ export interface AttributesParentBase {
|
|
|
20
21
|
* 获取指定属性
|
|
21
22
|
* @param key attribute name / 属性键
|
|
22
23
|
*/
|
|
23
|
-
getAttr(key: string): string |
|
|
24
|
+
getAttr(key: string): string | undefined;
|
|
24
25
|
/**
|
|
25
26
|
* Get all attribute names
|
|
26
27
|
*
|
|
@@ -32,7 +33,7 @@ export interface AttributesParentBase {
|
|
|
32
33
|
*
|
|
33
34
|
* 获取全部属性
|
|
34
35
|
*/
|
|
35
|
-
getAttrs(): Record<string, string
|
|
36
|
+
getAttrs(): Record<string, string>;
|
|
36
37
|
/**
|
|
37
38
|
* Set the attribute
|
|
38
39
|
*
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { AstNodes, Token, HtmlToken, ExtToken } from '../internal';
|
|
2
|
+
import type { TokenTypeMap, SelectedTokenTypes } from '../map';
|
|
2
3
|
declare type ElementConstructor = abstract new (...args: any[]) => {
|
|
4
|
+
readonly parentNode: Token | undefined;
|
|
3
5
|
readonly childNodes: readonly AstNodes[];
|
|
4
6
|
detach?: () => void;
|
|
5
7
|
};
|
|
@@ -12,12 +14,21 @@ export interface ElementLike {
|
|
|
12
14
|
readonly lastElementChild: Token | undefined;
|
|
13
15
|
/** number of child elements / 非文本子节点总数 */
|
|
14
16
|
readonly childElementCount: number;
|
|
17
|
+
/**
|
|
18
|
+
* Get the closest ancestor node that matches the selector
|
|
19
|
+
*
|
|
20
|
+
* 最近的符合选择器的祖先节点
|
|
21
|
+
* @param selector selector / 选择器
|
|
22
|
+
*/
|
|
23
|
+
closest<K extends SelectedTokenTypes>(selector: K): TokenTypeMap[K] | undefined;
|
|
24
|
+
closest<T = Token>(selector: string): T | undefined;
|
|
15
25
|
/**
|
|
16
26
|
* Get the first descendant that matches the selector
|
|
17
27
|
*
|
|
18
28
|
* 符合选择器的第一个后代节点
|
|
19
29
|
* @param selector selector / 选择器
|
|
20
30
|
*/
|
|
31
|
+
querySelector<K extends SelectedTokenTypes>(selector: K): TokenTypeMap[K] | undefined;
|
|
21
32
|
querySelector<T = Token>(selector: string): T | undefined;
|
|
22
33
|
/**
|
|
23
34
|
* Get all descendants that match the selector
|
|
@@ -25,6 +36,7 @@ export interface ElementLike {
|
|
|
25
36
|
* 符合选择器的所有后代节点
|
|
26
37
|
* @param selector selector / 选择器
|
|
27
38
|
*/
|
|
39
|
+
querySelectorAll<K extends SelectedTokenTypes>(selector: K): TokenTypeMap[K][];
|
|
28
40
|
querySelectorAll<T = Token>(selector: string): T[];
|
|
29
41
|
/**
|
|
30
42
|
* Escape `=` and `|`
|
|
@@ -39,6 +51,7 @@ export interface ElementLike {
|
|
|
39
51
|
* 类型选择器
|
|
40
52
|
* @param types token types / 节点类型
|
|
41
53
|
*/
|
|
54
|
+
getElementByTypes<K extends SelectedTokenTypes>(types: K): TokenTypeMap[K] | undefined;
|
|
42
55
|
getElementByTypes<T = Token>(types: string): T | undefined;
|
|
43
56
|
/**
|
|
44
57
|
* Get the first descendant with the id
|
|
@@ -7,30 +7,41 @@ const selector_1 = require("../util/selector");
|
|
|
7
7
|
const constants_1 = require("../util/constants");
|
|
8
8
|
/** @ignore */
|
|
9
9
|
const elementLike = (constructor) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
class ElementLike extends constructor {
|
|
11
|
+
/* NOT FOR BROWSER */
|
|
12
|
+
get children() {
|
|
13
|
+
return this.childNodes.filter((child) => child.type !== 'text');
|
|
14
|
+
}
|
|
15
|
+
get firstElementChild() {
|
|
16
|
+
return this.childNodes.find((child) => child.type !== 'text');
|
|
17
|
+
}
|
|
18
|
+
get lastElementChild() {
|
|
19
|
+
return this.childNodes.findLast((child) => child.type !== 'text');
|
|
20
|
+
}
|
|
21
|
+
get childElementCount() {
|
|
22
|
+
return this.children.length;
|
|
23
|
+
}
|
|
24
|
+
/* NOT FOR BROWSER END */
|
|
25
|
+
#getCondition(selector) {
|
|
26
|
+
return (0, selector_1.getCondition)(selector,
|
|
27
|
+
// eslint-disable-next-line unicorn/no-negated-condition
|
|
28
|
+
!('type' in this) ?
|
|
29
|
+
undefined :
|
|
30
|
+
this);
|
|
31
|
+
}
|
|
32
|
+
closest(selector) {
|
|
33
|
+
const condition = this.#getCondition(selector);
|
|
34
|
+
let { parentNode } = this;
|
|
35
|
+
while (parentNode) {
|
|
36
|
+
if (condition(parentNode)) {
|
|
37
|
+
return parentNode;
|
|
38
|
+
}
|
|
39
|
+
({ parentNode } = parentNode);
|
|
32
40
|
}
|
|
33
|
-
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
getElementBy(condition) {
|
|
44
|
+
LINT: {
|
|
34
45
|
const stack = [...this.childNodes].reverse();
|
|
35
46
|
while (stack.length > 0) {
|
|
36
47
|
const child = stack.pop(), { type, childNodes } = child;
|
|
@@ -46,10 +57,12 @@ const elementLike = (constructor) => {
|
|
|
46
57
|
}
|
|
47
58
|
return undefined;
|
|
48
59
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
}
|
|
61
|
+
querySelector(selector) {
|
|
62
|
+
LINT: return this.getElementBy(this.#getCondition(selector));
|
|
63
|
+
}
|
|
64
|
+
getElementsBy(condition) {
|
|
65
|
+
LINT: {
|
|
53
66
|
const stack = [...this.childNodes].reverse(), descendants = [];
|
|
54
67
|
while (stack.length > 0) {
|
|
55
68
|
const child = stack.pop(), { type, childNodes } = child;
|
|
@@ -65,36 +78,36 @@ const elementLike = (constructor) => {
|
|
|
65
78
|
}
|
|
66
79
|
return descendants;
|
|
67
80
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/* NOT FOR BROWSER */
|
|
77
|
-
this.detach?.();
|
|
81
|
+
}
|
|
82
|
+
querySelectorAll(selector) {
|
|
83
|
+
LINT: return this.getElementsBy(this.#getCondition(selector));
|
|
84
|
+
}
|
|
85
|
+
escape() {
|
|
86
|
+
LSP: {
|
|
87
|
+
for (const child of this.childNodes) {
|
|
88
|
+
child.escape();
|
|
78
89
|
}
|
|
90
|
+
/* NOT FOR BROWSER */
|
|
91
|
+
this.detach?.();
|
|
79
92
|
}
|
|
80
|
-
/* NOT FOR BROWSER */
|
|
81
|
-
getElementByTypes(types) {
|
|
82
|
-
const typeSet = new Set(types.split(',').map(str => str.trim()));
|
|
83
|
-
return this.getElementBy((({ type }) => typeSet.has(type)));
|
|
84
|
-
}
|
|
85
|
-
getElementById(id) {
|
|
86
|
-
return this.getElementBy((token => 'id' in token && token.id === id));
|
|
87
|
-
}
|
|
88
|
-
getElementsByClassName(className) {
|
|
89
|
-
return this.getElementsBy((token => 'classList' in token && token.classList.has(className)));
|
|
90
|
-
}
|
|
91
|
-
getElementsByTagName(tag) {
|
|
92
|
-
return this.getElementsBy((({ type, name }) => name === tag && (type === 'html' || type === 'ext')));
|
|
93
|
-
}
|
|
94
93
|
}
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
/* NOT FOR BROWSER */
|
|
95
|
+
getElementByTypes(types) {
|
|
96
|
+
const typeSet = new Set(types.split(',').map(str => str.trim()));
|
|
97
|
+
return this.getElementBy((({ type }) => typeSet.has(type)));
|
|
98
|
+
}
|
|
99
|
+
getElementById(id) {
|
|
100
|
+
return this.getElementBy((token => 'id' in token && token.id === id));
|
|
101
|
+
}
|
|
102
|
+
getElementsByClassName(className) {
|
|
103
|
+
return this.getElementsBy((token => 'classList' in token && token.classList.has(className)));
|
|
104
|
+
}
|
|
105
|
+
getElementsByTagName(tag) {
|
|
106
|
+
return this.getElementsBy((({ type, name }) => name === tag && (type === 'html' || type === 'ext')));
|
|
107
|
+
}
|
|
97
108
|
}
|
|
109
|
+
(0, debug_1.mixin)(ElementLike, constructor);
|
|
110
|
+
return ElementLike;
|
|
98
111
|
};
|
|
99
112
|
exports.elementLike = elementLike;
|
|
100
113
|
constants_1.mixins['elementLike'] = __filename;
|
package/dist/parser/selector.js
CHANGED
|
@@ -163,8 +163,8 @@ const matches = (token, step, scope, has) => {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
else if (selector.length === 4) { // 情形2:属性选择器
|
|
166
|
-
const [key, equal, val = '', i] = selector
|
|
167
|
-
if (!(key in token ||
|
|
166
|
+
const [key, equal, val = '', i] = selector;
|
|
167
|
+
if (!(key in token || typeof token.hasAttr === 'function' && token.hasAttr(key))) {
|
|
168
168
|
return equal === '!=';
|
|
169
169
|
}
|
|
170
170
|
const v = toCase(val, i), thisVal = getAttr(token, key);
|
|
@@ -234,7 +234,7 @@ const matches = (token, step, scope, has) => {
|
|
|
234
234
|
for (; node; node = node.parentNode) {
|
|
235
235
|
const lang = node.attributes?.['lang'];
|
|
236
236
|
if (lang !== undefined) {
|
|
237
|
-
return
|
|
237
|
+
return regex.test(lang);
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
return false;
|
package/dist/render/extension.js
CHANGED
|
@@ -3,6 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.renderExt = void 0;
|
|
4
4
|
const constants_1 = require("../util/constants");
|
|
5
5
|
const string_1 = require("../util/string");
|
|
6
|
+
const sharable_1 = require("../util/sharable");
|
|
7
|
+
/** @ignore */
|
|
8
|
+
const getCiteNoteId = (i, refName) => `cite_note${refName ? `-${(0, string_1.sanitizeAttr)(refName, true)}` : ''}-${i}`, getCiteRefId = (i, count, refName) => `cite_ref-${refName ? `${(0, string_1.sanitizeAttr)(refName, true)}_${i}-${count - 1}` : i}`, updateRef = (ref, content, dir) => {
|
|
9
|
+
if (!ref.content) {
|
|
10
|
+
ref.content = content;
|
|
11
|
+
ref.dir = dir;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
6
14
|
/**
|
|
7
15
|
* 将扩展标签渲染为HTML
|
|
8
16
|
* @param token 扩展标签节点
|
|
@@ -33,14 +41,16 @@ const renderExt = (token, opt) => {
|
|
|
33
41
|
.trim()}${padding}</div>`;
|
|
34
42
|
}
|
|
35
43
|
case 'gallery': {
|
|
36
|
-
const caption = firstChild.getAttrToken('caption'), perrow = parseInt(
|
|
44
|
+
const caption = firstChild.getAttrToken('caption'), perrow = parseInt(firstChild.getAttr('perrow') || ''), { classList } = firstChild, mode = firstChild.getAttr('mode')?.toLowerCase(), nolines = mode === 'nolines', padding = nolines ? 9 : 43;
|
|
37
45
|
classList.add('gallery');
|
|
38
46
|
if (nolines) {
|
|
39
47
|
classList.add('mw-gallery-nolines');
|
|
40
48
|
}
|
|
49
|
+
else if (!mode || mode === 'traditional') {
|
|
50
|
+
classList.add('mw-gallery-traditional');
|
|
51
|
+
}
|
|
41
52
|
if (perrow > 0) {
|
|
42
|
-
|
|
43
|
-
firstChild.setAttr('style', `max-width: ${(lastChild.widths + padding) * perrow}px;${typeof style === 'string' ? style : ''}`);
|
|
53
|
+
firstChild.setAttr('style', `max-width: ${(lastChild.widths + padding) * perrow}px;${firstChild.getAttr('style') || ''}`);
|
|
44
54
|
}
|
|
45
55
|
return `<ul${firstChild.toHtmlInternal()}>\n${caption
|
|
46
56
|
? `\t<li class="gallerycaption">${caption.lastChild.toHtmlInternal({ nowrap: true })}</li>\n`
|
|
@@ -51,7 +61,7 @@ const renderExt = (token, opt) => {
|
|
|
51
61
|
let html = lastChild.toHtmlInternal().trimEnd().replace(/^\n+/u, ''), lexer = firstChild.getAttr('lang');
|
|
52
62
|
const dir = firstChild.getAttr('dir') === 'rtl' ? ' rtl' : 'ltr', isInline = firstChild.getAttr('enclose') === 'none' || firstChild.hasAttr('inline'), showLines = firstChild.hasAttr('line'), { classList } = firstChild;
|
|
53
63
|
classList.add('mw-highlight');
|
|
54
|
-
if (lexer
|
|
64
|
+
if (lexer) {
|
|
55
65
|
const { Prism, loadLanguage } = require('./syntaxhighlight');
|
|
56
66
|
lexer = lexer.toLowerCase();
|
|
57
67
|
if (Prism) {
|
|
@@ -64,7 +74,7 @@ const renderExt = (token, opt) => {
|
|
|
64
74
|
}
|
|
65
75
|
catch { }
|
|
66
76
|
}
|
|
67
|
-
const highlight = firstChild.getAttr('highlight'), lines =
|
|
77
|
+
const highlight = firstChild.getAttr('highlight'), lines = highlight && new Set(highlight.split(',').flatMap((str) => {
|
|
68
78
|
const num = Number(str);
|
|
69
79
|
if (Number.isInteger(num) && num > 0) {
|
|
70
80
|
return num;
|
|
@@ -83,14 +93,14 @@ const renderExt = (token, opt) => {
|
|
|
83
93
|
if (linenos) {
|
|
84
94
|
const linelinks = firstChild.getAttr('linelinks'), startAttr = firstChild.getAttr('start');
|
|
85
95
|
lineReplace = '<span class="linenos" data-line="$1"></span>';
|
|
86
|
-
if (startAttr
|
|
96
|
+
if (startAttr) {
|
|
87
97
|
start = Number(startAttr);
|
|
88
98
|
if (!Number.isInteger(start) || start < 0) {
|
|
89
99
|
start = 1;
|
|
90
100
|
}
|
|
91
101
|
}
|
|
92
|
-
if (linelinks
|
|
93
|
-
lineReplace = `<a href="#${linelinks}-$1">${lineReplace}</a>`;
|
|
102
|
+
if (linelinks) {
|
|
103
|
+
lineReplace = `<a href="#${(0, string_1.sanitizeId)(linelinks)}-$1">${lineReplace}</a>`;
|
|
94
104
|
begin = `${linelinks}-`;
|
|
95
105
|
end = '</span>';
|
|
96
106
|
}
|
|
@@ -145,6 +155,127 @@ const renderExt = (token, opt) => {
|
|
|
145
155
|
? `<code${firstChild.toHtmlInternal()}>${html.trim().replaceAll('\n', ' ')}</code>`
|
|
146
156
|
: `<div${firstChild.toHtmlInternal()}>${html && `<pre>${(0, string_1.newline)(html)}</pre>`}</div>`;
|
|
147
157
|
}
|
|
158
|
+
case 'ref': {
|
|
159
|
+
const refs = constants_1.states.get(token.getRootNode())?.refs;
|
|
160
|
+
if (!refs) {
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
const follow = firstChild.getAttr('follow') || '';
|
|
164
|
+
if (/^\d+$/u.test(follow)) {
|
|
165
|
+
return '';
|
|
166
|
+
}
|
|
167
|
+
let refName = firstChild.getAttr('name') || '';
|
|
168
|
+
if (!/\D/u.test(refName)) {
|
|
169
|
+
refName = '';
|
|
170
|
+
}
|
|
171
|
+
else if (refName && follow) {
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
let dir = firstChild.getAttr('dir')?.toLowerCase();
|
|
175
|
+
if (dir !== 'ltr' && dir !== 'rtl') {
|
|
176
|
+
dir = undefined;
|
|
177
|
+
}
|
|
178
|
+
const text = token.innerText?.trim(), references = token.closest('ext#references');
|
|
179
|
+
if (references) {
|
|
180
|
+
const { referencesGroup } = refs.get(references.getAttr('group') || '');
|
|
181
|
+
if (refName && text) {
|
|
182
|
+
const ref = referencesGroup.find(({ name: n }) => n === refName);
|
|
183
|
+
if (ref) {
|
|
184
|
+
updateRef(ref, lastChild, dir);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return '';
|
|
188
|
+
}
|
|
189
|
+
else if (!refName && !text || text && /<references\b[^>]*>/iu.test(text)) {
|
|
190
|
+
return '';
|
|
191
|
+
}
|
|
192
|
+
else if (text && /<ref\b[^>]*>/iu.test(text)) {
|
|
193
|
+
const inner = lastChild.cloneNode();
|
|
194
|
+
for (const ref of inner.querySelectorAll('ext#ref')) {
|
|
195
|
+
ref.remove();
|
|
196
|
+
}
|
|
197
|
+
if (/<ref\b[^>]*>/iu.test(inner.toString())) {
|
|
198
|
+
return '';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const group = firstChild.getAttr('group') || '';
|
|
202
|
+
if (!refs.has(group)) {
|
|
203
|
+
refs.set(group, { referencesGroup: [], follows: [] });
|
|
204
|
+
}
|
|
205
|
+
const { referencesGroup, follows } = refs.get(group);
|
|
206
|
+
if (follow) {
|
|
207
|
+
const ref = referencesGroup.find(({ name: n }) => n === follow);
|
|
208
|
+
if (ref) {
|
|
209
|
+
if (ref.content) {
|
|
210
|
+
ref.content.safeAppend([' ', ...lastChild.childNodes]);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
ref.content = lastChild;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
refs.id++;
|
|
218
|
+
follows.push({ content: lastChild });
|
|
219
|
+
}
|
|
220
|
+
return '';
|
|
221
|
+
}
|
|
222
|
+
let i = refName ? referencesGroup.findIndex(({ name: n }) => n === refName) : -1, count = 1, ref;
|
|
223
|
+
if (i === -1) {
|
|
224
|
+
i = referencesGroup.length;
|
|
225
|
+
ref = {
|
|
226
|
+
...refName && { name: refName },
|
|
227
|
+
...text && { content: lastChild },
|
|
228
|
+
dir,
|
|
229
|
+
count,
|
|
230
|
+
id: ++refs.id,
|
|
231
|
+
};
|
|
232
|
+
referencesGroup.push(ref);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
ref = referencesGroup[i];
|
|
236
|
+
ref.count++;
|
|
237
|
+
({ count } = ref);
|
|
238
|
+
if (text) {
|
|
239
|
+
updateRef(ref, lastChild, dir);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return `<sup id="${getCiteRefId(ref.id, count, refName)}" class="reference"><a href="#${getCiteNoteId(ref.id, refName)}"><span class="cite-bracket">[</span>${group}${group && ' '}${i + 1}<span class="cite-bracket">]</span></a></sup>`;
|
|
243
|
+
}
|
|
244
|
+
case 'references': {
|
|
245
|
+
const refs = constants_1.states.get(token.getRootNode())?.refs;
|
|
246
|
+
if (!refs
|
|
247
|
+
|| firstChild.childNodes.filter(node => node.is('ext-attr'))
|
|
248
|
+
.some(({ name: key }) => !sharable_1.extAttrs['references'].has(key))) {
|
|
249
|
+
return '';
|
|
250
|
+
}
|
|
251
|
+
const group = firstChild.getAttr('group') || '';
|
|
252
|
+
if (!refs.has(group)) {
|
|
253
|
+
return '';
|
|
254
|
+
}
|
|
255
|
+
const html = lastChild.toHtmlInternal();
|
|
256
|
+
if (!refs.has(group)) { // 嵌套的`<ref>`
|
|
257
|
+
return html;
|
|
258
|
+
}
|
|
259
|
+
const { referencesGroup, follows } = refs.get(group);
|
|
260
|
+
if (referencesGroup.length === 0 && follows.length === 0) {
|
|
261
|
+
return '';
|
|
262
|
+
}
|
|
263
|
+
let ol = `<ol class="references"${group && ` data-mw-group="${group}"`}>`;
|
|
264
|
+
for (const { content } of follows) {
|
|
265
|
+
ol += `\n<p><span class="reference-text">${content.toHtmlInternal()}</span>\n</p>`;
|
|
266
|
+
}
|
|
267
|
+
for (let i = 0; i < referencesGroup.length; i++) {
|
|
268
|
+
const { content, count, dir, name: refName, id } = referencesGroup[i];
|
|
269
|
+
ol += `\n<li id="${getCiteNoteId(id, refName)}"${dir ? ` class="mw-cite-dir-${dir}"` : ''}><span class="mw-cite-backlink">${count === 1
|
|
270
|
+
? `<a href="#${getCiteRefId(id, 1, refName)}">↑</a>`
|
|
271
|
+
: `↑${Array.from({ length: count }, (_, j) => ` <sup><a href="#${getCiteRefId(id, j + 1, refName)}">${i + 1}.${j}</a></sup>`).join('')}`}</span> <span class="reference-text">${content?.toHtmlInternal() ?? ''}</span>\n</li>`;
|
|
272
|
+
}
|
|
273
|
+
ol += '\n</ol>';
|
|
274
|
+
refs.delete(group);
|
|
275
|
+
return firstChild.getAttr('responsive') === '0'
|
|
276
|
+
? ol
|
|
277
|
+
: `<div class="mw-references-wrap${referencesGroup.length > 10 ? ' mw-references-columns' : ''}">${ol}</div>`;
|
|
278
|
+
}
|
|
148
279
|
default:
|
|
149
280
|
return '';
|
|
150
281
|
}
|
package/dist/render/html.js
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.toHtml = void 0;
|
|
4
4
|
const constants_1 = require("../util/constants");
|
|
5
|
-
const blockElems = 'table|h[1-6]|pre|p|[uod]l', antiBlockElems = 't[dh]';
|
|
5
|
+
const blockElems = 'table|h[1-6]|pre|p|[uod]l', antiBlockElems = 't[dh]', allowed = new Set(['sup', 'sub', 'bdi', 'i', 'b', 's', 'strike', 'q']);
|
|
6
|
+
const tocContainer = '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
|
|
7
|
+
+ '<input type="checkbox" role="button" id="toctogglecheckbox" class="toctogglecheckbox" style="display:none" />'
|
|
8
|
+
+ '<div class="toctitle">'
|
|
9
|
+
+ '<h2 id="mw-toc-heading">Contents</h2>'
|
|
10
|
+
+ '<span class="toctogglespan"><label class="toctogglelabel" for="toctogglecheckbox"></label></span>'
|
|
11
|
+
+ '</div>';
|
|
6
12
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
7
13
|
/<(?:table|\/t[dh]|\/?(?:tr|caption|d[td]|li))\b/iu;
|
|
8
14
|
const openRegex = new RegExp(String.raw `<(?:${blockElems}|\/${antiBlockElems}|\/?(?:tr|caption|d[td]|li))\b`, 'iu');
|
|
@@ -14,8 +20,70 @@ const closeRegex = new RegExp(String.raw `<(?:\/(?:${blockElems})|${antiBlockEle
|
|
|
14
20
|
* @param token 展开后的节点
|
|
15
21
|
*/
|
|
16
22
|
const toHtml = (token) => {
|
|
17
|
-
constants_1.states.set(token, {
|
|
23
|
+
constants_1.states.set(token, {
|
|
24
|
+
headings: new Set(),
|
|
25
|
+
categories: new Set(),
|
|
26
|
+
refs: Object.assign(new Map(), { id: 0 }),
|
|
27
|
+
});
|
|
28
|
+
// 前处理引用
|
|
29
|
+
const hasCite = token.getAttribute('config').ext.includes('references');
|
|
30
|
+
if (hasCite) {
|
|
31
|
+
token.append('\n', token.createElement('references', { selfClosing: true }));
|
|
32
|
+
}
|
|
33
|
+
// 处理目录
|
|
34
|
+
const tocSwitch = token.querySelector('double-underscore#toc'), forcetoc = tocSwitch || token.querySelector('double-underscore#forcetoc'), tocData = [];
|
|
35
|
+
if (forcetoc || !token.querySelector('double-underscore#notoc')) {
|
|
36
|
+
const headings = token.querySelectorAll('heading,html#h1,html#h2,html#h3,html#h4,html#h5,html#h6');
|
|
37
|
+
let firstHeading;
|
|
38
|
+
for (const heading of headings) {
|
|
39
|
+
if (heading.type === 'heading') {
|
|
40
|
+
tocData.push([heading.level, heading.getRenderedId(), heading.firstChild.toHtmlInternal().trim()]);
|
|
41
|
+
firstHeading ??= heading;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const tocLine = heading.getTocLine();
|
|
45
|
+
if (tocLine) {
|
|
46
|
+
tocData.push([Number(heading.name.slice(1)), ...tocLine]);
|
|
47
|
+
firstHeading ??= heading;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const { length } = tocData;
|
|
52
|
+
if (forcetoc && length || length > 3) {
|
|
53
|
+
const levels = [], tocNumbers = [];
|
|
54
|
+
let toc = tocContainer, i = 0;
|
|
55
|
+
for (const [level, id, text] of tocData) {
|
|
56
|
+
const n = levels.length;
|
|
57
|
+
let j = levels.findIndex(l => l >= level), prefix;
|
|
58
|
+
if (j === -1) {
|
|
59
|
+
j = n;
|
|
60
|
+
prefix = '\n<ul>';
|
|
61
|
+
levels.push(level);
|
|
62
|
+
tocNumbers.push(1);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
prefix = `${'</li>\n</ul>\n'.repeat(n - j - 1)}</li>`;
|
|
66
|
+
levels.splice(j, Infinity, level);
|
|
67
|
+
tocNumbers.length = j + 1;
|
|
68
|
+
tocNumbers[j]++;
|
|
69
|
+
}
|
|
70
|
+
toc += `${prefix}\n<li class="toclevel-${j + 1} tocsection-${++i}"><a href="#${id}"><span class="tocnumber">${tocNumbers.join('.')}</span> <span class="toctext">${text.replaceAll(/<(\/?)([a-z]\w*)\b.*?>/gu, (_, slash, tag) => allowed.has(tag) ? `<${slash}${tag}>` : '').trim()}</span></a>`;
|
|
71
|
+
}
|
|
72
|
+
toc = i === 0 ? '' : `${toc}${'</li>\n</ul>\n'.repeat(levels.length)}</div>\n`;
|
|
73
|
+
if (tocSwitch) {
|
|
74
|
+
tocSwitch.tocData = toc;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
firstHeading.tocData = toc;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// 后处理引用
|
|
18
82
|
const lines = token.toHtmlInternal().split('\n');
|
|
83
|
+
if (hasCite && lines.at(-1) === '') {
|
|
84
|
+
lines.pop();
|
|
85
|
+
}
|
|
86
|
+
// 处理正文
|
|
19
87
|
let output = '', inBlockElem = false, pendingPTag = false, inBlockquote = false, lastParagraph = '';
|
|
20
88
|
const /** @ignore */ closeParagraph = () => {
|
|
21
89
|
if (lastParagraph) {
|
|
@@ -78,9 +146,10 @@ const toHtml = (token) => {
|
|
|
78
146
|
}
|
|
79
147
|
}
|
|
80
148
|
output += closeParagraph();
|
|
149
|
+
let html = output.trimEnd();
|
|
150
|
+
// 处理分类
|
|
81
151
|
const { categories } = constants_1.states.get(token);
|
|
82
152
|
constants_1.states.delete(token);
|
|
83
|
-
let html = output.trimEnd();
|
|
84
153
|
if (categories.size > 0) {
|
|
85
154
|
html += `
|
|
86
155
|
<div id="catlinks" class="catlinks"><div><a href="${token.normalizeTitle('Special:Categories', -1, { temporary: true }).getUrl()}" title="Special:Categories">Categories</a>: <ul>${[...categories].map(catlink => `<li>${catlink}</li>`).join('')}</div></div>`;
|