wikiparser-node 0.4.0 → 0.5.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/index.js +25 -2
- package/lib/element.js +69 -185
- package/lib/node.js +159 -1
- package/lib/ranges.js +1 -2
- package/lib/text.js +35 -6
- package/lib/title.js +1 -1
- package/mixin/fixedToken.js +4 -4
- package/mixin/sol.js +17 -7
- package/package.json +11 -1
- package/parser/commentAndExt.js +1 -1
- package/parser/converter.js +1 -1
- package/parser/externalLinks.js +1 -1
- package/parser/hrAndDoubleUnderscore.js +6 -5
- package/parser/links.js +1 -2
- package/parser/magicLinks.js +1 -1
- package/parser/selector.js +5 -5
- package/parser/table.js +12 -12
- package/src/arg.js +44 -20
- package/src/attribute.js +34 -7
- package/src/converter.js +13 -5
- package/src/converterFlags.js +42 -5
- package/src/converterRule.js +25 -19
- package/src/extLink.js +20 -14
- package/src/gallery.js +35 -4
- package/src/heading.js +28 -9
- package/src/html.js +46 -18
- package/src/imageParameter.js +13 -7
- package/src/index.js +22 -15
- package/src/link/category.js +6 -6
- package/src/link/file.js +25 -5
- package/src/link/index.js +36 -33
- package/src/magicLink.js +32 -4
- package/src/nowiki/comment.js +14 -0
- package/src/nowiki/doubleUnderscore.js +5 -0
- package/src/nowiki/quote.js +28 -1
- package/src/onlyinclude.js +5 -0
- package/src/parameter.js +48 -35
- package/src/table/index.js +37 -24
- package/src/table/td.js +23 -17
- package/src/table/tr.js +47 -30
- package/src/tagPair/ext.js +4 -5
- package/src/tagPair/include.js +10 -0
- package/src/tagPair/index.js +8 -0
- package/src/transclude.js +79 -46
- package/tool/index.js +1 -1
- package/{test/util.js → util/diff.js} +14 -18
- package/util/lint.js +40 -0
- package/util/string.js +20 -3
- package/.eslintrc.json +0 -714
- package/errors/README +0 -1
- package/jsconfig.json +0 -7
- package/printed/README +0 -1
- package/printed/example.json +0 -120
- package/test/api.js +0 -83
- package/test/real.js +0 -133
- package/test/test.js +0 -28
- package/typings/api.d.ts +0 -13
- package/typings/array.d.ts +0 -28
- package/typings/event.d.ts +0 -24
- package/typings/index.d.ts +0 -94
- package/typings/node.d.ts +0 -29
- package/typings/parser.d.ts +0 -16
- package/typings/table.d.ts +0 -14
- package/typings/token.d.ts +0 -22
- package/typings/tool.d.ts +0 -11
package/src/table/td.js
CHANGED
|
@@ -49,12 +49,12 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
49
49
|
|
|
50
50
|
/** 内部wikitext */
|
|
51
51
|
get innerText() {
|
|
52
|
-
return this.
|
|
52
|
+
return this.lastChild.text();
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/** 是否位于行首 */
|
|
56
56
|
isIndependent() {
|
|
57
|
-
return this.
|
|
57
|
+
return this.firstChild.text()[0] === '\n';
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -63,7 +63,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
63
63
|
* @complexity `n`
|
|
64
64
|
*/
|
|
65
65
|
getSyntax() {
|
|
66
|
-
const syntax = this.
|
|
66
|
+
const syntax = this.firstChild.text(),
|
|
67
67
|
esc = syntax.includes('{{'),
|
|
68
68
|
char = syntax.at(-1);
|
|
69
69
|
let subtype = 'td';
|
|
@@ -75,13 +75,13 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
75
75
|
if (this.isIndependent()) {
|
|
76
76
|
return {subtype, escape: esc, correction: false};
|
|
77
77
|
}
|
|
78
|
-
const {
|
|
79
|
-
if (
|
|
78
|
+
const {previousSibling} = this;
|
|
79
|
+
if (previousSibling?.type !== 'td') {
|
|
80
80
|
return {subtype, escape: esc, correction: true};
|
|
81
81
|
}
|
|
82
|
-
const result =
|
|
82
|
+
const result = previousSibling.getSyntax();
|
|
83
83
|
result.escape ||= esc;
|
|
84
|
-
result.correction =
|
|
84
|
+
result.correction = previousSibling.lastChild
|
|
85
85
|
.toString('comment, ext, include, noinclude, arg, template, magic-word, html')
|
|
86
86
|
.includes('\n');
|
|
87
87
|
if (subtype === 'th' && result.subtype !== 'th') {
|
|
@@ -144,7 +144,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
144
144
|
}
|
|
145
145
|
const token = Parser.run(() => new TdToken('\n|', undefined, config));
|
|
146
146
|
token.setSyntax(subtype);
|
|
147
|
-
token.
|
|
147
|
+
token.lastChild.safeReplaceWith(inner);
|
|
148
148
|
for (const [k, v] of Object.entries(attr)) {
|
|
149
149
|
token.setAttr(k, v);
|
|
150
150
|
}
|
|
@@ -201,7 +201,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
201
201
|
* @complexity `n`
|
|
202
202
|
*/
|
|
203
203
|
#correct() {
|
|
204
|
-
if (String(this.
|
|
204
|
+
if (String(this.childNodes[1])) {
|
|
205
205
|
this.#innerSyntax ||= '|';
|
|
206
206
|
}
|
|
207
207
|
const {subtype, escape, correction} = this.getSyntax();
|
|
@@ -229,7 +229,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
229
229
|
*/
|
|
230
230
|
toString(selector) {
|
|
231
231
|
this.#correct();
|
|
232
|
-
const {
|
|
232
|
+
const {childNodes: [syntax, attr, inner]} = this;
|
|
233
233
|
return selector && this.matches(selector)
|
|
234
234
|
? ''
|
|
235
235
|
: `${syntax.toString(selector)}${attr.toString(selector)}${this.#innerSyntax}${inner.toString(selector)}`;
|
|
@@ -241,11 +241,17 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
241
241
|
*/
|
|
242
242
|
getGaps(i = 0) {
|
|
243
243
|
i = i < 0 ? i + this.childNodes.length : i;
|
|
244
|
-
if (i
|
|
245
|
-
|
|
244
|
+
if (i === 1) {
|
|
245
|
+
this.#correct();
|
|
246
|
+
return this.#innerSyntax.length;
|
|
246
247
|
}
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
return 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** @override */
|
|
252
|
+
print() {
|
|
253
|
+
const {childNodes: [syntax, attr, inner]} = this;
|
|
254
|
+
return `<span class="wpb-td">${syntax.print()}${attr.print()}${this.#innerSyntax}${inner.print()}</span>`;
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
/**
|
|
@@ -255,7 +261,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
255
261
|
*/
|
|
256
262
|
text() {
|
|
257
263
|
this.#correct();
|
|
258
|
-
const {
|
|
264
|
+
const {childNodes: [syntax, attr, inner]} = this;
|
|
259
265
|
return `${syntax.text()}${attr.text()}${this.#innerSyntax}${inner.text()}`;
|
|
260
266
|
}
|
|
261
267
|
|
|
@@ -286,7 +292,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
286
292
|
value = value === 1 ? false : String(value);
|
|
287
293
|
}
|
|
288
294
|
const /** @type {boolean} */ result = super.setAttr(key, value);
|
|
289
|
-
if (!String(this.
|
|
295
|
+
if (!String(this.childNodes[1])) {
|
|
290
296
|
this.#innerSyntax = '';
|
|
291
297
|
}
|
|
292
298
|
return result;
|
|
@@ -295,7 +301,7 @@ class TdToken extends fixedToken(TrToken) {
|
|
|
295
301
|
/** @override */
|
|
296
302
|
escape() {
|
|
297
303
|
super.escape();
|
|
298
|
-
if (String(this.
|
|
304
|
+
if (String(this.childNodes[1])) {
|
|
299
305
|
this.#innerSyntax ||= '{{!}}';
|
|
300
306
|
}
|
|
301
307
|
if (this.#innerSyntax === '|') {
|
package/src/table/tr.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {generateForChild} = require('../../util/lint'),
|
|
4
|
+
attributeParent = require('../../mixin/attributeParent'),
|
|
4
5
|
Parser = require('../..'),
|
|
5
6
|
AstText = require('../../lib/text'),
|
|
6
7
|
Token = require('..'),
|
|
@@ -14,11 +15,10 @@ const openingPattern = /^\n[^\S\n]*(?:\|-+|\{\{\s*!\s*\}\}-+|\{\{\s*!-\s*\}\}-*)
|
|
|
14
15
|
* @param {SyntaxToken} syntax 表格语法节点
|
|
15
16
|
*/
|
|
16
17
|
const escapeTable = syntax => {
|
|
17
|
-
const
|
|
18
|
+
const templates = {'{|': '(!', '|}': '!)', '||': '!!', '|': '!'},
|
|
19
|
+
wikitext = syntax.childNodes.map(
|
|
18
20
|
child => child.type === 'text'
|
|
19
|
-
? String(child).replaceAll(
|
|
20
|
-
.replaceAll('||', '{{!!}}')
|
|
21
|
-
.replaceAll('|', '{{!}}')
|
|
21
|
+
? String(child).replaceAll(/\{\||\|\}|\|{2}|\|/gu, p => `{{${templates[p]}}}`)
|
|
22
22
|
: String(child),
|
|
23
23
|
).join(''),
|
|
24
24
|
token = Parser.parse(wikitext, syntax.getAttribute('include'), 2, syntax.getAttribute('config'));
|
|
@@ -49,6 +49,23 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
49
49
|
this.getAttribute('protectChildren')(0, 1);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @override
|
|
54
|
+
* @param {number} start 起始位置
|
|
55
|
+
*/
|
|
56
|
+
lint(start = 0) {
|
|
57
|
+
const errors = super.lint(start),
|
|
58
|
+
inter = this.childNodes.find(({type}) => type === 'table-inter'),
|
|
59
|
+
str = String(inter).trim();
|
|
60
|
+
if (inter && str && !/^<!--.*-->$/u.test(str)) {
|
|
61
|
+
const error = generateForChild(inter, this.getRootNode().posFromIndex(start), '将被移出表格的内容');
|
|
62
|
+
error.startLine++;
|
|
63
|
+
error.startCol = 0;
|
|
64
|
+
errors.push(error);
|
|
65
|
+
}
|
|
66
|
+
return errors;
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
/**
|
|
53
70
|
* @override
|
|
54
71
|
* @this {TrToken & {constructor: typeof TrToken}}
|
|
@@ -57,10 +74,10 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
57
74
|
const [syntax, attr, inner, ...cloned] = this.cloneChildNodes();
|
|
58
75
|
return Parser.run(() => {
|
|
59
76
|
const token = new this.constructor(undefined, undefined, this.getAttribute('config'));
|
|
60
|
-
token.
|
|
61
|
-
token.
|
|
77
|
+
token.firstChild.safeReplaceWith(syntax);
|
|
78
|
+
token.childNodes[1].safeReplaceWith(attr);
|
|
62
79
|
if (token.type === 'td') { // TdToken
|
|
63
|
-
token.
|
|
80
|
+
token.childNodes[2].safeReplaceWith(inner);
|
|
64
81
|
} else if (inner !== undefined) {
|
|
65
82
|
token.appendChild(inner);
|
|
66
83
|
}
|
|
@@ -71,13 +88,13 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
71
88
|
|
|
72
89
|
/** 修复简单的表格语法错误 */
|
|
73
90
|
#correct() {
|
|
74
|
-
const {
|
|
91
|
+
const {childNodes: [,, child]} = this;
|
|
75
92
|
if (child?.isPlain()) {
|
|
76
|
-
const /** @type {{firstChild: AstText}} */ {firstChild
|
|
77
|
-
if (type !== 'text') {
|
|
93
|
+
const /** @type {{firstChild: AstText}} */ {firstChild} = child;
|
|
94
|
+
if (firstChild.type !== 'text') {
|
|
78
95
|
child.prepend('\n');
|
|
79
|
-
} else if (data[0] !== '\n') {
|
|
80
|
-
|
|
96
|
+
} else if (firstChild.data[0] !== '\n') {
|
|
97
|
+
firstChild.insertData(0, '\n');
|
|
81
98
|
}
|
|
82
99
|
}
|
|
83
100
|
}
|
|
@@ -103,7 +120,7 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
103
120
|
* @complexity `n`
|
|
104
121
|
*/
|
|
105
122
|
escape() {
|
|
106
|
-
for (const child of this.
|
|
123
|
+
for (const child of this.childNodes) {
|
|
107
124
|
if (child instanceof SyntaxToken) {
|
|
108
125
|
escapeTable(child);
|
|
109
126
|
} else if (child instanceof TrToken) {
|
|
@@ -118,10 +135,10 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
118
135
|
* @param {boolean} esc 是否需要转义
|
|
119
136
|
*/
|
|
120
137
|
setSyntax(syntax, esc) {
|
|
121
|
-
const {
|
|
122
|
-
|
|
138
|
+
const {firstChild} = this;
|
|
139
|
+
firstChild.replaceChildren(syntax);
|
|
123
140
|
if (esc) {
|
|
124
|
-
escapeTable(
|
|
141
|
+
escapeTable(firstChild);
|
|
125
142
|
}
|
|
126
143
|
}
|
|
127
144
|
|
|
@@ -134,9 +151,9 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
134
151
|
const TdToken = require('./td');
|
|
135
152
|
const child = this.childNodes.at(i);
|
|
136
153
|
if (child instanceof TdToken && child.isIndependent()) {
|
|
137
|
-
const {
|
|
138
|
-
if (
|
|
139
|
-
|
|
154
|
+
const {nextSibling} = child;
|
|
155
|
+
if (nextSibling?.type === 'td') {
|
|
156
|
+
nextSibling.independence();
|
|
140
157
|
}
|
|
141
158
|
}
|
|
142
159
|
return super.removeAt(i);
|
|
@@ -169,14 +186,14 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
169
186
|
*/
|
|
170
187
|
getRowCount() {
|
|
171
188
|
const TdToken = require('./td');
|
|
172
|
-
return Number(this.
|
|
173
|
-
child => child instanceof TdToken && child.isIndependent() && child.
|
|
189
|
+
return Number(this.childNodes.some(
|
|
190
|
+
child => child instanceof TdToken && child.isIndependent() && child.firstChild.text().at(-1) !== '+',
|
|
174
191
|
));
|
|
175
192
|
}
|
|
176
193
|
|
|
177
194
|
/**
|
|
178
195
|
* 获取相邻行
|
|
179
|
-
* @param {(
|
|
196
|
+
* @param {(childNodes: Token[], index: number) => Token[]} subset 筛选兄弟节点的方法
|
|
180
197
|
* @complexity `n`
|
|
181
198
|
*/
|
|
182
199
|
#getSiblingRow(subset) {
|
|
@@ -184,9 +201,9 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
184
201
|
if (!parentNode) {
|
|
185
202
|
return undefined;
|
|
186
203
|
}
|
|
187
|
-
const {
|
|
188
|
-
index =
|
|
189
|
-
for (const child of subset(
|
|
204
|
+
const {childNodes} = parentNode,
|
|
205
|
+
index = childNodes.indexOf(this);
|
|
206
|
+
for (const child of subset(childNodes, index)) {
|
|
190
207
|
if (child instanceof TrToken && child.getRowCount()) {
|
|
191
208
|
return child;
|
|
192
209
|
}
|
|
@@ -199,7 +216,7 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
199
216
|
* @complexity `n`
|
|
200
217
|
*/
|
|
201
218
|
getNextRow() {
|
|
202
|
-
return this.#getSiblingRow((
|
|
219
|
+
return this.#getSiblingRow((childNodes, index) => childNodes.slice(index + 1));
|
|
203
220
|
}
|
|
204
221
|
|
|
205
222
|
/**
|
|
@@ -207,7 +224,7 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
207
224
|
* @complexity `n`
|
|
208
225
|
*/
|
|
209
226
|
getPreviousRow() {
|
|
210
|
-
return this.#getSiblingRow((
|
|
227
|
+
return this.#getSiblingRow((childNodes, index) => childNodes.slice(0, index).reverse());
|
|
211
228
|
}
|
|
212
229
|
|
|
213
230
|
/**
|
|
@@ -218,7 +235,7 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
218
235
|
const TdToken = require('./td');
|
|
219
236
|
let count = 0,
|
|
220
237
|
last = 0;
|
|
221
|
-
for (const child of this.
|
|
238
|
+
for (const child of this.childNodes) {
|
|
222
239
|
if (child instanceof TdToken) {
|
|
223
240
|
last = child.isIndependent() ? Number(child.subtype !== 'caption') : last;
|
|
224
241
|
count += last;
|
|
@@ -246,7 +263,7 @@ class TrToken extends attributeParent(Token, 1) {
|
|
|
246
263
|
}
|
|
247
264
|
const TdToken = require('./td');
|
|
248
265
|
let last = 0;
|
|
249
|
-
for (const child of this.
|
|
266
|
+
for (const child of this.childNodes.slice(2)) {
|
|
250
267
|
if (child instanceof TdToken) {
|
|
251
268
|
if (child.isIndependent()) {
|
|
252
269
|
last = Number(child.subtype !== 'caption');
|
package/src/tagPair/ext.js
CHANGED
|
@@ -81,8 +81,7 @@ class ExtToken extends attributeParent(TagPairToken) {
|
|
|
81
81
|
innerToken = new NowikiToken(inner, config);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
innerToken.type = 'ext-inner';
|
|
85
|
-
innerToken.setAttribute('name', lcName);
|
|
84
|
+
innerToken.setAttribute('name', lcName).type = 'ext-inner';
|
|
86
85
|
if (lcName === 'pre') {
|
|
87
86
|
innerToken.setAttribute('stage', Parser.MAX_STAGE - 1);
|
|
88
87
|
}
|
|
@@ -91,12 +90,12 @@ class ExtToken extends attributeParent(TagPairToken) {
|
|
|
91
90
|
|
|
92
91
|
/** @override */
|
|
93
92
|
cloneNode() {
|
|
94
|
-
const inner = this.
|
|
93
|
+
const inner = this.lastChild.cloneNode(),
|
|
95
94
|
tags = this.getAttribute('tags'),
|
|
96
95
|
config = this.getAttribute('config'),
|
|
97
|
-
attr = String(this.
|
|
96
|
+
attr = String(this.firstChild),
|
|
98
97
|
token = Parser.run(() => new ExtToken(tags[0], attr, '', this.selfClosing ? undefined : tags[1], config));
|
|
99
|
-
token.
|
|
98
|
+
token.lastChild.safeReplaceWith(inner);
|
|
100
99
|
return token;
|
|
101
100
|
}
|
|
102
101
|
}
|
package/src/tagPair/include.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const hidden = require('../../mixin/hidden'),
|
|
4
|
+
{generateForSelf} = require('../../util/lint'),
|
|
4
5
|
Parser = require('../..'),
|
|
5
6
|
TagPairToken = require('.');
|
|
6
7
|
|
|
@@ -22,6 +23,15 @@ class IncludeToken extends hidden(TagPairToken) {
|
|
|
22
23
|
super(name, attr, inner ?? '', inner === undefined ? closed : closed ?? '', config, accum, {AstText: [0, 1]});
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @override
|
|
28
|
+
* @param {number} start 起始位置
|
|
29
|
+
* @returns {LintError[]}
|
|
30
|
+
*/
|
|
31
|
+
lint(start = 0) {
|
|
32
|
+
return this.closed ? [] : [generateForSelf(this, this.getRootNode().posFromIndex(start), '未闭合的标签')];
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
/** @override */
|
|
26
36
|
cloneNode() {
|
|
27
37
|
const tags = this.getAttribute('tags'),
|
package/src/tagPair/index.js
CHANGED
|
@@ -101,6 +101,14 @@ class TagPairToken extends fixedToken(Token) {
|
|
|
101
101
|
return 1;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/** @override */
|
|
105
|
+
print() {
|
|
106
|
+
const [opening, closing] = this.#tags;
|
|
107
|
+
return super.print(this.#selfClosing
|
|
108
|
+
? {pre: `<${opening}`, post: '/>'}
|
|
109
|
+
: {pre: `<${opening}`, sep: '>', post: this.#closed ? `</${closing}>` : ''});
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
/**
|
|
105
113
|
* @override
|
|
106
114
|
* @returns {string}
|
package/src/transclude.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {removeComment, escapeRegExp, text, noWrap} = require('../util/string'),
|
|
3
|
+
const {removeComment, escapeRegExp, text, noWrap, print} = require('../util/string'),
|
|
4
4
|
{externalUse} = require('../util/debug'),
|
|
5
|
+
{generateForChild} = require('../util/lint'),
|
|
5
6
|
Parser = require('..'),
|
|
6
7
|
Token = require('.'),
|
|
7
8
|
ParameterToken = require('./parameter');
|
|
@@ -64,7 +65,7 @@ class TranscludeToken extends Token {
|
|
|
64
65
|
isSensitive = sensitive.includes(name);
|
|
65
66
|
if (isSensitive || insensitive.includes(name.toLowerCase())) {
|
|
66
67
|
this.setAttribute('name', name.toLowerCase().replace(/^#/u, '')).type = 'magic-word';
|
|
67
|
-
const pattern = new RegExp(`^\\s*${name}\\s*$`, isSensitive ? '' : '
|
|
68
|
+
const pattern = new RegExp(`^\\s*${name}\\s*$`, isSensitive ? 'u' : 'iu'),
|
|
68
69
|
token = new SyntaxToken(magicWord, pattern, 'magic-word-name', config, accum, {
|
|
69
70
|
'Stage-1': ':', '!ExtToken': '',
|
|
70
71
|
});
|
|
@@ -98,7 +99,7 @@ class TranscludeToken extends Token {
|
|
|
98
99
|
const token = new AtomToken(title, 'template-name', config, accum, {'Stage-2': ':', '!HeadingToken': ''});
|
|
99
100
|
this.appendChild(token);
|
|
100
101
|
}
|
|
101
|
-
const templateLike = this.
|
|
102
|
+
const templateLike = this.isTemplate();
|
|
102
103
|
let i = 1;
|
|
103
104
|
for (const part of parts) {
|
|
104
105
|
if (!templateLike) {
|
|
@@ -121,7 +122,7 @@ class TranscludeToken extends Token {
|
|
|
121
122
|
return Parser.run(() => {
|
|
122
123
|
const token = new TranscludeToken(this.type === 'template' ? '' : first.text(), [], config);
|
|
123
124
|
token.setModifier(this.modifier);
|
|
124
|
-
token.
|
|
125
|
+
token.firstChild.safeReplaceWith(first);
|
|
125
126
|
token.afterBuild();
|
|
126
127
|
token.append(...cloned);
|
|
127
128
|
return token;
|
|
@@ -133,7 +134,7 @@ class TranscludeToken extends Token {
|
|
|
133
134
|
if (this.name.includes('\0')) {
|
|
134
135
|
this.setAttribute('name', text(this.getAttribute('buildFromStr')(this.name)));
|
|
135
136
|
}
|
|
136
|
-
if (this.
|
|
137
|
+
if (this.isTemplate()) {
|
|
137
138
|
/**
|
|
138
139
|
* 当事件bubble到`parameter`时,将`oldKey`和`newKey`保存进AstEventData。
|
|
139
140
|
* 当继续bubble到`template`时,处理并删除`oldKey`和`newKey`。
|
|
@@ -146,7 +147,7 @@ class TranscludeToken extends Token {
|
|
|
146
147
|
delete data.oldKey;
|
|
147
148
|
delete data.newKey;
|
|
148
149
|
}
|
|
149
|
-
if (prevTarget === this.
|
|
150
|
+
if (prevTarget === this.firstChild && this.type === 'template') {
|
|
150
151
|
this.setAttribute('name', this.normalizeTitle(prevTarget.text(), 10).title);
|
|
151
152
|
} else if (oldKey !== newKey && prevTarget instanceof ParameterToken) {
|
|
152
153
|
const oldArgs = this.getArgs(oldKey, false, false);
|
|
@@ -196,10 +197,10 @@ class TranscludeToken extends Token {
|
|
|
196
197
|
if (selector && this.matches(selector)) {
|
|
197
198
|
return '';
|
|
198
199
|
}
|
|
199
|
-
const {
|
|
200
|
+
const {childNodes, firstChild, modifier} = this;
|
|
200
201
|
return `{{${modifier}${modifier && ':'}${
|
|
201
202
|
this.type === 'magic-word'
|
|
202
|
-
? `${String(firstChild)}${length > 1 ? ':' : ''}${
|
|
203
|
+
? `${String(firstChild)}${childNodes.length > 1 ? ':' : ''}${childNodes.slice(1).map(String).join('|')}`
|
|
203
204
|
: super.toString(selector, '|')
|
|
204
205
|
}}}`;
|
|
205
206
|
}
|
|
@@ -214,16 +215,50 @@ class TranscludeToken extends Token {
|
|
|
214
215
|
return 1;
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
/** @override */
|
|
219
|
+
print() {
|
|
220
|
+
const {childNodes, firstChild, modifier} = this;
|
|
221
|
+
return `<span class="wpb-${this.type}">{{${modifier}${modifier && ':'}${
|
|
222
|
+
this.type === 'magic-word'
|
|
223
|
+
? `${firstChild.print()}${childNodes.length > 1 ? ':' : ''}${print(childNodes.slice(1), {sep: '|'})}`
|
|
224
|
+
: print(childNodes, {sep: '|'})
|
|
225
|
+
}}}</span>`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @override
|
|
230
|
+
* @param {number} start 起始位置
|
|
231
|
+
*/
|
|
232
|
+
lint(start = 0) {
|
|
233
|
+
const errors = super.lint(start);
|
|
234
|
+
if (!this.isTemplate()) {
|
|
235
|
+
return errors;
|
|
236
|
+
}
|
|
237
|
+
const duplicatedArgs = this.getDuplicatedArgs();
|
|
238
|
+
if (duplicatedArgs.length > 0) {
|
|
239
|
+
const rect = this.getRootNode().posFromIndex(start);
|
|
240
|
+
errors.push(...duplicatedArgs.flatMap(([, args]) => [...args]).map(
|
|
241
|
+
arg => generateForChild(arg, rect, '重复参数'),
|
|
242
|
+
));
|
|
243
|
+
}
|
|
244
|
+
return errors;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** 是否是模板 */
|
|
248
|
+
isTemplate() {
|
|
249
|
+
return this.type === 'template' || this.type === 'magic-word' && this.name === 'invoke';
|
|
250
|
+
}
|
|
251
|
+
|
|
217
252
|
/**
|
|
218
253
|
* @override
|
|
219
254
|
* @returns {string}
|
|
220
255
|
* @complexity `n`
|
|
221
256
|
*/
|
|
222
257
|
text() {
|
|
223
|
-
const {
|
|
258
|
+
const {childNodes, firstChild, modifier} = this;
|
|
224
259
|
return `{{${modifier}${modifier && ':'}${
|
|
225
260
|
this.type === 'magic-word'
|
|
226
|
-
? `${
|
|
261
|
+
? `${firstChild.text()}${childNodes.length > 1 ? ':' : ''}${text(childNodes.slice(1), '|')}`
|
|
227
262
|
: super.text('|')
|
|
228
263
|
}}}`;
|
|
229
264
|
}
|
|
@@ -298,7 +333,7 @@ class TranscludeToken extends Token {
|
|
|
298
333
|
* @complexity `n`
|
|
299
334
|
*/
|
|
300
335
|
getAllArgs() {
|
|
301
|
-
return this.
|
|
336
|
+
return this.childNodes.filter(child => child instanceof ParameterToken);
|
|
302
337
|
}
|
|
303
338
|
|
|
304
339
|
/**
|
|
@@ -414,14 +449,16 @@ class TranscludeToken extends Token {
|
|
|
414
449
|
*/
|
|
415
450
|
newAnonArg(val) {
|
|
416
451
|
val = String(val);
|
|
417
|
-
const templateLike = this.
|
|
452
|
+
const templateLike = this.isTemplate(),
|
|
418
453
|
wikitext = `{{${templateLike ? ':T|' : 'lc:'}${val}}}`,
|
|
419
454
|
root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
420
|
-
{childNodes: {length},
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
455
|
+
{childNodes: {length}, firstChild: transclude} = root,
|
|
456
|
+
/** @type {Token & {lastChild: ParameterToken}} */
|
|
457
|
+
{type, name, childNodes: {length: transcludeLength}, lastChild} = transclude,
|
|
458
|
+
targetType = templateLike ? 'template' : 'magic-word',
|
|
459
|
+
targetName = templateLike ? 'T' : 'lc';
|
|
460
|
+
if (length === 1 && type === targetType && name === targetName && transcludeLength === 2 && lastChild.anon) {
|
|
461
|
+
return this.appendChild(lastChild);
|
|
425
462
|
}
|
|
426
463
|
throw new SyntaxError(`非法的匿名参数:${noWrap(val)}`);
|
|
427
464
|
}
|
|
@@ -437,7 +474,7 @@ class TranscludeToken extends Token {
|
|
|
437
474
|
setValue(key, value) {
|
|
438
475
|
if (typeof key !== 'string') {
|
|
439
476
|
this.typeError('setValue', 'String');
|
|
440
|
-
} else if (!this.
|
|
477
|
+
} else if (!this.isTemplate()) {
|
|
441
478
|
throw new Error(`${this.constructor.name}.setValue 方法仅供模板使用!`);
|
|
442
479
|
}
|
|
443
480
|
const token = this.getArg(key);
|
|
@@ -448,13 +485,12 @@ class TranscludeToken extends Token {
|
|
|
448
485
|
}
|
|
449
486
|
const wikitext = `{{:T|${key}=${value}}}`,
|
|
450
487
|
root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
451
|
-
{childNodes: {length},
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
) {
|
|
488
|
+
{childNodes: {length}, firstChild: template} = root,
|
|
489
|
+
{type, name, childNodes: {length: templateLength}, lastChild: parameter} = template;
|
|
490
|
+
if (length !== 1 || type !== 'template' || name !== 'T' || templateLength !== 2 || parameter.name !== key) {
|
|
455
491
|
throw new SyntaxError(`非法的命名参数:${key}=${noWrap(value)}`);
|
|
456
492
|
}
|
|
457
|
-
this.appendChild(
|
|
493
|
+
this.appendChild(parameter);
|
|
458
494
|
}
|
|
459
495
|
|
|
460
496
|
/**
|
|
@@ -463,11 +499,11 @@ class TranscludeToken extends Token {
|
|
|
463
499
|
* @throws `Error` 仅用于模板
|
|
464
500
|
*/
|
|
465
501
|
anonToNamed() {
|
|
466
|
-
if (!this.
|
|
502
|
+
if (!this.isTemplate()) {
|
|
467
503
|
throw new Error(`${this.constructor.name}.anonToNamed 方法仅供模板使用!`);
|
|
468
504
|
}
|
|
469
505
|
for (const token of this.getAnonArgs()) {
|
|
470
|
-
token.
|
|
506
|
+
token.firstChild.replaceChildren(token.name);
|
|
471
507
|
}
|
|
472
508
|
}
|
|
473
509
|
|
|
@@ -484,11 +520,11 @@ class TranscludeToken extends Token {
|
|
|
484
520
|
this.typeError('replaceTemplate', 'String');
|
|
485
521
|
}
|
|
486
522
|
const root = Parser.parse(`{{${title}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
487
|
-
{childNodes: {length},
|
|
488
|
-
if (length !== 1 ||
|
|
523
|
+
{childNodes: {length}, firstChild: template} = root;
|
|
524
|
+
if (length !== 1 || template.type !== 'template' || template.childNodes.length !== 1) {
|
|
489
525
|
throw new SyntaxError(`非法的模板名称:${title}`);
|
|
490
526
|
}
|
|
491
|
-
this.
|
|
527
|
+
this.firstChild.replaceChildren(...template.firstChild.childNodes);
|
|
492
528
|
}
|
|
493
529
|
|
|
494
530
|
/**
|
|
@@ -504,16 +540,14 @@ class TranscludeToken extends Token {
|
|
|
504
540
|
this.typeError('replaceModule', 'String');
|
|
505
541
|
}
|
|
506
542
|
const root = Parser.parse(`{{#invoke:${title}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
507
|
-
{childNodes: {length},
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
) {
|
|
543
|
+
{childNodes: {length}, firstChild: invoke} = root,
|
|
544
|
+
{type, name, childNodes: {length: invokeLength}, lastChild} = invoke;
|
|
545
|
+
if (length !== 1 || type !== 'magic-word' || name !== 'invoke' || invokeLength !== 2) {
|
|
511
546
|
throw new SyntaxError(`非法的模块名称:${title}`);
|
|
512
547
|
} else if (this.childNodes.length > 1) {
|
|
513
|
-
this.
|
|
548
|
+
this.childNodes[1].replaceChildren(...lastChild.childNodes);
|
|
514
549
|
} else {
|
|
515
|
-
|
|
516
|
-
firstElementChild.destroy(true);
|
|
550
|
+
invoke.destroy(true);
|
|
517
551
|
this.appendChild(lastChild);
|
|
518
552
|
}
|
|
519
553
|
}
|
|
@@ -536,16 +570,14 @@ class TranscludeToken extends Token {
|
|
|
536
570
|
const root = Parser.parse(
|
|
537
571
|
`{{#invoke:M|${func}}}`, this.getAttribute('include'), 2, this.getAttribute('config'),
|
|
538
572
|
),
|
|
539
|
-
{childNodes: {length},
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
) {
|
|
573
|
+
{childNodes: {length}, firstChild: invoke} = root,
|
|
574
|
+
{type, name, childNodes: {length: invokeLength}, lastChild} = invoke;
|
|
575
|
+
if (length !== 1 || type !== 'magic-word' || name !== 'invoke' || invokeLength !== 3) {
|
|
543
576
|
throw new SyntaxError(`非法的模块函数名:${func}`);
|
|
544
577
|
} else if (this.childNodes.length > 2) {
|
|
545
|
-
this.
|
|
578
|
+
this.childNodes[2].replaceChildren(...lastChild.childNodes);
|
|
546
579
|
} else {
|
|
547
|
-
|
|
548
|
-
firstElementChild.destroy(true);
|
|
580
|
+
invoke.destroy(true);
|
|
549
581
|
this.appendChild(lastChild);
|
|
550
582
|
}
|
|
551
583
|
}
|
|
@@ -556,7 +588,7 @@ class TranscludeToken extends Token {
|
|
|
556
588
|
* @throws `Error` 仅用于模板
|
|
557
589
|
*/
|
|
558
590
|
hasDuplicatedArgs() {
|
|
559
|
-
if (this.
|
|
591
|
+
if (this.isTemplate()) {
|
|
560
592
|
return this.getAllArgs().length - this.getKeys().length;
|
|
561
593
|
}
|
|
562
594
|
throw new Error(`${this.constructor.name}.hasDuplicatedArgs 方法仅供模板使用!`);
|
|
@@ -565,10 +597,11 @@ class TranscludeToken extends Token {
|
|
|
565
597
|
/**
|
|
566
598
|
* 获取重名参数
|
|
567
599
|
* @complexity `n`
|
|
600
|
+
* @returns {[string, Set<ParameterToken>][]}
|
|
568
601
|
* @throws `Error` 仅用于模板
|
|
569
602
|
*/
|
|
570
603
|
getDuplicatedArgs() {
|
|
571
|
-
if (this.
|
|
604
|
+
if (this.isTemplate()) {
|
|
572
605
|
return Object.entries(this.#args).filter(([, {size}]) => size > 1).map(([key, args]) => [key, new Set(args)]);
|
|
573
606
|
}
|
|
574
607
|
throw new Error(`${this.constructor.name}.getDuplicatedArgs 方法仅供模板使用!`);
|
|
@@ -652,7 +685,7 @@ class TranscludeToken extends Token {
|
|
|
652
685
|
if (remaining > 1) {
|
|
653
686
|
Parser.error(`${this.type === 'template'
|
|
654
687
|
? this.name
|
|
655
|
-
: this.normalizeTitle(this.
|
|
688
|
+
: this.normalizeTitle(this.childNodes[1]?.text() ?? '', 828).title
|
|
656
689
|
} 还留有 ${remaining} 个重复的 ${key} 参数:${[...this.getArgs(key)].map(arg => {
|
|
657
690
|
const {top, left} = arg.getBoundingClientRect();
|
|
658
691
|
return `第 ${top} 行第 ${left} 列`;
|
|
@@ -680,7 +713,7 @@ class TranscludeToken extends Token {
|
|
|
680
713
|
config = this.getAttribute('config'),
|
|
681
714
|
parsed = Parser.parse(stripped, include, 4, config);
|
|
682
715
|
const TableToken = require('./table');
|
|
683
|
-
for (const table of parsed.
|
|
716
|
+
for (const table of parsed.childNodes) {
|
|
684
717
|
if (table instanceof TableToken) {
|
|
685
718
|
table.escape();
|
|
686
719
|
}
|
package/tool/index.js
CHANGED
|
@@ -855,7 +855,7 @@ const $ = tokens => {
|
|
|
855
855
|
/** @param {PropertyKey} prop */
|
|
856
856
|
get(obj, prop) {
|
|
857
857
|
if (prop === Symbol.iterator || typeof obj[prop] !== 'function'
|
|
858
|
-
||
|
|
858
|
+
|| prop[0] !== '_' && Object.getOwnPropertyDescriptor(obj.constructor.prototype, prop)
|
|
859
859
|
|| !externalUse(prop, true)
|
|
860
860
|
) {
|
|
861
861
|
return obj[prop];
|