wikiparser-node 0.3.1 → 0.5.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 (80) hide show
  1. package/README.md +1 -1
  2. package/config/default.json +13 -17
  3. package/config/llwiki.json +11 -79
  4. package/config/moegirl.json +7 -1
  5. package/config/zhwiki.json +1269 -0
  6. package/index.js +130 -97
  7. package/lib/element.js +410 -518
  8. package/lib/node.js +493 -115
  9. package/lib/ranges.js +27 -19
  10. package/lib/text.js +175 -0
  11. package/lib/title.js +14 -6
  12. package/mixin/attributeParent.js +70 -24
  13. package/mixin/fixedToken.js +18 -10
  14. package/mixin/hidden.js +6 -4
  15. package/mixin/sol.js +39 -12
  16. package/package.json +17 -4
  17. package/parser/brackets.js +18 -18
  18. package/parser/commentAndExt.js +16 -14
  19. package/parser/converter.js +14 -13
  20. package/parser/externalLinks.js +12 -11
  21. package/parser/hrAndDoubleUnderscore.js +24 -14
  22. package/parser/html.js +8 -7
  23. package/parser/links.js +13 -13
  24. package/parser/list.js +12 -11
  25. package/parser/magicLinks.js +11 -10
  26. package/parser/quotes.js +6 -5
  27. package/parser/selector.js +175 -0
  28. package/parser/table.js +31 -24
  29. package/src/arg.js +91 -43
  30. package/src/atom/hidden.js +5 -2
  31. package/src/atom/index.js +17 -9
  32. package/src/attribute.js +210 -101
  33. package/src/converter.js +78 -43
  34. package/src/converterFlags.js +104 -45
  35. package/src/converterRule.js +136 -78
  36. package/src/extLink.js +81 -27
  37. package/src/gallery.js +63 -20
  38. package/src/heading.js +58 -20
  39. package/src/html.js +138 -48
  40. package/src/imageParameter.js +93 -58
  41. package/src/index.js +314 -186
  42. package/src/link/category.js +22 -54
  43. package/src/link/file.js +83 -32
  44. package/src/link/galleryImage.js +21 -7
  45. package/src/link/index.js +170 -81
  46. package/src/magicLink.js +64 -14
  47. package/src/nowiki/comment.js +36 -10
  48. package/src/nowiki/dd.js +37 -22
  49. package/src/nowiki/doubleUnderscore.js +21 -7
  50. package/src/nowiki/hr.js +11 -7
  51. package/src/nowiki/index.js +16 -9
  52. package/src/nowiki/list.js +2 -2
  53. package/src/nowiki/noinclude.js +8 -4
  54. package/src/nowiki/quote.js +38 -7
  55. package/src/onlyinclude.js +24 -7
  56. package/src/parameter.js +102 -62
  57. package/src/syntax.js +23 -20
  58. package/src/table/index.js +282 -174
  59. package/src/table/td.js +112 -61
  60. package/src/table/tr.js +135 -74
  61. package/src/tagPair/ext.js +30 -23
  62. package/src/tagPair/include.js +26 -11
  63. package/src/tagPair/index.js +72 -29
  64. package/src/transclude.js +235 -127
  65. package/tool/index.js +42 -32
  66. package/util/debug.js +21 -18
  67. package/util/diff.js +76 -0
  68. package/util/lint.js +40 -0
  69. package/util/string.js +56 -26
  70. package/.eslintrc.json +0 -319
  71. package/errors/README +0 -1
  72. package/jsconfig.json +0 -7
  73. package/printed/README +0 -1
  74. package/typings/element.d.ts +0 -28
  75. package/typings/index.d.ts +0 -52
  76. package/typings/node.d.ts +0 -23
  77. package/typings/parser.d.ts +0 -9
  78. package/typings/table.d.ts +0 -14
  79. package/typings/token.d.ts +0 -22
  80. package/typings/tool.d.ts +0 -10
package/src/table/tr.js CHANGED
@@ -1,11 +1,30 @@
1
1
  'use strict';
2
2
 
