wikiparser-node 0.2.3 → 0.3.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 (53) hide show
  1. package/.eslintrc.json +98 -8
  2. package/config/default.json +45 -13
  3. package/config/llwiki.json +11 -11
  4. package/config/moegirl.json +44 -12
  5. package/index.js +11 -11
  6. package/lib/element.js +8 -8
  7. package/lib/node.js +2 -2
  8. package/lib/ranges.js +1 -1
  9. package/lib/title.js +7 -3
  10. package/mixin/attributeParent.js +2 -2
  11. package/mixin/fixedToken.js +1 -1
  12. package/mixin/hidden.js +1 -1
  13. package/mixin/sol.js +2 -2
  14. package/package.json +6 -3
  15. package/parser/brackets.js +11 -6
  16. package/parser/commentAndExt.js +9 -9
  17. package/parser/converter.js +5 -5
  18. package/parser/externalLinks.js +4 -4
  19. package/parser/hrAndDoubleUnderscore.js +4 -4
  20. package/parser/html.js +4 -4
  21. package/parser/links.js +9 -9
  22. package/parser/list.js +7 -7
  23. package/parser/magicLinks.js +5 -5
  24. package/parser/quotes.js +3 -3
  25. package/parser/table.js +8 -8
  26. package/src/attribute.js +5 -5
  27. package/src/converterFlags.js +6 -6
  28. package/src/converterRule.js +1 -1
  29. package/src/extLink.js +2 -1
  30. package/src/gallery.js +59 -11
  31. package/src/heading.js +1 -1
  32. package/src/imageParameter.js +5 -5
  33. package/src/index.js +7 -7
  34. package/src/link/category.js +1 -1
  35. package/src/link/file.js +1 -1
  36. package/src/link/galleryImage.js +47 -0
  37. package/src/link/index.js +15 -14
  38. package/src/magicLink.js +14 -4
  39. package/src/nowiki/dd.js +1 -1
  40. package/src/syntax.js +3 -0
  41. package/src/table/index.js +7 -7
  42. package/src/table/td.js +18 -15
  43. package/src/table/tr.js +4 -4
  44. package/src/tagPair/ext.js +11 -3
  45. package/src/transclude.js +9 -7
  46. package/util/debug.js +1 -1
  47. package/util/string.js +7 -7
  48. package/errors/2022-12-07T10:07:09.577Z +0 -1
  49. package/errors/2022-12-07T10:07:09.577Z.err +0 -11
  50. package/errors/2022-12-07T10:07:09.577Z.json +0 -5
  51. package/errors/2022-12-07T10:22:31.325Z +0 -1
  52. package/errors/2022-12-07T10:22:31.325Z.err +0 -11
  53. package/errors/2022-12-07T10:22:31.325Z.json +0 -5
package/src/gallery.js CHANGED
@@ -1,30 +1,78 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('..'),
3
+ const {text} = require('../util/string'),
4
+ /** @type {Parser} */ Parser = require('..'),
4
5
  Token = require('.'),
5
- FileToken = require('./link/file');
6
+ GalleryImageToken = require('./link/galleryImage');
6
7
 
7
8
  /**
8
- * 扩展标签
9
- * @classdesc `{childNodes: [...FileToken]}`
9
+ * gallery标签
10
+ * @classdesc `{childNodes: (string|FileToken)[]]}`
10
11
  */
