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.
Files changed (87) hide show
  1. package/config/default.json +129 -66
  2. package/config/zhwiki.json +4 -4
  3. package/index.js +97 -65
  4. package/lib/element.js +159 -302
  5. package/lib/node.js +384 -198
  6. package/lib/ranges.js +3 -4
  7. package/lib/text.js +65 -36
  8. package/lib/title.js +9 -8
  9. package/mixin/fixedToken.js +4 -4
  10. package/mixin/hidden.js +2 -0
  11. package/mixin/sol.js +16 -7
  12. package/package.json +14 -3
  13. package/parser/brackets.js +8 -2
  14. package/parser/commentAndExt.js +1 -1
  15. package/parser/converter.js +1 -1
  16. package/parser/externalLinks.js +2 -2
  17. package/parser/hrAndDoubleUnderscore.js +8 -7
  18. package/parser/links.js +8 -9
  19. package/parser/magicLinks.js +1 -1
  20. package/parser/selector.js +5 -5
  21. package/parser/table.js +18 -16
  22. package/src/arg.js +71 -42
  23. package/src/atom/index.js +7 -5
  24. package/src/attribute.js +102 -64
  25. package/src/charinsert.js +91 -0
  26. package/src/converter.js +34 -15
  27. package/src/converterFlags.js +87 -40
  28. package/src/converterRule.js +59 -53
  29. package/src/extLink.js +45 -37
  30. package/src/gallery.js +71 -16
  31. package/src/hasNowiki/index.js +42 -0
  32. package/src/hasNowiki/pre.js +40 -0
  33. package/src/heading.js +41 -18
  34. package/src/html.js +76 -48
  35. package/src/imageParameter.js +73 -51
  36. package/src/imagemap.js +205 -0
  37. package/src/imagemapLink.js +43 -0
  38. package/src/index.js +243 -138
  39. package/src/link/category.js +10 -14
  40. package/src/link/file.js +112 -56
  41. package/src/link/galleryImage.js +74 -10
  42. package/src/link/index.js +86 -61
  43. package/src/magicLink.js +48 -21
  44. package/src/nested/choose.js +24 -0
  45. package/src/nested/combobox.js +23 -0
  46. package/src/nested/index.js +88 -0
  47. package/src/nested/references.js +23 -0
  48. package/src/nowiki/comment.js +18 -4
  49. package/src/nowiki/dd.js +2 -2
  50. package/src/nowiki/doubleUnderscore.js +16 -11
  51. package/src/nowiki/index.js +12 -0
  52. package/src/nowiki/quote.js +28 -1
  53. package/src/onlyinclude.js +15 -8
  54. package/src/paramTag/index.js +83 -0
  55. package/src/paramTag/inputbox.js +42 -0
  56. package/src/parameter.js +73 -46
  57. package/src/syntax.js +9 -1
  58. package/src/table/index.js +58 -44
  59. package/src/table/td.js +63 -63
  60. package/src/table/tr.js +52 -35
  61. package/src/tagPair/ext.js +60 -43
  62. package/src/tagPair/include.js +11 -1
  63. package/src/tagPair/index.js +29 -20
  64. package/src/transclude.js +214 -166
  65. package/tool/index.js +720 -439
  66. package/util/base.js +17 -0
  67. package/util/debug.js +1 -1
  68. package/{test/util.js → util/diff.js} +15 -19
  69. package/util/lint.js +40 -0
  70. package/util/string.js +37 -20
  71. package/.eslintrc.json +0 -714
  72. package/errors/README +0 -1
  73. package/jsconfig.json +0 -7
  74. package/printed/README +0 -1
  75. package/printed/example.json +0 -120
  76. package/test/api.js +0 -83
  77. package/test/real.js +0 -133
  78. package/test/test.js +0 -28
  79. package/typings/api.d.ts +0 -13
  80. package/typings/array.d.ts +0 -28
  81. package/typings/event.d.ts +0 -24
  82. package/typings/index.d.ts +0 -94
  83. package/typings/node.d.ts +0 -29
  84. package/typings/parser.d.ts +0 -16
  85. package/typings/table.d.ts +0 -14
  86. package/typings/token.d.ts +0 -22
  87. package/typings/tool.d.ts +0 -11
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ const Parser = require('../..'),
4
+ HasNowikiToken = require('.');
5
+
6
+ /**
7
+ * `<pre>`
8
+ * @classdesc `{childNodes: [...AstText|NoincludeToken|ConverterToken]}`
9
+ */
10
+ class PreToken extends HasNowikiToken {
11
+ name = 'pre';
12
+
13
+ /**
14
+ * @param {string} wikitext wikitext
15
+ * @param {accum} accum
16
+ */
17
+ constructor(wikitext, config = Parser.getConfig(), accum = []) {
18
+ super(wikitext, 'ext-inner', config, accum);
19
+ this.setAttribute('stage', Parser.MAX_STAGE - 1);
20
+ this.setAttribute('acceptable', {AstText: ':', NoincludeToken: ':', ConverterToken: ':'});
21
+ }
22
+
23
+ /** @override */
24
+ isPlain() {
25
+ return true;
26
+ }
27
+
28
+ /** @override */
29
+ cloneNode() {
30
+ const cloned = this.cloneChildNodes();
31
+ return Parser.run(() => {
32
+ const token = new PreToken(undefined, this.getAttribute('config'));
33
+ token.append(...cloned);
34
+ return token;
35
+ });
36
+ }
37
+ }
38
+
39
+ Parser.classes.PreToken = __filename;
40
+ module.exports = PreToken;
package/src/heading.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const fixedToken = require('../mixin/fixedToken'),
3
+ const {generateForSelf} = require('../util/lint'),
4
+ fixedToken = require('../mixin/fixedToken'),
4
5
  sol = require('../mixin/sol'),
