wikiparser-node 0.8.1 → 0.9.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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const Title = require('../../lib/title'),
3
+ const {decodeHtml} = require('../../util/string'),
4
4
  Parser = require('../..'),
5
5
  LinkToken = require('.');
6
6
 
@@ -13,11 +13,7 @@ class CategoryToken extends LinkToken {
13
13
 
14
14
  /** 分类排序关键字 */
15
15
  get sortkey() {
16
- return this.childNodes[1]?.text()?.replace(
17
- /&#(\d+|x[\da-f]+);|\n/gu,
18
- /** @param {string} p */
19
- (_, p) => p ? String.fromCodePoint(p[0] === 'x' ? parseInt(p.slice(1), 16) : Number(p)) : '',
20
- );
16
+ return decodeHtml(this.childNodes[1]?.text());
21
17
  }
22
18
 
23
19
  set sortkey(text) {
@@ -27,12 +23,11 @@ class CategoryToken extends LinkToken {
27
23
  /**
28
24
  * @param {string} link 分类名
29
25
  * @param {string|undefined} text 排序关键字
30
- * @param {Title} title 分类页面标题对象
31
26
  * @param {accum} accum
32
27
  * @param {string} delimiter `|`
33
28
  */
34
- constructor(link, text, title, config = Parser.getConfig(), accum = [], delimiter = '|') {
35
- super(link, text, title, config, accum, delimiter);
29
+ constructor(link, text, config = Parser.getConfig(), accum = [], delimiter = '|') {
30
+ super(link, text, config, accum, delimiter);
36
31
  this.seal(['selfLink', 'interwiki', 'setLangLink', 'setFragment', 'asSelfLink', 'pipeTrick'], true);
37
32
  }
38
33
 
package/src/link/file.js CHANGED
@@ -3,7 +3,6 @@
3
3
  const {explode, noWrap} = require('../../util/string'),
4
4
  {generateForChild} = require('../../util/lint'),
5
5
  Parser = require('../..'),
6
- Title = require('../../lib/title'),
7
6
  LinkToken = require('.'),
8
7
  ImageParameterToken = require('../imageParameter');
9
8
 
@@ -63,13 +62,12 @@ class FileToken extends LinkToken {
63
62
  /**
64
63
  * @param {string} link 文件名
65
64
  * @param {string|undefined} text 图片参数
66
- * @param {Title} title 文件标题对象
67
65
  * @param {accum} accum
68
66
  * @param {string} delimiter `|`
69
67
  * @complexity `n`
70
68
  */
71
- constructor(link, text, title, config = Parser.getConfig(), accum = [], delimiter = '|') {
72
- super(link, undefined, title, config, accum, delimiter);
69
+ constructor(link, text, config = Parser.getConfig(), accum = [], delimiter = '|') {
70
+ super(link, undefined, config, accum, delimiter);
73
71
  this.setAttribute('acceptable', {AtomToken: 0, ImageParameterToken: '1:'});
74
72
  this.append(...explode('-{', '}-', '|', text).map(part => new ImageParameterToken(part, config, accum)));
75
73
  this.seal(
@@ -82,10 +80,13 @@ class FileToken extends LinkToken {
82
80
  * @override
83
81
  * @param {number} start 起始位置
84
82
  */
85
- lint(start = 0) {
83
+ lint(start = this.getAbsoluteIndex()) {
86
84
  const errors = super.lint(start),
87
- args = this.getAllArgs(),
88
- keys = [...new Set(args.map(({name}) => name))],
85
+ args = this.getAllArgs().filter(({childNodes}) => {
86
+ const visibleNodes = childNodes.filter(node => node.text().trim());
87
+ return visibleNodes.length !== 1 || visibleNodes[0].type !== 'arg';
88
+ }),
89
+ keys = [...new Set(args.map(({name}) => name))].filter(key => key !== 'invalid'),
89
90
  frameKeys = keys.filter(key => frame.has(key)),
90
91
  horizAlignKeys = keys.filter(key => horizAlign.has(key)),
91
92
  vertAlignKeys = keys.filter(key => vertAlign.has(key));
@@ -104,24 +105,28 @@ class FileToken extends LinkToken {
104
105
  ];
105
106
  }
106
107
  if (relevantArgs.length > 1) {
107
- errors.push(...relevantArgs.map(arg => generateForChild(arg, rect, `重复的图片${key}参数`)));
108
+ errors.push(...relevantArgs.map(
109
+ arg => generateForChild(arg, rect, Parser.msg('duplicated image $1 parameter', key)),
110
+ ));
108
111
  }
109
112
  }
110
113
  if (frameKeys.size > 1) {
111
114
  errors.push(
112
- ...args.filter(({name}) => frame.has(name)).map(arg => generateForChild(arg, rect, '冲突的图片框架参数')),
115
+ ...args.filter(({name}) => frame.has(name)).map(
116
+ arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'frame'),
117
+ ),
113
118
  );
114
119
  }
115
120
  if (horizAlignKeys.size > 1) {
116
121
  errors.push(
117
122
  ...args.filter(({name}) => horizAlign.has(name))
118
- .map(arg => generateForChild(arg, rect, '冲突的图片水平对齐参数')),
123
+ .map(arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'horizontal-alignment')),
119
124
  );
120
125
  }
121
126
  if (vertAlignKeys.size > 1) {
122
127
  errors.push(
123
128
  ...args.filter(({name}) => vertAlign.has(name))
124
- .map(arg => generateForChild(arg, rect, '冲突的图片垂直对齐参数')),
129
+ .map(arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'vertical-alignment')),
125
130
  );
126
131
  }
127
132
  return errors;
@@ -3,7 +3,6 @@
3
3
  const {generateForSelf} = require('../../util/lint'),
4
4
  {undo} = require('../../util/debug'),
5
5
  singleLine = require('../../mixin/singleLine'),
6
- Title = require('../../lib/title'),
7
6
  Parser = require('../..'),
8
7
  Token = require('..'),
9
8
  FileToken = require('./file');
@@ -22,16 +21,17 @@ class GalleryImageToken extends singleLine(FileToken) {
22
21
  }
23
22
 
24
23
  set link(value) {
25
- super.link = value;
24
+ if (this.type !== 'imagemap-image') {
25
+ super.link = value;
26
+ }
26
27
  }
27
28
 
28
29
  /**
29
30
  * @param {string} link 图片文件名
30
31
  * @param {string|undefined} text 图片参数
31
- * @param {Title} title 图片文件标题对象
32
32
  * @param {accum} accum
33
33
  */
34
- constructor(link, text, title, config = Parser.getConfig(), accum = []) {
34
+ constructor(link, text, config = Parser.getConfig(), accum = []) {
35
35
  let token;
36
36
  if (text !== undefined) {
37
37
  token = new Token(text, config, true, accum);
@@ -41,7 +41,7 @@ class GalleryImageToken extends singleLine(FileToken) {
41
41
  }
42
42
  accum.splice(accum.indexOf(token), 1);
43
43
  }
44
- super(link, token?.toString(), title, config, accum);
44
+ super(link, token?.toString(), config, accum);
45
45
  this.setAttribute('bracket', false);
46
46
  if (!Object.values(config.img).includes('width')) {
47
47
  this.seal(['size', 'width', 'height'], true);
@@ -87,10 +87,10 @@ class GalleryImageToken extends singleLine(FileToken) {
87
87
  * @override
88
88
  * @param {number} start 起始位置
89
89
  */
90
- lint(start = 0) {
90
+ lint(start = this.getAbsoluteIndex()) {
91
91
  const errors = super.lint(start);
92
92
  if (this.#invalid) {
93
- errors.push(generateForSelf(this, {start}, '无效的图库图片'));
93
+ errors.push(generateForSelf(this, {start}, 'invalid gallery image'));
94
94
  }
95
95
  return errors;
96
96
  }
package/src/link/index.js CHANGED
@@ -4,7 +4,6 @@ const {generateForChild} = require('../../util/lint'),
4
4
  {noWrap} = require('../../util/string'),
5
5
  {undo} = require('../../util/debug'),
6
6
  Parser = require('../..'),
7
- Title = require('../../lib/title'),
8
7
  AstText = require('../../lib/text'),
9
8
  Token = require('..'),
10
9
  AtomToken = require('../atom');
@@ -17,12 +16,12 @@ class LinkToken extends Token {
17
16
  type = 'link';
18
17
  #bracket = true;
19
18
  #delimiter;
20
- #fragment = '';
19
+ #fragment;
21
20
  #encoded = false;
22
21
 
23
22
  /** 完整链接,和FileToken保持一致 */
24
23
  get link() {
25
- return String(this.#getTitle());
24
+ return this.#getTitle();
26
25
  }
27
26
 
28
27
  set link(link) {
@@ -31,7 +30,8 @@ class LinkToken extends Token {
31
30
 
32
31
  /** 是否链接到自身 */
33
32
  get selfLink() {
34
- return !this.#getTitle().title;
33
+ const {title, fragment} = this.#getTitle();
34
+ return !title && Boolean(fragment);
35
35
  }
36
36
 
37
37
  set selfLink(selfLink) {
@@ -46,7 +46,7 @@ class LinkToken extends Token {
46
46
  }
47
47
 
48
48
  set fragment(fragment) {
49
- this.setFragment(fragment);
49
+ this.#setFragment(fragment);
50
50
  }
51
51
 
52
52
  /** interwiki */
@@ -59,7 +59,7 @@ class LinkToken extends Token {
59
59
  this.typeError('set interwiki', 'String');
60
60
  }
61
61
  const {prefix, main, fragment} = this.#getTitle(),
62
- link = `${interwiki}:${prefix}${main}${fragment && '#'}${fragment}`;
62
+ link = `${interwiki}:${prefix}${main}${fragment === undefined ? '' : `#${fragment}`}`;
63
63
  if (interwiki && !this.isInterwiki(link)) {
64
64
  throw new RangeError(`${interwiki} 不是合法的跨维基前缀!`);
65
65
  }
@@ -79,11 +79,10 @@ class LinkToken extends Token {
79
79
  /**
80
80
  * @param {string} link 链接标题
81
81
  * @param {string|undefined} linkText 链接显示文字
82
- * @param {Title} title 链接标题对象
83
82
  * @param {accum} accum
84
83
  * @param {string} delimiter `|`
85
84
  */
86
- constructor(link, linkText, title, config = Parser.getConfig(), accum = [], delimiter = '|') {
85
+ constructor(link, linkText, config = Parser.getConfig(), accum = [], delimiter = '|') {
87
86
  super(undefined, config, true, accum, {
88
87
  AtomToken: 0, Token: 1,
89
88
  });
@@ -191,22 +190,22 @@ class LinkToken extends Token {
191
190
  * @override
192
191
  * @param {number} start 起始位置
193
192
  */
194
- lint(start = 0) {
193
+ lint(start = this.getAbsoluteIndex()) {
195
194
  const errors = super.lint(start),
196
195
  {childNodes: [target, linkText], type: linkType} = this;
197
196
  let rect;
198
- if (this.#encoded && !this.interwiki) {
197
+ if (this.#encoded) {
199
198
  rect = {start, ...this.getRootNode().posFromIndex(start)};
200
- errors.push(generateForChild(target, rect, '内链中不必要的URL编码'));
199
+ errors.push(generateForChild(target, rect, 'unnecessary URL encoding in an internal link'));
201
200
  }
202
201
  if (linkType === 'link' && linkText?.childNodes?.some(
203
202
  /** @param {AstText} */ ({type, data}) => type === 'text' && data.includes('|'),
204
203
  )) {
205
204
  rect ||= {start, ...this.getRootNode().posFromIndex(start)};
206
- errors.push(generateForChild(linkText, rect, '链接文本中多余的"|"', 'warning'));
207
- } else if (linkType !== 'link' && this.#fragment) {
205
+ errors.push(generateForChild(linkText, rect, 'additional "|" in the link text', 'warning'));
206
+ } else if (linkType !== 'link' && this.#fragment !== undefined) {
208
207
  rect ||= {start, ...this.getRootNode().posFromIndex(start)};
209
- errors.push(generateForChild(target, rect, '多余的fragment'));
208
+ errors.push(generateForChild(target, rect, 'useless fragment'));
210
209
  }
211
210
  return errors;
212
211
  }
@@ -286,10 +285,12 @@ class LinkToken extends Token {
286
285
  * @throws `SyntaxError` 非法的fragment
287
286
  */
288
287
  #setFragment(fragment, page = true) {
289
- fragment = String(fragment).replace(/[<>[\]#|=]/gu, p => encodeURIComponent(p));
288
+ fragment &&= String(fragment).replace(/[<>[\]#|=]/gu, p => encodeURIComponent(p));
290
289
  const include = this.getAttribute('include'),
291
290
  config = this.getAttribute('config'),
292
- root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
291
+ root = Parser.parse(`[[${page ? `:${this.name}` : ''}${
292
+ fragment === undefined ? '' : `#${fragment}`
293
+ }]]`, include, 6, config),
293
294
  {length, firstChild: wikiLink} = root,
294
295
  {type, length: linkLength, firstChild} = wikiLink;
295
296
  if (length !== 1 || type !== 'link' || linkLength !== 1) {
@@ -315,8 +316,8 @@ class LinkToken extends Token {
315
316
  * @throws `RangeError` 空fragment
316
317
  */
317
318
  asSelfLink(fragment = this.fragment) {
318
- fragment = String(fragment);
319
- if (!fragment.trim()) {
319
+ fragment &&= String(fragment);
320
+ if (!fragment?.trim()) {
320
321
  throw new RangeError(`${this.constructor.name}.asSelfLink 方法必须指定非空的 fragment!`);
321
322
  }
322
323
  this.#setFragment(fragment, false);
package/src/magicLink.js CHANGED
@@ -43,7 +43,7 @@ class MagicLinkToken extends Token {
43
43
  * @override
44
44
  * @param {number} start 起始位置
45
45
  */
46
- lint(start = 0) {
46
+ lint(start = this.getAbsoluteIndex()) {
47
47
  const errors = super.lint(start),
48
48
  source = `[,;。:!?()]+${this.type === 'ext-link-url' ? '|\\|+' : ''}`;
49
49
  let /** @type {{top: number, left: number}} */ rect;
@@ -63,7 +63,7 @@ class MagicLinkToken extends Token {
63
63
  startCol = (top > 1 ? 0 : refError.startCol) + left;
64
64
  return {
65
65
  ...refError,
66
- message: `URL中的${char === '|' ? '"|"' : '全角标点'}`,
66
+ message: Parser.msg('$1 in URL', char === '|' ? '"|"' : Parser.msg('full-width punctuation')),
67
67
  startIndex,
68
68
  endIndex: startIndex + length,
69
69
  startLine,
@@ -34,10 +34,7 @@ class NestedToken extends Token {
34
34
  }
35
35
  return str;
36
36
  },
37
- )?.replace(/(?<=^|\0\d+[ce]\x7F).*?(?=$|\0\d+[ce]\x7F)/gsu, substr => {
38
- if (substr === '') {
39
- return '';
40
- }
37
+ )?.replace(/(?<=^|\0\d+[ce]\x7F)[^\0]+(?=$|\0\d+[ce]\x7F)/gu, substr => {
41
38
  new NoincludeToken(substr, config, accum);
42
39
  return `\0${accum.length}c\x7F`;
43
40
  });
@@ -51,7 +48,7 @@ class NestedToken extends Token {
51
48
  * @override
52
49
  * @param {number} start 起始位置
53
50
  */
54
- lint(start = 0) {
51
+ lint(start = this.getAbsoluteIndex()) {
55
52
  let rect;
56
53
  return [
57
54
  ...super.lint(start),
@@ -63,7 +60,7 @@ class NestedToken extends Token {
63
60
  return str && !/^<!--.*-->$/u.test(str);
64
61
  }).map(child => {
65
62
  rect ||= {start, ...this.getRootNode().posFromIndex(start)};
66
- return generateForChild(child, rect, `<${this.name}>内的无效内容`);
63
+ return generateForChild(child, rect, Parser.msg('invalid content in <$1>', this.name));
67
64
  }),
68
65
  ];
69
66
  }
@@ -43,8 +43,8 @@ class CommentToken extends hidden(NowikiToken) {
43
43
  * @override
44
44
  * @param {number} start 起始位置
45
45
  */
46
- lint(start = 0) {
47
- return this.closed ? [] : [generateForSelf(this, {start}, '未闭合的HTML注释')];
46
+ lint(start = this.getAbsoluteIndex()) {
47
+ return this.closed ? [] : [generateForSelf(this, {start}, 'unclosed HTML comment')];
48
48
  }
49
49
 
50
50
  /**
@@ -25,10 +25,10 @@ class NowikiToken extends fixedToken(Token) {
25
25
  * @override
26
26
  * @param {number} start 起始位置
27
27
  */
28
- lint(start = 0) {
28
+ lint(start = this.getAbsoluteIndex()) {
29
29
  const {type, name} = this;
30
30
  return type === 'ext-inner' && (name === 'templatestyles' || name === 'section') && String(this)
31
- ? [generateForSelf(this, {start}, `<${name}>标签内不应有任何内容`)]
31
+ ? [generateForSelf(this, {start}, Parser.msg('nothing should be in <$1>', name))]
32
32
  : super.lint(start);
33
33
  }
34
34
 
@@ -26,12 +26,12 @@ class QuoteToken extends NowikiToken {
26
26
  * @this {AstText}
27
27
  * @param {number} start 起始位置
28
28
  */
29
- lint(start = 0) {
29
+ lint(start = this.getAbsoluteIndex()) {
30
30
  const {previousSibling, nextSibling} = this,
31
- message = `孤立的"'"`,
31
+ message = Parser.msg('lonely "$1"', `'`),
32
32
  /** @type {LintError[]} */ errors = [];
33
33
  let refError, wikitext;
34
- if (previousSibling?.type === 'text' && previousSibling.data.at(-1) === `'`) {
34
+ if (previousSibling?.type === 'text' && previousSibling.data.endsWith(`'`)) {
35
35
  refError = generateForSelf(this, {start}, message);
36
36
  wikitext = String(this.getRootNode());
37
37
  const {startIndex: endIndex, startLine: endLine, startCol: endCol} = refError,
@@ -58,16 +58,16 @@ class ParamTagToken extends Token {
58
58
  * @override
59
59
  * @param {number} start 起始位置
60
60
  */
61
- lint(start = 0) {
61
+ lint(start = this.getAbsoluteIndex()) {
62
62
  let /** @type {{top: number, left: number}} */ rect;
63
63
  return this.childNodes.filter(child => {
64
64
  const {childNodes} = child,
65
65
  i = childNodes.findIndex(({type}) => type !== 'text'),
66
66
  str = (i >= 0 ? childNodes.slice(0, i).map(String).join('') : String(child)).trim();
67
- return str && !(i >= 0 ? /^[a-z]+\s*(?:=|$)/iu : /^[a-z]+\s*=/iu).test(str);
67
+ return str && !(i >= 0 ? /^[a-z]+(?:\[\])?\s*(?:=|$)/iu : /^[a-z]+(?:\[\])?\s*=/iu).test(str);
68
68
  }).map(child => {
69
69
  rect ||= {start, ...this.getRootNode().posFromIndex(start)};
70
- return generateForChild(child, rect, `${this.name}的无效参数`);
70
+ return generateForChild(child, rect, Parser.msg('invalid parameter of $1', this.name));
71
71
  });
72
72
  }
73
73
 
package/src/parameter.js CHANGED
@@ -117,13 +117,13 @@ class ParameterToken extends fixedToken(Token) {
117
117
  * @override
118
118
  * @param {number} start 起始位置
119
119
  */
120
- lint(start = 0) {
120
+ lint(start = this.getAbsoluteIndex()) {
121
121
  const errors = super.lint(start),
122
122
  {firstChild, lastChild} = this,
123
123
  link = new RegExp(`https?://${extUrlCharFirst}${extUrlChar}$`, 'iu')
124
124
  .exec(firstChild.toString('comment, noinclude, include'))?.[0];
125
125
  if (link && new URL(link).search) {
126
- const e = generateForChild(firstChild, {start}, '匿名参数中未转义的查询参数');
126
+ const e = generateForChild(firstChild, {start}, 'unescaped query string in an anonymous parameter');
127
127
  errors.push({
128
128
  ...e,
129
129
  startIndex: e.endIndex,
@@ -72,13 +72,12 @@ const format = (cells, attr = {}, multi = false) => {
72
72
  */
73
73
  const fill = (y, rowToken, layout, maxCol, token) => {
74
74
  const rowLayout = layout[y],
75
- {childNodes} = rowToken,
76
- lastIndex = childNodes.findLastIndex(child => child instanceof TdToken && child.subtype !== 'caption') + 1
77
- || undefined;
75
+ lastIndex = rowToken.childNodes.findLastIndex(child => child instanceof TdToken && child.subtype !== 'caption'),
76
+ pos = lastIndex + 1 || undefined;
78
77
  Parser.run(() => {
79
78
  for (let i = 0; i < maxCol; i++) {
80
79
  if (!rowLayout[i]) {
81
- rowToken.insertAt(token.cloneNode(), lastIndex);
80
+ rowToken.insertAt(token.cloneNode(), pos);
82
81
  }
83
82
  }
84
83
  });
@@ -171,12 +170,12 @@ class TableToken extends TrToken {
171
170
  * @override
172
171
  * @param {number} start 起始位置
173
172
  */
174
- lint(start = 0) {
173
+ lint(start = this.getAbsoluteIndex()) {
175
174
  const errors = super.lint(start);
176
175
  if (!this.closed) {
177
176
  const {firstChild, lastChild: tr} = this,
178
177
  {lastChild: td} = tr,
179
- error = generateForChild(firstChild, {start}, '未闭合的表格');
178
+ error = generateForChild(firstChild, {start}, 'unclosed table');
180
179
  errors.push({...error, excerpt: String(td?.type === 'td' ? td : tr).slice(0, 50)});
181
180
  }
182
181
  return errors;
package/src/table/td.js CHANGED
@@ -9,7 +9,7 @@ const {generateForChild} = require('../../util/lint'),
9
9
  TrToken = require('./tr');
10
10
 
11
11
  const aliases = {td: '\n|', th: '\n!', caption: '\n|+'},
12
- openingPattern = /^(?:\n[\S\n]*(?:[|!]|\|\+|\{\{\s*!\s*\}\}\+?)|(?:\||\{\{\s*!\s*\}\}){2}|!!|\{\{\s*!!\s*\}\})$/u;
12
+ openingPattern = /^(?:\n[^\S\n]*(?:[|!]|\|\+|\{\{\s*!\s*\}\}\+?)|(?:\||\{\{\s*!\s*\}\}){2}|!!|\{\{\s*!!\s*\}\})$/u;
13
13
 
14
14
  /**
15
15
  * `<td>`、`<th>`和`<caption>`
@@ -166,13 +166,12 @@ class TdToken extends fixedToken(TrToken) {
166
166
  * @override
167
167
  * @param {number} start 起始位置
168
168
  */
169
- lint(start = 0) {
170
- const errors = super.lint(start),
171
- {lastChild} = this;
169
+ lint(start = this.getAbsoluteIndex()) {
170
+ const errors = super.lint(start);
172
171
  start += this.getRelativeIndex(-1);
173
- for (const child of lastChild.childNodes) {
172
+ for (const child of this.lastChild.childNodes) {
174
173
  if (child.type === 'text' && child.data.includes('|')) {
175
- errors.push(generateForChild(child, {start}, '表格单元格中多余的"|"', 'warning'));
174
+ errors.push(generateForChild(child, {start}, 'additional "|" in a table cell', 'warning'));
176
175
  }
177
176
  }
178
177
  return errors;
package/src/table/tr.js CHANGED
@@ -55,7 +55,7 @@ class TrToken extends attributeParent(Token, 1) {
55
55
  * @override
56
56
  * @param {number} start 起始位置
57
57
  */
58
- lint(start = 0) {
58
+ lint(start = this.getAbsoluteIndex()) {
59
59
  const TranscludeToken = require('../transclude'),
60
60
  ArgToken = require('../arg');
61
61
  const errors = super.lint(start),
@@ -75,7 +75,7 @@ class TrToken extends attributeParent(Token, 1) {
75
75
  }
76
76
  } catch {}
77
77
  }
78
- const error = generateForChild(inter, {start}, '将被移出表格的内容');
78
+ const error = generateForChild(inter, {start}, 'content to be moved out from the table');
79
79
  errors.push({
80
80
  ...error,
81
81
  startIndex: error.startIndex + 1,
@@ -207,7 +207,7 @@ class TrToken extends attributeParent(Token, 1) {
207
207
  getRowCount() {
208
208
  const TdToken = require('./td');
209
209
  return Number(this.childNodes.some(
210
- child => child instanceof TdToken && child.isIndependent() && child.firstChild.text().at(-1) !== '+',
210
+ child => child instanceof TdToken && child.isIndependent() && !child.firstChild.text().endsWith('+'),
211
211
  ));
212
212
  }
213
213
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  const {generateForSelf} = require('../../util/lint'),
4
4
  attributeParent = require('../../mixin/attributeParent'),
5
- path = require('path'),
6
5
  Parser = require('../..'),
7
6
  Token = require('..'),
8
7
  TagPairToken = require('.'),
@@ -65,12 +64,19 @@ class ExtToken extends attributeParent(TagPairToken) {
65
64
  innerToken = new CharinsertToken(inner, newConfig, accum);
66
65
  break;
67
66
  }
68
- case 'references':
69
- case 'choose':
67
+ case 'references': {
68
+ const ReferencesToken = require('../nested/references');
69
+ innerToken = new ReferencesToken(inner, newConfig, accum);
70
+ break;
71
+ }
72
+ case 'choose': {
73
+ const ChooseToken = require('../nested/choose');
74
+ innerToken = new ChooseToken(inner, newConfig, accum);
75
+ break;
76
+ }
70
77
  case 'combobox': {
71
- const NestedToken = require('../nested'),
72
- /** @type {typeof NestedToken} */ NestedExtToken = require(path.join('..', 'nested', lcName));
73
- innerToken = new NestedExtToken(inner, newConfig, accum);
78
+ const ComboboxToken = require('../nested/combobox');
79
+ innerToken = new ComboboxToken(inner, newConfig, accum);
74
80
  break;
75
81
  }
76
82
  case 'imagemap': {
@@ -113,13 +119,13 @@ class ExtToken extends attributeParent(TagPairToken) {
113
119
  * @override
114
120
  * @param {number} start 起始位置
115
121
  */
116
- lint(start = 0) {
122
+ lint(start = this.getAbsoluteIndex()) {
117
123
  const errors = super.lint(start);
118
124
  if (this.name !== 'nowiki' && this.closest('html-attrs, table-attrs')) {
119
125
  const root = this.getRootNode(),
120
126
  excerpt = String(root).slice(Math.max(0, start - 25), start + 25),
121
127
  rect = {start, ...root.posFromIndex(start)};
122
- errors.push({...generateForSelf(this, rect, 'HTML标签属性中的扩展标签'), excerpt});
128
+ errors.push({...generateForSelf(this, rect, 'extension tag in HTML tag attributes'), excerpt});
123
129
  }
124
130
  return errors;
125
131
  }