wikiparser-node 0.4.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 +97 -65
- package/lib/element.js +159 -302
- package/lib/node.js +384 -198
- package/lib/ranges.js +3 -4
- package/lib/text.js +65 -36
- package/lib/title.js +9 -8
- package/mixin/fixedToken.js +4 -4
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +16 -7
- package/package.json +14 -3
- package/parser/brackets.js +8 -2
- package/parser/commentAndExt.js +1 -1
- package/parser/converter.js +1 -1
- package/parser/externalLinks.js +2 -2
- package/parser/hrAndDoubleUnderscore.js +8 -7
- package/parser/links.js +8 -9
- package/parser/magicLinks.js +1 -1
- package/parser/selector.js +5 -5
- package/parser/table.js +18 -16
- package/src/arg.js +71 -42
- package/src/atom/index.js +7 -5
- package/src/attribute.js +102 -64
- package/src/charinsert.js +91 -0
- package/src/converter.js +34 -15
- package/src/converterFlags.js +87 -40
- package/src/converterRule.js +59 -53
- package/src/extLink.js +45 -37
- package/src/gallery.js +71 -16
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +41 -18
- package/src/html.js +76 -48
- package/src/imageParameter.js +73 -51
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +243 -138
- package/src/link/category.js +10 -14
- package/src/link/file.js +112 -56
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +86 -61
- package/src/magicLink.js +48 -21
- 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 +18 -4
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +16 -11
- package/src/nowiki/index.js +12 -0
- package/src/nowiki/quote.js +28 -1
- package/src/onlyinclude.js +15 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +73 -46
- package/src/syntax.js +9 -1
- package/src/table/index.js +58 -44
- package/src/table/td.js +63 -63
- package/src/table/tr.js +52 -35
- package/src/tagPair/ext.js +60 -43
- package/src/tagPair/include.js +11 -1
- package/src/tagPair/index.js +29 -20
- package/src/transclude.js +214 -166
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/{test/util.js → util/diff.js} +15 -19
- package/util/lint.js +40 -0
- package/util/string.js +37 -20
- 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/lib/element.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const fs = require('fs'),
|
|
4
|
-
|
|
5
|
-
{toCase, noWrap} = require('../util/string'),
|
|
4
|
+
path = require('path'),
|
|
5
|
+
{toCase, noWrap, print} = require('../util/string'),
|
|
6
6
|
{nth} = require('./ranges'),
|
|
7
7
|
parseSelector = require('../parser/selector'),
|
|
8
8
|
Parser = require('..'),
|
|
@@ -29,6 +29,7 @@ class AstElement extends AstNode {
|
|
|
29
29
|
* @param {string|undefined} equal 属性规则运算符,`equal`存在时`val`和`i`也一定存在
|
|
30
30
|
* @param {string|undefined} val 属性值
|
|
31
31
|
* @param {string|undefined} i 是否对大小写不敏感
|
|
32
|
+
* @throws `RangeError` 复杂属性
|
|
32
33
|
*/
|
|
33
34
|
#matchesAttr = (key, equal, val, i) => {
|
|
34
35
|
if (!equal) {
|
|
@@ -37,14 +38,17 @@ class AstElement extends AstNode {
|
|
|
37
38
|
return equal === '!=';
|
|
38
39
|
}
|
|
39
40
|
val = toCase(val, i);
|
|
41
|
+
let thisVal = this.getAttribute(key);
|
|
42
|
+
if (thisVal instanceof RegExp) {
|
|
43
|
+
thisVal = thisVal.source;
|
|
44
|
+
}
|
|
40
45
|
if (equal === '~=') {
|
|
41
|
-
|
|
42
|
-
if (typeof thisVals === 'string') {
|
|
43
|
-
thisVals = thisVals.split(/\s/u);
|
|
44
|
-
}
|
|
46
|
+
const thisVals = typeof thisVal === 'string' ? thisVal.split(/\s/u) : thisVal;
|
|
45
47
|
return Boolean(thisVals?.[Symbol.iterator]) && [...thisVals].some(v => toCase(v, i) === val);
|
|
48
|
+
} else if (typeof thisVal !== 'string') {
|
|
49
|
+
throw new RangeError(`复杂属性 ${key} 不能用于选择器!`);
|
|
46
50
|
}
|
|
47
|
-
|
|
51
|
+
thisVal = toCase(thisVal, i);
|
|
48
52
|
switch (equal) {
|
|
49
53
|
case '|=':
|
|
50
54
|
return thisVal === val || thisVal.startsWith(`${val}-`);
|
|
@@ -61,6 +65,11 @@ class AstElement extends AstNode {
|
|
|
61
65
|
}
|
|
62
66
|
};
|
|
63
67
|
|
|
68
|
+
/** 子节点总数 */
|
|
69
|
+
get length() {
|
|
70
|
+
return this.childNodes.length;
|
|
71
|
+
}
|
|
72
|
+
|
|
64
73
|
/**
|
|
65
74
|
* 全部非文本子节点
|
|
66
75
|
* @complexity `n`
|
|
@@ -70,14 +79,6 @@ class AstElement extends AstNode {
|
|
|
70
79
|
return children;
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
/**
|
|
74
|
-
* 非文本子节点总数
|
|
75
|
-
* @complexity `n`
|
|
76
|
-
*/
|
|
77
|
-
get childElementCount() {
|
|
78
|
-
return this.children.length;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
82
|
/**
|
|
82
83
|
* 首位非文本子节点
|
|
83
84
|
* @returns {this}
|
|
@@ -88,10 +89,18 @@ class AstElement extends AstNode {
|
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* 末位非文本子节点
|
|
91
|
-
* @
|
|
92
|
+
* @complexity `n`
|
|
92
93
|
*/
|
|
93
94
|
get lastElementChild() {
|
|
94
|
-
return this.
|
|
95
|
+
return this.children.at(-1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 非文本子节点总数
|
|
100
|
+
* @complexity `n`
|
|
101
|
+
*/
|
|
102
|
+
get childElementCount() {
|
|
103
|
+
return this.children.length;
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
/** 父节点 */
|
|
@@ -138,6 +147,18 @@ class AstElement extends AstNode {
|
|
|
138
147
|
return previousSibling;
|
|
139
148
|
}
|
|
140
149
|
|
|
150
|
+
/** 内部高度 */
|
|
151
|
+
get clientHeight() {
|
|
152
|
+
const {innerText} = this;
|
|
153
|
+
return typeof innerText === 'string' ? innerText.split('\n').length : undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** 内部宽度 */
|
|
157
|
+
get clientWidth() {
|
|
158
|
+
const {innerText} = this;
|
|
159
|
+
return typeof innerText === 'string' ? innerText.split('\n').at(-1).length : undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
141
162
|
constructor() {
|
|
142
163
|
super();
|
|
143
164
|
this.seal('name');
|
|
@@ -170,71 +191,6 @@ class AstElement extends AstNode {
|
|
|
170
191
|
return key === 'matchesAttr' ? this.#matchesAttr : super.getAttribute(key);
|
|
171
192
|
}
|
|
172
193
|
|
|
173
|
-
/**
|
|
174
|
-
* @override
|
|
175
|
-
* @template {string} T
|
|
176
|
-
* @param {T} key 属性键
|
|
177
|
-
* @param {TokenAttribute<T>} value 属性值
|
|
178
|
-
* @throws `RangeError` 禁止手动指定的属性
|
|
179
|
-
*/
|
|
180
|
-
setAttribute(key, value) {
|
|
181
|
-
if (key === 'name' && externalUse('setAttribute')) {
|
|
182
|
-
throw new RangeError(`禁止手动指定 ${key} 属性!`);
|
|
183
|
-
}
|
|
184
|
-
return super.setAttribute(key, value);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 在末尾批量插入子节点
|
|
189
|
-
* @param {...this} elements 插入节点
|
|
190
|
-
* @complexity `n`
|
|
191
|
-
*/
|
|
192
|
-
append(...elements) {
|
|
193
|
-
for (const element of elements) {
|
|
194
|
-
this.appendChild(element);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* 在开头批量插入子节点
|
|
200
|
-
* @param {...this} elements 插入节点
|
|
201
|
-
* @complexity `n`
|
|
202
|
-
*/
|
|
203
|
-
prepend(...elements) {
|
|
204
|
-
for (let i = 0; i < elements.length; i++) {
|
|
205
|
-
this.insertAt(elements[i], i);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* 批量替换子节点
|
|
211
|
-
* @param {...this} elements 新的子节点
|
|
212
|
-
* @complexity `n`
|
|
213
|
-
*/
|
|
214
|
-
replaceChildren(...elements) {
|
|
215
|
-
for (let i = this.childNodes.length - 1; i >= 0; i--) {
|
|
216
|
-
this.removeAt(i);
|
|
217
|
-
}
|
|
218
|
-
this.append(...elements);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* 修改文本子节点
|
|
223
|
-
* @param {string} str 新文本
|
|
224
|
-
* @param {number} i 子节点位置
|
|
225
|
-
* @throws `RangeError` 对应位置的子节点不是文本节点
|
|
226
|
-
*/
|
|
227
|
-
setText(str, i = 0) {
|
|
228
|
-
this.getAttribute('verifyChild')(i);
|
|
229
|
-
const /** @type {AstText} */ oldText = this.childNodes.at(i),
|
|
230
|
-
{type, data, constructor: {name}} = oldText;
|
|
231
|
-
if (type !== 'text') {
|
|
232
|
-
throw new RangeError(`第 ${i} 个子节点是 ${name}!`);
|
|
233
|
-
}
|
|
234
|
-
oldText.replaceData(str);
|
|
235
|
-
return data;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
194
|
/** 是否受保护。保护条件来自Token,这里仅提前用于:required和:optional伪选择器。 */
|
|
239
195
|
#isProtected() {
|
|
240
196
|
const /** @type {{parentNode: AstElement & {constructor: {fixed: boolean}}}} */ {parentNode} = this;
|
|
@@ -298,7 +254,7 @@ class AstElement extends AstNode {
|
|
|
298
254
|
|| (type === 'file' || type === 'gallery-image' && link);
|
|
299
255
|
case ':local-link':
|
|
300
256
|
return (type === 'link' || type === 'file' || type === 'gallery-image')
|
|
301
|
-
&& link?.
|
|
257
|
+
&& link?.[0] === '#';
|
|
302
258
|
case ':read-only':
|
|
303
259
|
return fixed;
|
|
304
260
|
case ':read-write':
|
|
@@ -353,17 +309,17 @@ class AstElement extends AstNode {
|
|
|
353
309
|
* @returns {boolean}
|
|
354
310
|
* @complexity `n`
|
|
355
311
|
*/
|
|
356
|
-
matches(selector
|
|
357
|
-
if (
|
|
358
|
-
return
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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)));
|
|
367
323
|
} else if (!Parser.running) {
|
|
368
324
|
this.typeError('matches', 'String');
|
|
369
325
|
}
|
|
@@ -393,59 +349,11 @@ class AstElement extends AstNode {
|
|
|
393
349
|
return false;
|
|
394
350
|
}
|
|
395
351
|
|
|
396
|
-
/**
|
|
397
|
-
* 还原为wikitext
|
|
398
|
-
* @param {string} selector
|
|
399
|
-
* @param {string} separator 子节点间的连接符
|
|
400
|
-
*/
|
|
401
|
-
toString(selector, separator = '') {
|
|
402
|
-
return selector && this.matches(selector)
|
|
403
|
-
? ''
|
|
404
|
-
: this.childNodes.map(child => child.toString(selector)).join(separator);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/** 获取所有祖先节点 */
|
|
408
|
-
getAncestors() {
|
|
409
|
-
const /** @type {this[]} */ ancestors = [];
|
|
410
|
-
let {parentNode} = this;
|
|
411
|
-
while (parentNode) {
|
|
412
|
-
ancestors.push(parentNode);
|
|
413
|
-
({parentNode} = parentNode);
|
|
414
|
-
}
|
|
415
|
-
return ancestors;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* 比较和另一个节点的相对位置
|
|
420
|
-
* @param {this} other 待比较的节点
|
|
421
|
-
* @complexity `n`
|
|
422
|
-
* @throws `Error` 不在同一个语法树
|
|
423
|
-
*/
|
|
424
|
-
compareDocumentPosition(other) {
|
|
425
|
-
if (!(other instanceof AstElement)) {
|
|
426
|
-
this.typeError('compareDocumentPosition', 'AstElement');
|
|
427
|
-
} else if (this === other) {
|
|
428
|
-
return 0;
|
|
429
|
-
} else if (this.contains(other)) {
|
|
430
|
-
return -1;
|
|
431
|
-
} else if (other.contains(this)) {
|
|
432
|
-
return 1;
|
|
433
|
-
} else if (this.getRootNode() !== other.getRootNode()) {
|
|
434
|
-
throw new Error('不在同一个语法树!');
|
|
435
|
-
}
|
|
436
|
-
const aAncestors = [...this.getAncestors().reverse(), this],
|
|
437
|
-
bAncestors = [...other.getAncestors().reverse(), other],
|
|
438
|
-
depth = aAncestors.findIndex((ancestor, i) => bAncestors[i] !== ancestor),
|
|
439
|
-
commonAncestor = aAncestors[depth - 1],
|
|
440
|
-
{childNodes} = commonAncestor;
|
|
441
|
-
return childNodes.indexOf(aAncestors[depth]) - childNodes.indexOf(bAncestors[depth]);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
352
|
/**
|
|
445
353
|
* 最近的祖先节点
|
|
446
354
|
* @param {string} selector
|
|
447
355
|
*/
|
|
448
|
-
closest(selector
|
|
356
|
+
closest(selector) {
|
|
449
357
|
let {parentNode} = this;
|
|
450
358
|
while (parentNode) {
|
|
451
359
|
if (parentNode.matches(selector)) {
|
|
@@ -462,7 +370,7 @@ class AstElement extends AstNode {
|
|
|
462
370
|
* @returns {this|undefined}
|
|
463
371
|
* @complexity `n`
|
|
464
372
|
*/
|
|
465
|
-
querySelector(selector
|
|
373
|
+
querySelector(selector) {
|
|
466
374
|
for (const child of this.children) {
|
|
467
375
|
if (child.matches(selector)) {
|
|
468
376
|
return child;
|
|
@@ -480,7 +388,7 @@ class AstElement extends AstNode {
|
|
|
480
388
|
* @param {string} selector
|
|
481
389
|
* @complexity `n`
|
|
482
390
|
*/
|
|
483
|
-
querySelectorAll(selector
|
|
391
|
+
querySelectorAll(selector) {
|
|
484
392
|
const /** @type {this[]} */ descendants = [];
|
|
485
393
|
for (const child of this.children) {
|
|
486
394
|
if (child.matches(selector)) {
|
|
@@ -522,203 +430,151 @@ class AstElement extends AstNode {
|
|
|
522
430
|
}
|
|
523
431
|
|
|
524
432
|
/**
|
|
525
|
-
*
|
|
526
|
-
* @param {
|
|
433
|
+
* 在开头批量插入子节点
|
|
434
|
+
* @param {...this} elements 插入节点
|
|
527
435
|
* @complexity `n`
|
|
528
436
|
*/
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
this.
|
|
532
|
-
}
|
|
533
|
-
const text = String(this);
|
|
534
|
-
if (index >= -text.length && index < text.length && Number.isInteger(index)) {
|
|
535
|
-
const lines = text.slice(0, index).split('\n');
|
|
536
|
-
return {top: lines.length - 1, left: lines.at(-1).length};
|
|
437
|
+
prepend(...elements) {
|
|
438
|
+
for (let i = 0; i < elements.length; i++) {
|
|
439
|
+
this.insertAt(elements[i], i);
|
|
537
440
|
}
|
|
538
|
-
return undefined;
|
|
539
441
|
}
|
|
540
442
|
|
|
541
443
|
/**
|
|
542
|
-
*
|
|
543
|
-
* @param {
|
|
544
|
-
* @param {number} left 列号
|
|
444
|
+
* 在末尾批量插入子节点
|
|
445
|
+
* @param {...this} elements 插入节点
|
|
545
446
|
* @complexity `n`
|
|
546
447
|
*/
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
this.
|
|
448
|
+
append(...elements) {
|
|
449
|
+
for (const element of elements) {
|
|
450
|
+
this.insertAt(element);
|
|
550
451
|
}
|
|
551
|
-
const lines = String(this).split('\n');
|
|
552
|
-
return top >= 0 && left >= 0 && Number.isInteger(top) && Number.isInteger(left)
|
|
553
|
-
&& lines.length >= top + 1 && lines[top].length >= left
|
|
554
|
-
? lines.slice(0, top).reduce((acc, curLine) => acc + curLine.length + 1, 0) + left
|
|
555
|
-
: undefined;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* 获取行数和最后一行的列数
|
|
560
|
-
* @complexity `n`
|
|
561
|
-
*/
|
|
562
|
-
#getDimension() {
|
|
563
|
-
const lines = String(this).split('\n');
|
|
564
|
-
return {height: lines.length, width: lines.at(-1).length};
|
|
565
452
|
}
|
|
566
453
|
|
|
567
454
|
/**
|
|
568
|
-
*
|
|
569
|
-
* @param {
|
|
455
|
+
* 批量替换子节点
|
|
456
|
+
* @param {...this} elements 新的子节点
|
|
570
457
|
* @complexity `n`
|
|
571
458
|
*/
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
this.
|
|
575
|
-
}
|
|
576
|
-
let /** @type {this[]} */ childNodes;
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* 获取子节点相对于父节点的字符位置,使用前需要先给`childNodes`赋值
|
|
580
|
-
* @param {number} end 子节点序号
|
|
581
|
-
* @param {this} parent 父节点
|
|
582
|
-
* @returns {number}
|
|
583
|
-
*/
|
|
584
|
-
const getIndex = (end, parent) => childNodes.slice(0, end).reduce(
|
|
585
|
-
(acc, cur, i) => acc + String(cur).length + parent.getGaps(i),
|
|
586
|
-
0,
|
|
587
|
-
) + parent.getPadding();
|
|
588
|
-
if (j === undefined) {
|
|
589
|
-
const {parentNode} = this;
|
|
590
|
-
if (!parentNode) {
|
|
591
|
-
return 0;
|
|
592
|
-
}
|
|
593
|
-
({childNodes} = parentNode);
|
|
594
|
-
return getIndex(childNodes.indexOf(this), parentNode);
|
|
459
|
+
replaceChildren(...elements) {
|
|
460
|
+
for (let i = this.childNodes.length - 1; i >= 0; i--) {
|
|
461
|
+
this.removeAt(i);
|
|
595
462
|
}
|
|
596
|
-
this.
|
|
597
|
-
({childNodes} = this);
|
|
598
|
-
return getIndex(j, this);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* 获取当前节点的绝对位置
|
|
603
|
-
* @returns {number}
|
|
604
|
-
* @complexity `n`
|
|
605
|
-
*/
|
|
606
|
-
getAbsoluteIndex() {
|
|
607
|
-
const {parentNode} = this;
|
|
608
|
-
return parentNode ? parentNode.getAbsoluteIndex() + this.getRelativeIndex() : 0;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* 获取当前节点的相对位置,或其第`j`个子节点的相对位置
|
|
613
|
-
* @param {number|undefined} j 子节点序号
|
|
614
|
-
* @complexity `n`
|
|
615
|
-
*/
|
|
616
|
-
#getPosition(j) {
|
|
617
|
-
return j === undefined
|
|
618
|
-
? this.parentNode?.posFromIndex(this.getRelativeIndex()) ?? {top: 0, left: 0}
|
|
619
|
-
: this.posFromIndex(this.getRelativeIndex(j));
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* 获取当前节点的行列位置和大小
|
|
624
|
-
* @complexity `n`
|
|
625
|
-
*/
|
|
626
|
-
getBoundingClientRect() {
|
|
627
|
-
const root = this.getRootNode();
|
|
628
|
-
return {...this.#getDimension(), ...root.posFromIndex(this.getAbsoluteIndex())};
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
/** 第一个子节点前的间距 */
|
|
632
|
-
getPadding() {
|
|
633
|
-
return 0;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/** 子节点间距 */
|
|
637
|
-
getGaps() {
|
|
638
|
-
return 0;
|
|
463
|
+
this.append(...elements);
|
|
639
464
|
}
|
|
640
465
|
|
|
641
466
|
/**
|
|
642
|
-
*
|
|
643
|
-
* @
|
|
467
|
+
* 修改文本子节点
|
|
468
|
+
* @param {string} str 新文本
|
|
469
|
+
* @param {number} i 子节点位置
|
|
470
|
+
* @throws `RangeError` 对应位置的子节点不是文本节点
|
|
644
471
|
*/
|
|
645
|
-
|
|
646
|
-
|
|
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}!`);
|
|
647
481
|
}
|
|
648
482
|
|
|
649
483
|
/**
|
|
650
|
-
*
|
|
651
|
-
* @
|
|
484
|
+
* 还原为wikitext
|
|
485
|
+
* @param {string} selector
|
|
486
|
+
* @param {string} separator 子节点间的连接符
|
|
487
|
+
* @returns {string}
|
|
652
488
|
*/
|
|
653
|
-
|
|
654
|
-
return this
|
|
489
|
+
toString(selector, separator = '') {
|
|
490
|
+
return selector && this.matches(selector)
|
|
491
|
+
? ''
|
|
492
|
+
: this.childNodes.map(child => child.toString(selector)).join(separator);
|
|
655
493
|
}
|
|
656
494
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
+
]);
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Linter
|
|
515
|
+
* @param {number} start 起始位置
|
|
516
|
+
*/
|
|
517
|
+
lint(start = 0) {
|
|
518
|
+
const SyntaxToken = require('../src/syntax');
|
|
519
|
+
if (this instanceof SyntaxToken || this.constructor.hidden
|
|
520
|
+
|| this.type === 'ext-inner' && AstElement.lintIgnoredExt.has(this.name)
|
|
521
|
+
) {
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
const /** @type {LintError[]} */ errors = [];
|
|
525
|
+
for (let i = 0, cur = start + this.getPadding(); i < this.childNodes.length; i++) {
|
|
526
|
+
const child = this.childNodes[i];
|
|
527
|
+
errors.push(...child.lint(cur));
|
|
528
|
+
cur += String(child).length + this.getGaps(i);
|
|
529
|
+
}
|
|
530
|
+
return errors;
|
|
663
531
|
}
|
|
664
532
|
|
|
665
533
|
/**
|
|
666
|
-
*
|
|
667
|
-
* @
|
|
534
|
+
* 以HTML格式打印
|
|
535
|
+
* @param {printOpt} opt 选项
|
|
536
|
+
* @returns {string}
|
|
668
537
|
*/
|
|
669
|
-
|
|
670
|
-
return this
|
|
538
|
+
print(opt = {}) {
|
|
539
|
+
return String(this)
|
|
540
|
+
? `<span class="wpb-${opt.class ?? this.type}">${print(this.childNodes, opt)}</span>`
|
|
541
|
+
: '';
|
|
671
542
|
}
|
|
672
543
|
|
|
673
544
|
/**
|
|
674
|
-
*
|
|
675
|
-
* @
|
|
545
|
+
* 保存为JSON
|
|
546
|
+
* @param {string} file 文件名
|
|
547
|
+
* @returns {Record<string, *>}
|
|
676
548
|
*/
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
return typeof innerText === 'string' ? innerText.split('\n').at(-1).length : undefined;
|
|
549
|
+
json(file) {
|
|
550
|
+
const {childNodes, ...prop} = this,
|
|
551
|
+
json = {
|
|
552
|
+
...prop,
|
|
553
|
+
childNodes: childNodes.map(child => child.type === 'text' ? String(child) : child.json()),
|
|
554
|
+
};
|
|
555
|
+
if (typeof file === 'string') {
|
|
556
|
+
fs.writeFileSync(
|
|
557
|
+
path.join(__dirname.slice(0, -4), 'printed', `${file}${file.endsWith('.json') ? '' : '.json'}`),
|
|
558
|
+
JSON.stringify(json, null, 2),
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
return json;
|
|
691
562
|
}
|
|
692
563
|
|
|
693
564
|
/**
|
|
694
565
|
* 输出AST
|
|
695
|
-
* @
|
|
696
|
-
* @
|
|
697
|
-
* @param {T extends 'markup' ? number : string} depth 输出深度
|
|
698
|
-
* @returns {T extends 'markup' ? void : Record<string, any>}
|
|
566
|
+
* @param {number} depth 当前深度
|
|
567
|
+
* @returns {void}
|
|
699
568
|
*/
|
|
700
|
-
|
|
701
|
-
if (
|
|
702
|
-
const {childNodes, ...prop} = this,
|
|
703
|
-
json = {
|
|
704
|
-
...prop,
|
|
705
|
-
childNodes: childNodes.map(child => child.type === 'text' ? String(child) : child.print('json')),
|
|
706
|
-
};
|
|
707
|
-
if (typeof depth === 'string') {
|
|
708
|
-
fs.writeFileSync(
|
|
709
|
-
`${__dirname.slice(0, -3)}printed/${depth}${depth.endsWith('.json') ? '' : '.json'}`,
|
|
710
|
-
JSON.stringify(json, null, 2),
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
return json;
|
|
714
|
-
} else if (typeof depth !== 'number') {
|
|
569
|
+
echo(depth = 0) {
|
|
570
|
+
if (!Number.isInteger(depth) || depth < 0) {
|
|
715
571
|
this.typeError('print', 'Number');
|
|
716
572
|
}
|
|
717
573
|
const indent = ' '.repeat(depth),
|
|
718
574
|
str = String(this),
|
|
719
|
-
{childNodes, type
|
|
575
|
+
{childNodes, type} = this,
|
|
720
576
|
{length} = childNodes;
|
|
721
|
-
if (
|
|
577
|
+
if (childNodes.every(child => child.type === 'text' || !String(child))) {
|
|
722
578
|
console.log(`${indent}\x1B[32m<%s>\x1B[0m${noWrap(str)}\x1B[32m</%s>\x1B[0m`, type, type);
|
|
723
579
|
return undefined;
|
|
724
580
|
}
|
|
@@ -736,11 +592,12 @@ class AstElement extends AstNode {
|
|
|
736
592
|
} else if (child.type === 'text') {
|
|
737
593
|
console.log(`${indent} ${noWrap(String(child))}`);
|
|
738
594
|
} else {
|
|
739
|
-
child.
|
|
595
|
+
child.echo(depth + 1);
|
|
740
596
|
}
|
|
741
|
-
i += childStr.length
|
|
597
|
+
i += childStr.length;
|
|
742
598
|
if (gap) {
|
|
743
|
-
console.log(`${indent} ${noWrap(str.slice(i
|
|
599
|
+
console.log(`${indent} ${noWrap(str.slice(i, i + gap))}`);
|
|
600
|
+
i += gap;
|
|
744
601
|
}
|
|
745
602
|
}
|
|
746
603
|
if (i < str.length) {
|