wikiparser-node 0.0.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.
Files changed (65) hide show
  1. package/.eslintrc.json +229 -0
  2. package/LICENSE +674 -0
  3. package/README.md +1896 -0
  4. package/config/default.json +766 -0
  5. package/config/llwiki.json +686 -0
  6. package/config/moegirl.json +721 -0
  7. package/index.js +159 -0
  8. package/jsconfig.json +7 -0
  9. package/lib/element.js +690 -0
  10. package/lib/node.js +357 -0
  11. package/lib/ranges.js +122 -0
  12. package/lib/title.js +57 -0
  13. package/mixin/attributeParent.js +67 -0
  14. package/mixin/fixedToken.js +32 -0
  15. package/mixin/hidden.js +22 -0
  16. package/package.json +30 -0
  17. package/parser/brackets.js +107 -0
  18. package/parser/commentAndExt.js +61 -0
  19. package/parser/externalLinks.js +30 -0
  20. package/parser/hrAndDoubleUnderscore.js +26 -0
  21. package/parser/html.js +41 -0
  22. package/parser/links.js +92 -0
  23. package/parser/magicLinks.js +40 -0
  24. package/parser/quotes.js +63 -0
  25. package/parser/table.js +97 -0
  26. package/src/arg.js +150 -0
  27. package/src/atom/hidden.js +10 -0
  28. package/src/atom/index.js +33 -0
  29. package/src/attribute.js +342 -0
  30. package/src/extLink.js +116 -0
  31. package/src/heading.js +91 -0
  32. package/src/html.js +144 -0
  33. package/src/imageParameter.js +172 -0
  34. package/src/index.js +602 -0
  35. package/src/link/category.js +88 -0
  36. package/src/link/file.js +201 -0
  37. package/src/link/index.js +214 -0
  38. package/src/listToken.js +47 -0
  39. package/src/magicLink.js +66 -0
  40. package/src/nowiki/comment.js +45 -0
  41. package/src/nowiki/doubleUnderscore.js +42 -0
  42. package/src/nowiki/hr.js +41 -0
  43. package/src/nowiki/index.js +37 -0
  44. package/src/nowiki/noinclude.js +24 -0
  45. package/src/nowiki/quote.js +37 -0
  46. package/src/onlyinclude.js +42 -0
  47. package/src/parameter.js +165 -0
  48. package/src/syntax.js +80 -0
  49. package/src/table/index.js +867 -0
  50. package/src/table/td.js +259 -0
  51. package/src/table/tr.js +244 -0
  52. package/src/tagPair/ext.js +85 -0
  53. package/src/tagPair/include.js +45 -0
  54. package/src/tagPair/index.js +91 -0
  55. package/src/transclude.js +627 -0
  56. package/tool/index.js +898 -0
  57. package/typings/element.d.ts +28 -0
  58. package/typings/index.d.ts +49 -0
  59. package/typings/node.d.ts +23 -0
  60. package/typings/parser.d.ts +9 -0
  61. package/typings/table.d.ts +14 -0
  62. package/typings/token.d.ts +21 -0
  63. package/typings/tool.d.ts +10 -0
  64. package/util/debug.js +70 -0
  65. package/util/string.js +60 -0
