wikiparser-node 0.11.0 → 1.0.0-b

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.
Files changed (179) hide show
  1. package/README.md +10 -9
  2. package/bundle/bundle.min.js +37 -0
  3. package/config/llwiki.json +1 -35
  4. package/config/minimum.json +136 -0
  5. package/config/moegirl.json +1 -44
  6. package/config/zhwiki.json +1 -466
  7. package/extensions/dist/base.js +105 -0
  8. package/extensions/dist/editor.js +172 -0
  9. package/extensions/dist/highlight.js +46 -0
  10. package/extensions/dist/lint.js +43 -0
  11. package/extensions/editor.css +63 -0
  12. package/extensions/ui.css +119 -0
  13. package/i18n/zh-hans.json +1 -0
  14. package/i18n/zh-hant.json +1 -0
  15. package/package.json +38 -31
  16. package/config/.schema.json +0 -134
  17. package/dist/index.d.ts +0 -114
  18. package/dist/lib/element.d.ts +0 -162
  19. package/dist/lib/node.d.ts +0 -291
  20. package/dist/lib/ranges.d.ts +0 -37
  21. package/dist/lib/text.d.ts +0 -64
  22. package/dist/lib/title.d.ts +0 -21
  23. package/dist/mixin/attributeParent.d.ts +0 -9
  24. package/dist/mixin/fixedToken.d.ts +0 -8
  25. package/dist/mixin/hidden.d.ts +0 -8
  26. package/dist/mixin/singleLine.d.ts +0 -8
  27. package/dist/mixin/sol.d.ts +0 -8
  28. package/dist/parser/brackets.d.ts +0 -12
  29. package/dist/parser/commentAndExt.d.ts +0 -8
  30. package/dist/parser/converter.d.ts +0 -7
  31. package/dist/parser/externalLinks.d.ts +0 -7
  32. package/dist/parser/hrAndDoubleUnderscore.d.ts +0 -11
  33. package/dist/parser/html.d.ts +0 -7
  34. package/dist/parser/links.d.ts +0 -7
  35. package/dist/parser/list.d.ts +0 -7
  36. package/dist/parser/magicLinks.d.ts +0 -7
  37. package/dist/parser/quotes.d.ts +0 -7
  38. package/dist/parser/selector.d.ts +0 -12
  39. package/dist/parser/table.d.ts +0 -11
  40. package/dist/src/arg.d.ts +0 -54
  41. package/dist/src/atom/hidden.d.ts +0 -5
  42. package/dist/src/atom/index.d.ts +0 -15
  43. package/dist/src/attribute.d.ts +0 -65
  44. package/dist/src/attributes.d.ts +0 -112
  45. package/dist/src/charinsert.d.ts +0 -32
  46. package/dist/src/converter.d.ts +0 -103
  47. package/dist/src/converterFlags.d.ts +0 -83
  48. package/dist/src/converterRule.d.ts +0 -75
  49. package/dist/src/extLink.d.ts +0 -62
  50. package/dist/src/gallery.d.ts +0 -33
  51. package/dist/src/hasNowiki/index.d.ts +0 -14
  52. package/dist/src/hasNowiki/pre.d.ts +0 -13
  53. package/dist/src/heading.d.ts +0 -44
  54. package/dist/src/html.d.ts +0 -56
  55. package/dist/src/imageParameter.d.ts +0 -65
  56. package/dist/src/imagemap.d.ts +0 -37
  57. package/dist/src/imagemapLink.d.ts +0 -21
  58. package/dist/src/index.d.ts +0 -186
  59. package/dist/src/link/category.d.ts +0 -16
  60. package/dist/src/link/file.d.ts +0 -85
  61. package/dist/src/link/galleryImage.d.ts +0 -15
  62. package/dist/src/link/index.d.ts +0 -88
  63. package/dist/src/magicLink.d.ts +0 -36
  64. package/dist/src/nested/choose.d.ts +0 -13
  65. package/dist/src/nested/combobox.d.ts +0 -13
  66. package/dist/src/nested/index.d.ts +0 -18
  67. package/dist/src/nested/references.d.ts +0 -13
  68. package/dist/src/nowiki/comment.d.ts +0 -31
  69. package/dist/src/nowiki/dd.d.ts +0 -17
  70. package/dist/src/nowiki/doubleUnderscore.d.ts +0 -22
  71. package/dist/src/nowiki/hr.d.ts +0 -13
  72. package/dist/src/nowiki/index.d.ts +0 -27
  73. package/dist/src/nowiki/list.d.ts +0 -8
  74. package/dist/src/nowiki/noinclude.d.ts +0 -8
  75. package/dist/src/nowiki/quote.d.ts +0 -13
  76. package/dist/src/onlyinclude.d.ts +0 -24
  77. package/dist/src/paramTag/index.d.ts +0 -29
  78. package/dist/src/paramTag/inputbox.d.ts +0 -8
  79. package/dist/src/parameter.d.ts +0 -75
  80. package/dist/src/syntax.d.ts +0 -20
  81. package/dist/src/table/index.d.ts +0 -273
  82. package/dist/src/table/td.d.ts +0 -100
  83. package/dist/src/table/tr.d.ts +0 -91
  84. package/dist/src/tagPair/ext.d.ts +0 -18
  85. package/dist/src/tagPair/include.d.ts +0 -25
  86. package/dist/src/tagPair/index.d.ts +0 -41
  87. package/dist/src/transclude.d.ts +0 -199
  88. package/dist/tool/index.d.ts +0 -420
  89. package/dist/util/base.d.ts +0 -10
  90. package/dist/util/debug.d.ts +0 -20
  91. package/dist/util/diff.d.ts +0 -8
  92. package/dist/util/lint.d.ts +0 -28
  93. package/dist/util/string.d.ts +0 -55
  94. package/index.js +0 -333
  95. package/lib/element.js +0 -618
  96. package/lib/node.js +0 -730
  97. package/lib/ranges.js +0 -130
  98. package/lib/text.js +0 -265
  99. package/lib/title.js +0 -83
  100. package/mixin/attributeParent.js +0 -117
  101. package/mixin/fixedToken.js +0 -40
  102. package/mixin/hidden.js +0 -21
  103. package/mixin/singleLine.js +0 -31
  104. package/mixin/sol.js +0 -54
  105. package/parser/brackets.js +0 -128
  106. package/parser/commentAndExt.js +0 -62
  107. package/parser/converter.js +0 -46
  108. package/parser/externalLinks.js +0 -33
  109. package/parser/hrAndDoubleUnderscore.js +0 -49
  110. package/parser/html.js +0 -42
  111. package/parser/links.js +0 -94
  112. package/parser/list.js +0 -59
  113. package/parser/magicLinks.js +0 -41
  114. package/parser/quotes.js +0 -64
  115. package/parser/selector.js +0 -180
  116. package/parser/table.js +0 -114
  117. package/src/arg.js +0 -207
  118. package/src/atom/hidden.js +0 -13
  119. package/src/atom/index.js +0 -43
  120. package/src/attribute.js +0 -472
  121. package/src/attributes.js +0 -453
  122. package/src/charinsert.js +0 -97
  123. package/src/converter.js +0 -176
  124. package/src/converterFlags.js +0 -284
  125. package/src/converterRule.js +0 -256
  126. package/src/extLink.js +0 -180
  127. package/src/gallery.js +0 -149
  128. package/src/hasNowiki/index.js +0 -44
  129. package/src/hasNowiki/pre.js +0 -40
  130. package/src/heading.js +0 -134
  131. package/src/html.js +0 -254
  132. package/src/imageParameter.js +0 -303
  133. package/src/imagemap.js +0 -199
  134. package/src/imagemapLink.js +0 -41
  135. package/src/index.js +0 -938
  136. package/src/link/category.js +0 -44
  137. package/src/link/file.js +0 -287
  138. package/src/link/galleryImage.js +0 -120
  139. package/src/link/index.js +0 -388
  140. package/src/magicLink.js +0 -151
  141. package/src/nested/choose.js +0 -24
  142. package/src/nested/combobox.js +0 -23
  143. package/src/nested/index.js +0 -96
  144. package/src/nested/references.js +0 -23
  145. package/src/nowiki/comment.js +0 -71
  146. package/src/nowiki/dd.js +0 -59
  147. package/src/nowiki/doubleUnderscore.js +0 -56
  148. package/src/nowiki/hr.js +0 -41
  149. package/src/nowiki/index.js +0 -56
  150. package/src/nowiki/list.js +0 -16
  151. package/src/nowiki/noinclude.js +0 -28
  152. package/src/nowiki/quote.js +0 -69
  153. package/src/onlyinclude.js +0 -64
  154. package/src/paramTag/index.js +0 -89
  155. package/src/paramTag/inputbox.js +0 -35
  156. package/src/parameter.js +0 -239
  157. package/src/syntax.js +0 -91
  158. package/src/table/index.js +0 -985
  159. package/src/table/td.js +0 -343
  160. package/src/table/tr.js +0 -319
  161. package/src/tagPair/ext.js +0 -146
  162. package/src/tagPair/include.js +0 -50
  163. package/src/tagPair/index.js +0 -131
  164. package/src/transclude.js +0 -843
  165. package/tool/index.js +0 -1209
  166. package/typings/api.d.ts +0 -9
  167. package/typings/array.d.ts +0 -29
  168. package/typings/event.d.ts +0 -22
  169. package/typings/index.d.ts +0 -118
  170. package/typings/node.d.ts +0 -35
  171. package/typings/parser.d.ts +0 -12
  172. package/typings/table.d.ts +0 -10
  173. package/typings/token.d.ts +0 -31
  174. package/typings/tool.d.ts +0 -6
  175. package/util/base.js +0 -17
  176. package/util/debug.js +0 -73
  177. package/util/diff.js +0 -76
  178. package/util/lint.js +0 -57
  179. package/util/string.js +0 -126
