wikiparser-node 0.3.0 → 0.4.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/.eslintrc.json +472 -34
- package/README.md +1 -1
- package/config/default.json +58 -30
- package/config/llwiki.json +22 -90
- package/config/moegirl.json +51 -13
- package/config/zhwiki.json +1269 -0
- package/index.js +114 -104
- package/lib/element.js +448 -440
- package/lib/node.js +335 -115
- package/lib/ranges.js +27 -18
- package/lib/text.js +146 -0
- package/lib/title.js +13 -5
- package/mixin/attributeParent.js +70 -24
- package/mixin/fixedToken.js +14 -6
- package/mixin/hidden.js +6 -4
- package/mixin/sol.js +27 -10
- package/package.json +9 -3
- package/parser/brackets.js +22 -17
- package/parser/commentAndExt.js +18 -16
- package/parser/converter.js +14 -13
- package/parser/externalLinks.js +12 -11
- package/parser/hrAndDoubleUnderscore.js +23 -14
- package/parser/html.js +10 -9
- package/parser/links.js +15 -14
- package/parser/list.js +12 -11
- package/parser/magicLinks.js +12 -11
- package/parser/quotes.js +6 -5
- package/parser/selector.js +175 -0
- package/parser/table.js +25 -18
- package/printed/example.json +120 -0
- package/src/arg.js +56 -32
- package/src/atom/hidden.js +5 -2
- package/src/atom/index.js +17 -9
- package/src/attribute.js +182 -100
- package/src/converter.js +68 -41
- package/src/converterFlags.js +67 -45
- package/src/converterRule.js +117 -65
- package/src/extLink.js +66 -18
- package/src/gallery.js +42 -15
- package/src/heading.js +34 -15
- package/src/html.js +97 -35
- package/src/imageParameter.js +83 -54
- package/src/index.js +299 -178
- package/src/link/category.js +20 -52
- package/src/link/file.js +59 -28
- package/src/link/galleryImage.js +21 -7
- package/src/link/index.js +146 -60
- package/src/magicLink.js +34 -12
- package/src/nowiki/comment.js +22 -10
- package/src/nowiki/dd.js +37 -22
- package/src/nowiki/doubleUnderscore.js +16 -7
- package/src/nowiki/hr.js +11 -7
- package/src/nowiki/index.js +16 -9
- package/src/nowiki/list.js +2 -2
- package/src/nowiki/noinclude.js +8 -4
- package/src/nowiki/quote.js +11 -7
- package/src/onlyinclude.js +19 -7
- package/src/parameter.js +65 -38
- package/src/syntax.js +26 -20
- package/src/table/index.js +260 -165
- package/src/table/td.js +98 -52
- package/src/table/tr.js +102 -58
- package/src/tagPair/ext.js +27 -19
- package/src/tagPair/include.js +16 -11
- package/src/tagPair/index.js +64 -29
- package/src/transclude.js +170 -93
- package/test/api.js +83 -0
- package/test/real.js +133 -0
- package/test/test.js +28 -0
- package/test/util.js +80 -0
- package/tool/index.js +41 -31
- package/typings/api.d.ts +13 -0
- package/typings/array.d.ts +28 -0
- package/typings/event.d.ts +24 -0
- package/typings/index.d.ts +46 -4
- package/typings/node.d.ts +15 -9
- package/typings/parser.d.ts +7 -0
- package/typings/tool.d.ts +3 -2
- package/util/debug.js +21 -18
- package/util/string.js +40 -27
- package/typings/element.d.ts +0 -28
package/src/gallery.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
/** @type {Parser} */ Parser = require('..'),
|
|
3
|
+
const Parser = require('..'),
|
|
5
4
|
Token = require('.'),
|
|
6
|
-
GalleryImageToken = require('./link/galleryImage')
|
|
5
|
+
GalleryImageToken = require('./link/galleryImage'),
|
|
6
|
+
HiddenToken = require('./atom/hidden');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* gallery标签
|
|
10
|
-
* @classdesc `{childNodes: (
|
|
10
|
+
* @classdesc `{childNodes: ...(GalleryImageToken|HiddenToken|AstText)}`
|
|
11
11
|
*/
|
|
12
12
|
class GalleryToken extends Token {
|
|
13
13
|
type = 'ext-inner';
|
|
14
14
|
name = 'gallery';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @param {string} inner
|
|
17
|
+
* @param {string} inner 标签内部wikitext
|
|
18
18
|
* @param {accum} accum
|
|
19
19
|
*/
|
|
20
20
|
constructor(inner, config = Parser.getConfig(), accum = []) {
|
|
21
|
-
super(undefined, config, true, accum, {
|
|
21
|
+
super(undefined, config, true, accum, {AstText: ':', GalleryImageToken: ':'});
|
|
22
22
|
for (const line of inner?.split('\n') ?? []) {
|
|
23
|
-
const matches =
|
|
23
|
+
const matches = /^([^|]+)(?:\|(.*))?/u.exec(line);
|
|
24
24
|
if (!matches) {
|
|
25
|
-
this.appendChild(line);
|
|
25
|
+
this.appendChild(line.trim() ? new HiddenToken(line, undefined, config, [], {AstText: ':'}) : line);
|
|
26
26
|
continue;
|
|
27
27
|
}
|
|
28
28
|
const [, file, alt] = matches;
|
|
@@ -32,31 +32,58 @@ class GalleryToken extends Token {
|
|
|
32
32
|
} catch {
|
|
33
33
|
title = this.normalizeTitle(file, 6, true);
|
|
34
34
|
}
|
|
35
|
-
if (
|
|
36
|
-
this.appendChild(line);
|
|
37
|
-
} else {
|
|
35
|
+
if (title.valid) {
|
|
38
36
|
this.appendChild(new GalleryImageToken(file, alt, title, config, accum));
|
|
37
|
+
} else {
|
|
38
|
+
this.appendChild(new HiddenToken(line, undefined, config, [], {AstText: ':'}));
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** @override */
|
|
43
44
|
cloneNode() {
|
|
44
|
-
const cloned = this.
|
|
45
|
+
const cloned = this.cloneChildNodes(),
|
|
45
46
|
token = Parser.run(() => new GalleryToken(undefined, this.getAttribute('config')));
|
|
46
47
|
token.append(...cloned);
|
|
47
48
|
return token;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @override
|
|
53
|
+
* @param {string} selector
|
|
54
|
+
*/
|
|
55
|
+
toString(selector) {
|
|
56
|
+
return super.toString(selector, '\n');
|
|
52
57
|
}
|
|
53
58
|
|
|
59
|
+
/** @override */
|
|
54
60
|
getGaps() {
|
|
55
61
|
return 1;
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
/** @override */
|
|
58
65
|
text() {
|
|
59
|
-
return text(
|
|
66
|
+
return super.text('\n').replaceAll(/\n\s*\n/gu, '\n');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 插入图片
|
|
71
|
+
* @param {string} file 图片文件名
|
|
72
|
+
* @param {number} i 插入位置
|
|
73
|
+
* @throws `SyntaxError` 非法的文件名
|
|
74
|
+
*/
|
|
75
|
+
insertImage(file, i = this.childNodes.length) {
|
|
76
|
+
let title;
|
|
77
|
+
try {
|
|
78
|
+
title = this.normalizeTitle(decodeURIComponent(file), 6, true);
|
|
79
|
+
} catch {
|
|
80
|
+
title = this.normalizeTitle(file, 6, true);
|
|
81
|
+
}
|
|
82
|
+
if (!title.valid) {
|
|
83
|
+
throw new SyntaxError(`非法的文件名:${file}`);
|
|
84
|
+
}
|
|
85
|
+
const token = Parser.run(() => new GalleryImageToken(file, undefined, title, this.getAttribute('config')));
|
|
86
|
+
return this.insertAt(token, i);
|
|
60
87
|
}
|
|
61
88
|
}
|
|
62
89
|
|
package/src/heading.js
CHANGED
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
const fixedToken = require('../mixin/fixedToken'),
|
|
4
4
|
sol = require('../mixin/sol'),
|
|
5
|
-
|
|
5
|
+
Parser = require('..'),
|
|
6
6
|
Token = require('.');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* 章节标题
|
|
10
|
-
* @classdesc `{childNodes: [Token,
|
|
10
|
+
* @classdesc `{childNodes: [Token, SyntaxToken]}`
|
|
11
11
|
*/
|
|
12
12
|
class HeadingToken extends fixedToken(sol(Token)) {
|
|
13
13
|
type = 'heading';
|
|
14
14
|
|
|
15
|
+
/** 内部wikitext */
|
|
16
|
+
get innerText() {
|
|
17
|
+
return this.firstElementChild.text();
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
|
-
* @param {number} level
|
|
17
|
-
* @param {string[]} input
|
|
21
|
+
* @param {number} level 标题层级
|
|
22
|
+
* @param {string[]} input 标题文字
|
|
18
23
|
* @param {accum} accum
|
|
19
24
|
*/
|
|
20
25
|
constructor(level, input, config = Parser.getConfig(), accum = []) {
|
|
@@ -23,38 +28,48 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
23
28
|
const token = new Token(input[0], config, true, accum);
|
|
24
29
|
token.type = 'heading-title';
|
|
25
30
|
token.setAttribute('name', this.name).setAttribute('stage', 2);
|
|
26
|
-
const SyntaxToken = require('./syntax')
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
const SyntaxToken = require('./syntax');
|
|
32
|
+
const trail = new SyntaxToken(input[1], /^[^\S\n]*$/u, 'heading-trail', config, accum, {
|
|
33
|
+
'Stage-1': ':', '!ExtToken': '',
|
|
34
|
+
});
|
|
30
35
|
this.append(token, trail);
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
/** @override */
|
|
33
39
|
cloneNode() {
|
|
34
|
-
const [title, trail] = this.
|
|
40
|
+
const [title, trail] = this.cloneChildNodes(),
|
|
35
41
|
token = Parser.run(() => new HeadingToken(Number(this.name), [], this.getAttribute('config')));
|
|
36
42
|
token.firstElementChild.safeReplaceWith(title);
|
|
37
43
|
token.lastElementChild.safeReplaceWith(trail);
|
|
38
44
|
return token;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
47
|
+
/**
|
|
48
|
+
* @override
|
|
49
|
+
* @this {{prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'} & HeadingToken}
|
|
50
|
+
* @param {string} selector
|
|
51
|
+
*/
|
|
52
|
+
toString(selector) {
|
|
43
53
|
const equals = '='.repeat(Number(this.name));
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
return selector && this.matches(selector)
|
|
55
|
+
? ''
|
|
56
|
+
: `${this.prependNewLine()}${equals}${
|
|
57
|
+
this.firstElementChild.toString(selector)
|
|
58
|
+
}${equals}${this.lastElementChild.toString(selector)}${this.appendNewLine()}`;
|
|
47
59
|
}
|
|
48
60
|
|
|
61
|
+
/** @override */
|
|
49
62
|
getPadding() {
|
|
50
63
|
return super.getPadding() + Number(this.name);
|
|
51
64
|
}
|
|
52
65
|
|
|
66
|
+
/** @override */
|
|
53
67
|
getGaps() {
|
|
54
68
|
return Number(this.name);
|
|
55
69
|
}
|
|
56
70
|
|
|
57
71
|
/**
|
|
72
|
+
* @override
|
|
58
73
|
* @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}}
|
|
59
74
|
* @returns {string}
|
|
60
75
|
*/
|
|
@@ -63,7 +78,10 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
63
78
|
return `${this.prependNewLine()}${equals}${this.firstElementChild.text()}${equals}${this.appendNewLine()}`;
|
|
64
79
|
}
|
|
65
80
|
|
|
66
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* 设置标题层级
|
|
83
|
+
* @param {number} n 标题层级
|
|
84
|
+
*/
|
|
67
85
|
setLevel(n) {
|
|
68
86
|
if (typeof n !== 'number') {
|
|
69
87
|
this.typeError('setLevel', 'Number');
|
|
@@ -72,6 +90,7 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
72
90
|
this.setAttribute('name', String(n)).firstElementChild.setAttribute('name', this.name);
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
/** 移除标题后的不可见内容 */
|
|
75
94
|
removeTrail() {
|
|
76
95
|
this.lastElementChild.replaceChildren();
|
|
77
96
|
}
|
package/src/html.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const {noWrap} = require('../util/string'),
|
|
4
4
|
fixedToken = require('../mixin/fixedToken'),
|
|
5
5
|
attributeParent = require('../mixin/attributeParent'),
|
|
6
|
-
|
|
6
|
+
Parser = require('..'),
|
|
7
7
|
Token = require('.');
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -12,57 +12,108 @@ const {noWrap} = require('../util/string'),
|
|
|
12
12
|
*/
|
|
13
13
|
class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
14
14
|
type = 'html';
|
|
15
|
-
closing;
|
|
16
|
-
selfClosing;
|
|
15
|
+
#closing;
|
|
16
|
+
#selfClosing;
|
|
17
17
|
#tag;
|
|
18
18
|
|
|
19
|
+
/** getter */
|
|
20
|
+
get closing() {
|
|
21
|
+
return this.#closing;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** @throws `Error` 自闭合标签或空标签 */
|
|
25
|
+
set closing(value) {
|
|
26
|
+
if (!value) {
|
|
27
|
+
this.#closing = false;
|
|
28
|
+
return;
|
|
29
|
+
} else if (this.#selfClosing) {
|
|
30
|
+
throw new Error(`这是一个自闭合标签!`);
|
|
31
|
+
}
|
|
32
|
+
const {html: [,, tags]} = this.getAttribute('config');
|
|
33
|
+
if (tags.includes(this.name)) {
|
|
34
|
+
throw new Error(`这是一个空标签!`);
|
|
35
|
+
}
|
|
36
|
+
this.#closing = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** getter */
|
|
40
|
+
get selfClosing() {
|
|
41
|
+
return this.#selfClosing;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @throws `Error` 闭合标签或无效自闭合标签 */
|
|
45
|
+
set selfClosing(value) {
|
|
46
|
+
if (!value) {
|
|
47
|
+
this.#selfClosing = false;
|
|
48
|
+
return;
|
|
49
|
+
} else if (this.#closing) {
|
|
50
|
+
throw new Error('这是一个闭合标签!');
|
|
51
|
+
}
|
|
52
|
+
const {html: [tags]} = this.getAttribute('config');
|
|
53
|
+
if (tags.includes(this.name)) {
|
|
54
|
+
throw new Error(`<${this.name}>标签自闭合无效!`);
|
|
55
|
+
}
|
|
56
|
+
this.#selfClosing = true;
|
|
57
|
+
}
|
|
58
|
+
|
|
19
59
|
/**
|
|
20
|
-
* @param {string} name
|
|
21
|
-
* @param {AttributeToken} attr
|
|
22
|
-
* @param {boolean} closing
|
|
23
|
-
* @param {boolean} selfClosing
|
|
60
|
+
* @param {string} name 标签名
|
|
61
|
+
* @param {AttributeToken} attr 标签属性
|
|
62
|
+
* @param {boolean} closing 是否闭合
|
|
63
|
+
* @param {boolean} selfClosing 是否自封闭
|
|
24
64
|
* @param {accum} accum
|
|
25
65
|
*/
|
|
26
66
|
constructor(name, attr, closing, selfClosing, config = Parser.getConfig(), accum = []) {
|
|
27
67
|
super(undefined, config, true, accum);
|
|
28
68
|
this.appendChild(attr);
|
|
29
69
|
this.setAttribute('name', name.toLowerCase());
|
|
30
|
-
this
|
|
31
|
-
this
|
|
70
|
+
this.#closing = closing;
|
|
71
|
+
this.#selfClosing = selfClosing;
|
|
32
72
|
this.#tag = name;
|
|
33
73
|
}
|
|
34
74
|
|
|
75
|
+
/** @override */
|
|
35
76
|
cloneNode() {
|
|
36
|
-
const [attr] = this.
|
|
77
|
+
const [attr] = this.cloneChildNodes(),
|
|
37
78
|
config = this.getAttribute('config');
|
|
38
|
-
return Parser.run(() => new HtmlToken(this.#tag, attr, this
|
|
79
|
+
return Parser.run(() => new HtmlToken(this.#tag, attr, this.#closing, this.#selfClosing, config));
|
|
39
80
|
}
|
|
40
81
|
|
|
41
82
|
/**
|
|
83
|
+
* @override
|
|
42
84
|
* @template {string} T
|
|
43
|
-
* @param {T} key
|
|
85
|
+
* @param {T} key 属性键
|
|
44
86
|
* @returns {TokenAttribute<T>}
|
|
45
87
|
*/
|
|
46
88
|
getAttribute(key) {
|
|
47
|
-
|
|
48
|
-
return this.#tag;
|
|
49
|
-
}
|
|
50
|
-
return super.getAttribute(key);
|
|
89
|
+
return key === 'tag' ? this.#tag : super.getAttribute(key);
|
|
51
90
|
}
|
|
52
91
|
|
|
53
|
-
|
|
54
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @override
|
|
94
|
+
* @param {string} selector
|
|
95
|
+
*/
|
|
96
|
+
toString(selector) {
|
|
97
|
+
return selector && this.matches(selector)
|
|
98
|
+
? ''
|
|
99
|
+
: `<${this.#closing ? '/' : ''}${this.#tag}${super.toString(selector)}${this.#selfClosing ? '/' : ''}>`;
|
|
55
100
|
}
|
|
56
101
|
|
|
102
|
+
/** @override */
|
|
57
103
|
getPadding() {
|
|
58
|
-
return this.#tag.length + (this
|
|
104
|
+
return this.#tag.length + (this.#closing ? 2 : 1);
|
|
59
105
|
}
|
|
60
106
|
|
|
107
|
+
/** @override */
|
|
61
108
|
text() {
|
|
62
|
-
return `<${this
|
|
109
|
+
return `<${this.#closing ? '/' : ''}${this.#tag}${super.text()}${this.#selfClosing ? '/' : ''}>`;
|
|
63
110
|
}
|
|
64
111
|
|
|
65
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* 更换标签名
|
|
114
|
+
* @param {string} tag 标签名
|
|
115
|
+
* @throws `RangeError` 非法的HTML标签
|
|
116
|
+
*/
|
|
66
117
|
replaceTag(tag) {
|
|
67
118
|
const name = tag.toLowerCase();
|
|
68
119
|
if (!this.getAttribute('config').html.flat().includes(name)) {
|
|
@@ -71,21 +122,27 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
71
122
|
this.setAttribute('name', name).#tag = tag;
|
|
72
123
|
}
|
|
73
124
|
|
|
74
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* 搜索匹配的标签
|
|
127
|
+
* @complexity `n`
|
|
128
|
+
* @throws `SyntaxError` 同时闭合和自封闭的标签
|
|
129
|
+
* @throws `SyntaxError` 无效自封闭标签
|
|
130
|
+
* @throws `SyntaxError` 未闭合的标签
|
|
131
|
+
*/
|
|
75
132
|
findMatchingTag() {
|
|
76
133
|
const {html} = this.getAttribute('config'),
|
|
77
|
-
{name,
|
|
78
|
-
string = noWrap(this
|
|
134
|
+
{name, parentNode, closing, selfClosing} = this,
|
|
135
|
+
string = noWrap(String(this));
|
|
79
136
|
if (closing && selfClosing) {
|
|
80
137
|
throw new SyntaxError(`同时闭合和自封闭的标签:${string}`);
|
|
81
138
|
} else if (html[2].includes(name) || selfClosing && html[1].includes(name)) { // 自封闭标签
|
|
82
139
|
return this;
|
|
83
140
|
} else if (selfClosing && html[0].includes(name)) {
|
|
84
141
|
throw new SyntaxError(`无效自封闭标签:${string}`);
|
|
85
|
-
} else if (!
|
|
86
|
-
return;
|
|
142
|
+
} else if (!parentNode) {
|
|
143
|
+
return undefined;
|
|
87
144
|
}
|
|
88
|
-
const {children} =
|
|
145
|
+
const {children} = parentNode,
|
|
89
146
|
i = children.indexOf(this),
|
|
90
147
|
selector = `html#${name}`,
|
|
91
148
|
siblings = closing
|
|
@@ -105,32 +162,37 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
105
162
|
throw new SyntaxError(`未${closing ? '匹配的闭合' : '闭合的'}标签:${string}`);
|
|
106
163
|
}
|
|
107
164
|
|
|
165
|
+
/** 局部闭合 */
|
|
108
166
|
#localMatch() {
|
|
109
|
-
this
|
|
167
|
+
this.#selfClosing = false;
|
|
110
168
|
const root = Parser.parse(`</${this.name}>`, false, 3, this.getAttribute('config'));
|
|
111
169
|
this.after(root.firstChild);
|
|
112
170
|
}
|
|
113
171
|
|
|
114
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* 修复无效自封闭标签
|
|
174
|
+
* @complexity `n`
|
|
175
|
+
* @throws `Error` 无法修复无效自封闭标签
|
|
176
|
+
*/
|
|
115
177
|
fix() {
|
|
116
178
|
const config = this.getAttribute('config'),
|
|
117
|
-
{
|
|
118
|
-
if (!
|
|
179
|
+
{parentNode, name, firstElementChild} = this;
|
|
180
|
+
if (!parentNode || !this.#selfClosing || !config.html[0].includes(name)) {
|
|
119
181
|
return;
|
|
120
182
|
} else if (firstElementChild.text().trim()) {
|
|
121
183
|
this.#localMatch();
|
|
122
184
|
return;
|
|
123
185
|
}
|
|
124
|
-
const {children} =
|
|
186
|
+
const {children} = parentNode,
|
|
125
187
|
i = children.indexOf(this),
|
|
126
188
|
/** @type {HtmlToken[]} */
|
|
127
189
|
prevSiblings = children.slice(0, i).filter(child => child.matches(`html#${name}`)),
|
|
128
190
|
imbalance = prevSiblings.reduce((acc, {closing}) => acc + (closing ? 1 : -1), 0);
|
|
129
191
|
if (imbalance < 0) {
|
|
130
|
-
this
|
|
131
|
-
this
|
|
192
|
+
this.#selfClosing = false;
|
|
193
|
+
this.#closing = true;
|
|
132
194
|
} else {
|
|
133
|
-
Parser.warn('无法修复无效自封闭标签', noWrap(this
|
|
195
|
+
Parser.warn('无法修复无效自封闭标签', noWrap(String(this)));
|
|
134
196
|
throw new Error(`无法修复无效自封闭标签:前文共有 ${imbalance} 个未匹配的闭合标签`);
|
|
135
197
|
}
|
|
136
198
|
}
|