wikiparser-node 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/default.json +129 -66
- package/config/zhwiki.json +4 -4
- package/index.js +74 -65
- package/lib/element.js +125 -152
- package/lib/node.js +251 -223
- package/lib/ranges.js +2 -2
- package/lib/text.js +64 -64
- package/lib/title.js +8 -7
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +1 -2
- package/package.json +4 -3
- package/parser/brackets.js +8 -2
- package/parser/externalLinks.js +1 -1
- package/parser/hrAndDoubleUnderscore.js +4 -4
- package/parser/links.js +7 -7
- package/parser/table.js +12 -10
- package/src/arg.js +53 -48
- package/src/atom/index.js +7 -5
- package/src/attribute.js +91 -80
- package/src/charinsert.js +91 -0
- package/src/converter.js +22 -11
- package/src/converterFlags.js +72 -62
- package/src/converterRule.js +49 -49
- package/src/extLink.js +30 -28
- package/src/gallery.js +56 -32
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +15 -11
- package/src/html.js +38 -38
- package/src/imageParameter.js +64 -48
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +222 -124
- package/src/link/category.js +4 -8
- package/src/link/file.js +95 -59
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +61 -39
- package/src/magicLink.js +21 -22
- package/src/nested/choose.js +24 -0
- package/src/nested/combobox.js +23 -0
- package/src/nested/index.js +88 -0
- package/src/nested/references.js +23 -0
- package/src/nowiki/comment.js +17 -17
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +14 -14
- package/src/nowiki/index.js +12 -0
- package/src/onlyinclude.js +10 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +32 -18
- package/src/syntax.js +9 -1
- package/src/table/index.js +33 -32
- package/src/table/td.js +51 -57
- package/src/table/tr.js +6 -6
- package/src/tagPair/ext.js +58 -40
- package/src/tagPair/include.js +1 -1
- package/src/tagPair/index.js +21 -20
- package/src/transclude.js +158 -143
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/util/diff.js +1 -1
- package/util/string.js +20 -20
package/lib/element.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs'),
|
|
4
4
|
path = require('path'),
|
|
5
|
-
{externalUse} = require('../util/debug'),
|
|
6
5
|
{toCase, noWrap, print} = require('../util/string'),
|
|
7
6
|
{nth} = require('./ranges'),
|
|
8
7
|
parseSelector = require('../parser/selector'),
|
|
@@ -30,6 +29,7 @@ class AstElement extends AstNode {
|
|
|
30
29
|
* @param {string|undefined} equal 属性规则运算符,`equal`存在时`val`和`i`也一定存在
|
|
31
30
|
* @param {string|undefined} val 属性值
|
|
32
31
|
* @param {string|undefined} i 是否对大小写不敏感
|
|
32
|
+
* @throws `RangeError` 复杂属性
|
|
33
33
|
*/
|
|
34
34
|
#matchesAttr = (key, equal, val, i) => {
|
|
35
35
|
if (!equal) {
|
|
@@ -38,14 +38,17 @@ class AstElement extends AstNode {
|
|
|
38
38
|
return equal === '!=';
|
|
39
39
|
}
|
|
40
40
|
val = toCase(val, i);
|
|
41
|
+
let thisVal = this.getAttribute(key);
|
|
42
|
+
if (thisVal instanceof RegExp) {
|
|
43
|
+
thisVal = thisVal.source;
|
|
44
|
+
}
|
|
41
45
|
if (equal === '~=') {
|
|
42
|
-
|
|
43
|
-
if (typeof thisVals === 'string') {
|
|
44
|
-
thisVals = thisVals.split(/\s/u);
|
|
45
|
-
}
|
|
46
|
+
const thisVals = typeof thisVal === 'string' ? thisVal.split(/\s/u) : thisVal;
|
|
46
47
|
return Boolean(thisVals?.[Symbol.iterator]) && [...thisVals].some(v => toCase(v, i) === val);
|
|
48
|
+
} else if (typeof thisVal !== 'string') {
|
|
49
|
+
throw new RangeError(`复杂属性 ${key} 不能用于选择器!`);
|
|
47
50
|
}
|
|
48
|
-
|
|
51
|
+
thisVal = toCase(thisVal, i);
|
|
49
52
|
switch (equal) {
|
|
50
53
|
case '|=':
|
|
51
54
|
return thisVal === val || thisVal.startsWith(`${val}-`);
|
|
@@ -62,6 +65,11 @@ class AstElement extends AstNode {
|
|
|
62
65
|
}
|
|
63
66
|
};
|
|
64
67
|
|
|
68
|
+
/** 子节点总数 */
|
|
69
|
+
get length() {
|
|
70
|
+
return this.childNodes.length;
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
/**
|
|
66
74
|
* 全部非文本子节点
|
|
67
75
|
* @complexity `n`
|
|
@@ -71,14 +79,6 @@ class AstElement extends AstNode {
|
|
|
71
79
|
return children;
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
/**
|
|
75
|
-
* 非文本子节点总数
|
|
76
|
-
* @complexity `n`
|
|
77
|
-
*/
|
|
78
|
-
get childElementCount() {
|
|
79
|
-
return this.children.length;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
82
|
/**
|
|
83
83
|
* 首位非文本子节点
|
|
84
84
|
* @returns {this}
|
|
@@ -89,10 +89,18 @@ class AstElement extends AstNode {
|
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* 末位非文本子节点
|
|
92
|
-
* @
|
|
92
|
+
* @complexity `n`
|
|
93
93
|
*/
|
|
94
94
|
get lastElementChild() {
|
|
95
|
-
return this.
|
|
95
|
+
return this.children.at(-1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 非文本子节点总数
|
|
100
|
+
* @complexity `n`
|
|
101
|
+
*/
|
|
102
|
+
get childElementCount() {
|
|
103
|
+
return this.children.length;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
/** 父节点 */
|
|
@@ -183,71 +191,6 @@ class AstElement extends AstNode {
|
|
|
183
191
|
return key === 'matchesAttr' ? this.#matchesAttr : super.getAttribute(key);
|
|
184
192
|
}
|
|
185
193
|
|
|
186
|
-
/**
|
|
187
|
-
* @override
|
|
188
|
-
* @template {string} T
|
|
189
|
-
* @param {T} key 属性键
|
|
190
|
-
* @param {TokenAttribute<T>} value 属性值
|
|
191
|
-
* @throws `RangeError` 禁止手动指定的属性
|
|
192
|
-
*/
|
|
193
|
-
setAttribute(key, value) {
|
|
194
|
-
if (key === 'name' && externalUse('setAttribute')) {
|
|
195
|
-
throw new RangeError(`禁止手动指定 ${key} 属性!`);
|
|
196
|
-
}
|
|
197
|
-
return super.setAttribute(key, value);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* 在末尾批量插入子节点
|
|
202
|
-
* @param {...this} elements 插入节点
|
|
203
|
-
* @complexity `n`
|
|
204
|
-
*/
|
|
205
|
-
append(...elements) {
|
|
206
|
-
for (const element of elements) {
|
|
207
|
-
this.appendChild(element);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* 在开头批量插入子节点
|
|
213
|
-
* @param {...this} elements 插入节点
|
|
214
|
-
* @complexity `n`
|
|
215
|
-
*/
|
|
216
|
-
prepend(...elements) {
|
|
217
|
-
for (let i = 0; i < elements.length; i++) {
|
|
218
|
-
this.insertAt(elements[i], i);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* 批量替换子节点
|
|
224
|
-
* @param {...this} elements 新的子节点
|
|
225
|
-
* @complexity `n`
|
|
226
|
-
*/
|
|
227
|
-
replaceChildren(...elements) {
|
|
228
|
-
for (let i = this.childNodes.length - 1; i >= 0; i--) {
|
|
229
|
-
this.removeAt(i);
|
|
230
|
-
}
|
|
231
|
-
this.append(...elements);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* 修改文本子节点
|
|
236
|
-
* @param {string} str 新文本
|
|
237
|
-
* @param {number} i 子节点位置
|
|
238
|
-
* @throws `RangeError` 对应位置的子节点不是文本节点
|
|
239
|
-
*/
|
|
240
|
-
setText(str, i = 0) {
|
|
241
|
-
this.getAttribute('verifyChild')(i);
|
|
242
|
-
const /** @type {AstText} */ oldText = this.childNodes.at(i),
|
|
243
|
-
{type, data, constructor: {name}} = oldText;
|
|
244
|
-
if (type === 'text') {
|
|
245
|
-
oldText.replaceData(str);
|
|
246
|
-
return data;
|
|
247
|
-
}
|
|
248
|
-
throw new RangeError(`第 ${i} 个子节点是 ${name}!`);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
194
|
/** 是否受保护。保护条件来自Token,这里仅提前用于:required和:optional伪选择器。 */
|
|
252
195
|
#isProtected() {
|
|
253
196
|
const /** @type {{parentNode: AstElement & {constructor: {fixed: boolean}}}} */ {parentNode} = this;
|
|
@@ -366,17 +309,17 @@ class AstElement extends AstNode {
|
|
|
366
309
|
* @returns {boolean}
|
|
367
310
|
* @complexity `n`
|
|
368
311
|
*/
|
|
369
|
-
matches(selector
|
|
370
|
-
if (
|
|
371
|
-
return
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
312
|
+
matches(selector) {
|
|
313
|
+
if (selector === undefined) {
|
|
314
|
+
return true;
|
|
315
|
+
} else if (typeof selector === 'string') {
|
|
316
|
+
const stack = parseSelector(selector),
|
|
317
|
+
/** @type {Set<string>} */
|
|
318
|
+
pseudos = new Set(stack.flat(2).filter(step => typeof step === 'string' && step[0] === ':'));
|
|
319
|
+
if (pseudos.size > 0) {
|
|
320
|
+
Parser.warn('检测到伪选择器,请确认是否需要将":"转义成"\\:"。', pseudos);
|
|
321
|
+
}
|
|
322
|
+
return Parser.run(() => stack.some(condition => this.matches(condition)));
|
|
380
323
|
} else if (!Parser.running) {
|
|
381
324
|
this.typeError('matches', 'String');
|
|
382
325
|
}
|
|
@@ -406,59 +349,11 @@ class AstElement extends AstNode {
|
|
|
406
349
|
return false;
|
|
407
350
|
}
|
|
408
351
|
|
|
409
|
-
/**
|
|
410
|
-
* 还原为wikitext
|
|
411
|
-
* @param {string} selector
|
|
412
|
-
* @param {string} separator 子节点间的连接符
|
|
413
|
-
*/
|
|
414
|
-
toString(selector, separator = '') {
|
|
415
|
-
return selector && this.matches(selector)
|
|
416
|
-
? ''
|
|
417
|
-
: this.childNodes.map(child => child.toString(selector)).join(separator);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/** 获取所有祖先节点 */
|
|
421
|
-
getAncestors() {
|
|
422
|
-
const /** @type {this[]} */ ancestors = [];
|
|
423
|
-
let {parentNode} = this;
|
|
424
|
-
while (parentNode) {
|
|
425
|
-
ancestors.push(parentNode);
|
|
426
|
-
({parentNode} = parentNode);
|
|
427
|
-
}
|
|
428
|
-
return ancestors;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* 比较和另一个节点的相对位置
|
|
433
|
-
* @param {this} other 待比较的节点
|
|
434
|
-
* @complexity `n`
|
|
435
|
-
* @throws `Error` 不在同一个语法树
|
|
436
|
-
*/
|
|
437
|
-
compareDocumentPosition(other) {
|
|
438
|
-
if (!(other instanceof AstElement)) {
|
|
439
|
-
this.typeError('compareDocumentPosition', 'AstElement');
|
|
440
|
-
} else if (this === other) {
|
|
441
|
-
return 0;
|
|
442
|
-
} else if (this.contains(other)) {
|
|
443
|
-
return -1;
|
|
444
|
-
} else if (other.contains(this)) {
|
|
445
|
-
return 1;
|
|
446
|
-
} else if (this.getRootNode() !== other.getRootNode()) {
|
|
447
|
-
throw new Error('不在同一个语法树!');
|
|
448
|
-
}
|
|
449
|
-
const aAncestors = [...this.getAncestors().reverse(), this],
|
|
450
|
-
bAncestors = [...other.getAncestors().reverse(), other],
|
|
451
|
-
depth = aAncestors.findIndex((ancestor, i) => bAncestors[i] !== ancestor),
|
|
452
|
-
commonAncestor = aAncestors[depth - 1],
|
|
453
|
-
{childNodes} = commonAncestor;
|
|
454
|
-
return childNodes.indexOf(aAncestors[depth]) - childNodes.indexOf(bAncestors[depth]);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
352
|
/**
|
|
458
353
|
* 最近的祖先节点
|
|
459
354
|
* @param {string} selector
|
|
460
355
|
*/
|
|
461
|
-
closest(selector
|
|
356
|
+
closest(selector) {
|
|
462
357
|
let {parentNode} = this;
|
|
463
358
|
while (parentNode) {
|
|
464
359
|
if (parentNode.matches(selector)) {
|
|
@@ -475,7 +370,7 @@ class AstElement extends AstNode {
|
|
|
475
370
|
* @returns {this|undefined}
|
|
476
371
|
* @complexity `n`
|
|
477
372
|
*/
|
|
478
|
-
querySelector(selector
|
|
373
|
+
querySelector(selector) {
|
|
479
374
|
for (const child of this.children) {
|
|
480
375
|
if (child.matches(selector)) {
|
|
481
376
|
return child;
|
|
@@ -493,7 +388,7 @@ class AstElement extends AstNode {
|
|
|
493
388
|
* @param {string} selector
|
|
494
389
|
* @complexity `n`
|
|
495
390
|
*/
|
|
496
|
-
querySelectorAll(selector
|
|
391
|
+
querySelectorAll(selector) {
|
|
497
392
|
const /** @type {this[]} */ descendants = [];
|
|
498
393
|
for (const child of this.children) {
|
|
499
394
|
if (child.matches(selector)) {
|
|
@@ -534,16 +429,94 @@ class AstElement extends AstNode {
|
|
|
534
429
|
return String(this).split('\n', n + 1).at(-1);
|
|
535
430
|
}
|
|
536
431
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
432
|
+
/**
|
|
433
|
+
* 在开头批量插入子节点
|
|
434
|
+
* @param {...this} elements 插入节点
|
|
435
|
+
* @complexity `n`
|
|
436
|
+
*/
|
|
437
|
+
prepend(...elements) {
|
|
438
|
+
for (let i = 0; i < elements.length; i++) {
|
|
439
|
+
this.insertAt(elements[i], i);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* 在末尾批量插入子节点
|
|
445
|
+
* @param {...this} elements 插入节点
|
|
446
|
+
* @complexity `n`
|
|
447
|
+
*/
|
|
448
|
+
append(...elements) {
|
|
449
|
+
for (const element of elements) {
|
|
450
|
+
this.insertAt(element);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* 批量替换子节点
|
|
456
|
+
* @param {...this} elements 新的子节点
|
|
457
|
+
* @complexity `n`
|
|
458
|
+
*/
|
|
459
|
+
replaceChildren(...elements) {
|
|
460
|
+
for (let i = this.childNodes.length - 1; i >= 0; i--) {
|
|
461
|
+
this.removeAt(i);
|
|
462
|
+
}
|
|
463
|
+
this.append(...elements);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* 修改文本子节点
|
|
468
|
+
* @param {string} str 新文本
|
|
469
|
+
* @param {number} i 子节点位置
|
|
470
|
+
* @throws `RangeError` 对应位置的子节点不是文本节点
|
|
471
|
+
*/
|
|
472
|
+
setText(str, i = 0) {
|
|
473
|
+
this.getAttribute('verifyChild')(i);
|
|
474
|
+
const /** @type {AstText} */ oldText = this.childNodes.at(i),
|
|
475
|
+
{type, data, constructor: {name}} = oldText;
|
|
476
|
+
if (type === 'text') {
|
|
477
|
+
oldText.replaceData(str);
|
|
478
|
+
return data;
|
|
479
|
+
}
|
|
480
|
+
throw new RangeError(`第 ${i} 个子节点是 ${name}!`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* 还原为wikitext
|
|
485
|
+
* @param {string} selector
|
|
486
|
+
* @param {string} separator 子节点间的连接符
|
|
487
|
+
* @returns {string}
|
|
488
|
+
*/
|
|
489
|
+
toString(selector, separator = '') {
|
|
490
|
+
return selector && this.matches(selector)
|
|
491
|
+
? ''
|
|
492
|
+
: this.childNodes.map(child => child.toString(selector)).join(separator);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
static lintIgnoredExt = new Set([
|
|
496
|
+
'nowiki',
|
|
497
|
+
'pre',
|
|
498
|
+
'charinsert',
|
|
499
|
+
'score',
|
|
500
|
+
'syntaxhighlight',
|
|
501
|
+
'source',
|
|
502
|
+
'math',
|
|
503
|
+
'chem',
|
|
504
|
+
'ce',
|
|
505
|
+
'graph',
|
|
506
|
+
'mapframe',
|
|
507
|
+
'maplink',
|
|
508
|
+
'quiz',
|
|
509
|
+
'templatedata',
|
|
510
|
+
'timeline',
|
|
511
|
+
]);
|
|
540
512
|
|
|
541
513
|
/**
|
|
542
514
|
* Linter
|
|
543
515
|
* @param {number} start 起始位置
|
|
544
516
|
*/
|
|
545
517
|
lint(start = 0) {
|
|
546
|
-
|
|
518
|
+
const SyntaxToken = require('../src/syntax');
|
|
519
|
+
if (this instanceof SyntaxToken || this.constructor.hidden
|
|
547
520
|
|| this.type === 'ext-inner' && AstElement.lintIgnoredExt.has(this.name)
|
|
548
521
|
) {
|
|
549
522
|
return [];
|
|
@@ -563,15 +536,15 @@ class AstElement extends AstNode {
|
|
|
563
536
|
* @returns {string}
|
|
564
537
|
*/
|
|
565
538
|
print(opt = {}) {
|
|
566
|
-
return this
|
|
567
|
-
?
|
|
568
|
-
:
|
|
539
|
+
return String(this)
|
|
540
|
+
? `<span class="wpb-${opt.class ?? this.type}">${print(this.childNodes, opt)}</span>`
|
|
541
|
+
: '';
|
|
569
542
|
}
|
|
570
543
|
|
|
571
544
|
/**
|
|
572
545
|
* 保存为JSON
|
|
573
546
|
* @param {string} file 文件名
|
|
574
|
-
* @returns {Record<string,
|
|
547
|
+
* @returns {Record<string, *>}
|
|
575
548
|
*/
|
|
576
549
|
json(file) {
|
|
577
550
|
const {childNodes, ...prop} = this,
|
|
@@ -594,7 +567,7 @@ class AstElement extends AstNode {
|
|
|
594
567
|
* @returns {void}
|
|
595
568
|
*/
|
|
596
569
|
echo(depth = 0) {
|
|
597
|
-
if (
|
|
570
|
+
if (!Number.isInteger(depth) || depth < 0) {
|
|
598
571
|
this.typeError('print', 'Number');
|
|
599
572
|
}
|
|
600
573
|
const indent = ' '.repeat(depth),
|