wikiparser-node 0.3.0 → 0.4.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 (81) hide show
  1. package/.eslintrc.json +472 -34
  2. package/README.md +1 -1
  3. package/config/default.json +58 -30
  4. package/config/llwiki.json +22 -90
  5. package/config/moegirl.json +51 -13
  6. package/config/zhwiki.json +1269 -0
  7. package/index.js +114 -104
  8. package/lib/element.js +448 -440
  9. package/lib/node.js +335 -115
  10. package/lib/ranges.js +27 -18
  11. package/lib/text.js +146 -0
  12. package/lib/title.js +13 -5
  13. package/mixin/attributeParent.js +70 -24
  14. package/mixin/fixedToken.js +14 -6
  15. package/mixin/hidden.js +6 -4
  16. package/mixin/sol.js +27 -10
  17. package/package.json +9 -3
  18. package/parser/brackets.js +22 -17
  19. package/parser/commentAndExt.js +18 -16
  20. package/parser/converter.js +14 -13
  21. package/parser/externalLinks.js +12 -11
  22. package/parser/hrAndDoubleUnderscore.js +23 -14
  23. package/parser/html.js +10 -9
  24. package/parser/links.js +15 -14
  25. package/parser/list.js +12 -11
  26. package/parser/magicLinks.js +12 -11
  27. package/parser/quotes.js +6 -5
  28. package/parser/selector.js +175 -0
  29. package/parser/table.js +25 -18
  30. package/printed/example.json +120 -0
  31. package/src/arg.js +56 -32
  32. package/src/atom/hidden.js +5 -2
  33. package/src/atom/index.js +17 -9
  34. package/src/attribute.js +182 -100
  35. package/src/converter.js +68 -41
  36. package/src/converterFlags.js +67 -45
  37. package/src/converterRule.js +117 -65
  38. package/src/extLink.js +66 -18
  39. package/src/gallery.js +42 -15
  40. package/src/heading.js +34 -15
  41. package/src/html.js +97 -35
  42. package/src/imageParameter.js +83 -54
  43. package/src/index.js +299 -178
  44. package/src/link/category.js +20 -52
  45. package/src/link/file.js +59 -28
  46. package/src/link/galleryImage.js +21 -7
  47. package/src/link/index.js +146 -60
  48. package/src/magicLink.js +34 -12
  49. package/src/nowiki/comment.js +22 -10
  50. package/src/nowiki/dd.js +37 -22
  51. package/src/nowiki/doubleUnderscore.js +16 -7
  52. package/src/nowiki/hr.js +11 -7
  53. package/src/nowiki/index.js +16 -9
  54. package/src/nowiki/list.js +2 -2
  55. package/src/nowiki/noinclude.js +8 -4
  56. package/src/nowiki/quote.js +11 -7
  57. package/src/onlyinclude.js +19 -7
  58. package/src/parameter.js +65 -38
  59. package/src/syntax.js +26 -20
  60. package/src/table/index.js +260 -165
  61. package/src/table/td.js +98 -52
  62. package/src/table/tr.js +102 -58
  63. package/src/tagPair/ext.js +27 -19
  64. package/src/tagPair/include.js +16 -11
  65. package/src/tagPair/index.js +64 -29
  66. package/src/transclude.js +170 -93
  67. package/test/api.js +83 -0
  68. package/test/real.js +133 -0
  69. package/test/test.js +28 -0
  70. package/test/util.js +80 -0
  71. package/tool/index.js +41 -31
  72. package/typings/api.d.ts +13 -0
  73. package/typings/array.d.ts +28 -0
  74. package/typings/event.d.ts +24 -0
  75. package/typings/index.d.ts +46 -4
  76. package/typings/node.d.ts +15 -9
  77. package/typings/parser.d.ts +7 -0
  78. package/typings/tool.d.ts +3 -2
  79. package/util/debug.js +21 -18
  80. package/util/string.js +40 -27
  81. package/typings/element.d.ts +0 -28
@@ -1,33 +1,40 @@
1
1
  'use strict';
2
2
 
3
3
  const fixedToken = require('../../mixin/fixedToken'),
4
- /** @type {Parser} */ Parser = require('../..'),
5
- Token = require('..');
4
+ Parser = require('../..'),
5
+ Token = require('..'),
6
+ AstText = require('../../lib/text');
6
7
 
7
8
  /**
8
9
  * 纯文字Token,不会被解析
9
- * @classdesc `{childNodes: [string]}`
10
+ * @classdesc `{childNodes: [AstText]}`
10
11
  */