11
12
  class GalleryToken extends Token {
12
13
  type = 'ext-inner';
13
14
  name = 'gallery';
14
15
 
15
- /** @param {accum} accum */
16
- constructor(wikitext = '', config = Parser.getConfig(), accum = []) {
17
- super(undefined, config, true, accum, {FileToken: ':'});
18
- for (const line of wikitext.split('\n')) {
19
- const [link, ...text] = line.split('|'),
20
- title = Parser.normalizeTitle(link, 6, false, config);
21
- this.appendChild(new FileToken(link, text.length ? text.join('|') : undefined, title, config, accum));
16
+ /**
17
+ * @param {string} inner
18
+ * @param {accum} accum
19
+ */
20
+ constructor(inner, config = Parser.getConfig(), accum = []) {
21
+ super(undefined, config, true, accum, {String: ':', GalleryImageToken: ':'});
22
+ for (const line of inner?.split('\n') ?? []) {
23
+ const matches = /^([^|]+)(?:\|(.*))?/.exec(line);
24
+ if (!matches) {
25
+ this.appendChild(line);
26
+ continue;
27
+ }
28
+ const [, file, alt] = matches;
29
+ let title;
30
+ try {
31
+ title = this.normalizeTitle(decodeURIComponent(file), 6, true);
32
+ } catch {
33
+ title = this.normalizeTitle(file, 6, true);
34
+ }
35
+ if (!title.valid) {
36
+ this.appendChild(line);
37
+ } else {
38
+ this.appendChild(new GalleryImageToken(file, alt, title, config, accum));
39
+ }
22
40
  }
23
41
  }
24
42
 
43
+ cloneNode() {
44
+ const cloned = this.cloneChildren(),
45
+ token = Parser.run(() => new GalleryToken(undefined, this.getAttribute('config')));
46
+ token.append(...cloned);
47
+ return token;
48
+ }
49
+
25
50
  toString() {
26
51
  return super.toString('\n');
27
52
  }
53
+
54
+ getGaps() {
55
+ return 1;
56
+ }
57
+
58
+ text() {
59
+ return text(this.children, '\n');
60
+ }
61
+
62
+ /** @param {string} file */
63
+ insertImage(file, i = this.childNodes.length) {
64
+ let title;
65
+ try {
66
+ title = this.normalizeTitle(decodeURIComponent(file), 6, true);
67
+ } catch {
68
+ title = this.normalizeTitle(file, 6, true);
69
+ }
70
+ if (!title.valid) {
71
+ throw new SyntaxError(`非法的文件名:${file}`);
72
+ }
73
+ const token = Parser.run(() => new GalleryImageToken(file, undefined, title, this.getAttribute('config')));
74
+ return this.insertAt(token, i);
75
+ }
28
76
  }
29
77
 
30
78
  Parser.classes.GalleryToken = __filename;
package/src/heading.js CHANGED
@@ -7,7 +7,7 @@ const fixedToken = require('../mixin/fixedToken'),
7
7
 
8
8
  /**
9
9
  * 章节标题
10
- * @classdesc `{childNodes: [Token, HiddenToken]}`
10
+ * @classdesc `{childNodes: [Token, SyntaxToken]}`
11
11
  */
12
12
  class HeadingToken extends fixedToken(sol(Token)) {
13
13
  type = 'heading';
@@ -22,7 +22,7 @@ class ImageParameterToken extends Token {
22
22
  * @returns {T extends 'link' ? string|Symbol : boolean}
23
23
  */
24
24
  static #validate(key, value, config = Parser.getConfig()) {
25
- value = value.replace(/\x00\d+t\x7f/g, '').trim();
25
+ value = value.replace(/\0\d+t\x7f/g, '').trim();
26
26
  if (key === 'width') {
27
27
  return /^\d*(?:x\d*)?$/.test(value);
28
28
  } else if (['alt', 'class', 'manualthumb', 'frameless', 'framed', 'thumbnail'].includes(key)) {
@@ -31,11 +31,11 @@ class ImageParameterToken extends Token {
31
31
  if (!value) {
32
32
  return this.#noLink;
33
33
  }
34
- const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\x00\\d+t\x7f|$)`, 'ui');
34
+ const regex = RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\0\\d+t\x7f|$)`, 'iu');
35
35
  if (regex.test(value)) {
36
36
  return value;
37
37
  }
38
- if (/^\[\[.+]]$/.test(value)) {
38
+ if (/^\[\[.+\]\]$/.test(value)) {
39
39
  value = value.slice(2, -2);
40
40
  }
41
41
  if (value.includes('%')) {
@@ -106,11 +106,11 @@ class ImageParameterToken extends Token {
106
106
  constructor(str, config = Parser.getConfig(), accum = []) {
107
107
  const regexes = Object.entries(config.img).map(
108
108
  /** @returns {[string, string, RegExp]} */
109
- ([syntax, param]) => [syntax, param, new RegExp(`^(\\s*)${syntax.replace('$1', '(.*)')}(\\s*)$`)],
109
+ ([syntax, param]) => [syntax, param, RegExp(`^(\\s*)${syntax.replace('$1', '(.*)')}(\\s*)$`)],
110
110
  ),
111
111
  param = regexes.find(([,, regex]) => regex.test(str));
112
112
  if (param) {
113
- const mt = str.match(param[2]);
113
+ const mt = param[2].exec(str);
114
114
  if (mt.length === 4 && !ImageParameterToken.#validate(param[1], mt[2], config)) {
115
115
  // pass
116
116
  } else {
package/src/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  /*
4
4
  * PHP解析器的步骤:
5
5
  * -1. 替换签名和`{{subst:}}`,参见Parser::preSaveTransform;这在revision中不可能保留,可以跳过
6
- * 0. 移除特定字符`\x00`和`\x7f`,参见Parser::parse
6
+ * 0. 移除特定字符`\0`和`\x7f`,参见Parser::parse
7
7
  * 1. 注释/扩展标签('<'相关),参见Preprocessor_Hash::buildDomTreeArrayFromText和Sanitizer::decodeTagAttributes
8
8
  * 2. 模板/模板变量/标题,注意rightmost法则,以及`-{`和`[[`可以破坏`{{`或`{{{`语法,
9
9
  * 参见Preprocessor_Hash::buildDomTreeArrayFromText
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  /*
22
- * \x00\d+.\x7f标记Token:
22
+ * \0\d+.\x7f标记Token:
23
23
  * e: ExtToken
24
24
  * c: CommentToken、NoIncludeToken和IncludeToken
25
25
  * !: `{{!}}`专用
@@ -52,7 +52,7 @@ class Token extends AstElement {
52
52
  type = 'root';
53
53
  /** 解析阶段,参见顶部注释。只对plain Token有意义。 */ #stage = 0;
54
54
  #config;
55
- /** 这个数组起两个作用:1. 数组中的Token会在build时替换`/\x00\d+.\x7f/`标记;2. 数组中的Token会依次执行parseOnce和build方法。 */
55
+ /** 这个数组起两个作用:1. 数组中的Token会在build时替换`/\0\d+.\x7f/`标记;2. 数组中的Token会依次执行parseOnce和build方法。 */
56
56
  #accum;
57
57
  /** @type {Record<string, Ranges>} */ #acceptable;
58
58
  #protectedChildren = new Ranges();
@@ -66,7 +66,7 @@ class Token extends AstElement {
66
66
  constructor(wikitext, config = Parser.getConfig(), halfParsed = false, accum = [], acceptable = null) {
67
67
  super();
68
68
  if (typeof wikitext === 'string') {
69
- this.appendChild(halfParsed ? wikitext : wikitext.replace(/[\x00\x7f]/g, ''));
69
+ this.appendChild(halfParsed ? wikitext : wikitext.replace(/[\0\x7f]/g, ''));
70
70
  }
71
71
  this.#config = config;
72
72
  this.#accum = accum;
@@ -474,7 +474,7 @@ class Token extends AstElement {
474
474
  if (!Parser.debugging && externalUse('buildFromStr')) {
475
475
  this.debugOnly('buildFromStr');
476
476
  }
477
- return str.split(/[\x00\x7f]/).map((s, i) => {
477
+ return str.split(/[\0\x7f]/).map((s, i) => {
478
478
  if (i % 2 === 0) {
479
479
  return s;
480
480
  } else if (!isNaN(s.at(-1))) {
@@ -494,7 +494,7 @@ class Token extends AstElement {
494
494
  }
495
495
  this.#stage = MAX_STAGE;
496
496
  const {childNodes: {length}, firstChild} = this;
497
- if (length !== 1 || typeof firstChild !== 'string' || !firstChild.includes('\x00')) {
497
+ if (length !== 1 || typeof firstChild !== 'string' || !firstChild.includes('\0')) {
498
498
  return this;
499
499
  }
500
500
  this.replaceChildren(...this.buildFromStr(firstChild));
@@ -556,7 +556,7 @@ class Token extends AstElement {
556
556
  if (table instanceof TableToken && table.type !== 'td') {
557
557
  table.normalize();
558
558
  const [, child] = table.childNodes;
559
- if (typeof child === 'string' && child.includes('\x00')) {
559
+ if (typeof child === 'string' && child.includes('\0')) {
560
560
  table.removeAt(1);
561
561
  const inner = new Token(child, this.#config, true, this.#accum);
562
562
  table.insertAt(inner, 1);
@@ -2,7 +2,7 @@
2
2
 
3
3
  const /** @type {Parser} */ Parser = require('../..'),
4
4
  LinkToken = require('.'),
5
- Token = require('..'); // eslint-disable-line no-unused-vars
5
+ Token = require('..');
6
6
 
7
7
  /**
8
8
  * 分类
package/src/link/file.js CHANGED
@@ -64,7 +64,7 @@ class FileToken extends LinkToken {
64
64
  super(link, undefined, title, config, accum);
65
65
  this.setAttribute('acceptable', {AtomToken: 0, ImageParameterToken: '1:'});
66
66
  this.append(...explode('-{', '}-', '|', text).map(part => new ImageParameterToken(part, config, accum)));
67
- this.seal(['setFragment', 'asSelfLink', 'setLinkText', 'pipeTrick']);
67
+ this.seal(['setLangLink', 'setFragment', 'asSelfLink', 'setLinkText', 'pipeTrick']);
68
68
  }
69
69
 
70
70
  /**
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('../..'),
4
+ Token = require('..'),
5
+ FileToken = require('./file');
6
+
7
+ /**
8
+ * 图片
9
+ * @classdesc `{childNodes: [AtomToken, ...ImageParameterToken]}`
10
+ */
11
+ class GalleryImageToken extends FileToken {
12
+ type = 'gallery-image';
13
+
14
+ size = undefined;
15
+ width = undefined;
16
+ height = undefined;
17
+
18
+ /**
19
+ * @param {string} link
20
+ * @param {string|undefined} text
21
+ * @param {Title} title
22
+ * @param {accum} accum
23
+ */
24
+ constructor(link, text, title, config = Parser.getConfig(), accum = []) {
25
+ let token;
26
+ if (text !== undefined) {
27
+ token = new Token(text, config, true, accum);
28
+ token.type = 'temp';
29
+ token.setAttribute('stage', 1);
30
+ for (let n = 1; n < Parser.MAX_STAGE; n++) {
31
+ token.parseOnce();
32
+ }
33
+ accum.splice(accum.indexOf(token), 1);
34
+ }
35
+ const newConfig = structuredClone(config);
36
+ newConfig.img = Object.fromEntries(Object.entries(config.img).filter(([, param]) => param !== 'width'));
37
+ super(link, token?.toString(), title, newConfig, accum);
38
+ this.seal(['size', 'width', 'height']);
39
+ }
40
+
41
+ getPadding() {
42
+ return 0;
43
+ }
44
+ }
45
+
46
+ Parser.classes.GalleryImageToken = __filename;
47
+ module.exports = GalleryImageToken;
package/src/link/index.js CHANGED
@@ -47,16 +47,16 @@ class LinkToken extends Token {
47
47
  title: this.name, interwiki: this.interwiki, fragment: this.fragment,
48
48
  }, this.getAttribute('config'));
49
49
  token.firstElementChild.safeReplaceWith(link);
50
- token.appendChild(...linkText);
50
+ token.append(...linkText);
51
51
  return token.afterBuild();
52
52
  });
53
53
  }
54
54
 
55
55
  afterBuild() {
56
- if (this.name.includes('\x00')) {
56
+ if (this.name.includes('\0')) {
57
57
  this.setAttribute('name', text(this.buildFromStr(this.name)));
58
58
  }
59
- if (this.fragment.includes('\x00')) {
59
+ if (this.fragment.includes('\0')) {
60
60
  this.setAttribute('fragment', text(this.buildFromStr(this.fragment)));
61
61
  }
62
62
  const that = this;
@@ -91,7 +91,7 @@ class LinkToken extends Token {
91
91
 
92
92
  toString() {
93
93
  const str = super.toString('|');
94
- return this.parentElement?.matches('ext-inner#gallery') ? str : `[[${str}]]`;
94
+ return this.type === 'gallery-image' ? str : `[[${str}]]`;
95
95
  }
96
96
 
97
97
  getPadding() {
@@ -103,19 +103,20 @@ class LinkToken extends Token {
103
103
  }
104
104
 
105
105
  text() {
106
- return `[[${super.text('|')}]]`;
106
+ const str = super.text('|');
107
+ return this.type === 'gallery-image' ? str : `[[${str}]]`;
107
108
  }
108
109
 
109
110
  /** @param {string} link */
110
111
  setTarget(link) {
111
112
  link = String(link);
112
- if (!/^\s*[:#]/.test(link)) {
113
+ if (link.type === 'link' && !/^\s*[:#]/.test(link)) {
113
114
  link = `:${link}`;
114
115
  }
115
116
  const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
116
117
  {childNodes: {length}, firstElementChild} = root;
117
118
  if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 1) {
118
- const msgs = {link: '内链', file: '文件链接', category: '分类'};
119
+ const msgs = {link: '内链', file: '文件链接', category: '分类', 'gallery-image': '文件链接'};
119
120
  throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
120
121
  }
121
122
  const {firstChild} = firstElementChild;
@@ -153,7 +154,7 @@ class LinkToken extends Token {
153
154
 
154
155
  /** @param {string} fragment */
155
156
  #setFragment(fragment, page = true) {
156
- fragment = String(fragment).replace(/[<>[]#|=!]/g, p => encodeURIComponent(p));
157
+ fragment = String(fragment).replace(/[<>[]#|=!\]/g, p => encodeURIComponent(p));
157
158
  const include = this.getAttribute('include'),
158
159
  config = this.getAttribute('config'),
159
160
  root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
@@ -212,19 +213,19 @@ class LinkToken extends Token {
212
213
  if (/[#%]/.test(linkText)) {
213
214
  throw new Error('Pipe trick 不能用于带有"#"或"%"的场合!');
214
215
  }
215
- const m1 = linkText.match(/^:?(?:[ \w\x80-\xff-]+:)?(.+?) ?\(.+\)$/);
216
+ const m1 = /^:?(?:[ \w\x80-\xff-]+:)?([^(]+)\(.+\)$/.exec(linkText);
216
217
  if (m1) {
217
- this.setLinkText(m1[1]);
218
+ this.setLinkText(m1[1].trim());
218
219
  return;
219
220
  }
220
- const m2 = linkText.match(/^:?(?:[ \w\x80-\xff-]+:)?(.+?) ?(.+)$/);
221
+ const m2 = /^:?(?:[ \w\x80-\xff-]+:)?([^(]+)(.+)$/.exec(linkText);
221
222
  if (m2) {
222
- this.setLinkText(m2[1]);
223
+ this.setLinkText(m2[1].trim());
223
224
  return;
224
225
  }
225
- const m3 = linkText.match(/^:?(?:[ \w\x80-\xff-]+:)?(.+?)(?: ?\(.+\))?(?:, |,|، ).+/);
226
+ const m3 = /^:?(?:[ \w\x80-\xff-]+:)?(.+?)(?:(?<!\()\(.+\))?(?:, |,|، )./.exec(linkText);
226
227
  if (m3) {
227
- this.setLinkText(m3[1]);
228
+ this.setLinkText(m3[1].trim());
228
229
  return;
229
230
  }
230
231
  this.setLinkText(linkText);
package/src/magicLink.js CHANGED
@@ -12,13 +12,13 @@ class MagicLinkToken extends Token {
12
12
  #protocolRegex;
13
13
 
14
14
  get protocol() {
15
- return this.text().match(this.#protocolRegex)?.[0];
15
+ return this.#protocolRegex.exec(this.text())?.[0];
16
16
  }
17
17
  set protocol(value) {
18
18
  if (typeof value !== 'string') {
19
19
  this.typeError('protocol', 'String');
20
20
  }
21
- if (!new RegExp(`${this.#protocolRegex.source}$`, 'i').test(value)) {
21
+ if (!RegExp(`${this.#protocolRegex.source}$`, 'i').test(value)) {
22
22
  throw new RangeError(`非法的外链协议:${value}`);
23
23
  }
24
24
  this.replaceChildren(this.text().replace(this.#protocolRegex, value));
@@ -33,11 +33,11 @@ class MagicLinkToken extends Token {
33
33
  if (doubleSlash) {
34
34
  this.type = 'ext-link-url';
35
35
  }
36
- this.#protocolRegex = new RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, 'i');
36
+ this.#protocolRegex = RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, 'i');
37
37
  }
38
38
 
39
39
  afterBuild() {
40
- const ParameterToken = require('./parameter'), // eslint-disable-line no-unused-vars
40
+ const ParameterToken = require('./parameter'),
41
41
  /** @type {ParameterToken} */ parameter = this.closest('parameter');
42
42
  if (parameter?.getValue() === this.text()) {
43
43
  this.replaceWith(this.toString());
@@ -45,6 +45,16 @@ class MagicLinkToken extends Token {
45
45
  return this;
46
46
  }
47
47
 
48
+ cloneNode() {
49
+ const cloned = this.cloneChildren(),
50
+ token = Parser.run(() => new MagicLinkToken(
51
+ undefined, this.type === 'ext-link-url', this.getAttribute('config'),
52
+ ));
53
+ token.append(...cloned);
54
+ token.afterBuild();
55
+ return token;
56
+ }
57
+
48
58
  getUrl() {
49
59
  let url = this.text();
50
60
  if (url.startsWith('//')) {
package/src/nowiki/dd.js CHANGED
@@ -32,7 +32,7 @@ class DdToken extends NowikiToken {
32
32
  /** @param {string} str */
33
33
  setText(str) {
34
34
  const src = this.type === 'dd' ? ':' : ';:*#';
35
- if (new RegExp(`[^${src}]`).test(str)) {
35
+ if (RegExp(`[^${src}]`).test(str)) {
36
36
  throw new RangeError(`${this.constructor.name} 仅能包含${src.split('').map(c => `"${c}"`).join('、')}!`);
37
37
  }
38
38
  this.#update(str);
package/src/syntax.js CHANGED
@@ -19,6 +19,9 @@ class SyntaxToken extends Token {
19
19
  * @param {acceptable} acceptable
20
20
  */
21
21
  constructor(wikitext, pattern, type = 'plain', config = Parser.getConfig(), accum = [], acceptable = null) {
22
+ if (pattern.global) {
23
+ throw new RangeError(`SyntaxToken 的语法正则不能含有 g 修饰符:${pattern}`);
24
+ }
22
25
  super(wikitext, config, true, accum, acceptable);
23
26
  this.type = type;
24
27
  this.#pattern = pattern;
@@ -3,19 +3,19 @@
3
3
  const assert = require('assert/strict'),
4
4
  {noWrap} = require('../../util/string'),
5
5
  /** @type {Parser} */ Parser = require('../..'),
6
- Token = require('..'), // eslint-disable-line no-unused-vars
6
+ Token = require('..'),
7
7
  TrToken = require('./tr'),
8
8
  TdToken = require('./td'),
9
9
  SyntaxToken = require('../syntax'),
10
- AttributeToken = require('../attribute'); // eslint-disable-line no-unused-vars
10
+ AttributeToken = require('../attribute');
11
11
 
12
12
  /**
13
13
  * @param {TableCoords} coords1
14
14
  * @param {TableCoords} coords2
15
15
  */
16
16
  const cmpCoords = (coords1, coords2) => {
17
- const diff = coords1?.row - coords2?.row;
18
- return diff === 0 ? coords1?.column - coords2?.column : diff;
17
+ const diff = coords1.row - coords2.row;
18
+ return diff === 0 ? coords1.column - coords2.column : diff;
19
19
  };
20
20
  const isRowEnd = /** @param {Token} */ ({type}) => ['tr', 'table-syntax'].includes(type);
21
21
 
@@ -55,8 +55,8 @@ class Layout extends Array {
55
55
  class TableToken extends TrToken {
56
56
  type = 'table';
57
57
 
58
- static openingPattern = /^(?:{\||{{{\s*!\s*}}|{{\s*\(!\s*}})$/;
59
- static closingPattern = /^\n[^\S\n]*(?:\|}|{{\s*!\s*}}}|{{\s*!\)\s*}})$/;
58
+ static openingPattern = /^(?:\{\||\{\{\{\s*!\s*\}\}|\{\{\s*\(!\s*\}\})$/;
59
+ static closingPattern = /^\n[^\S\n]*(?:\|\}|\{\{\s*!\s*\}\}\}|\{\{\s*!\)\s*\}\})$/;
60
60
 
61
61
  /**
62
62
  * @param {string} syntax
@@ -192,7 +192,7 @@ class TableToken extends TrToken {
192
192
  {length} = rows,
193
193
  /** @type {Layout} */ layout = new Layout(length).fill().map(() => []);
194
194
  for (const [i, rowToken] of rows.entries()) {
195
- if (i > stop.row ?? stop.y) {
195
+ if (i > (stop.row ?? stop.y)) {
196
196
  break;
197
197
  }
198
198
  const rowLayout = layout[i];
package/src/table/td.js CHANGED
@@ -45,7 +45,7 @@ class TdToken extends fixedToken(TrToken) {
45
45
  */
46
46
  getSyntax() {
47
47
  const syntax = this.firstElementChild.text(),
48
- escape = syntax.includes('{{');
48
+ esc = syntax.includes('{{');
49
49
  let subtype = 'td';
50
50
  if (syntax.endsWith('!')) {
51
51
  subtype = 'th';
@@ -53,14 +53,14 @@ class TdToken extends fixedToken(TrToken) {
53
53
  subtype = 'caption';
54
54
  }
55
55
  if (this.isIndependent()) {
56
- return {subtype, escape, correction: false};
56
+ return {subtype, escape: esc, correction: false};
57
57
  }
58
58
  const {previousElementSibling} = this;
59
59
  if (previousElementSibling?.type !== 'td') {
60
- return {subtype, escape, correction: true};
60
+ return {subtype, escape: esc, correction: true};
61
61
  }
62
62
  const result = previousElementSibling.getSyntax();
63
- result.escape ||= escape;
63
+ result.escape ||= esc;
64
64
  result.correction = previousElementSibling.lastElementChild.offsetHeight > 1;
65
65
  if (subtype === 'th' && result.subtype !== 'th') {
66
66
  result.subtype = 'th';
@@ -69,7 +69,8 @@ class TdToken extends fixedToken(TrToken) {
69
69
  return result;
70
70
  }
71
71
 
72
- static openingPattern = /^(?:\n[\S\n]*(?:[|!]|\|\+|{{\s*!\s*}}\+?)|(?:\||{{\s*!\s*}}){2}|!!|{{\s*!!\s*}})$/;
72
+ static openingPattern
73
+ = /^(?:\n[\S\n]*(?:[|!]|\|\+|\{\{\s*!\s*\}\}\+?)|(?:\||\{\{\s*!\s*\}\}){2}|!!|\{\{\s*!!\s*\}\})$/;
73
74
 
74
75
  getRowCount = undefined;
75
76
  getNthCol = undefined;
@@ -81,9 +82,9 @@ class TdToken extends fixedToken(TrToken) {
81
82
  * @param {accum} accum
82
83
  */
83
84
  constructor(syntax, inner, config = Parser.getConfig(), accum = []) {
84
- let innerSyntax = inner?.match(/\||\x00\d+!\x7f/),
85
+ let innerSyntax = /\||\0\d+!\x7f/.exec(inner),
85
86
  attr = innerSyntax ? inner.slice(0, innerSyntax.index) : '';
86
- if (/\[\[|-{/.test(attr)) {
87
+ if (/\[\[|-\{/.test(attr)) {
87
88
  innerSyntax = null;
88
89
  attr = '';
89
90
  }
@@ -91,6 +92,7 @@ class TdToken extends fixedToken(TrToken) {
91
92
  if (innerSyntax) {
92
93
  [this.#innerSyntax] = innerSyntax;
93
94
  }
95
+ // eslint-disable-next-line no-unsafe-optional-chaining
94
96
  const innerToken = new Token(inner?.slice(innerSyntax?.index + this.#innerSyntax.length), config, true, accum);
95
97
  innerToken.type = 'td-inner';
96
98
  this.setAttribute('acceptable', {SyntaxToken: 0, AttributeToken: 1, Token: 2})
@@ -98,7 +100,7 @@ class TdToken extends fixedToken(TrToken) {
98
100
  }
99
101
 
100
102
  cloneNode() {
101
- const token = super.cloneNode();
103
+ const /** @type {TdToken} */ token = super.cloneNode();
102
104
  token.setAttribute('innerSyntax', this.#innerSyntax);
103
105
  return token;
104
106
  }
@@ -141,6 +143,7 @@ class TdToken extends fixedToken(TrToken) {
141
143
  * @template {string} T
142
144
  * @param {T} key
143
145
  * @param {TokenAttribute<T>} value
146
+ * @return {this}
144
147
  */
145
148
  setAttribute(key, value) {
146
149
  if (key !== 'innerSyntax') {
@@ -153,7 +156,7 @@ class TdToken extends fixedToken(TrToken) {
153
156
  }
154
157
 
155
158
  afterBuild() {
156
- if (this.#innerSyntax.includes('\x00')) {
159
+ if (this.#innerSyntax.includes('\0')) {
157
160
  this.#innerSyntax = this.buildFromStr(this.#innerSyntax).map(String).join('');
158
161
  }
159
162
  return this;
@@ -162,8 +165,8 @@ class TdToken extends fixedToken(TrToken) {
162
165
  static #aliases = {td: '\n|', th: '\n!', caption: '\n|+'};
163
166
 
164
167
  /** @param {string} syntax */
165
- setSyntax(syntax, escape = false) {
166
- super.setSyntax(TdToken.#aliases[syntax] ?? syntax, escape);
168
+ setSyntax(syntax, esc = false) {
169
+ super.setSyntax(TdToken.#aliases[syntax] ?? syntax, esc);
167
170
  }
168
171
 
169
172
  /** @complexity `n` */
@@ -171,17 +174,17 @@ class TdToken extends fixedToken(TrToken) {
171
174
  if (this.children[1].toString()) {
172
175
  this.#innerSyntax ||= '|';
173
176
  }
174
- const {subtype, escape, correction} = this.getSyntax();
177
+ const {subtype, escape: esc, correction} = this.getSyntax();
175
178
  if (correction) {
176
- this.setSyntax(subtype, escape);
179
+ this.setSyntax(subtype, esc);
177
180
  }
178
181
  }
179
182
 
180
183
  /** @complexity `n` */
181
184
  independence() {
182
185
  if (!this.isIndependent()) {
183
- const {subtype, escape} = this.getSyntax();
184
- this.setSyntax(subtype, escape);
186
+ const {subtype, escape: esc} = this.getSyntax();
187
+ this.setSyntax(subtype, esc);
185
188
  }
186
189
  }
187
190
 
package/src/table/tr.js CHANGED
@@ -13,7 +13,7 @@ const attributeParent = require('../../mixin/attributeParent'),
13
13
  class TrToken extends attributeParent(Token, 1) {
14
14
  type = 'tr';
15
15
 
16
- static openingPattern = /^\n[^\S\n]*(?:\|-+|{{\s*!\s*}}-+|{{\s*!-\s*}}-*)$/;
16
+ static openingPattern = /^\n[^\S\n]*(?:\|-+|\{\{\s*!\s*\}\}-+|\{\{\s*!-\s*\}\}-*)$/;
17
17
 
18
18
  /**
19
19
  * @param {string} syntax
@@ -93,10 +93,10 @@ class TrToken extends attributeParent(Token, 1) {
93
93
  }
94
94
 
95
95
  /** @param {string} syntax */
96
- setSyntax(syntax, escape = false) {
96
+ setSyntax(syntax, esc = false) {
97
97
  const {firstElementChild} = this;
98
98
  firstElementChild.replaceChildren(syntax);
99
- if (escape) {
99
+ if (esc) {
100
100
  TrToken.escape(firstElementChild);
101
101
  }
102
102
  }
@@ -202,7 +202,7 @@ class TrToken extends attributeParent(Token, 1) {
202
202
  if (n < 0 || n > nCols || n === nCols && !insert) {
203
203
  throw new RangeError(`不存在第 ${n} 个单元格!`);
204
204
  }
205
- const TdToken = require('./td'); // eslint-disable-line no-unused-vars
205
+ const TdToken = require('./td');
206
206
  let last = 0;
207
207
  for (const child of this.children.slice(2)) {
208
208
  if (child instanceof TdToken) {
@@ -23,7 +23,7 @@ class ExtToken extends attributeParent(TagPairToken) {
23
23
  attrToken = new AttributeToken(attr, 'ext-attr', lcName, config, accum),
24
24
  newConfig = structuredClone(config),
25
25
  ext = new Set(newConfig.ext);
26
- let /** @type {acceptable} */ acceptable, innerToken;
26
+ let /** @type {acceptable} */ acceptable, /** @type {Token} */ innerToken;
27
27
  switch (lcName) {
28
28
  case 'choose':
29
29
  ext.add('option');
@@ -42,15 +42,23 @@ class ExtToken extends attributeParent(TagPairToken) {
42
42
  innerToken = new Token(inner, newConfig, false, accum);
43
43
  break;
44
44
  }
45
+ case 'gallery': {
46
+ ext.delete(lcName);
47
+ newConfig.ext = [...ext];
48
+ const GalleryToken = require('../gallery');
49
+ acceptable = {AttributeToken: 0, GalleryToken: 1};
50
+ innerToken = new GalleryToken(inner, newConfig, accum);
51
+ break;
52
+ }
45
53
  /*
46
54
  * 更多定制扩展的代码示例:
47
55
  * ```
48
56
  * case 'extensionName': {
49
- * ext.delete(this.name);
57
+ * ext.delete(lcName);
50
58
  * newConfig.ext = [...ext];
51
59
  * const ExtensionToken = require('../extension');
52
60
  * acceptable = {AttributeToken: 0, ExtensionToken: 1};
53
- * innerToken = new ExtensionToken(extInner, newConfig, false, accum);
61
+ * innerToken = new ExtensionToken(inner, newConfig, accum);
54
62
  * break;
55
63
  * }
56
64
  * ```