wikiparser-node 0.1.0 → 0.2.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.
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..'),
4
+ Token = require('.'),
5
+ AtomToken = require('./atom');
6
+
7
+ /**
8
+ * 转换flags
9
+ * @classdesc `{childNodes: ...AtomToken}`
10
+ */
11
+ class ConverterFlagsToken extends Token {
12
+ type = 'converter-flags';
13
+ /** @type {string[]} */ #flags;
14
+
15
+ /**
16
+ * @param {string[]} flags
17
+ * @param {accum} accum
18
+ */
19
+ constructor(flags, config = Parser.getConfig(), accum = []) {
20
+ super(undefined, config, false, accum, {AtomToken: ':'});
21
+ this.append(...flags.map(flag => new AtomToken(flag, 'converter-flag', config, accum)));
22
+ }
23
+
24
+ cloneNode() {
25
+ const cloned = this.cloneChildren(),
26
+ token = Parser.run(() => new ConverterFlagsToken([], this.getAttribute('config')));
27
+ token.append(...cloned);
28
+ token.afterBuild();
29
+ return token;
30
+ }
31
+
32
+ /** @complexity `n` */
33
+ afterBuild() {
34
+ this.#flags = this.children.map(child => child.text().trim());
35
+ const that = this,
36
+ /** @type {AstListener} */ converterFlagsListener = ({prevTarget}) => {
37
+ if (prevTarget) {
38
+ that.#flags[that.childNodes.indexOf(prevTarget)] = prevTarget.text().trim();
39
+ }
40
+ };
41
+ this.addEventListener(['remove', 'insert', 'text', 'replace'], converterFlagsListener);
42
+ }
43
+
44
+ /**
45
+ * @template {string} T
46
+ * @param {T} key
47
+ * @returns {TokenAttribute<T>}
48
+ */
49
+ getAttribute(key) {
50
+ if (key === 'flags') {
51
+ return Parser.debugging ? this.#flags : [...this.#flags];
52
+ }
53
+ return super.getAttribute(key);
54
+ }
55
+
56
+ /**
57
+ * @param {number} i
58
+ * @complexity `n`
59
+ */
60
+ removeAt(i) {
61
+ const /** @type {AtomToken} */ token = super.removeAt(i);
62
+ this.#flags?.splice(i, 1);
63
+ return token;
64
+ }
65
+
66
+ /**
67
+ * @param {AtomToken} token
68
+ * @complexity `n`
69
+ */
70
+ insertAt(token, i = this.childNodes.length) {
71
+ super.insertAt(token, i);
72
+ this.#flags?.splice(i, 0, token.text());
73
+ return token;
74
+ }
75
+
76
+ toString() {
77
+ return super.toString(';');
78
+ }
79
+
80
+ getGaps() {
81
+ return 1;
82
+ }
83
+
84
+ text() {
85
+ return super.text(';');
86
+ }
87
+
88
+ /** @returns {[number, string][]} */
89
+ plain() {
90
+ return [];
91
+ }
92
+
93
+ /**
94
+ * @param {string} flag
95
+ * @returns {AtomToken[]}
96
+ * @complexity `n`
97
+ */
98
+ getFlagToken(flag) {
99
+ return this.#flags.includes(flag) ? this.children.filter(child => child.text().trim() === flag) : [];
100
+ }
101
+
102
+ getAllFlags() {
103
+ return new Set(this.#flags);
104
+ }
105
+
106
+ /** @complexity `n` */
107
+ getEffectiveFlags() {
108
+ const {variants} = this.getAttribute('config'),
109
+ variantFlags = this.#flags.filter(flag => variants.includes(flag)),
110
+ unknownFlags = this.#flags.filter(flag => /{{.+}}/.test(flag));
111
+ if (variantFlags.length) {
112
+ return new Set([...variantFlags, ...unknownFlags]);
113
+ }
114
+ const validFlags = ['A', 'T', 'R', 'D', '-', 'H', 'N'],
115
+ flags = new Set([...this.#flags.filter(flag => validFlags.includes(flag)), ...unknownFlags]);
116
+ if (flags.size === 0) {
117
+ return new Set('S');
118
+ } else if (flags.has('R')) {
119
+ return new Set('R');
120
+ } else if (flags.has('N')) {
121
+ return new Set('N');
122
+ } else if (flags.has('-')) {
123
+ return new Set('-');
124
+ } else if (flags.has('H')) {
125
+ const hasT = flags.has('T'),
126
+ hasD = flags.has('D');
127
+ if (hasT && hasD) {
128
+ return new Set(['+', 'H', 'T', 'D']);
129
+ }
130
+ return new Set(['+', 'H', ...hasT ? ['T'] : [], ...hasD ? ['D'] : [], ...unknownFlags]);
131
+ }
132
+ if (flags.size === 1 && flags.has('T')) {
133
+ flags.add('H');
134
+ }
135
+ if (flags.has('A')) {
136
+ flags.add('+');
137
+ flags.add('S');
138
+ }
139
+ if (flags.has('D')) {
140
+ flags.delete('S');
141
+ }
142
+ return flags;
143
+ }
144
+
145
+ /** @complexity `n` */
146
+ getUnknownFlags() {
147
+ return [...this.getFlags()].filter(flag => /{{.+}}/.test(flag));
148
+ }
149
+
150
+ /** @param {string} flag */
151
+ hasFlag(flag) {
152
+ if (typeof flag !== 'string') {
153
+ this.typeError('hasFlag', 'String');
154
+ }
155
+ return this.#flags.includes(flag);
156
+ }
157
+
158
+ /**
159
+ * @param {string} flag
160
+ * @complexity `n`
161
+ */
162
+ hasEffectiveFlag(flag) {
163
+ if (typeof flag !== 'string') {
164
+ this.typeError('hasFlag', 'String');
165
+ }
166
+ return this.getEffectiveFlags().has(flag);
167
+ }
168
+
169
+ /**
170
+ * @param {string} flag
171
+ * @complexity `n²`
172
+ */
173
+ removeFlag(flag) {
174
+ for (const token of this.getFlagToken(flag)) {
175
+ token.remove();
176
+ }
177
+ }
178
+
179
+ /**
180
+ * @param {string} flag
181
+ * @complexity `n`
182
+ */
183
+ #newFlag(flag) {
184
+ const token = Parser.run(() => new AtomToken(flag, 'converter-flag', this.getAttribute('config')));
185
+ this.appendChild(token);
186
+ }
187
+
188
+ /**
189
+ * @param {string} flag
190
+ * @complexity `n`
191
+ */
192
+ setFlag(flag) {
193
+ if (!this.#flags.includes(flag)) {
194
+ this.#newFlag(flag);
195
+ } else if (!this.getEffectiveFlags().has(flag)) {
196
+ Parser.error('此 flag 不会生效', flag);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * @param {string} flag
202
+ * @complexity `n²`
203
+ */
204
+ toggleFlag(flag) {
205
+ if (!this.#flags.includes(flag)) {
206
+ this.#newFlag(flag);
207
+ } else {
208
+ this.removeFlag(flag);
209
+ }
210
+ }
211
+ }
212
+
213
+ Parser.classes.ConverterFlagsToken = __filename;
214
+ module.exports = ConverterFlagsToken;
@@ -0,0 +1,209 @@
1
+ 'use strict';
2
+
3
+ const {undo} = require('../util/debug'),
4
+ {noWrap} = require('../util/string'),
5
+ /** @type {Parser} */ Parser = require('..'),
6
+ Token = require('.'),
7
+ AtomToken = require('./atom');
8
+
9
+ /**
10
+ * 转换规则
11
+ * @classdesc `{childNodes: ...AtomToken)}`
12
+ */
13
+ class ConverterRuleToken extends Token {
14
+ type = 'converter-rule';
15
+ variant = '';
16
+ unidirectional = false;
17
+ bidirectional = false;
18
+
19
+ /**
20
+ * @param {string} rule
21
+ * @param {accum} accum
22
+ */
23
+ constructor(rule, hasColon = true, config = Parser.getConfig(), accum = []) {
24
+ super(undefined, config, true, accum, {AtomToken: ':'});
25
+ if (!hasColon) {
26
+ super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
27
+ } else {
28
+ const i = rule.indexOf(':'),
29
+ j = rule.slice(0, i).indexOf('=>'),
30
+ v = (j === -1 ? rule.slice(0, i) : rule.slice(j + 2, i)).trim(),
31
+ {variants} = config;
32
+ if (!variants.includes(v)) {
33
+ super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
34
+ } else {
35
+ super.insertAt(new AtomToken(v, 'converter-rule-variant', config, accum));
36
+ super.insertAt(new AtomToken(rule.slice(i + 1), 'converter-rule-to', config, accum));
37
+ if (j === -1) {
38
+ this.bidirectional = true;
39
+ } else {
40
+ super.insertAt(new AtomToken(rule.slice(0, j), 'converter-rule-from', config, accum), 0);
41
+ this.unidirectional = true;
42
+ }
43
+ }
44
+ }
45
+ this.seal(['variant', 'unidirectional', 'bidirectional']);
46
+ }
47
+
48
+ cloneNode() {
49
+ const cloned = this.cloneChildren(),
50
+ placeholders = ['', ':', '=>:'],
51
+ placeholder = placeholders[cloned.length - 1],
52
+ token = Parser.run(() => new ConverterRuleToken(placeholder, placeholder, this.getAttribute('config')));
53
+ for (let i = 0; i < cloned.length; i++) {
54
+ token.children[i].safeReplaceWith(cloned[i]);
55
+ }
56
+ token.afterBuild();
57
+ return token;
58
+ }
59
+
60
+ afterBuild() {
61
+ if (this.childNodes.length > 1) {
62
+ this.setAttribute('variant', this.children.at(-2).text().trim());
63
+ }
64
+ const that = this,
65
+ /** @type {AstListener} */ converterRuleListener = (e, data) => {
66
+ const {childNodes} = that,
67
+ {prevTarget} = e;
68
+ if (childNodes.length > 1 && childNodes.at(-2) === prevTarget) {
69
+ const v = prevTarget.text().trim(),
70
+ {variants} = that.getAttribute('config');
71
+ if (variants.includes(v)) {
72
+ that.setAttribute('variant', v);
73
+ } else {
74
+ undo(e, data);
75
+ throw new Error(`无效的语言变体:${v}`);
76
+ }
77
+ }
78
+ };
79
+ this.addEventListener(['remove', 'insert', 'text', 'replace'], converterRuleListener);
80
+ }
81
+
82
+ /**
83
+ * @param {number} i
84
+ * @returns {AtomToken}
85
+ */
86
+ removeAt(i) {
87
+ if (i !== 0 && i !== -this.childNodes.length) {
88
+ throw new RangeError(`${this.constructor.name} 禁止移除第 ${i} 个子节点!`);
89
+ }
90
+ return super.removeAt(i);
91
+ }
92
+
93
+ insertAt() {
94
+ throw new Error('转换规则语法复杂,请勿尝试手动插入子节点!');
95
+ }
96
+
97
+ /** @returns {string} */
98
+ toString() {
99
+ if (this.childNodes.length === 3) {
100
+ const [from, variant, to] = this.children;
101
+ return `${from.toString()}=>${variant.toString()}:${to.toString()}`;
102
+ }
103
+ return super.toString(':');
104
+ }
105
+
106
+ /** @param {number} i */
107
+ getGaps(i = 0) {
108
+ const {length} = this.childNodes;
109
+ i = i < 0 ? i + length : i;
110
+ return i === 0 && length === 3 ? 2 : 1;
111
+ }
112
+
113
+ /** @returns {string} */
114
+ text() {
115
+ if (this.childNodes.length === 3) {
116
+ const [from, variant, to] = this.children;
117
+ return `${from.text()}=>${variant.text()}:${to.text()}`;
118
+ }
119
+ return super.text(':');
120
+ }
121
+
122
+ /** @returns {[number, string][]} */
123
+ plain() {
124
+ const {firstElementChild, lastElementChild, unidirectional} = this;
125
+ if (unidirectional) {
126
+ return [...firstElementChild.plain(), ...lastElementChild.plain()];
127
+ }
128
+ return lastElementChild.plain();
129
+ }
130
+
131
+ noConvert() {
132
+ for (let i = this.childNodes.length - 2; i >= 0; i--) {
133
+ super.removeAt(i);
134
+ }
135
+ this.setAttribute('unidirectional', false).setAttribute('bidirectional', false).setAttribute('variant', '');
136
+ }
137
+
138
+ /** @param {string} to */
139
+ setTo(to) {
140
+ to = String(to);
141
+ const config = this.getAttribute('config'),
142
+ root = Parser.parse(`-{|${config.variants[0]}:${to}}-`, this.getAttribute('include'), undefined, config),
143
+ {childNodes: {length}, firstElementChild} = root;
144
+ if (length !== 1 || firstElementChild.type !== 'converter' || firstElementChild.childNodes.length !== 2
145
+ || firstElementChild.lastElementChild.childNodes.length !== 2
146
+ ) {
147
+ throw new SyntaxError(`非法的转换目标:${noWrap(to)}`);
148
+ }
149
+ const {lastChild} = firstElementChild.lastElementChild;
150
+ firstElementChild.lastElementChild.removeAt(0);
151
+ this.lastElementChild.safeReplaceWith(lastChild);
152
+ }
153
+
154
+ /** @param {string} variant */
155
+ setVariant(variant) {
156
+ if (typeof variant !== 'string') {
157
+ this.typeError('setVariant', 'String');
158
+ }
159
+ const config = this.getAttribute('config'),
160
+ v = variant.trim();
161
+ if (!config.variants.includes(v)) {
162
+ throw new RangeError(`无效的语言变体:${v}`);
163
+ } else if (this.childNodes.length === 1) {
164
+ super.insertAt(Parser.run(() => new AtomToken(variant, 'converter-rule-variant', config)), 0);
165
+ this.setAttribute('bidirectional', true);
166
+ } else {
167
+ this.children.at(-2).setText(variant);
168
+ }
169
+ this.setAttribute('variant', v);
170
+ }
171
+
172
+ /** @param {string} from */
173
+ setFrom(from) {
174
+ const {variant} = this;
175
+ if (!variant) {
176
+ throw new Error('请先指定语言变体!');
177
+ }
178
+ from = String(from);
179
+ const config = this.getAttribute('config'),
180
+ root = Parser.parse(`-{|${from}=>${variant}:}-`, this.getAttribute('include'), undefined, config),
181
+ {childNodes: {length}, firstElementChild} = root;
182
+ if (length !== 1 || firstElementChild.type !== 'converter' || firstElementChild.childNodes.length !== 2
183
+ || firstElementChild.lastElementChild.childNodes.length !== 3
184
+ ) {
185
+ throw new SyntaxError(`非法的转换原文:${noWrap(from)}`);
186
+ }
187
+ if (this.unidirectional) {
188
+ this.firstElementChild.safeReplaceWith(firstElementChild.lastElementChild.firstChild);
189
+ } else {
190
+ super.insertAt(firstElementChild.lastElementChild.firstChild, 0);
191
+ this.setAttribute('unidirectional', true).setAttribute('bidirectional', false);
192
+ }
193
+ }
194
+
195
+ /** @param {string} from */
196
+ makeUnidirectional(from) {
197
+ this.setFrom(from);
198
+ }
199
+
200
+ makeBidirectional() {
201
+ if (this.unidirectional) {
202
+ super.removeAt(0);
203
+ this.setAttribute('unidirectional', false).setAttribute('bidirectional', true);
204
+ }
205
+ }
206
+ }
207
+
208
+ Parser.classes.ConverterRuleToken = __filename;
209
+ module.exports = ConverterRuleToken;
package/src/extLink.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {noWrap} = require('../util/string'),
3
+ const {noWrap, normalizeSpace} = require('../util/string'),
4
4
  /** @type {Parser} */ Parser = require('..'),
5
5
  Token = require('.'),
6
6
  MagicLinkToken = require('./magicLink');
@@ -33,9 +33,9 @@ class ExtLinkToken extends Token {
33
33
  this.appendChild(new MagicLinkToken(url, true, config, accum));
34
34
  this.#space = space;
35
35
  if (text) {
36
- const inner = new Token(text, config, true, accum);
36
+ const inner = new Token(text, config, true, accum, {'Stage-7': ':', ConverterToken: ':'});
37
37
  inner.type = 'ext-link-text';
38
- this.appendChild(inner.setAttribute('stage', 8));
38
+ this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
39
39
  }
40
40
  this.protectChildren(0);
41
41
  }
@@ -50,9 +50,9 @@ class ExtLinkToken extends Token {
50
50
  }
51
51
 
52
52
  #correct() {
53
- if (!this.#space
53
+ if (!this.#space && this.childNodes.length > 1
54
54
  // 都替换成`<`肯定不对,但无妨
55
- && /^[^[\]<>"\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.children[1]?.text().replace(/&[lg]t;/, '<'))
55
+ && /^[^[\]<>"{\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.lastElementChild.text().replace(/&[lg]t;/, '<'))
56
56
  ) {
57
57
  this.#space = ' ';
58
58
  }
@@ -60,7 +60,7 @@ class ExtLinkToken extends Token {
60
60
 
61
61
  toString() {
62
62
  this.#correct();
63
- return `[${this.firstElementChild.toString()}${this.#space}${this.children[1]?.toString() ?? ''}]`;
63
+ return `[${this.firstElementChild.toString()}${this.#space}${normalizeSpace(this.children[1])}]`;
64
64
  }
65
65
 
66
66
  getPadding() {
@@ -74,7 +74,7 @@ class ExtLinkToken extends Token {
74
74
  }
75
75
 
76
76
  text() {
77
- return `[${super.text(' ')}]`;
77
+ return `[${super.text(' ').replaceAll('\n', ' ')}]`;
78
78
  }
79
79
 
80
80
  /**
@@ -82,7 +82,7 @@ class ExtLinkToken extends Token {
82
82
  * @complexity `n`
83
83
  */
84
84
  plain() {
85
- return this.childElementCount === 1 ? [] : this.lastElementChild.plain();
85
+ return this.childNodes.length === 1 ? [] : this.lastElementChild.plain();
86
86
  }
87
87
 
88
88
  /** @this {ExtLinkToken & {firstElementChild: MagicLinkToken}} */
@@ -95,7 +95,7 @@ class ExtLinkToken extends Token {
95
95
  url = String(url);
96
96
  const root = Parser.parse(`[${url}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
97
97
  {childNodes: {length}, firstElementChild} = root;
98
- if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childElementCount !== 1) {
98
+ if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childNodes.length !== 1) {
99
99
  throw new SyntaxError(`非法的外链目标:${url}`);
100
100
  }
101
101
  const {firstChild} = firstElementChild;
@@ -109,11 +109,11 @@ class ExtLinkToken extends Token {
109
109
  text = String(text);
110
110
  const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
111
111
  {childNodes: {length}, firstElementChild} = root;
112
- if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childElementCount !== 2) {
112
+ if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childNodes.length !== 2) {
113
113
  throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
114
114
  }
115
115
  const {lastChild} = firstElementChild;
116
- if (this.childElementCount === 1) {
116
+ if (this.childNodes.length === 1) {
117
117
  this.appendChild(lastChild);
118
118
  } else {
119
119
  this.lastElementChild.replaceWith(lastChild);
@@ -213,7 +213,7 @@ class ImageParameterToken extends Token {
213
213
  {childNodes: {length}, firstElementChild} = root,
214
214
  param = firstElementChild?.lastElementChild;
215
215
  if (length !== 1 || !firstElementChild?.matches('file#File:F')
216
- || firstElementChild.childElementCount !== 2 || param.name !== this.name
216
+ || firstElementChild.childNodes.length !== 2 || param.name !== this.name
217
217
  ) {
218
218
  throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
219
219
  }
package/src/index.js CHANGED
@@ -38,6 +38,7 @@
38
38
  * q: QuoteToken
39
39
  * w: ExtLinkToken
40
40
  * d: ListToken
41
+ * v: ConverterToken
41
42
  */
42
43
 
43
44
  const {externalUse} = require('../util/debug'),
@@ -202,9 +203,13 @@ class Token extends AstElement {
202
203
  * @complexity `n`
203
204
  */
204
205
  removeAt(i) {
206
+ if (typeof i !== 'number') {
207
+ this.typeError('removeAt', 'Number');
208
+ }
209
+ const iPos = i < 0 ? i + this.childNodes.length : i;
205
210
  if (!Parser.running) {
206
211
  const protectedIndices = this.#protectedChildren.applyTo(this.childNodes);
207
- if (protectedIndices.includes(i)) {
212
+ if (protectedIndices.includes(iPos)) {
208
213
  throw new Error(`${this.constructor.name} 的第 ${i} 个子节点不可移除!`);
209
214
  } else if (this.#acceptable) {
210
215
  const acceptableIndices = Object.fromEntries(
@@ -450,6 +455,7 @@ class Token extends AstElement {
450
455
  this.#parseList();
451
456
  break;
452
457
  case 10:
458
+ this.#parseConverter();
453
459
  // no default
454
460
  }
455
461
  if (this.type === 'root') {
@@ -528,25 +534,21 @@ class Token extends AstElement {
528
534
  return n ? this.build().afterBuild() : this;
529
535
  }
530
536
 
531
- /** @this {Token & {firstChild: string}} */
532
537
  #parseCommentAndExt(includeOnly = false) {
533
538
  const parseCommentAndExt = require('../parser/commentAndExt');
534
539
  this.setText(parseCommentAndExt(this.firstChild, this.#config, this.#accum, includeOnly));
535
540
  }
536
541
 
537
- /** @this {Token & {firstChild: string}} */
538
542
  #parseBrackets() {
539
543
  const parseBrackets = require('../parser/brackets');
540
544
  this.setText(parseBrackets(this.firstChild, this.#config, this.#accum));
541
545
  }
542
546
 
543
- /** @this {Token & {firstChild: string}} */
544
547
  #parseHtml() {
545
548
  const parseHtml = require('../parser/html');
546
549
  this.setText(parseHtml(this.firstChild, this.#config, this.#accum));
547
550
  }
548
551
 
549
- /** @this {Token & {firstChild: string}} */
550
552
  #parseTable() {
551
553
  const parseTable = require('../parser/table'),
552
554
  TableToken = require('./table');
@@ -565,13 +567,11 @@ class Token extends AstElement {
565
567
  }
566
568
  }
567
569
 
568
- /** @this {Token & {firstChild: string}} */
569
570
  #parseHrAndDoubleUndescore() {
570
571
  const parseHrAndDoubleUnderscore = require('../parser/hrAndDoubleUnderscore');
571
572
  this.setText(parseHrAndDoubleUnderscore(this.firstChild, this.#config, this.#accum));
572
573
  }
573
574
 
574
- /** @this {Token & {firstChild: string}} */
575
575
  #parseLinks() {
576
576
  const parseLinks = require('../parser/links');
577
577
  this.setText(parseLinks(this.firstChild, this.#config, this.#accum));
@@ -587,26 +587,30 @@ class Token extends AstElement {
587
587
  this.setText(lines.join('\n'));
588
588
  }
589
589
 
590
- /** @this {Token & {firstChild: string}} */
591
590
  #parseExternalLinks() {
592
591
  const parseExternalLinks = require('../parser/externalLinks');
593
592
  this.setText(parseExternalLinks(this.firstChild, this.#config, this.#accum));
594
593
  }
595
594
 
596
- /** @this {Token & {firstChild: string}} */
597
595
  #parseMagicLinks() {
598
596
  const parseMagicLinks = require('../parser/magicLinks');
599
597
  this.setText(parseMagicLinks(this.firstChild, this.#config, this.#accum));
600
598
  }
601
599
 
600
+ /** @this {Token & {firstChild: string}} */
602
601
  #parseList() {
603
602
  const parseList = require('../parser/list'),
604
603
  lines = this.firstChild.split('\n');
605
- for (let i = 0; i < lines.length; i++) {
604
+ for (let i = this.type === 'root' ? 0 : 1; i < lines.length; i++) {
606
605
  lines[i] = parseList(lines[i], this.#config, this.#accum);
607
606
  }
608
607
  this.setText(lines.join('\n'));
609
608
  }
609
+
610
+ #parseConverter() {
611
+ const parseConverter = require('../parser/converter');
612
+ this.setText(parseConverter(this.firstChild, this.#config, this.#accum));
613
+ }
610
614
  }
611
615
 
612
616
  Parser.classes.Token = __filename;
package/src/link/file.js CHANGED
@@ -84,7 +84,7 @@ class FileToken extends LinkToken {
84
84
  * @param {ImageParameterToken} token
85
85
  * @complexity `n`
86
86
  */
87
- insertAt(token, i = this.childElementCount) {
87
+ insertAt(token, i = this.childNodes.length) {
88
88
  if (!Parser.running) {
89
89
  this.getArgs(token.name, false, false).add(token);
90
90
  this.#keys.add(token.name);
@@ -221,7 +221,7 @@ class FileToken extends LinkToken {
221
221
  root = Parser.parse(wikitext, this.getAttribute('include'), 6, config),
222
222
  {childNodes: {length}, firstElementChild} = root;
223
223
  if (length !== 1 || !firstElementChild?.matches('file#File:F')
224
- || firstElementChild.childElementCount !== 2 || firstElementChild.lastElementChild.name !== key
224
+ || firstElementChild.childNodes.length !== 2 || firstElementChild.lastElementChild.name !== key
225
225
  ) {
226
226
  throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
227
227
  }
package/src/link/index.js CHANGED
@@ -29,9 +29,9 @@ class LinkToken extends Token {
29
29
  'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '',
30
30
  }));
31
31
  if (linkText !== undefined) {
32
- const inner = new Token(linkText, config, true, accum);
32
+ const inner = new Token(linkText, config, true, accum, {'Stage-5': ':', ConverterToken: ':'});
33
33
  inner.type = 'link-text';
34
- this.appendChild(inner.setAttribute('stage', 7));
34
+ this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
35
35
  }
36
36
  this.selfLink = !title.title;
37
37
  this.fragment = title.fragment;
@@ -107,7 +107,7 @@ class LinkToken extends Token {
107
107
 
108
108
  /** @returns {[number, string][]} */
109
109
  plain() {
110
- return this.childElementCount === 1 ? [] : this.lastElementChild.plain();
110
+ return this.childNodes.length === 1 ? [] : this.lastElementChild.plain();
111
111
  }
112
112
 
113
113
  /** @param {string} link */
@@ -118,7 +118,7 @@ class LinkToken extends Token {
118
118
  }
119
119
  const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
120
120
  {childNodes: {length}, firstElementChild} = root;
121
- if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childElementCount !== 1) {
121
+ if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 1) {
122
122
  const msgs = {link: '内链', file: '文件链接', category: '分类'};
123
123
  throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
124
124
  }
@@ -135,7 +135,7 @@ class LinkToken extends Token {
135
135
  config = this.getAttribute('config'),
136
136
  root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
137
137
  {childNodes: {length}, firstElementChild} = root;
138
- if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.childElementCount !== 1) {
138
+ if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.childNodes.length !== 1) {
139
139
  throw new SyntaxError(`非法的 fragment:${fragment}`);
140
140
  }
141
141
  if (page) {
@@ -169,7 +169,7 @@ class LinkToken extends Token {
169
169
  this.type === 'category' ? 'Category:' : ''
170
170
  }L|${linkText}]]`, this.getAttribute('include'), 6, config),
171
171
  {childNodes: {length}, firstElementChild} = root;
172
- if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childElementCount !== 2) {
172
+ if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 2) {
173
173
  throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${noWrap(linkText)}`);
174
174
  }
175
175
  ({lastElementChild} = firstElementChild);
@@ -177,7 +177,7 @@ class LinkToken extends Token {
177
177
  lastElementChild = Parser.run(() => new Token('', config));
178
178
  lastElementChild.setAttribute('stage', 7).type = 'link-text';
179
179
  }
180
- if (this.childElementCount === 1) {
180
+ if (this.childNodes.length === 1) {
181
181
  this.appendChild(lastElementChild);
182
182
  } else {
183
183
  this.lastElementChild.safeReplaceWith(lastElementChild);