package/lib/element.js DELETED
@@ -1,618 +0,0 @@
1
- 'use strict';
2
-
3
- /** @typedef {import('../typings/parser').SelectorArray} SelectorArray */
4
-
5
- const fs = require('fs'),
6
- path = require('path'),
7
- {toCase, noWrap, print} = require('../util/string'),
8
- {nth} = require('./ranges'),
9
- parseSelector = require('../parser/selector'),
10
- Parser = require('..'),
11
- AstNode = require('./node'),
12
- AstText = require('./text');
13
-
14
- const lintIgnoredExt = new Set([
15
- 'nowiki',
16
- 'pre',
17
- 'charinsert',
18
- 'score',
19
- 'syntaxhighlight',
20
- 'source',
21
- 'math',
22
- 'chem',
23
- 'ce',
24
- 'graph',
25
- 'mapframe',
26
- 'maplink',
27
- 'quiz',
28
- 'templatedata',
29
- 'timeline',
30
- ]);
31
-
32
- /**
33
- * 检测:lang()伪选择器
34
- * @param {AstElement & {attributes: Records<string, string|true>}} node 节点
35
- * @param {RegExp} regex 语言正则
36
- */
37
- const matchesLang = ({attributes}, regex) => {
38
- const /** @type {string} */ lang = attributes?.lang;
39
- return typeof lang === 'string' && regex.test(lang);
40
- };
41
-
42
- /** 类似HTMLElement */
43
- class AstElement extends AstNode {
44
- /** @type {string} */ name;
45
-
46
- /**
47
- * 检查是否符合某条属性规则
48
- * @param {string} key 属性键
49
- * @param {string|undefined} equal 属性规则运算符,`equal`存在时`val`和`i`也一定存在
50
- * @param {string|undefined} val 属性值
51
- * @param {string|undefined} i 是否对大小写不敏感
52
- * @throws `RangeError` 复杂属性
53
- */
54
- #matchesAttr = (key, equal, val, i) => {
55
- if (!equal) {
56
- return this.hasAttribute(key);
57
- } else if (!this.hasAttribute(key)) {
58
- return equal === '!=';
59
- }
60
- val = toCase(val, i);
61
- let thisVal = this.getAttribute(key);
62
- if (thisVal instanceof RegExp) {
63
- thisVal = thisVal.source;
64
- }
65
- if (equal === '~=') {
66
- const thisVals = typeof thisVal === 'string' ? thisVal.split(/\s/u) : thisVal;
67
- return Boolean(thisVals?.[Symbol.iterator]) && [...thisVals].some(v => toCase(v, i) === val);
68
- } else if (typeof thisVal !== 'string') {
69
- throw new RangeError(`复杂属性 ${key} 不能用于选择器!`);
70
- }
71
- thisVal = toCase(thisVal, i);
72
- switch (equal) {
73
- case '|=':
74
- return thisVal === val || thisVal.startsWith(`${val}-`);
75
- case '^=':
76
- return thisVal.startsWith(val);
77
- case '$=':
78
- return thisVal.endsWith(val);
79
- case '*=':
80
- return thisVal.includes(val);
81
- case '!=':
82
- return thisVal !== val;
83
- default: // `=`
84
- return thisVal === val;
85
- }
86
- };
87
-
88
- /** 子节点总数 */
89
- get length() {
90
- return this.childNodes.length;
91
- }
92
-
93
- /**
94
- * 全部非文本子节点
95
- * @complexity `n`
96
- */
97
- get children() {
98
- const /** @type {this[]} */ children = this.childNodes.filter(({type}) => type !== 'text');
99
- return children;
100
- }
101
-
102
- /**
103
- * 首位非文本子节点
104
- * @returns {this}
105
- */
106
- get firstElementChild() {
107
- return this.childNodes.find(({type}) => type !== 'text');
108
- }
109
-
110
- /**
111
- * 末位非文本子节点
112
- * @complexity `n`
113
- */
114
- get lastElementChild() {
115
- return this.children.at(-1);
116
- }
117
-
118
- /**
119
- * 非文本子节点总数
120
- * @complexity `n`
121
- */
122
- get childElementCount() {
123
- return this.children.length;
124
- }
125
-
126
- /** 父节点 */
127
- get parentElement() {
128
- return this.parentNode;
129
- }
130
-
131
- /**
132
- * AstElement.prototype.text()的getter写法
133
- * @complexity `n`
134
- */
135
- get outerText() {
136
- return this.text();
137
- }
138
-
139
- /**
140
- * 不可见
141
- */
142
- get hidden() {
143
- return this.text() === '';
144
- }
145
-
146
- /**
147
- * 后一个可见的兄弟节点
148
- * @complexity `n`
149
- */
150
- get nextVisibleSibling() {
151
- let {nextSibling} = this;
152
- while (nextSibling?.text() === '') {
153
- ({nextSibling} = nextSibling);
154
- }
155
- return nextSibling;
156
- }
157
-
158
- /**
159
- * 前一个可见的兄弟节点
160
- * @complexity `n`
161
- */
162
- get previousVisibleSibling() {
163
- let {previousSibling} = this;
164
- while (previousSibling?.text() === '') {
165
- ({previousSibling} = previousSibling);
166
- }
167
- return previousSibling;
168
- }
169
-
170
- /** 内部高度 */
171
- get clientHeight() {
172
- const {innerText} = this;
173
- return typeof innerText === 'string' ? innerText.split('\n').length : undefined;
174
- }
175
-
176
- /** 内部宽度 */
177
- get clientWidth() {
178
- const {innerText} = this;
179
- return typeof innerText === 'string' ? innerText.split('\n').at(-1).length : undefined;
180
- }
181
-
182
- constructor() {
183
- super();
184
- this.seal('name');
185
- }
186
-
187
- /**
188
- * 销毁
189
- * @complexity `n`
190
- */
191
- destroy() {
192
- this.parentNode?.destroy();
193
- for (const child of this.childNodes) {
194
- child.setAttribute('parentNode');
195
- }
196
- Object.setPrototypeOf(this, null);
197
- }
198
-
199
- /**
200
- * @override
201
- * @template {string} T
202
- * @param {T} key 属性键
203
- * @returns {import('../typings/node').TokenAttribute<T>}
204
- */
205
- getAttribute(key) {
206
- return key === 'matchesAttr' ? this.#matchesAttr : super.getAttribute(key);
207
- }
208
-
209
- /** 是否受保护。保护条件来自Token,这里仅提前用于:required和:optional伪选择器。 */
210
- #isProtected() {
211
- const /** @type {{parentNode: AstElement & {constructor: {fixed: boolean}}}} */ {parentNode} = this;
212
- if (!parentNode) {
213
- return undefined;
214
- }
215
- const {childNodes, constructor: {fixed}} = parentNode,
216
- protectedIndices = parentNode.getAttribute('protectedChildren')?.applyTo(childNodes);
217
- return fixed || protectedIndices?.includes(childNodes.indexOf(this));
218
- }
219
-
220
- /**
221
- * 检查是否符合解析后的选择器,不含节点关系
222
- * @this {AstElement & {link: string|Title, constructor: {fixed: boolean}}}
223
- * @param {SelectorArray} step 解析后的选择器
224
- * @throws `SyntaxError` 未定义的伪选择器
225
- */
226
- #matches(step) {
227
- const Title = require('./title');
228
- const {parentNode, type, name, childNodes, link, constructor: {fixed, name: tokenName}} = this,
229
- children = parentNode?.children,
230
- childrenOfType = children?.filter(({type: t}) => t === type),
231
- siblingsCount = children?.length ?? 1,
232
- siblingsCountOfType = childrenOfType?.length ?? 1,
233
- index = (children?.indexOf(this) ?? 0) + 1,
234
- indexOfType = (childrenOfType?.indexOf(this) ?? 0) + 1,
235
- lastIndex = siblingsCount - index + 1,
236
- lastIndexOfType = siblingsCountOfType - indexOfType + 1;
237
- return step.every(selector => {
238
- if (typeof selector === 'string') {
239
- switch (selector) { // 情形1:简单伪选择器、type和name
240
- case '*':
241
- return true;
242
- case ':root':
243
- return !parentNode;
244
- case ':first-child':
245
- return index === 1;
246
- case ':first-of-type':
247
- return indexOfType === 1;
248
- case ':last-child':
249
- return lastIndex === 1;
250
- case ':last-of-type':
251
- return lastIndexOfType === 1;
252
- case ':only-child':
253
- return siblingsCount === 1;
254
- case ':only-of-type':
255
- return siblingsCountOfType === 1;
256
- case ':empty':
257
- return !childNodes.some(child => child instanceof AstElement || String(child));
258
- case ':parent':
259
- return childNodes.some(child => child instanceof AstElement || String(child));
260
- case ':header':
261
- return type === 'heading';
262
- case ':hidden':
263
- return this.text() === '';
264
- case ':visible':
265
- return this.text() !== '';
266
- case ':only-whitespace':
267
- return this.text().trim() === '';
268
- case ':any-link':
269
- return type === 'link' || type === 'free-ext-link' || type === 'ext-link'
270
- || (type === 'file' || type === 'gallery-image' && link);
271
- case ':local-link':
272
- return (type === 'link' || type === 'file' || type === 'gallery-image')
273
- && link instanceof Title && link.title === '';
274
- case ':read-only':
275
- return fixed;
276
- case ':read-write':
277
- return !fixed;
278
- case ':invalid':
279
- return type === 'table-inter' || tokenName === 'HiddenToken';
280
- case ':required':
281
- return this.#isProtected() === true;
282
- case ':optional':
283
- return this.#isProtected() === false;
284
- default: {
285
- const [t, n] = selector.split('#');
286
- return (!t || t === type || Boolean(Parser.typeAliases[type]?.includes(t)))
287
- && (!n || n === name);
288
- }
289
- }
290
- } else if (selector.length === 4) { // 情形2:属性选择器
291
- return this.getAttribute('matchesAttr')(...selector);
292
- }
293
- const [s, pseudo] = selector; // 情形3:复杂伪选择器
294
- switch (pseudo) {
295
- case 'is':
296
- return this.matches(s);
297
- case 'not':
298
- return !this.matches(s);
299
- case 'nth-child':
300
- return nth(s, index);
301
- case 'nth-of-type':
302
- return nth(s, indexOfType);
303
- case 'nth-last-child':
304
- return nth(s, lastIndex);
305
- case 'nth-last-of-type':
306
- return nth(s, lastIndexOfType);
307
- case 'contains':
308
- return this.text().includes(s);
309
- case 'has':
310
- return Boolean(this.querySelector(s));
311
- case 'lang': {
312
- const regex = new RegExp(`^${s}(?:-|$)`, 'u');
313
- return matchesLang(this, regex)
314
- || this.getAncestors().some(ancestor => matchesLang(ancestor, regex));
315
- }
316
- default:
317
- throw new SyntaxError(`未定义的伪选择器!${pseudo}`);
318
- }
319
- });
320
- }
321
-
322
- /**
323
- * 检查是否符合选择器
324
- * @param {string|SelectorArray[]} selector
325
- * @returns {boolean}
326
- * @complexity `n`
327
- */
328
- matches(selector) {
329
- if (selector === undefined) {
330
- return true;
331
- } else if (typeof selector === 'string') {
332
- const stack = parseSelector(selector),
333
- /** @type {Set<string>} */
334
- pseudos = new Set(stack.flat(2).filter(step => typeof step === 'string' && step[0] === ':'));
335
- if (pseudos.size > 0) {
336
- Parser.warn('检测到伪选择器,请确认是否需要将":"转义成"\\:"。', pseudos);
337
- }
338
- return Parser.run(() => stack.some(condition => this.matches(condition)));
339
- } else if (!Parser.running) {
340
- this.typeError('matches', 'String');
341
- }
342
- selector = [...selector];
343
- const step = selector.pop();
344
- if (this.#matches(step)) {
345
- const {parentNode, previousElementSibling} = this;
346
- switch (selector.at(-1)?.relation) {
347
- case undefined:
348
- return true;
349
- case '>':
350
- return parentNode?.matches(selector);
351
- case '+':
352
- return previousElementSibling?.matches(selector);
353
- case '~': {
354
- if (!parentNode) {
355
- return false;
356
- }
357
- const {children} = parentNode,
358
- i = children.indexOf(this);
359
- return children.slice(0, i).some(child => child.matches(selector));
360
- }
361
- default: // ' '
362
- return this.getAncestors().some(ancestor => ancestor.matches(selector));
363
- }
364
- }
365
- return false;
366
- }
367
-
368
- /**
369
- * 符合选择器的第一个后代节点
370
- * @param {string} selector
371
- * @returns {this|undefined}
372
- * @complexity `n`
373
- */
374
- querySelector(selector) {
375
- for (const child of this.children) {
376
- if (child.matches(selector)) {
377
- return child;
378
- }
379
- const descendant = child.querySelector(selector);
380
- if (descendant) {
381
- return descendant;
382
- }
383
- }
384
- return undefined;
385
- }
386
-
387
- /**
388
- * 符合选择器的所有后代节点
389
- * @param {string} selector
390
- * @complexity `n`
391
- */
392
- querySelectorAll(selector) {
393
- const /** @type {this[]} */ descendants = [];
394
- for (const child of this.children) {
395
- if (child.matches(selector)) {
396
- descendants.push(child);
397
- }
398
- descendants.push(...child.querySelectorAll(selector));
399
- }
400
- return descendants;
401
- }
402
-
403
- /**
404
- * id选择器
405
- * @param {string} id id名
406
- */
407
- getElementById(id) {
408
- if (typeof id === 'string') {
409
- id = id.replace(/(?<!\\)"/gu, '\\"');
410
- return this.querySelector(`ext[id="${id}"], html[id="${id}"]`);
411
- }
412
- return this.typeError('getElementById', 'String');
413
- }
414
-
415
- /**
416
- * 类选择器
417
- * @param {string} className 类名之一
418
- */
419
- getElementsByClassName(className) {
420
- return typeof className === 'string'
421
- ? this.querySelectorAll(`[className~="${className.replace(/(?<!\\)"/gu, '\\"')}"]`)
422
- : this.typeError('getElementsByClassName', 'String');
423
- }
424
-
425
- /**
426
- * 标签名选择器
427
- * @param {string} name 标签名
428
- */
429
- getElementsByTagName(name) {
430
- if (typeof name === 'string') {
431
- name = name.replace(/(?<!\\)"/gu, '\\"');
432
- return this.querySelectorAll(`ext[name="${name}"], html[name="${name}"]`);
433
- }
434
- return this.typeError('getElementsByTagName', 'String');
435
- }
436
-
437
- /**
438
- * 获取某一行的wikitext
439
- * @param {number} n 行号
440
- */
441
- getLine(n) {
442
- return String(this).split('\n', n + 1).at(-1);
443
- }
444
-
445
- /**
446
- * 在开头批量插入子节点
447
- * @param {...this} elements 插入节点
448
- * @complexity `n`
449
- */
450
- prepend(...elements) {
451
- for (let i = 0; i < elements.length; i++) {
452
- this.insertAt(elements[i], i);
453
- }
454
- }
455
-
456
- /**
457
- * 最近的祖先节点
458
- * @param {string} selector
459
- */
460
- closest(selector) {
461
- let {parentNode} = this;
462
- while (parentNode) {
463
- if (parentNode.matches(selector)) {
464
- return parentNode;
465
- }
466
- ({parentNode} = parentNode);
467
- }
468
- return undefined;
469
- }
470
-
471
- /**
472
- * 在末尾批量插入子节点
473
- * @param {...this} elements 插入节点
474
- * @complexity `n`
475
- */
476
- append(...elements) {
477
- for (const element of elements) {
478
- this.insertAt(element);
479
- }
480
- }
481
-
482
- /**
483
- * 批量替换子节点
484
- * @param {...this} elements 新的子节点
485
- * @complexity `n`
486
- */
487
- replaceChildren(...elements) {
488
- for (let i = this.length - 1; i >= 0; i--) {
489
- this.removeAt(i);
490
- }
491
- this.append(...elements);
492
- }
493
-
494
- /**
495
- * 修改文本子节点
496
- * @param {string} str 新文本
497
- * @param {number} i 子节点位置
498
- * @throws `RangeError` 对应位置的子节点不是文本节点
499
- */
500
- setText(str, i = 0) {
501
- this.getAttribute('verifyChild')(i);
502
- const /** @type {AstText} */ oldText = this.childNodes.at(i),
503
- {type, data, constructor: {name}} = oldText;
504
- if (type === 'text') {
505
- oldText.replaceData(str);
506
- return data;
507
- }
508
- throw new RangeError(`第 ${i} 个子节点是 ${name}!`);
509
- }
510
-
511
- /**
512
- * 还原为wikitext
513
- * @param {string} selector
514
- * @param {string} separator 子节点间的连接符
515
- * @returns {string}
516
- */
517
- toString(selector, separator = '') {
518
- return selector && this.matches(selector)
519
- ? ''
520
- : this.childNodes.map(child => child.toString(selector)).join(separator);
521
- }
522
-
523
- /**
524
- * Linter
525
- * @param {number} start 起始位置
526
- */
527
- lint(start = this.getAbsoluteIndex()) {
528
- const SyntaxToken = require('../src/syntax');
529
- if (this instanceof SyntaxToken || this.constructor.hidden
530
- || this.type === 'ext-inner' && lintIgnoredExt.has(this.name)
531
- ) {
532
- return [];
533
- }
534
- const /** @type {import('../typings/token').LintError[]} */ errors = [];
535
- for (let i = 0, cur = start + this.getPadding(); i < this.length; i++) {
536
- const child = this.childNodes[i];
537
- errors.push(...child.lint(cur));
538
- cur += String(child).length + this.getGaps(i);
539
- }
540
- return errors;
541
- }
542
-
543
- /**
544
- * 以HTML格式打印
545
- * @param {import('../typings/node').printOpt} opt 选项
546
- * @returns {string}
547
- */
548
- print(opt = {}) {
549
- return String(this)
550
- ? `<span class="wpb-${opt.class || this.type}">${print(this.childNodes, opt)}</span>`
551
- : '';
552
- }
553
-
554
- /**
555
- * 保存为JSON
556
- * @param {string} file 文件名
557
- * @returns {Record<string, *>}
558
- */
559
- json(file) {
560
- const json = {
561
- ...this,
562
- childNodes: this.childNodes.map(child => child.type === 'text' ? String(child) : child.json()),
563
- };
564
- if (typeof file === 'string') {
565
- fs.writeFileSync(
566
- path.join(__dirname.slice(0, -4), 'printed', `${file}${file.endsWith('.json') ? '' : '.json'}`),
567
- JSON.stringify(json, null, 2),
568
- );
569
- }
570
- return json;
571
- }
572
-
573
- /**
574
- * 输出AST
575
- * @param {number} depth 当前深度
576
- */
577
- echo(depth = 0) {
578
- if (!Number.isInteger(depth) || depth < 0) {
579
- this.typeError('print', 'Number');
580
- }
581
- const indent = ' '.repeat(depth),
582
- str = String(this),
583
- {childNodes, type, length} = this;
584
- if (childNodes.every(child => child.type === 'text' || !String(child))) {
585
- console.log(`${indent}\x1B[32m<%s>\x1B[0m${noWrap(str)}\x1B[32m</%s>\x1B[0m`, type, type);
586
- return;
587
- }
588
- Parser.info(`${indent}<${type}>`);
589
- let i = this.getPadding();
590
- if (i) {
591
- console.log(`${indent} ${noWrap(str.slice(0, i))}`);
592
- }
593
- for (let j = 0; j < length; j++) {
594
- const child = childNodes[j],
595
- childStr = String(child),
596
- gap = j === length - 1 ? 0 : this.getGaps(j);
597
- if (!childStr) {
598
- // pass
599
- } else if (child.type === 'text') {
600
- console.log(`${indent} ${noWrap(String(child))}`);
601
- } else {
602
- child.echo(depth + 1);
603
- }
604
- i += childStr.length;
605
- if (gap) {
606
- console.log(`${indent} ${noWrap(str.slice(i, i + gap))}`);
607
- i += gap;
608
- }
609
- }
610
- if (i < str.length) {
611
- console.log(`${indent} ${noWrap(str.slice(i))}`);
612
- }
613
- Parser.info(`${indent}</${type}>`);
614
- }
615
- }
616
-
617
- Parser.classes.AstElement = __filename;
618
- module.exports = AstElement;