5
6
  Parser = require('..'),
6
7
  Token = require('.');
@@ -14,7 +15,7 @@ class HeadingToken extends fixedToken(sol(Token)) {
14
15
 
15
16
  /** 内部wikitext */
16
17
  get innerText() {
17
- return this.firstElementChild.text();
18
+ return this.firstChild.text();
18
19
  }
19
20
 
20
21
  /**
@@ -27,7 +28,8 @@ class HeadingToken extends fixedToken(sol(Token)) {
27
28
  this.setAttribute('name', String(level));
28
29
  const token = new Token(input[0], config, true, accum);
29
30
  token.type = 'heading-title';
30
- token.setAttribute('name', this.name).setAttribute('stage', 2);
31
+ token.setAttribute('name', this.name);
32
+ token.setAttribute('stage', 2);
31
33
  const SyntaxToken = require('./syntax');
32
34
  const trail = new SyntaxToken(input[1], /^[^\S\n]*$/u, 'heading-trail', config, accum, {
33
35
  'Stage-1': ':', '!ExtToken': '',
@@ -35,27 +37,19 @@ class HeadingToken extends fixedToken(sol(Token)) {
35
37
  this.append(token, trail);
36
38
  }
37
39
 
38
- /** @override */
39
- cloneNode() {
40
- const [title, trail] = this.cloneChildNodes(),
41
- token = Parser.run(() => new HeadingToken(Number(this.name), [], this.getAttribute('config')));
42
- token.firstElementChild.safeReplaceWith(title);
43
- token.lastElementChild.safeReplaceWith(trail);
44
- return token;
45
- }
46
-
47
40
  /**
48
41
  * @override
49
42
  * @this {{prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'} & HeadingToken}
50
43
  * @param {string} selector
44
+ * @returns {string}
51
45
  */
52
46
  toString(selector) {
53
47
  const equals = '='.repeat(Number(this.name));
54
48
  return selector && this.matches(selector)
55
49
  ? ''
56
50
  : `${this.prependNewLine()}${equals}${
57
- this.firstElementChild.toString(selector)
58
- }${equals}${this.lastElementChild.toString(selector)}${this.appendNewLine()}`;
51
+ this.firstChild.toString(selector)
52
+ }${equals}${this.lastChild.toString(selector)}${this.appendNewLine()}`;
59
53
  }
60
54
 
61
55
  /** @override */
@@ -68,6 +62,35 @@ class HeadingToken extends fixedToken(sol(Token)) {
68
62
  return Number(this.name);
69
63
  }
70
64
 
65
+ /** @override */
66
+ print() {
67
+ const equals = '='.repeat(Number(this.name));
68
+ return super.print({pre: equals, sep: equals});
69
+ }
70
+
71
+ /**
72
+ * @override
73
+ * @param {number} start 起始位置
74
+ */
75
+ lint(start = 0) {
76
+ const errors = super.lint(start);
77
+ if (this.name === '1') {
78
+ errors.push(generateForSelf(this, this.getRootNode().posFromIndex(start), '<h1>'));
79
+ }
80
+ return errors;
81
+ }
82
+
83
+ /** @override */
84
+ cloneNode() {
85
+ const [title, trail] = this.cloneChildNodes();
86
+ return Parser.run(() => {
87
+ const token = new HeadingToken(Number(this.name), [], this.getAttribute('config'));
88
+ token.firsthild.safeReplaceWith(title);
89
+ token.lastChild.safeReplaceWith(trail);
90
+ return token;
91
+ });
92
+ }
93
+
71
94
  /**
72
95
  * @override
73
96
  * @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}}
@@ -75,7 +98,7 @@ class HeadingToken extends fixedToken(sol(Token)) {
75
98
  */
76
99
  text() {
77
100
  const equals = '='.repeat(Number(this.name));
78
- return `${this.prependNewLine()}${equals}${this.firstElementChild.text()}${equals}${this.appendNewLine()}`;
101
+ return `${this.prependNewLine()}${equals}${this.firstChild.text()}${equals}${this.appendNewLine()}`;
79
102
  }
80
103
 
81
104
  /**
@@ -83,16 +106,16 @@ class HeadingToken extends fixedToken(sol(Token)) {
83
106
  * @param {number} n 标题层级
84
107
  */
85
108
  setLevel(n) {
86
- if (typeof n !== 'number') {
109
+ if (!Number.isInteger(n)) {
87
110
  this.typeError('setLevel', 'Number');
88
111
  }
89
112
  n = Math.min(Math.max(n, 1), 6);
90
- this.setAttribute('name', String(n)).firstElementChild.setAttribute('name', this.name);
113
+ this.setAttribute('name', String(n)).firstChild.setAttribute('name', this.name);
91
114
  }
92
115
 
93
116
  /** 移除标题后的不可见内容 */
94
117
  removeTrail() {
95
- this.lastElementChild.replaceChildren();
118
+ this.lastChild.replaceChildren();
96
119
  }
97
120
  }
98
121
 
package/src/html.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {noWrap} = require('../util/string'),
4
+ {generateForSelf} = require('../util/lint'),
4
5
  fixedToken = require('../mixin/fixedToken'),
5
6
  attributeParent = require('../mixin/attributeParent'),
6
7
  Parser = require('..'),
@@ -27,11 +28,11 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
27
28
  this.#closing = false;
28
29
  return;
29
30
  } else if (this.#selfClosing) {
30
- throw new Error(`这是一个自闭合标签!`);
31
+ throw new Error('这是一个自闭合标签!');
31
32
  }
32
33
  const {html: [,, tags]} = this.getAttribute('config');
33
34
  if (tags.includes(this.name)) {
34
- throw new Error(`这是一个空标签!`);
35
+ throw new Error('这是一个空标签!');
35
36
  }
36
37
  this.#closing = true;
37
38
  }
@@ -65,30 +66,13 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
65
66
  */
66
67
  constructor(name, attr, closing, selfClosing, config = Parser.getConfig(), accum = []) {
67
68
  super(undefined, config, true, accum);
68
- this.appendChild(attr);
69
+ this.insertAt(attr);
69
70
  this.setAttribute('name', name.toLowerCase());
70
71
  this.#closing = closing;
71
72
  this.#selfClosing = selfClosing;
72
73
  this.#tag = name;
73
74
  }
74
75
 
75
- /** @override */
76
- cloneNode() {
77
- const [attr] = this.cloneChildNodes(),
78
- config = this.getAttribute('config');
79
- return Parser.run(() => new HtmlToken(this.#tag, attr, this.#closing, this.#selfClosing, config));
80
- }
81
-
82
- /**
83
- * @override
84
- * @template {string} T
85
- * @param {T} key 属性键
86
- * @returns {TokenAttribute<T>}
87
- */
88
- getAttribute(key) {
89
- return key === 'tag' ? this.#tag : super.getAttribute(key);
90
- }
91
-
92
76
  /**
93
77
  * @override
94
78
  * @param {string} selector
@@ -105,21 +89,31 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
105
89
  }
106
90
 
107
91
  /** @override */
108
- text() {
109
- return `<${this.#closing ? '/' : ''}${this.#tag}${super.text()}${this.#selfClosing ? '/' : ''}>`;
92
+ print() {
93
+ return super.print({
94
+ pre: `&lt;${this.#closing ? '/' : ''}${this.#tag}`, post: `${this.#selfClosing ? '/' : ''}&gt;`,
95
+ });
110
96
  }
111
97
 
112
98
  /**
113
- * 更换标签名
114
- * @param {string} tag 标签名
115
- * @throws `RangeError` 非法的HTML标签
99
+ * @override
100
+ * @param {number} start 起始位置
116
101
  */
117
- replaceTag(tag) {
118
- const name = tag.toLowerCase();
119
- if (!this.getAttribute('config').html.flat().includes(name)) {
120
- throw new RangeError(`非法的HTML标签:${tag}`);
102
+ lint(start = 0) {
103
+ const errors = super.lint(start);
104
+ let /** @type {LintError} */ refError;
105
+ if (this.name === 'h1' && !this.#closing) {
106
+ refError = generateForSelf(this, this.getRootNode().posFromIndex(start), '<h1>');
107
+ errors.push(refError);
121
108
  }
122
- this.setAttribute('name', name).#tag = tag;
109
+ try {
110
+ this.findMatchingTag();
111
+ } catch ({message: errorMsg}) {
112
+ const [message] = errorMsg.split(':');
113
+ refError ||= generateForSelf(this, this.getRootNode().posFromIndex(start), '');
114
+ errors.push({...refError, message, severity: message === '未闭合的标签' ? 'warning' : 'error'});
115
+ }
116
+ return errors;
123
117
  }
124
118
 
125
119
  /**
@@ -131,24 +125,23 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
131
125
  */
132
126
  findMatchingTag() {
133
127
  const {html} = this.getAttribute('config'),
134
- {name, parentNode, closing, selfClosing} = this,
128
+ {name: tagName, parentNode} = this,
135
129
  string = noWrap(String(this));
136
- if (closing && selfClosing) {
130
+ if (this.#closing && this.#selfClosing) {
137
131
  throw new SyntaxError(`同时闭合和自封闭的标签:${string}`);
138
- } else if (html[2].includes(name) || selfClosing && html[1].includes(name)) { // 自封闭标签
132
+ } else if (html[2].includes(tagName) || this.#selfClosing && html[1].includes(tagName)) { // 自封闭标签
139
133
  return this;
140
- } else if (selfClosing && html[0].includes(name)) {
134
+ } else if (this.#selfClosing && html[0].includes(tagName)) {
141
135
  throw new SyntaxError(`无效自封闭标签:${string}`);
142
136
  } else if (!parentNode) {
143
137
  return undefined;
144
138
  }
145
- const {children} = parentNode,
146
- i = children.indexOf(this),
147
- selector = `html#${name}`,
148
- siblings = closing
149
- ? children.slice(0, i).reverse().filter(child => child.matches(selector))
150
- : children.slice(i + 1).filter(child => child.matches(selector));
151
- let imbalance = closing ? -1 : 1;
139
+ const {childNodes} = parentNode,
140
+ i = childNodes.indexOf(this),
141
+ siblings = this.#closing
142
+ ? childNodes.slice(0, i).reverse().filter(({type, name}) => type === 'html' && name === tagName)
143
+ : childNodes.slice(i + 1).filter(({type, name}) => type === 'html' && name === tagName);
144
+ let imbalance = this.#closing ? -1 : 1;
152
145
  for (const token of siblings) {
153
146
  if (token.closing) {
154
147
  imbalance--;
@@ -159,7 +152,42 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
159
152
  return token;
160
153
  }
161
154
  }
162
- throw new SyntaxError(`未${closing ? '匹配的闭合' : '闭合的'}标签:${string}`);
155
+ throw new SyntaxError(`未${this.#closing ? '匹配的闭合' : '闭合的'}标签:${string}`);
156
+ }
157
+
158
+ /** @override */
159
+ cloneNode() {
160
+ const [attr] = this.cloneChildNodes(),
161
+ config = this.getAttribute('config');
162
+ return Parser.run(() => new HtmlToken(this.#tag, attr, this.#closing, this.#selfClosing, config));
163
+ }
164
+
165
+ /**
166
+ * @override
167
+ * @template {string} T
168
+ * @param {T} key 属性键
169
+ * @returns {TokenAttribute<T>}
170
+ */
171
+ getAttribute(key) {
172
+ return key === 'tag' ? this.#tag : super.getAttribute(key);
173
+ }
174
+
175
+ /** @override */
176
+ text() {
177
+ return `<${this.#closing ? '/' : ''}${this.#tag}${super.text()}${this.#selfClosing ? '/' : ''}>`;
178
+ }
179
+
180
+ /**
181
+ * 更换标签名
182
+ * @param {string} tag 标签名
183
+ * @throws `RangeError` 非法的HTML标签
184
+ */
185
+ replaceTag(tag) {
186
+ const name = tag.toLowerCase();
187
+ if (!this.getAttribute('config').html.flat().includes(name)) {
188
+ throw new RangeError(`非法的HTML标签:${tag}`);
189
+ }
190
+ this.setAttribute('name', name).#tag = tag;
163
191
  }
164
192
 
165
193
  /** 局部闭合 */
@@ -176,17 +204,17 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
176
204
  */
177
205
  fix() {
178
206
  const config = this.getAttribute('config'),
179
- {parentNode, name, firstElementChild} = this;
180
- if (!parentNode || !this.#selfClosing || !config.html[0].includes(name)) {
207
+ {parentNode, name: tagName, firstChild} = this;
208
+ if (!parentNode || !this.#selfClosing || !config.html[0].includes(tagName)) {
181
209
  return;
182
- } else if (firstElementChild.text().trim()) {
210
+ } else if (firstChild.text().trim()) {
183
211
  this.#localMatch();
184
212
  return;
185
213
  }
186
- const {children} = parentNode,
187
- i = children.indexOf(this),
214
+ const {childNodes} = parentNode,
215
+ i = childNodes.indexOf(this),
188
216
  /** @type {HtmlToken[]} */
189
- prevSiblings = children.slice(0, i).filter(child => child.matches(`html#${name}`)),
217
+ prevSiblings = childNodes.slice(0, i).filter(({type, name}) => type === 'html' && name === tagName),
190
218
  imbalance = prevSiblings.reduce((acc, {closing}) => acc + (closing ? 1 : -1), 0);
191
219
  if (imbalance < 0) {
192
220
  this.#selfClosing = false;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {text, noWrap, extUrlChar} = require('../util/string'),
3
+ const {text, noWrap, print, extUrlChar} = require('../util/string'),
4
4
  Parser = require('..'),
5
5
  Token = require('.'),
6
6
  AstText = require('../lib/text');
@@ -21,30 +21,36 @@ class ImageParameterToken extends Token {
21
21
  */
22
22
  static #validate(key, value, config = Parser.getConfig(), halfParsed = false) {
23
23
  value = value.replaceAll(/\0\d+t\x7F/gu, '').trim();
24
- if (key === 'width') {
25
- return /^\d*(?:x\d*)?$/u.test(value);
26
- } else if (['alt', 'class', 'manualthumb', 'frameless', 'framed', 'thumbnail'].includes(key)) {
27
- return true;
28
- } else if (key === 'link') {
29
- if (!value) {
30
- return this.noLink;
31
- }
32
- const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\0\\d+t\x7F|$)`, 'iu');
33
- if (regex.test(value)) {
34
- return value;
35
- }
36
- if (value.startsWith('[[') && value.endsWith(']]')) {
37
- value = value.slice(2, -2);
38
- }
39
- if (value.includes('%')) {
40
- try {
41
- value = decodeURIComponent(value);
42
- } catch {}
24
+ switch (key) {
25
+ case 'width':
26
+ return /^\d*(?:x\d*)?$/u.test(value);
27
+ case 'link': {
28
+ if (!value) {
29
+ return this.noLink;
30
+ }
31
+ const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\0\\d+t\x7F|$)`, 'iu');
32
+ if (regex.test(value)) {
33
+ return value;
34
+ } else if (value.startsWith('[[') && value.endsWith(']]')) {
35
+ value = value.slice(2, -2);
36
+ }
37
+ if (value.includes('%')) {
38
+ try {
39
+ value = decodeURIComponent(value);
40
+ } catch {}
41
+ }
42
+ const title = Parser.normalizeTitle(value, 0, false, config, halfParsed);
43
+ return title.valid && String(title);
43
44
  }
44
- const title = Parser.normalizeTitle(value, 0, false, config, halfParsed);
45
- return title.valid && String(title);
45
+ case 'lang':
46
+ return config.variants.includes(value);
47
+ case 'alt':
48
+ case 'class':
49
+ case 'manualthumb':
50
+ return true;
51
+ default:
52
+ return !isNaN(value);
46
53
  }
47
- return !isNaN(value);
48
54
  }
49
55
 
50
56
  type = 'image-parameter';
@@ -89,7 +95,6 @@ class ImageParameterToken extends Token {
89
95
  }
90
96
  str.splitText(str.data.indexOf('x'));
91
97
  str.nextSibling.splitText(1);
92
- // eslint-disable-next-line unicorn/consistent-destructuring
93
98
  return {width: text(token.childNodes.slice(0, i + 1)), height: text(token.childNodes.slice(i + 2))};
94
99
  }
95
100
  return undefined;
@@ -147,13 +152,42 @@ class ImageParameterToken extends Token {
147
152
  this.setAttribute('name', 'caption').setAttribute('stage', 7);
148
153
  }
149
154
 
155
+ /** @override */
156
+ isPlain() {
157
+ return true;
158
+ }
159
+
160
+ /**
161
+ * @override
162
+ * @param {string} selector
163
+ */
164
+ toString(selector) {
165
+ return this.#syntax && !(selector && this.matches(selector))
166
+ ? this.#syntax.replace('$1', super.toString(selector))
167
+ : super.toString(selector);
168
+ }
169
+
170
+ /** @override */
171
+ getPadding() {
172
+ return Math.max(0, this.#syntax.indexOf('$1'));
173
+ }
174
+
175
+ /** @override */
176
+ print() {
177
+ return this.#syntax
178
+ ? `<span class="wpb-image-parameter">${this.#syntax.replace('$1', print(this.childNodes))}</span>`
179
+ : super.print({class: 'image-caption'});
180
+ }
181
+
150
182
  /** @override */
151
183
  cloneNode() {
152
184
  const cloned = this.cloneChildNodes(),
153
- config = this.getAttribute('config'),
154
- token = Parser.run(() => new ImageParameterToken(this.#syntax.replace('$1', ''), config));
155
- token.replaceChildren(...cloned);
156
- return token;
185
+ config = this.getAttribute('config');
186
+ return Parser.run(() => {
187
+ const token = new ImageParameterToken(this.#syntax.replace('$1', ''), config);
188
+ token.replaceChildren(...cloned);
189
+ return token;
190
+ });
157
191
  }
158
192
 
159
193
  /**
@@ -166,29 +200,17 @@ class ImageParameterToken extends Token {
166
200
  return key === 'syntax' ? this.#syntax : super.getAttribute(key);
167
201
  }
168
202
 
169
- /** @override */
170
- isPlain() {
171
- return true;
172
- }
173
-
174
- /** 是否是不可变参数 */
175
- #isVoid() {
176
- return this.#syntax && !this.#syntax.includes('$1');
177
- }
178
-
179
203
  /**
180
204
  * @override
181
- * @param {string} selector
205
+ * @param {PropertyKey} key 属性键
182
206
  */
183
- toString(selector) {
184
- return this.#syntax && !(selector && this.matches(selector))
185
- ? this.#syntax.replace('$1', super.toString(selector))
186
- : super.toString(selector);
207
+ hasAttribute(key) {
208
+ return key === 'syntax' || super.hasAttribute(key);
187
209
  }
188
210
 
189
- /** @override */
190
- getPadding() {
191
- return Math.max(0, this.#syntax.indexOf('$1'));
211
+ /** 是否是不可变参数 */
212
+ #isVoid() {
213
+ return this.#syntax && !this.#syntax.includes('$1');
192
214
  }
193
215
 
194
216
  /** @override */
@@ -239,14 +261,14 @@ class ImageParameterToken extends Token {
239
261
  const root = Parser.parse(`[[File:F|${
240
262
  this.#syntax ? this.#syntax.replace('$1', value) : value
241
263
  }]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
242
- {childNodes: {length}, firstElementChild} = root,
243
- param = firstElementChild?.lastElementChild;
244
- if (length !== 1 || !firstElementChild?.matches('file#File\\:F')
245
- || firstElementChild.childNodes.length !== 2 || param.name !== this.name
264
+ {length, firstChild: file} = root,
265
+ {lastChild: imageParameter, type, name, length: fileLength} = file;
266
+ if (length !== 1 || type !== 'file' || name !== 'File:F' || fileLength !== 2
267
+ || imageParameter.name !== this.name
246
268
  ) {
247
269
  throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
248
270
  }
249
- this.replaceChildren(...param.childNodes);
271
+ this.replaceChildren(...imageParameter.childNodes);
250
272
  }
251
273
  }
252
274