11
12
  class NowikiToken extends fixedToken(Token) {
12
13
  type = 'ext-inner';
13
14
 
14
15
  /**
15
- * @param {string} wikitext
16
+ * @param {string} wikitext wikitext
16
17
  * @param {accum} accum
17
18
  */
18
19
  constructor(wikitext, config = Parser.getConfig(), accum = []) {
19
20
  super(wikitext, config, true, accum);
20
21
  }
21
22
 
22
- /** @this {NowikiToken & {firstChild: string}} */
23
+ /**
24
+ * @override
25
+ * @this {NowikiToken & {firstChild: AstText, constructor: typeof NowikiToken}}
26
+ */
23
27
  cloneNode() {
24
- const /** @type {typeof NowikiToken} */ Constructor = this.constructor,
25
- token = Parser.run(() => new Constructor(this.firstChild, this.getAttribute('config')));
26
- token.type = this.type;
28
+ const {constructor, firstChild: {data}, type} = this,
29
+ token = Parser.run(() => new constructor(data, this.getAttribute('config')));
30
+ token.type = type;
27
31
  return token;
28
32
  }
29
33
 
30
- /** @param {string} str */
34
+ /**
35
+ * @override
36
+ * @param {string} str 新文本
37
+ */
31
38
  setText(str) {
32
39
  return super.setText(str, 0);
33
40
  }
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  const sol = require('../../mixin/sol'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  DdToken = require('./dd');
6
6
 
7
7
  /**
8
8
  * ;:*#
9
- * @classdesc `{childNodes: [string]}`
9
+ * @classdesc `{childNodes: [AstText]}`
10
10
  */
11
11
  class ListToken extends sol(DdToken) {
12
12
  type = 'list';
@@ -1,19 +1,23 @@
1
1
  'use strict';
2
2
 
3
3
  const hidden = require('../../mixin/hidden'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  NowikiToken = require('.');
6
6
 
7
7
  /**
8
8
  * `<noinclude>`和`</noinclude>`,不可进行任何更改
9
- * @classdesc `{childNodes: [string]}`
9
+ * @classdesc `{childNodes: [AstText]}`
10
10
  */
11
11
  class NoincludeToken extends hidden(NowikiToken) {
12
12
  type = 'noinclude';
13
13
 
14
- /** @param {string} str */
14
+ /**
15
+ * @override
16
+ * @param {string} str 新文本
17
+ * @throws `Error` 不可更改
18
+ */
15
19
  setText(str) {
16
- if (/^<\/?(?:(?:no|only)include|includeonly)(?:\s.*)?\/?>$/is.test(this.toString())) {
20
+ if (/^<\/?(?:(?:no|only)include|includeonly)(?:\s.*)?\/?>$/isu.test(String(this))) {
17
21
  throw new Error(`${this.constructor.name} 不可更改文字内容!`);
18
22
  }
19
23
  return super.setText(str);
@@ -1,17 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('../..'),
3
+ const Parser = require('../..'),
4
4
  NowikiToken = require('.');
5
5
 
6
6
  /**
7
7
  * `<hr>`
8
- * @classdesc `{childNodes: [string]}`
8
+ * @classdesc `{childNodes: [AstText]}`
9
9
  */
10
10
  class QuoteToken extends NowikiToken {
11
11
  type = 'quote';
12
12
 
13
13
  /**
14
- * @param {number} n
14
+ * @param {number} n 字符串长度
15
15
  * @param {accum} accum
16
16
  */
17
17
  constructor(n, config = Parser.getConfig(), accum = []) {
@@ -19,12 +19,16 @@ class QuoteToken extends NowikiToken {
19
19
  this.setAttribute('name', String(n));
20
20
  }
21
21
 
22
- /** @param {string} str */
22
+ /**
23
+ * @override
24
+ * @param {string} str 新文本
25
+ * @throws `RangeError` 错误的单引号语法
26
+ */
23
27
  setText(str) {
24
- if (!["''", "'''", "'''''"].includes(str)) {
25
- throw new RangeError(`${this.constructor.name} 的内部文本只能为连续 2/3/5 个"'"!`);
28
+ if (str === "''" || str === "'''" || str === "'''''") {
29
+ return super.setText(str);
26
30
  }
27
- return super.setText(str);
31
+ throw new RangeError(`${this.constructor.name} 的内部文本只能为连续 2/3/5 个"'"!`);
28
32
  }
29
33
  }
30
34
 
@@ -1,40 +1,52 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('..'),
3
+ const Parser = require('..'),
4
4
  Token = require('.');
5
5
 
6
6
  /**
7
7
  * 嵌入时的`<onlyinclude>`
8
- * @classdesc `{childNodes: ...string|Token}`
8
+ * @classdesc `{childNodes: ...AstText|Token}`
9
9
  */
10
10
  class OnlyincludeToken extends Token {
11
11
  type = 'onlyinclude';
12
12
 
13
+ /** 内部wikitext */
14
+ get innerText() {
15
+ return this.text();
16
+ }
17
+
13
18
  /**
14
- * @param {string} inner
19
+ * @param {string} inner 标签内部wikitext
15
20
  * @param {accum} accum
16
21
  */
17
22
  constructor(inner, config = Parser.getConfig(), accum = []) {
18
23
  super(inner, config, true, accum);
19
24
  }
20
25
 
26
+ /** @override */
21
27
  cloneNode() {
22
- const cloned = this.cloneChildren(),
28
+ const cloned = this.cloneChildNodes(),
23
29
  token = Parser.run(() => new OnlyincludeToken(undefined, this.getAttribute('config')));
24
30
  token.append(...cloned);
25
31
  return token;
26
32
  }
27
33
 
28
- toString() {
29
- return `<onlyinclude>${super.toString()}</onlyinclude>`;
34
+ /**
35
+ * @override
36
+ * @param {string} selector
37
+ */
38
+ toString(selector) {
39
+ return selector && this.matches(selector) ? '' : `<onlyinclude>${super.toString(selector)}</onlyinclude>`;
30
40
  }
31
41
 
42
+ /** @override */
32
43
  getPadding() {
33
44
  return 13;
34
45
  }
35
46
 
47
+ /** @override */
36
48
  isPlain() {
37
- return this.constructor === OnlyincludeToken;
49
+ return true;
38
50
  }
39
51
  }
40
52
 
package/src/parameter.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const {noWrap} = require('../util/string'),
4
4
  fixedToken = require('../mixin/fixedToken'),
5
- /** @type {Parser} */ Parser = require('..'),
5
+ Parser = require('..'),
6
6
  Token = require('.');
7
7
 
8
8
  /**
@@ -11,18 +11,30 @@ const {noWrap} = require('../util/string'),
11
11
  */
12
12
  class ParameterToken extends fixedToken(Token) {
13
13
  type = 'parameter';
14
- anon;
14
+
15
+ /** 是否是匿名参数 */
16
+ get anon() {
17
+ return this.firstElementChild.childNodes.length === 0;
18
+ }
19
+
20
+ /** getValue()的getter */
21
+ get value() {
22
+ return this.getValue();
23
+ }
24
+
25
+ set value(value) {
26
+ this.setValue(value);
27
+ }
15
28
 
16
29
  /**
17
- * @param {string|number} key
18
- * @param {string} value
30
+ * @param {string|number} key 参数名
31
+ * @param {string} value 参数值
19
32
  * @param {accum} accum
20
33
  */
21
34
  constructor(key, value, config = Parser.getConfig(), accum = []) {
22
35
  super(undefined, config, true, accum);
23
- this.anon = typeof key === 'number';
24
- const AtomToken = require('./atom'),
25
- keyToken = new AtomToken(this.anon ? undefined : key, 'parameter-key', config, accum, {
36
+ const AtomToken = require('./atom');
37
+ const keyToken = new AtomToken(typeof key === 'number' ? undefined : key, 'parameter-key', config, accum, {
26
38
  'Stage-2': ':', '!HeadingToken': '',
27
39
  }),
28
40
  token = new Token(value, config, true, accum);
@@ -30,8 +42,9 @@ class ParameterToken extends fixedToken(Token) {
30
42
  this.append(keyToken, token.setAttribute('stage', 2));
31
43
  }
32
44
 
45
+ /** @override */
33
46
  cloneNode() {
34
- const [key, value] = this.cloneChildren(),
47
+ const [key, value] = this.cloneChildNodes(),
35
48
  config = this.getAttribute('config');
36
49
  return Parser.run(() => {
37
50
  const token = new ParameterToken(this.anon ? Number(this.name) : undefined, undefined, config);
@@ -41,6 +54,7 @@ class ParameterToken extends fixedToken(Token) {
41
54
  });
42
55
  }
43
56
 
57
+ /** @override */
44
58
  afterBuild() {
45
59
  if (!this.anon) {
46
60
  const name = this.firstElementChild.text().trim(),
@@ -51,19 +65,14 @@ class ParameterToken extends fixedToken(Token) {
51
65
  parentNode.getArgs(name, false, false).add(this);
52
66
  }
53
67
  }
54
- const that = this;
55
- /**
56
- * 在AstEventData中记录`oldKey`和`newKey`
57
- * @type {AstListener}
58
- */
59
- const parameterListener = ({prevTarget}, data) => {
60
- if (!that.anon) { // 匿名参数不管怎么变动还是匿名
61
- const {firstElementChild} = that;
68
+ const /** @type {AstListener} */ parameterListener = ({prevTarget}, data) => {
69
+ if (!this.anon) { // 匿名参数不管怎么变动还是匿名
70
+ const {firstElementChild, name} = this;
62
71
  if (prevTarget === firstElementChild) {
63
72
  const newKey = firstElementChild.text().trim();
64
- data.oldKey = that.name;
73
+ data.oldKey = name;
65
74
  data.newKey = newKey;
66
- that.setAttribute('name', newKey);
75
+ this.setAttribute('name', newKey);
67
76
  }
68
77
  }
69
78
  };
@@ -71,22 +80,33 @@ class ParameterToken extends fixedToken(Token) {
71
80
  return this;
72
81
  }
73
82
 
74
- /** @returns {string} */
75
- toString() {
76
- return this.anon ? this.lastElementChild.toString() : super.toString('=');
83
+ /**
84
+ * @override
85
+ * @param {string} selector
86
+ * @returns {string}
87
+ */
88
+ toString(selector) {
89
+ return this.anon && !(selector && this.matches(selector))
90
+ ? this.lastElementChild.toString(selector)
91
+ : super.toString(selector, '=');
77
92
  }
78
93
 
94
+ /** @override */
79
95
  getGaps() {
80
96
  return this.anon ? 0 : 1;
81
97
  }
82
98
 
83
- /** @returns {string} */
99
+ /**
100
+ * @override
101
+ * @returns {string}
102
+ */
84
103
  text() {
85
104
  return this.anon ? this.lastElementChild.text() : super.text('=');
86
105
  }
87
106
 
88
107
  /**
89
- * @param {ParameterToken} token
108
+ * @override
109
+ * @param {ParameterToken} token 待替换的节点
90
110
  * @complexity `n`
91
111
  */
92
112
  safeReplaceWith(token) {
@@ -94,15 +114,20 @@ class ParameterToken extends fixedToken(Token) {
94
114
  return this.replaceWith(token);
95
115
  }
96
116
 
117
+ /** 获取参数值 */
97
118
  getValue() {
98
119
  const value = this.lastElementChild.text();
99
120
  return this.anon && this.parentNode?.matches('template, magic-word#invoke') ? value : value.trim();
100
121
  }
101
122
 
102
- /** @param {string} value */
123
+ /**
124
+ * 设置参数值
125
+ * @param {string} value 参数值
126
+ * @throws `SyntaxError` 非法的模板参数
127
+ */
103
128
  setValue(value) {
104
129
  value = String(value);
105
- const templateLike = this.parentElement?.matches('template, magic-word#invoke'),
130
+ const templateLike = this.parentNode?.matches('template, magic-word#invoke'),
106
131
  wikitext = `{{${templateLike ? ':T|' : 'lc:'}${this.anon ? '' : '1='}${value}}}`,
107
132
  root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
108
133
  {childNodes: {length}, firstElementChild} = root,
@@ -113,15 +138,20 @@ class ParameterToken extends fixedToken(Token) {
113
138
  ) {
114
139
  throw new SyntaxError(`非法的模板参数:${noWrap(value)}`);
115
140
  }
116
- const newValue = lastElementChild.lastChild;
117
- root.destroy();
118
- firstElementChild.destroy();
119
- lastElementChild.destroy();
120
- this.lastElementChild.safeReplaceWith(newValue);
141
+ const {lastChild} = lastElementChild;
142
+ lastElementChild.destroy(true);
143
+ this.lastElementChild.safeReplaceWith(lastChild);
121
144
  }
122
145
 
123
- /** @param {string} key */
124
- rename(key, force = false) {
146
+ /**
147
+ * 修改参数名
148
+ * @param {string} key 新参数名
149
+ * @param {boolean} force 是否无视冲突命名
150
+ * @throws `Error` 仅用于模板参数
151
+ * @throws `SyntaxError` 非法的模板参数名
152
+ * @throws `RangeError` 更名造成重复参数
153
+ */
154
+ rename(key, force) {
125
155
  if (typeof key !== 'string') {
126
156
  this.typeError('rename', 'String');
127
157
  }
@@ -138,8 +168,7 @@ class ParameterToken extends fixedToken(Token) {
138
168
  throw new SyntaxError(`非法的模板参数名:${key}`);
139
169
  }
140
170
  const {lastElementChild} = firstElementChild,
141
- {name} = lastElementChild,
142
- keyToken = lastElementChild.firstChild;
171
+ {name, firstChild} = lastElementChild;
143
172
  if (this.name === name) {
144
173
  Parser.warn('未改变实际参数名', name);
145
174
  } else if (parentNode.hasArg(name)) {
@@ -149,10 +178,8 @@ class ParameterToken extends fixedToken(Token) {
149
178
  throw new RangeError(`参数更名造成重复参数:${name}`);
150
179
  }
151
180
  }
152
- root.destroy();
153
- firstElementChild.destroy();
154
- lastElementChild.destroy();
155
- this.firstElementChild.safeReplaceWith(keyToken);
181
+ lastElementChild.destroy(true);
182
+ this.firstElementChild.safeReplaceWith(firstChild);
156
183
  }
157
184
  }
158
185
 
package/src/syntax.js CHANGED
@@ -2,30 +2,36 @@
2
2
 
3
3
  const {undo} = require('../util/debug'),
4
4
  {text} = require('../util/string'),
5
- /** @type {Parser} */ Parser = require('..'),
5
+ Parser = require('..'),
6
6
  Token = require('.');
7
7
 
8
8
  /**
9
9
  * 满足特定语法格式的plain Token
10
- * @classdesc `{childNodes: (string|Token)[]}`
10
+ * @classdesc `{childNodes: (AstText|Token)[]}`
11
11
  */
12
12
  class SyntaxToken extends Token {
13
13
  #pattern;
14
14
 
15
15
  /**
16
- * @param {?string} wikitext
17
- * @param {RegExp} pattern
16
+ * @param {string} wikitext 语法wikitext
17
+ * @param {RegExp} pattern 语法正则
18
+ * @param {string} type Token.type
18
19
  * @param {accum} accum
19
- * @param {acceptable} acceptable
20
+ * @param {acceptable} acceptable 可接受的子节点设置
21
+ * @throws `RangeError` 含有g修饰符的语法正则
20
22
  */
21
23
  constructor(wikitext, pattern, type = 'plain', config = Parser.getConfig(), accum = [], acceptable = null) {
24
+ if (pattern.global) {
25
+ throw new RangeError(`SyntaxToken 的语法正则不能含有 g 修饰符:${pattern}`);
26
+ }
22
27
  super(wikitext, config, true, accum, acceptable);
23
28
  this.type = type;
24
29
  this.#pattern = pattern;
25
30
  }
26
31
 
32
+ /** @override */
27
33
  cloneNode() {
28
- const cloned = this.cloneChildren(),
34
+ const cloned = this.cloneChildNodes(),
29
35
  config = this.getAttribute('config'),
30
36
  acceptable = this.getAttribute('acceptable');
31
37
  return Parser.run(() => {
@@ -35,33 +41,33 @@ class SyntaxToken extends Token {
35
41
  });
36
42
  }
37
43
 
44
+ /** @override */
38
45
  afterBuild() {
39
- const that = this,
40
- /** @type {AstListener} */ syntaxListener = (e, data) => {
41
- const pattern = that.#pattern;
42
- if (!Parser.running && !pattern.test(that.text())) {
43
- undo(e, data);
44
- throw new Error(`不可修改 ${that.constructor.name} 的语法:/${pattern.source}/${pattern.flags}`);
45
- }
46
- };
46
+ const /** @type {AstListener} */ syntaxListener = (e, data) => {
47
+ const pattern = this.#pattern;
48
+ if (!Parser.running && !pattern.test(this.text())) {
49
+ undo(e, data);
50
+ Parser.error(`不可修改 ${this.constructor.name} 的语法!`, pattern);
51
+ throw new Error(`不可修改 ${this.constructor.name} 的语法!`);
52
+ }
53
+ };
47
54
  this.addEventListener(['remove', 'insert', 'replace', 'text'], syntaxListener);
48
55
  return this;
49
56
  }
50
57
 
51
58
  /**
59
+ * @override
52
60
  * @template {string} T
53
- * @param {T} key
61
+ * @param {T} key 属性键
54
62
  * @returns {TokenAttribute<T>}
55
63
  */
56
64
  getAttribute(key) {
57
- if (key === 'pattern') {
58
- return this.#pattern;
59
- }
60
- return super.getAttribute(key);
65
+ return key === 'pattern' ? this.#pattern : super.getAttribute(key);
61
66
  }
62
67
 
63
68
  /**
64
- * @param {...string|Token} elements
69
+ * @override
70
+ * @param {...Token} elements 待替换的子节点
65
71
  * @complexity `n`
66
72
  */
67
73
  replaceChildren(...elements) {