package/lib/node.js ADDED
@@ -0,0 +1,357 @@
1
+ 'use strict';
2
+
3
+ const {typeError, externalUse} = require('../util/debug'),
4
+ {text} = require('../util/string'),
5
+ assert = require('assert/strict'),
6
+ /** @type {Parser} */ Parser = require('..');
7
+
8
+ class AstNode {
9
+ /** @type {(string|this)[]} */ childNodes = [];
10
+ /** @type {this} */ #parentNode;
11
+ /** @type {string[]} */ #optional = [];
12
+
13
+ get firstChild() {
14
+ return this.childNodes[0];
15
+ }
16
+ get lastChild() {
17
+ return this.childNodes.at(-1);
18
+ }
19
+ get parentNode() {
20
+ return this.#parentNode;
21
+ }
22
+ /** @complexity `n` */
23
+ get nextSibling() {
24
+ const childNodes = this.#parentNode?.childNodes;
25
+ return childNodes?.[childNodes?.indexOf(this) + 1];
26
+ }
27
+ /** @complexity `n` */
28
+ get previousSibling() {
29
+ const childNodes = this.#parentNode?.childNodes;
30
+ return childNodes?.[childNodes?.indexOf(this) - 1];
31
+ }
32
+
33
+ debugOnly(method = 'debugOnly') {
34
+ throw new Error(`${this.constructor.name}.${method} 方法仅用于代码调试!`);
35
+ }
36
+
37
+ /** @param {string|string[]} keys */
38
+ seal(keys) {
39
+ if (!Parser.running && !Parser.debugging) {
40
+ this.debugOnly('seal');
41
+ }
42
+ keys = Array.isArray(keys) ? keys : [keys];
43
+ this.#optional.push(...keys);
44
+ for (const key of keys) {
45
+ Object.defineProperty(this, key, {writable: false, enumerable: Boolean(this[key])});
46
+ }
47
+ return this;
48
+ }
49
+
50
+ constructor() {
51
+ Object.defineProperty(this, 'childNodes', {writable: false});
52
+ Object.freeze(this.childNodes);
53
+ }
54
+
55
+ /** @param {this} node */
56
+ isEqualNode(node) {
57
+ try {
58
+ assert.deepStrictEqual(this, node);
59
+ } catch (e) {
60
+ if (e instanceof assert.AssertionError) {
61
+ return false;
62
+ }
63
+ throw e;
64
+ }
65
+ return true;
66
+ }
67
+
68
+ /** @param {PropertyKey} key */
69
+ hasAttribute(key) {
70
+ if (!['string', 'number', 'symbol'].includes(typeof key)) {
71
+ typeError(this, 'hasAttribute', 'String', 'Number', 'Symbol');
72
+ }
73
+ return key in this;
74
+ }
75
+
76
+ /**
77
+ * 除非用于私有属性,否则总是返回字符串
78
+ * @template {string} T
79
+ * @param {T} key
80
+ * @returns {TokenAttribute<T>}
81
+ */
82
+ getAttribute(key) {
83
+ if (key === 'optional') {
84
+ return [...this.#optional];
85
+ }
86
+ return this.hasAttribute(key) ? String(this[key]) : undefined;
87
+ }
88
+
89
+ getAttributeNames() {
90
+ const names = Object.getOwnPropertyNames(this);
91
+ return names.filter(name => typeof this[name] !== 'function');
92
+ }
93
+
94
+ hasAttributes() {
95
+ return this.getAttributeNames().length > 0;
96
+ }
97
+
98
+ /**
99
+ * @template {string} T
100
+ * @param {T} key
101
+ * @param {TokenAttribute<T>} value
102
+ */
103
+ setAttribute(key, value) {
104
+ if (key === 'parentNode') {
105
+ if (externalUse('setAttribute')) {
106
+ throw new RangeError(`禁止手动指定 ${key} 属性!`);
107
+ }
108
+ this.#parentNode = value;
109
+ } else if (this.hasAttribute(key)) {
110
+ const descriptor = Object.getOwnPropertyDescriptor(this, key);
111
+ if (!descriptor || !descriptor.writable && externalUse('setAttribute')) {
112
+ throw new RangeError(`禁止手动指定 ${key} 属性!`);
113
+ } else if (this.#optional.includes(key)) {
114
+ descriptor.enumerable = Boolean(value);
115
+ }
116
+ const oldValue = this[key],
117
+ frozen = oldValue !== null && typeof oldValue === 'object' && Object.isFrozen(oldValue);
118
+ Object.defineProperty(this, key, {...descriptor, value});
119
+ if (frozen && value !== null && typeof value === 'object') {
120
+ Object.freeze(value);
121
+ }
122
+ } else {
123
+ this[key] = value;
124
+ }
125
+ return this;
126
+ }
127
+
128
+ /** @param {PropertyKey} key */
129
+ removeAttribute(key) {
130
+ if (this.hasAttribute(key)) {
131
+ const descriptor = Object.getOwnPropertyDescriptor(this, key);
132
+ if (!descriptor || !descriptor.writable) {
133
+ throw new RangeError(`属性 ${key} 不可删除!`);
134
+ }
135
+ delete this[key];
136
+ }
137
+ }
138
+
139
+ /**
140
+ * @param {PropertyKey} name
141
+ * @param {boolean|undefined} force
142
+ */
143
+ toggleAttribute(key, force) {
144
+ if (force !== undefined && typeof force !== 'boolean') {
145
+ typeError(this, 'toggleAttribute', 'Boolean');
146
+ } else if (this.hasAttribute(key) && typeof this[key] !== 'boolean') {
147
+ throw new RangeError(`${key} 属性的值不为 Boolean!`);
148
+ }
149
+ this.setAttribute(key, force === true || force === undefined && !this[key]);
150
+ }
151
+
152
+ /** @complexity `n` */
153
+ toString(separator = '') {
154
+ return this.childNodes.map(String).join(separator);
155
+ }
156
+
157
+ /**
158
+ * 可见部分
159
+ * @returns {string}
160
+ * @complexity `n`
161
+ */
162
+ text(separator = '') {
163
+ return text(this.childNodes, separator);
164
+ }
165
+
166
+ hasChildNodes() {
167
+ return this.childNodes.length > 0;
168
+ }
169
+
170
+ /**
171
+ * 是自身或子孙节点
172
+ * @param {this} node
173
+ * @returns {boolean}
174
+ * @complexity `n`
175
+ */
176
+ contains(node) {
177
+ if (!(node instanceof AstNode)) {
178
+ typeError(this, 'contains', 'Token');
179
+ }
180
+ return node === this || this.childNodes.some(child => child instanceof AstNode && child.contains(node));
181
+ }
182
+
183
+ /** @param {number} i */
184
+ verifyChild(i, addition = 0) {
185
+ if (!Parser.debugging && externalUse('verifyChild')) {
186
+ this.debugOnly('verifyChild');
187
+ } else if (typeof i !== 'number') {
188
+ typeError(this, 'verifyChild', 'Number');
189
+ }
190
+ const {length} = this.childNodes;
191
+ if (i < -length || i >= length + addition || !Number.isInteger(i)) {
192
+ throw new RangeError(`不存在第 ${i} 个子节点!`);
193
+ }
194
+ }
195
+
196
+ /** @param {number} i */
197
+ removeAt(i) {
198
+ this.verifyChild(i);
199
+ const childNodes = [...this.childNodes],
200
+ [node] = childNodes.splice(i, 1);
201
+ if (node instanceof AstNode) {
202
+ node.setAttribute('parentNode');
203
+ }
204
+ this.setAttribute('childNodes', childNodes);
205
+ return node;
206
+ }
207
+
208
+ /**
209
+ * @param {string|this} node
210
+ * @complexity `n`
211
+ */
212
+ #getChildIndex(node) {
213
+ const {childNodes} = this,
214
+ i = childNodes.indexOf(node);
215
+ if (i === -1) {
216
+ Parser.error('找不到子节点!', node);
217
+ throw new RangeError('找不到子节点!');
218
+ } else if (typeof node === 'string' && childNodes.lastIndexOf(node) > i) {
219
+ throw new RangeError(`重复的纯文本节点 ${node.replaceAll('\n', '\\n')}!`);
220
+ }
221
+ return i;
222
+ }
223
+
224
+ /**
225
+ * @template {string|this} T
226
+ * @param {T} node
227
+ * @complexity `n`
228
+ */
229
+ removeChild(node) {
230
+ this.removeAt(this.#getChildIndex(node));
231
+ return node;
232
+ }
233
+
234
+ /**
235
+ * @template {string|this} T
236
+ * @param {T} node
237
+ * @complexity `n`
238
+ */
239
+ insertAt(node, i = this.childNodes.length) {
240
+ if (typeof node !== 'string' && !(node instanceof AstNode)) {
241
+ typeError(this, 'insertAt', 'String', 'Token');
242
+ } else if (node instanceof AstNode && node.contains(this)) {
243
+ Parser.error('不能插入祖先节点!', node);
244
+ throw new RangeError('不能插入祖先节点!');
245
+ }
246
+ this.verifyChild(i, 1);
247
+ const childNodes = [...this.childNodes];
248
+ if (node instanceof AstNode) {
249
+ const j = Parser.running ? -1 : childNodes.indexOf(node);
250
+ if (j !== -1) {
251
+ childNodes.splice(j, 1);
252
+ } else {
253
+ node.parentNode?.removeChild(node);
254
+ node.setAttribute('parentNode', this);
255
+ }
256
+ }
257
+ childNodes.splice(i, 0, node);
258
+ this.setAttribute('childNodes', childNodes);
259
+ return node;
260
+ }
261
+
262
+ /**
263
+ * @template {string|this} T
264
+ * @param {T} node
265
+ * @complexity `n`
266
+ */
267
+ appendChild(node) {
268
+ return this.insertAt(node);
269
+ }
270
+
271
+ /**
272
+ * @template {string|this} T
273
+ * @param {T} child
274
+ * @param {string|this} reference
275
+ * @complexity `n`
276
+ */
277
+ insertBefore(child, reference) {
278
+ if (reference === undefined) {
279
+ return this.appendChild(child);
280
+ }
281
+ return this.insertAt(child, this.#getChildIndex(reference));
282
+ }
283
+
284
+ /**
285
+ * @template {string|this} T
286
+ * @param {string|this} newChild
287
+ * @param {T} oldChild
288
+ * @complexity `n`
289
+ */
290
+ replaceChild(newChild, oldChild) {
291
+ const i = this.#getChildIndex(oldChild);
292
+ this.removeAt(i);
293
+ this.insertAt(newChild, i);
294
+ return oldChild;
295
+ }
296
+
297
+ /** @param {string} str */
298
+ setText(str, i = 0) {
299
+ if (typeof str !== 'string') {
300
+ typeError(this, 'setText', 'String');
301
+ }
302
+ this.verifyChild(i);
303
+ const oldText = this.childNodes.at(i);
304
+ if (typeof oldText !== 'string') {
305
+ throw new RangeError(`第 ${i} 个子节点是 ${oldText.constructor.name}!`);
306
+ }
307
+ const childNodes = [...this.childNodes];
308
+ childNodes.splice(i, 1, str);
309
+ this.setAttribute('childNodes', childNodes);
310
+ return oldText;
311
+ }
312
+
313
+ /**
314
+ * @param {number} i
315
+ * @param {number} offset
316
+ */
317
+ splitText(i, offset) {
318
+ if (typeof offset !== 'number') {
319
+ typeError(this, 'splitText', 'Number');
320
+ }
321
+ this.verifyChild(i);
322
+ const oldText = this.childNodes.at(i);
323
+ if (typeof oldText !== 'string') {
324
+ throw new RangeError(`第 ${i} 个子节点是 ${oldText.constructor.name}!`);
325
+ }
326
+ const newText = oldText.slice(offset);
327
+ this.insertAt(newText, i + 1);
328
+ this.setText(oldText.slice(0, offset), i);
329
+ return newText;
330
+ }
331
+
332
+ /** @complexity `n` */
333
+ normalize() {
334
+ const childNodes = [...this.childNodes];
335
+ for (let i = childNodes.length - 1; i >= 0; i--) {
336
+ const str = childNodes[i];
337
+ if (str === '') {
338
+ childNodes.splice(i, 1);
339
+ } else if (typeof str === 'string' && typeof childNodes[i - 1] === 'string') {
340
+ childNodes[i - 1] += str;
341
+ childNodes.splice(i, 1);
342
+ }
343
+ }
344
+ this.setAttribute('childNodes', childNodes);
345
+ }
346
+
347
+ getRootNode() {
348
+ let {parentNode} = this;
349
+ while (parentNode?.parentNode) {
350
+ ({parentNode} = parentNode);
351
+ }
352
+ return parentNode ?? this;
353
+ }
354
+ }
355
+
356
+ Parser.classes.AstNode = __filename;
357
+ module.exports = AstNode;
package/lib/ranges.js ADDED
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..');
4
+
5
+ /** 模拟Python的Range对象。除`step`至少为`1`外,允许负数、小数或`end < start`的情形。 */
6
+ class Range {
7
+ start;
8
+ end;
9
+ step;
10
+
11
+ /** @param {string|Range} str */
12
+ constructor(str) {
13
+ if (str instanceof Range) {
14
+ Object.assign(this, str);
15
+ return;
16
+ }
17
+ str = str.trim();
18
+ if (str === 'odd') {
19
+ Object.assign(this, {start: 1, end: Infinity, step: 2});
20
+ } else if (str === 'even') {
21
+ Object.assign(this, {start: 0, end: Infinity, step: 2});
22
+ } else if (str.includes(':')) {
23
+ const [start, end, step = '1'] = str.split(':', 3);
24
+ this.start = Number(start);
25
+ this.end = Number(end || Infinity);
26
+ this.step = Math.max(Number(step), 1);
27
+ if (!Number.isInteger(this.start)) {
28
+ throw new RangeError(`起点 ${this.start} 应为整数!`);
29
+ } else if (this.end !== Infinity && !Number.isInteger(this.end)) {
30
+ throw new RangeError(`终点 ${this.end} 应为整数!`);
31
+ } else if (!Number.isInteger(this.step)) {
32
+ throw new RangeError(`步长 ${this.step} 应为整数!`);
33
+ }
34
+ } else {
35
+ const mt = str.match(/^([+-])?(\d+)?n(?:([+-])(\d+))?$/);
36
+ if (mt) {
37
+ const [, sgnA = '+', a = 1, sgnB = '+'] = mt,
38
+ b = Number(mt[4] ?? 0);
39
+ this.step = Number(a);
40
+ if (this.step === 0) {
41
+ throw new RangeError(`参数 ${str} 中 "n" 的系数不允许为 0!`);
42
+ } else if (sgnA === '+') {
43
+ this.start = sgnB === '+' || b === 0 ? b : this.step - 1 - (b - 1) % this.step;
44
+ this.end = Infinity;
45
+ } else if (sgnB === '-') {
46
+ this.start = 0;
47
+ this.end = b > 0 ? 0 : this.step;
48
+ } else {
49
+ this.start = b % this.step;
50
+ this.end = this.step + b;
51
+ }
52
+ } else {
53
+ throw new RangeError(`参数 ${str} 应写作CSS选择器的 "an+b" 形式或Python切片!`);
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 将Range转换为针对特定Array的下标集
60
+ * @param {number|any[]} arr
61
+ * @complexity `n`
62
+ */
63
+ applyTo(arr) {
64
+ return new Array(typeof arr === 'number' ? arr : arr.length).fill().map((_, i) => i)
65
+ .slice(this.start, this.end).filter((_, j) => j % this.step === 0);
66
+ }
67
+ }
68
+
69
+ /** @extends {Array<number|Range>} */
70
+ class Ranges extends Array {
71
+ /** @param {number|string|Range|(number|string|Range)[]} arr */
72
+ constructor(arr) {
73
+ super();
74
+ if (arr === undefined) {
75
+ return;
76
+ }
77
+ arr = Array.isArray(arr) ? arr : [arr];
78
+ for (const ele of arr) {
79
+ if (ele instanceof Range) {
80
+ this.push(new Range(ele));
81
+ continue;
82
+ }
83
+ const number = Number(ele);
84
+ if (Number.isInteger(number)) {
85
+ this.push(number);
86
+ } else if (typeof ele === 'string' && Number.isNaN(number)) {
87
+ try {
88
+ const range = new Range(ele);
89
+ this.push(range);
90
+ } catch {}
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * 将Ranges转换为针对特定Array的下标集
97
+ * @param {number|any[]} arr
98
+ * @complexity `n`
99
+ */
100
+ applyTo(arr) {
101
+ const length = typeof arr === 'number' ? arr : arr.length;
102
+ return [...new Set(
103
+ Array.from(this).flatMap(ele => {
104
+ if (typeof ele === 'number') {
105
+ return ele < 0 ? ele + length : ele;
106
+ }
107
+ return ele.applyTo(length);
108
+ }),
109
+ )].filter(i => i >= 0 && i < length).sort();
110
+ }
111
+
112
+ /**
113
+ * @param {string} str
114
+ * @param {number} i
115
+ */
116
+ static nth(str, i) {
117
+ return new Ranges(str.split(',')).applyTo(i + 1).includes(i);
118
+ }
119
+ }
120
+
121
+ Parser.classes.Ranges = __filename;
122
+ module.exports = Ranges;
package/lib/title.js ADDED
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const {ucfirst} = require('../util/string'),
4
+ /** @type {Parser} */ Parser = require('..');
5
+
6
+ class Title {
7
+ title = '';
8
+ ns = 0;
9
+ interwiki = '';
10
+ fragment = '';
11
+ valid = true;
12
+
13
+ /** @param {string} title */
14
+ constructor(title, defaultNs = 0, config = Parser.getConfig()) {
15
+ const {namespaces, nsid} = config;
16
+ let namespace = namespaces[defaultNs];
17
+ title = title.replaceAll('_', ' ').trim();
18
+ if (title[0] === ':') {
19
+ namespace = '';
20
+ title = title.slice(1).trim();
21
+ }
22
+ const iw = Parser.isInterwiki(title, config);
23
+ if (iw) {
24
+ this.interwiki = iw[1].toLowerCase();
25
+ title = title.slice(iw[0].length);
26
+ }
27
+ const m = title.split(':');
28
+ if (m.length > 1) {
29
+ const id = namespaces[nsid[m[0].trim().toLowerCase()]];
30
+ if (id !== undefined) {
31
+ namespace = id;
32
+ title = m.slice(1).join(':').trim();
33
+ }
34
+ }
35
+ this.ns = nsid[namespace.toLowerCase()];
36
+ const i = title.indexOf('#');
37
+ if (i !== -1) {
38
+ const fragment = title.slice(i + 1).trimEnd();
39
+ if (fragment.includes('%')) {
40
+ try {
41
+ this.fragment = decodeURIComponent(fragment);
42
+ } catch {}
43
+ } else if (fragment.includes('.')) {
44
+ try {
45
+ this.fragment = decodeURIComponent(fragment.replaceAll('.', '%'));
46
+ } catch {}
47
+ }
48
+ this.fragment ||= fragment;
49
+ title = title.slice(0, i).trim();
50
+ }
51
+ this.title = `${iw ? `${this.interwiki}:` : ''}${namespace}${namespace && ':'}${ucfirst(title)}`;
52
+ this.valid = !/\x00\d+[eh!+-]\x7f|[<>[\]{}|]/.test(this.title);
53
+ }
54
+ }
55
+
56
+ Parser.classes.Title = __filename;
57
+ module.exports = Title;
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..'),
4
+ AttributeToken = require('../src/attribute'); // eslint-disable-line no-unused-vars
5
+
6
+ /**
7
+ * @template T
8
+ * @param {T} constructor
9
+ * @returns {T}
10
+ */
11
+ const attributeParent = (constructor, i = 0) => class extends constructor {
12
+ /**
13
+ * @this {{children: AttributeToken[]}}
14
+ * @param {string} key
15
+ */
16
+ hasAttr(key) {
17
+ return this.children.at(i).hasAttr(key);
18
+ }
19
+
20
+ /**
21
+ * @this {{children: AttributeToken[]}}
22
+ * @template {string|undefined} T
23
+ * @param {T} key
24
+ */
25
+ getAttr(key) {
26
+ return this.children.at(i).getAttr(key);
27
+ }
28
+
29
+ /** @this {{children: AttributeToken[]}} */
30
+ getAttrNames() {
31
+ return this.children.at(i).getAttrNames();
32
+ }
33
+
34
+ /** @this {{children: AttributeToken[]}} */
35
+ hasAttrs() {
36
+ return this.children.at(i).hasAttrs();
37
+ }
38
+
39
+ /**
40
+ * @this {{children: AttributeToken[]}}
41
+ * @param {string} key
42
+ * @param {string|boolean} value
43
+ */
44
+ setAttr(key, value) {
45
+ return this.children.at(i).setAttr(key, value);
46
+ }
47
+
48
+ /**
49
+ * @this {{children: AttributeToken[]}}
50
+ * @param {string} key
51
+ */
52
+ removeAttr(key) {
53
+ this.children.at(i).removeAttr(key);
54
+ }
55
+
56
+ /**
57
+ * @this {{children: AttributeToken[]}}
58
+ * @param {string} key
59
+ * @param {boolean|undefined} force
60
+ */
61
+ toggleAttr(key, force) {
62
+ this.children.at(i).toggleAttr(key, force);
63
+ }
64
+ };
65
+
66
+ Parser.mixins.attributeParent = __filename;
67
+ module.exports = attributeParent;
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..');
4
+
5
+ /**
6
+ * @template T
7
+ * @param {T} constructor
8
+ * @returns {T}
9
+ */
10
+ const fixedToken = constructor => class extends constructor {
11
+ static fixed = true;
12
+
13
+ removeAt() {
14
+ throw new Error(`${this.constructor.name} 不可删除元素!`);
15
+ }
16
+
17
+ /**
18
+ * @template {string|Token} T
19
+ * @param {T} token
20
+ * @param {number} i
21
+ */
22
+ insertAt(token, i = this.childNodes.length) {
23
+ if (!Parser.running) {
24
+ throw new Error(`${this.constructor.name} 不可插入元素!`);
25
+ }
26
+ super.insertAt(token, i);
27
+ return token;
28
+ }
29
+ };
30
+
31
+ Parser.mixins.fixedToken = __filename;
32
+ module.exports = fixedToken;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..');
4
+
5
+ /**
6
+ * @template T
7
+ * @param {T} constructor
8
+ * @returns {T}
9
+ */
10
+ const hidden = constructor => class extends constructor {
11
+ text() {
12
+ return '';
13
+ }
14
+
15
+ /** @returns {[number, string][]} */
16
+ plain() {
17
+ return [];
18
+ }
19
+ };
20
+
21
+ Parser.mixins.hidden = __filename;
22
+ module.exports = hidden;
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "wikiparser-node",
3
+ "version": "0.0.0",
4
+ "description": "A Node.js parser for MediaWiki markup with AST",
5
+ "keywords": [
6
+ "mediawiki",
7
+ "wikitext",
8
+ "parser"
9
+ ],
10
+ "homepage": "https://github.com/bhsd-harry/wikiparser-node#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/bhsd-harry/wikiparser-node/issues"
13
+ },
14
+ "license": "GPL-3.0",
15
+ "author": "Bhsd",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/bhsd-harry/wikiparser-node.git"
19
+ },
20
+ "scripts": {
21
+ "test": "echo 'Error: no test specified' && exit 1"
22
+ },
23
+ "devDependencies": {
24
+ "eslint": "^8.8.0",
25
+ "@types/node": "^17.0.23"
26
+ },
27
+ "engines": {
28
+ "node": "^18.4.0"
29
+ }
30
+ }