3
- const attributeParent = require('../../mixin/attributeParent'),
4
- /** @type {Parser} */ Parser = require('../..'),
3
+ const {generateForChild} = require('../../util/lint'),
4
+ attributeParent = require('../../mixin/attributeParent'),
5
+ Parser = require('../..'),
6
+ AstText = require('../../lib/text'),
5
7
  Token = require('..'),
6
8
  SyntaxToken = require('../syntax'),
7
9
  AttributeToken = require('../attribute');
8
10
 
11
+ const openingPattern = /^\n[^\S\n]*(?:\|-+|\{\{\s*!\s*\}\}-+|\{\{\s*!-\s*\}\}-*)$/u;
12
+
13
+ /**
14
+ * 转义表格语法
15
+ * @param {SyntaxToken} syntax 表格语法节点
16
+ */
17
+ const escapeTable = syntax => {
18
+ const templates = {'{|': '(!', '|}': '!)', '||': '!!', '|': '!'},
19
+ wikitext = syntax.childNodes.map(
20
+ child => child.type === 'text'
21
+ ? String(child).replaceAll(/\{\||\|\}|\|{2}|\|/gu, p => `{{${templates[p]}}}`)
22
+ : String(child),
23
+ ).join(''),
24
+ token = Parser.parse(wikitext, syntax.getAttribute('include'), 2, syntax.getAttribute('config'));
25
+ syntax.replaceChildren(...token.childNodes);
26
+ };
27
+
9
28
  /**
10
29
  * 表格行,含开头的换行,不含结尾的换行
11
30
  * @classdesc `{childNodes: [SyntaxToken, AttributeToken, ?Token, ...TdToken]}`
@@ -13,13 +32,13 @@ const attributeParent = require('../../mixin/attributeParent'),
13
32
  class TrToken extends attributeParent(Token, 1) {
14
33
  type = 'tr';
15
34
 
16
- static openingPattern = /^\n[^\S\n]*(?:\|-+|\{\{\s*!\s*\}\}-+|\{\{\s*!-\s*\}\}-*)$/;
17
-
18
35
  /**
19
- * @param {string} syntax
36
+ * @param {string} syntax 表格语法
37
+ * @param {string} attr 表格属性
20
38
  * @param {accum} accum
39
+ * @param {RegExp} pattern 表格语法正则
21
40
  */
22
- constructor(syntax, attr = '', config = Parser.getConfig(), accum = [], pattern = TrToken.openingPattern) {
41
+ constructor(syntax, attr = '', config = Parser.getConfig(), accum = [], pattern = openingPattern) {
23
42
  super(undefined, config, true, accum, {Token: 2, SyntaxToken: 0, AttributeToken: 1, TdToken: '2:'});
24
43
  this.append(
25
44
  new SyntaxToken(syntax, pattern, 'table-syntax', config, accum, {
@@ -27,18 +46,38 @@ class TrToken extends attributeParent(Token, 1) {
27
46
  }),
28
47
  new AttributeToken(attr, 'table-attr', 'tr', config, accum),
29
48
  );
30
- this.protectChildren(0, 1);
49
+ this.getAttribute('protectChildren')(0, 1);
31
50
  }
32
51
 
52
+ /**
53
+ * @override
54
+ * @param {number} start 起始位置
55
+ */
56
+ lint(start = 0) {
57
+ const errors = super.lint(start),
58
+ inter = this.childNodes.find(({type}) => type === 'table-inter'),
59
+ str = String(inter).trim();
60
+ if (inter && str && !/^<!--.*-->$/u.test(str)) {
61
+ const error = generateForChild(inter, this.getRootNode().posFromIndex(start), '将被移出表格的内容');
62
+ error.startLine++;
63
+ error.startCol = 0;
64
+ errors.push(error);
65
+ }
66
+ return errors;
67
+ }
68
+
69
+ /**
70
+ * @override
71
+ * @this {TrToken & {constructor: typeof TrToken}}
72
+ */
33
73
  cloneNode() {
34
- const [syntax, attr, inner, ...cloned] = this.cloneChildren(),
35
- /** @type {typeof TrToken} */ Constructor = this.constructor;
74
+ const [syntax, attr, inner, ...cloned] = this.cloneChildNodes();
36
75
  return Parser.run(() => {
37
- const token = new Constructor(undefined, undefined, this.getAttribute('config'));
38
- token.firstElementChild.safeReplaceWith(syntax);
39
- token.children[1].safeReplaceWith(attr);
76
+ const token = new this.constructor(undefined, undefined, this.getAttribute('config'));
77
+ token.firstChild.safeReplaceWith(syntax);
78
+ token.childNodes[1].safeReplaceWith(attr);
40
79
  if (token.type === 'td') { // TdToken
41
- token.children[2].safeReplaceWith(inner);
80
+ token.childNodes[2].safeReplaceWith(inner);
42
81
  } else if (inner !== undefined) {
43
82
  token.appendChild(inner);
44
83
  }
@@ -47,79 +86,84 @@ class TrToken extends attributeParent(Token, 1) {
47
86
  });
48
87
  }
49
88
 
89
+ /** 修复简单的表格语法错误 */
50
90
  #correct() {
51
- const [,, child] = this.children;
91
+ const {childNodes: [,, child]} = this;
52
92
  if (child?.isPlain()) {
53
- const {firstChild} = child;
54
- if (typeof firstChild !== 'string') {
93
+ const /** @type {{firstChild: AstText}} */ {firstChild} = child;
94
+ if (firstChild.type !== 'text') {
55
95
  child.prepend('\n');
56
- } else if (!firstChild.startsWith('\n')) {
57
- child.setText(`\n${firstChild}`);
96
+ } else if (firstChild.data[0] !== '\n') {
97
+ firstChild.insertData(0, '\n');
58
98
  }
59
99
  }
60
100
  }
61
101
 
62
- toString() {
102
+ /**
103
+ * @override
104
+ * @param {string} selector
105
+ */
106
+ toString(selector) {
63
107
  this.#correct();
64
- return super.toString();
108
+ return super.toString(selector);
65
109
  }
66
110
 
111
+ /** @override */
67
112
  text() {
68
113
  this.#correct();
69
114
  const str = super.text();
70
115
  return this.type === 'tr' && !str.trim().includes('\n') ? '' : str;
71
116
  }
72
117
 
73
- /** @param {SyntaxToken} syntax */
74
- static escape(syntax) {
75
- const wikitext = syntax.childNodes.map(child => typeof child === 'string'
76
- ? child.replaceAll('{|', '{{(!}}').replaceAll('|}', '{{!)}}').replaceAll('||', '{{!!}}')
77
- .replaceAll('|', '{{!}}')
78
- : child.toString(),
79
- ).join(''),
80
- token = Parser.parse(wikitext, syntax.getAttribute('include'), 2, syntax.getAttribute('config'));
81
- syntax.replaceChildren(...token.childNodes);
82
- }
83
-
84
- /** @complexity `n` */
118
+ /**
119
+ * 转义表格语法
120
+ * @complexity `n`
121
+ */
85
122
  escape() {
86
- for (const child of this.children) {
123
+ for (const child of this.childNodes) {
87
124
  if (child instanceof SyntaxToken) {
88
- TrToken.escape(child);
125
+ escapeTable(child);
89
126
  } else if (child instanceof TrToken) {
90
127
  child.escape();
91
128
  }
92
129
  }
93
130
  }
94
131
 
95
- /** @param {string} syntax */
96
- setSyntax(syntax, esc = false) {
97
- const {firstElementChild} = this;
98
- firstElementChild.replaceChildren(syntax);
132
+ /**
133
+ * 设置表格语法
134
+ * @param {string} syntax 表格语法
135
+ * @param {boolean} esc 是否需要转义
136
+ */
137
+ setSyntax(syntax, esc) {
138
+ const {firstChild} = this;
139
+ firstChild.replaceChildren(syntax);
99
140
  if (esc) {
100
- TrToken.escape(firstElementChild);
141
+ escapeTable(firstChild);
101
142
  }
102
143
  }
103
144
 
104
145
  /**
105
- * @param {number} i
146
+ * @override
147
+ * @param {number} i 移除位置
106
148
  * @complexity `n`
107
149
  */
108
150
  removeAt(i) {
109
- const TdToken = require('./td'),
110
- child = this.childNodes.at(i);
151
+ const TdToken = require('./td');
152
+ const child = this.childNodes.at(i);
111
153
  if (child instanceof TdToken && child.isIndependent()) {
112
- const {nextElementSibling} = child;
113
- if (nextElementSibling?.type === 'td') {
114
- nextElementSibling.independence();
154
+ const {nextSibling} = child;
155
+ if (nextSibling?.type === 'td') {
156
+ nextSibling.independence();
115
157
  }
116
158
  }
117
159
  return super.removeAt(i);
118
160
  }
119
161
 
120
162
  /**
121
- * @template {string|Token} T
122
- * @param {T} token
163
+ * @override
164
+ * @template {AstText|Token} T
165
+ * @param {T} token 待插入的子节点
166
+ * @param {number} i 插入位置
123
167
  * @returns {T}
124
168
  * @complexity `n`
125
169
  */
@@ -127,8 +171,8 @@ class TrToken extends attributeParent(Token, 1) {
127
171
  if (!Parser.running && !(token instanceof TrToken)) {
128
172
  this.typeError('insertAt', 'TrToken');
129
173
  }
130
- const TdToken = require('./td'),
131
- child = this.childNodes.at(i);
174
+ const TdToken = require('./td');
175
+ const child = this.childNodes.at(i);
132
176
  if (token instanceof TdToken && token.isIndependent() && child instanceof TdToken) {
133
177
  child.independence();
134
178
  }
@@ -136,50 +180,62 @@ class TrToken extends attributeParent(Token, 1) {
136
180
  }
137
181
 
138
182
  /**
183
+ * 获取行数
139
184
  * @returns {0|1}
140
185
  * @complexity `n`
141
186
  */
142
187
  getRowCount() {
143
188
  const TdToken = require('./td');
144
- return Number(this.children.some(child =>
145
- child instanceof TdToken && child.isIndependent() && !child.firstElementChild.text().endsWith('+'),
189
+ return Number(this.childNodes.some(
190
+ child => child instanceof TdToken && child.isIndependent() && child.firstChild.text().at(-1) !== '+',
146
191
  ));
147
192
  }
148
193
 
149
194
  /**
150
- * @param {(children: Token[], index: number) => Token[]} subset
195
+ * 获取相邻行
196
+ * @param {(childNodes: Token[], index: number) => Token[]} subset 筛选兄弟节点的方法
151
197
  * @complexity `n`
152
198
  */
153
199
  #getSiblingRow(subset) {
154
- const {parentElement} = this;
155
- if (!parentElement) {
156
- return;
200
+ const {parentNode} = this;
201
+ if (!parentNode) {
202
+ return undefined;
157
203
  }
158
- const {children} = parentElement,
159
- index = children.indexOf(this);
160
- for (const child of subset(children, index)) {
204
+ const {childNodes} = parentNode,
205
+ index = childNodes.indexOf(this);
206
+ for (const child of subset(childNodes, index)) {
161
207
  if (child instanceof TrToken && child.getRowCount()) {
162
208
  return child;
163
209
  }
164
210
  }
211
+ return undefined;
165
212
  }
166
213
 
167
- /** @complexity `n` */
214
+ /**
215
+ * 获取下一行
216
+ * @complexity `n`
217
+ */
168
218
  getNextRow() {
169
- return this.#getSiblingRow((children, index) => children.slice(index + 1));
219
+ return this.#getSiblingRow((childNodes, index) => childNodes.slice(index + 1));
170
220
  }
171
221
 
172
- /** @complexity `n` */
222
+ /**
223
+ * 获取前一行
224
+ * @complexity `n`
225
+ */
173
226
  getPreviousRow() {
174
- return this.#getSiblingRow((children, index) => children.slice(0, index).reverse());
227
+ return this.#getSiblingRow((childNodes, index) => childNodes.slice(0, index).reverse());
175
228
  }
176
229
 
177
- /** @complexity `n` */
230
+ /**
231
+ * 获取列数
232
+ * @complexity `n`
233
+ */
178
234
  getColCount() {
179
235
  const TdToken = require('./td');
180
236
  let count = 0,
181
237
  last = 0;
182
- for (const child of this.children) {
238
+ for (const child of this.childNodes) {
183
239
  if (child instanceof TdToken) {
184
240
  last = child.isIndependent() ? Number(child.subtype !== 'caption') : last;
185
241
  count += last;
@@ -189,11 +245,14 @@ class TrToken extends attributeParent(Token, 1) {
189
245
  }
190
246
 
191
247
  /**
192
- * @param {number} n
248
+ * 获取第n
249
+ * @param {number} n 列号
250
+ * @param {boolean} insert 是否用于判断插入新列的位置
193
251
  * @returns {TdToken}
194
252
  * @complexity `n`
253
+ * @throws `RangeError` 不存在对应单元格
195
254
  */
196
- getNthCol(n, insert = false) {
255
+ getNthCol(n, insert) {
197
256
  if (typeof n !== 'number') {
198
257
  this.typeError('getNthCol', 'Number');
199
258
  }
@@ -204,7 +263,7 @@ class TrToken extends attributeParent(Token, 1) {
204
263
  }
205
264
  const TdToken = require('./td');
206
265
  let last = 0;
207
- for (const child of this.children.slice(2)) {
266
+ for (const child of this.childNodes.slice(2)) {
208
267
  if (child instanceof TdToken) {
209
268
  if (child.isIndependent()) {
210
269
  last = Number(child.subtype !== 'caption');
@@ -213,23 +272,25 @@ class TrToken extends attributeParent(Token, 1) {
213
272
  if (n < 0) {
214
273
  return child;
215
274
  }
216
- } else if (['tr', 'table-syntax'].includes(child.type)) {
275
+ } else if (child.type === 'tr' || child.type === 'table-syntax') {
217
276
  return child;
218
277
  }
219
278
  }
279
+ return undefined;
220
280
  }
221
281
 
222
282
  /**
223
- * @param {string|Token} inner
224
- * @param {TableCoords}
225
- * @param {'td'|'th'|'caption'} subtype
226
- * @param {Record<string, string|boolean>} attr
283
+ * 插入新的单元格
284
+ * @param {string|Token} inner 单元格内部wikitext
285
+ * @param {TableCoords} coord 单元格坐标
286
+ * @param {'td'|'th'|'caption'} subtype 单元格类型
287
+ * @param {Record<string, string|boolean>} attr 单元格属性
227
288
  * @returns {TdToken}
228
289
  * @complexity `n`
229
290
  */
230
291
  insertTableCell(inner, {column}, subtype = 'td', attr = {}) {
231
- const TdToken = require('./td'),
232
- token = TdToken.create(inner, subtype, attr, this.getAttribute('include'), this.getAttribute('config'));
292
+ const TdToken = require('./td');
293
+ const token = TdToken.create(inner, subtype, attr, this.getAttribute('include'), this.getAttribute('config'));
233
294
  return this.insertBefore(token, this.getNthCol(column, true));
234
295
  }
235
296
  }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const attributeParent = require('../../mixin/attributeParent'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  TagPairToken = require('.');
6
6
 
7
7
  /**
@@ -11,35 +11,46 @@ const attributeParent = require('../../mixin/attributeParent'),
11
11
  class ExtToken extends attributeParent(TagPairToken) {
12
12
  type = 'ext';
13
13
 
14
+ /** @override */
15
+ get closed() {
16
+ return super.closed;
17
+ }
18
+
14
19
  /**
15
- * @param {string} name
16
- * @param {string|undefined} closing
20
+ * @param {string} name 标签名
21
+ * @param {string} attr 标签属性
22
+ * @param {string} inner 内部wikitext
23
+ * @param {string|undefined} closed 是否封闭
17
24
  * @param {accum} accum
18
25
  */
19
- constructor(name, attr = '', inner = '', closing = undefined, config = Parser.getConfig(), accum = []) {
20
- attr = !attr || /^\s/.test(attr) ? attr : ` ${attr}`;
26
+ constructor(name, attr = '', inner = '', closed = undefined, config = Parser.getConfig(), accum = []) {
27
+ attr = !attr || attr.trimStart() !== attr ? attr : ` ${attr}`;
28
+ const AttributeToken = require('../attribute');
21
29
  const lcName = name.toLowerCase(),
22
- AttributeToken = require('../attribute'),
23
30
  attrToken = new AttributeToken(attr, 'ext-attr', lcName, config, accum),
24
31
  newConfig = structuredClone(config),
25
32
  ext = new Set(newConfig.ext);
26
33
  let /** @type {acceptable} */ acceptable, /** @type {Token} */ innerToken;
27
34
  switch (lcName) {
28
35
  case 'choose':
29
- ext.add('option');
30
- // fall through
31
- case 'ref':
32
36
  case 'option':
37
+ case 'ref':
33
38
  case 'poem':
34
39
  case 'indicator':
35
40
  case 'tab':
36
41
  case 'tabs':
37
- case 'pre': {
42
+ case 'pre':
43
+ case 'combobox':
44
+ case 'combooption': {
38
45
  ext.delete(lcName);
39
- newConfig.ext = [...ext];
46
+ newConfig.ext = [
47
+ ...ext,
48
+ ...lcName === 'choose' ? ['option'] : [],
49
+ ...lcName === 'combobox' ? ['combooption'] : [],
50
+ ];
40
51
  const Token = require('..');
41
52
  acceptable = {AttributeToken: 0, Token: 1};
42
- innerToken = new Token(inner, newConfig, false, accum);
53
+ innerToken = new Token(inner, newConfig, true, accum);
43
54
  break;
44
55
  }
45
56
  case 'gallery': {
@@ -50,6 +61,7 @@ class ExtToken extends attributeParent(TagPairToken) {
50
61
  innerToken = new GalleryToken(inner, newConfig, accum);
51
62
  break;
52
63
  }
64
+
53
65
  /*
54
66
  * 更多定制扩展的代码示例:
55
67
  * ```
@@ -69,28 +81,23 @@ class ExtToken extends attributeParent(TagPairToken) {
69
81
  innerToken = new NowikiToken(inner, config);
70
82
  }
71
83
  }
72
- innerToken.type = 'ext-inner';
73
- innerToken.setAttribute('name', lcName);
84
+ innerToken.setAttribute('name', lcName).type = 'ext-inner';
74
85
  if (lcName === 'pre') {
75
86
  innerToken.setAttribute('stage', Parser.MAX_STAGE - 1);
76
87
  }
77
- super(name, attrToken, innerToken, closing, config, accum, acceptable);
78
- Object.defineProperty(this, 'closed', {value: true, enumerable: false, writable: false, configurable: false});
88
+ super(name, attrToken, innerToken, closed, config, accum, acceptable);
79
89
  }
80
90
 
91
+ /** @override */
81
92
  cloneNode() {
82
- const inner = this.lastElementChild.cloneNode(),
93
+ const inner = this.lastChild.cloneNode(),
83
94
  tags = this.getAttribute('tags'),
84
95
  config = this.getAttribute('config'),
85
- attr = this.firstElementChild.toString(),
96
+ attr = String(this.firstChild),
86
97
  token = Parser.run(() => new ExtToken(tags[0], attr, '', this.selfClosing ? undefined : tags[1], config));
87
- token.lastElementChild.safeReplaceWith(inner);
98
+ token.lastChild.safeReplaceWith(inner);
88
99
  return token;
89
100
  }
90
-
91
- get innerText() {
92
- return this.selfClosing ? '' : this.lastElementChild.text();
93
- }
94
101
  }
95
102
 
96
103
  Parser.classes.ExtToken = __filename;
@@ -1,41 +1,56 @@
1
1
  'use strict';
2
2
 
3
3
  const hidden = require('../../mixin/hidden'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ {generateForSelf} = require('../../util/lint'),
5
+ Parser = require('../..'),
5
6
  TagPairToken = require('.');
6
7
 
7
8
  /**
8
9
  * `<includeonly>`或`<noinclude>`
9
- * @classdesc `{childNodes: [string, string]}`
10
+ * @classdesc `{childNodes: [AstText, AstText]}`
10
11
  */
11
12
  class IncludeToken extends hidden(TagPairToken) {
12
13
  type = 'include';
13
14
 
14
15
  /**
15
- * @param {string} name
16
- * @param {string|undefined} inner
17
- * @param {string|undefined} closing
16
+ * @param {string} name 标签名
17
+ * @param {string} attr 标签属性
18
+ * @param {string|undefined} inner 内部wikitext
19
+ * @param {string|undefined} closed 是否封闭
18
20
  * @param {accum} accum
19
21
  */
20
- constructor(name, attr = '', inner = undefined, closing = undefined, config = Parser.getConfig(), accum = []) {
21
- super(name, attr, inner ?? '', inner !== undefined ? closing ?? '' : closing, config, accum, {String: [0, 1]});
22
+ constructor(name, attr = '', inner = undefined, closed = undefined, config = Parser.getConfig(), accum = []) {
23
+ super(name, attr, inner ?? '', inner === undefined ? closed : closed ?? '', config, accum, {AstText: [0, 1]});
22
24
  }
23
25
 
24
- /** @this {IncludeToken & {firstChild: string, lastChild: string}} */
26
+ /**
27
+ * @override
28
+ * @param {number} start 起始位置
29
+ * @returns {LintError[]}
30
+ */
31
+ lint(start = 0) {
32
+ return this.closed ? [] : [generateForSelf(this, this.getRootNode().posFromIndex(start), '未闭合的标签')];
33
+ }
34
+
35
+ /** @override */
25
36
  cloneNode() {
26
37
  const tags = this.getAttribute('tags'),
27
38
  config = this.getAttribute('config'),
28
- inner = this.selfClosing ? undefined : this.lastChild,
39
+ inner = this.selfClosing ? undefined : String(this.lastChild),
29
40
  closing = this.selfClosing || !this.closed ? undefined : tags[1],
30
- token = Parser.run(() => new IncludeToken(tags[0], this.firstChild, inner, closing, config));
41
+ token = Parser.run(() => new IncludeToken(tags[0], String(this.firstChild), inner, closing, config));
31
42
  return token;
32
43
  }
33
44
 
34
- /** @param {string} str */
45
+ /**
46
+ * @override
47
+ * @param {string} str 新文本
48
+ */
35
49
  setText(str) {
36
50
  return super.setText(str, 1);
37
51
  }
38
52
 
53
+ /** 清除标签属性 */
39
54
  removeAttr() {
40
55
  super.setText('', 0);
41
56
  }
@@ -1,30 +1,57 @@
1
1
  'use strict';
2
2
 
3
3
  const fixedToken = require('../../mixin/fixedToken'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  Token = require('..');
6
6
 
7
7
  /**
8
8
  * 成对标签
9
- * @classdesc `{childNodes: [string|AttributeToken, string|Token]}`
9
+ * @classdesc `{childNodes: [AstText|AttributeToken, AstText|Token]}`
10
10
  */
11
11
  class TagPairToken extends fixedToken(Token) {
12
- selfClosing;
13
- closed;
12
+ #selfClosing;
13
+ #closed;
14
14
  #tags;
15
15
 
16
+ /** getter */
17
+ get selfClosing() {
18
+ return this.#selfClosing;
19
+ }
20
+
21
+ set selfClosing(value) {
22
+ value = Boolean(value);
23
+ if (value !== this.#selfClosing && this.lastChild.text()) {
24
+ Parser.warn(`<${this.name}>标签内部的${value ? '文本将被隐藏' : '原有文本将再次可见'}!`);
25
+ }
26
+ this.#selfClosing = value;
27
+ }
28
+
29
+ /** getter */
30
+ get closed() {
31
+ return this.#closed;
32
+ }
33
+
34
+ set closed(value) {
35
+ this.#closed ||= Boolean(value);
36
+ }
37
+
38
+ /** 内部wikitext */
39
+ get innerText() {
40
+ return this.#selfClosing ? undefined : this.lastChild.text();
41
+ }
42
+
16
43
  /**
17
- * @param {string} name
18
- * @param {string|Token} attr
19
- * @param {string|Token} inner
20
- * @param {string|undefined} closing - 约定`undefined`表示自闭合,`''`表示未闭合
44
+ * @param {string} name 标签名
45
+ * @param {string|Token} attr 标签属性
46
+ * @param {string|Token} inner 内部wikitext
47
+ * @param {string|undefined} closed 是否封闭;约定`undefined`表示自闭合,`''`表示未闭合
21
48
  * @param {accum} accum
22
49
  */
23
- constructor(name, attr, inner, closing, config = Parser.getConfig(), accum = []) {
50
+ constructor(name, attr, inner, closed, config = Parser.getConfig(), accum = []) {
24
51
  super(undefined, config, true);
25
- this.setAttribute('name', name.toLowerCase()).#tags = [name, closing || name];
26
- this.selfClosing = closing === undefined;
27
- this.closed = closing !== '';
52
+ this.setAttribute('name', name.toLowerCase()).#tags = [name, closed || name];
53
+ this.#selfClosing = closed === undefined;
54
+ this.#closed = closed !== '';
28
55
  this.append(attr, inner);
29
56
  let index = accum.indexOf(attr);
30
57
  if (index === -1) {
@@ -37,44 +64,60 @@ class TagPairToken extends fixedToken(Token) {
37
64
  }
38
65
 
39
66
  /**
67
+ * @override
40
68
  * @template {string} T
41
- * @param {T} key
69
+ * @param {T} key 属性键
42
70
  * @returns {TokenAttribute<T>}
43
71
  */
44
72
  getAttribute(key) {
45
- if (key === 'tags') {
46
- return [...this.#tags];
47
- }
48
- return super.getAttribute(key);
73
+ return key === 'tags' ? [...this.#tags] : super.getAttribute(key);
49
74
  }
50
75
 
51
- toString() {
52
- const {closed, firstChild, lastChild, nextSibling, name, selfClosing} = this,
76
+ /**
77
+ * @override
78
+ * @param {string} selector
79
+ */
80
+ toString(selector) {
81
+ const {firstChild, lastChild, nextSibling, name} = this,
53
82
  [opening, closing] = this.#tags;
54
- if (!closed && nextSibling) {
83
+ if (selector && this.matches(selector)) {
84
+ return '';
85
+ } else if (!this.#closed && nextSibling) {
55
86
  Parser.error(`自动闭合 <${name}>`, lastChild);
56
- this.closed = true;
87
+ this.#closed = true;
57
88
  }
58
- return selfClosing
89
+ return this.#selfClosing
59
90
  ? `<${opening}${String(firstChild)}/>`
60
- : `<${opening}${String(firstChild)}>${String(lastChild)}${this.closed ? `</${closing}>` : ''}`;
91
+ : `<${opening}${String(firstChild)}>${String(lastChild)}${this.#closed ? `</${closing}>` : ''}`;
61
92
  }
62
93
 
94
+ /** @override */
63
95
  getPadding() {
64
96
  return this.#tags[0].length + 1;
65
97
  }
66
98
 
99
+ /** @override */
67
100
  getGaps() {
68
101
  return 1;
69
102
  }
70
103
 
71
- /** @returns {string} */
104
+ /** @override */
105
+ print() {
106
+ const [opening, closing] = this.#tags;
107
+ return super.print(this.#selfClosing
108
+ ? {pre: `&lt;${opening}`, post: '/&gt;'}
109
+ : {pre: `&lt;${opening}`, sep: '&gt;', post: this.#closed ? `&lt;/${closing}&gt;` : ''});
110
+ }
111
+
112
+ /**
113
+ * @override
114
+ * @returns {string}
115
+ */
72
116
  text() {
73
- const {closed, firstChild, selfClosing} = this,
74
- [opening, closing] = this.#tags;
75
- return selfClosing
76
- ? `<${opening}${typeof firstChild === 'string' ? firstChild : firstChild.text()}/>`
77
- : `<${opening}${super.text('>')}${closed ? `</${closing}>` : ''}`;
117
+ const [opening, closing] = this.#tags;
118
+ return this.#selfClosing
119
+ ? `<${opening}${this.firstChild.text()}/>`
120
+ : `<${opening}${super.text('>')}${this.#closed ? `</${closing}>` : ''}`;
78
121
  }
79
122
  }
80
123