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
package/parser/table.js CHANGED
@@ -1,29 +1,36 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('..');
3
+ const Parser = require('..'),
4
+ AstText = require('../lib/text');
4
5
 
5
6
  /**
6
- * `tr`和`td`包含开头的换行
7
- * @param {{firstChild: string, type: string}}
7
+ * 解析表格,注意`tr`和`td`包含开头的换行
8
+ * @param {{firstChild: AstText, type: string}} root 根节点
8
9
  * @param {accum} accum
9
10
  */
10
- const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = []) => {
11
+ const parseTable = ({firstChild: {data}, type}, config = Parser.getConfig(), accum = []) => {
11
12
  const Token = require('../src'),
12
13
  TableToken = require('../src/table'),
13
14
  TrToken = require('../src/table/tr'),
14
15
  TdToken = require('../src/table/td'),
15
- DdToken = require('../src/nowiki/dd'),
16
- /** @type {TrToken[]} */ stack = [],
17
- lines = firstChild.split('\n');
16
+ DdToken = require('../src/nowiki/dd');
17
+ const /** @type {TrToken[]} */ stack = [],
18
+ lines = data.split('\n');
18
19
  let out = type === 'root' ? '' : `\n${lines.shift()}`;
19
- const /** @type {(str: string, top: TrToken & {firstChild: string}) => void} */ push = (str, top) => {
20
+
21
+ /**
22
+ * 向表格中插入纯文本
23
+ * @param {string} str 待插入的文本
24
+ * @param {TrToken} top 当前解析的表格或表格行
25
+ */
26
+ const push = (str, top) => {
20
27
  if (!top) {
21
28
  out += str;
22
29
  return;
23
30
  }
24
31
  const {lastElementChild} = top;
25
32
  if (lastElementChild.isPlain()) {
26
- lastElementChild.setText(lastElementChild.firstChild + str, 0);
33
+ lastElementChild.setText(String(lastElementChild) + str);
27
34
  } else {
28
35
  const token = new Token(str, config, true, accum);
29
36
  token.type = 'table-inter';
@@ -32,9 +39,9 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
32
39
  };
33
40
  for (const outLine of lines) {
34
41
  let top = stack.pop();
35
- const [spaces] = outLine.match(/^(?:\s|\x00\d+c\x7f)*/);
36
- const line = outLine.slice(spaces.length),
37
- matchesStart = line.match(/^(:*)((?:\s|\x00\d+c\x7f)*)({\||{\x00\d+!\x7f|\x00\d+{\x7f)(.*)/);
42
+ const [spaces] = /^(?:\s|\0\d+c\x7F)*/u.exec(outLine),
43
+ line = outLine.slice(spaces.length),
44
+ matchesStart = /^(:*)((?:\s|\0\d+c\x7F)*)(\{\||\{\0\d+!\x7F|\0\d+\{\x7F)(.*)$/u.exec(line);
38
45
  if (matchesStart) {
39
46
  while (top && top.type !== 'td') {
40
47
  top = stack.pop();
@@ -43,7 +50,7 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
43
50
  if (indent) {
44
51
  new DdToken(indent, config, accum);
45
52
  }
46
- push(`\n${spaces}${indent && `\x00${accum.length - 1}d\x7f`}${moreSpaces}\x00${accum.length}b\x7f`, top);
53
+ push(`\n${spaces}${indent && `\0${accum.length - 1}d\x7F`}${moreSpaces}\0${accum.length}b\x7F`, top);
47
54
  const table = new TableToken(tableSyntax, attr, config, accum);
48
55
  stack.push(...top ? [top] : [], table);
49
56
  continue;
@@ -51,9 +58,9 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
51
58
  out += `\n${outLine}`;
52
59
  continue;
53
60
  }
54
- const matches = line.match(
55
- /^(?:(\|}|\x00\d+!\x7f}|\x00\d+}\x7f)|(\|-+|\x00\d+!\x7f-+|\x00\d+-\x7f-*)|(!|(?:\||\x00\d+!\x7f)\+?))(.*)/,
56
- );
61
+ const matches = // eslint-disable-line operator-linebreak
62
+ /^(?:(\|\}|\0\d+!\x7F\}|\0\d+\}\x7F)|(\|-+|\0\d+!\x7F-+|\0\d+-\x7F-*)(?!-)|(!|(?:\||\0\d+!\x7F)\+?))(.*)$/u
63
+ .exec(line);
57
64
  if (!matches) {
58
65
  push(`\n${outLine}`, top);
59
66
  stack.push(...top ? [top] : []);
@@ -81,8 +88,8 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
81
88
  top = stack.pop();
82
89
  }
83
90
  const regex = cell === '!'
84
- ? /!!|(?:\||\x00\d+!\x7f){2}|\x00\d+\+\x7f/g
85
- : /(?:\||\x00\d+!\x7f){2}|\x00\d+\+\x7f/g;
91
+ ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu
92
+ : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
86
93
  let mt = regex.exec(attr),
87
94
  lastIndex = 0,
88
95
  lastSyntax = `\n${spaces}${cell}`;
@@ -0,0 +1,120 @@
1
+ {
2
+ "type": "root",
3
+ "childNodes": [
4
+ {
5
+ "type": "ext",
6
+ "name": "ref",
7
+ "childNodes": [
8
+ {
9
+ "type": "ext-attr",
10
+ "name": "ref",
11
+ "childNodes": [
12
+ ""
13
+ ]
14
+ },
15
+ {
16
+ "type": "ext-inner",
17
+ "name": "ref",
18
+ "childNodes": [
19
+ {
20
+ "type": "template",
21
+ "name": "Template:T",
22
+ "childNodes": [
23
+ {
24
+ "type": "template-name",
25
+ "childNodes": [
26
+ "T"
27
+ ]
28
+ },
29
+ {
30
+ "type": "parameter",
31
+ "name": "1",
32
+ "childNodes": [
33
+ {
34
+ "type": "parameter-key",
35
+ "childNodes": []
36
+ },
37
+ {
38
+ "type": "parameter-value",
39
+ "childNodes": [
40
+ {
41
+ "type": "html",
42
+ "name": "br",
43
+ "childNodes": [
44
+ {
45
+ "type": "html-attr",
46
+ "name": "br",
47
+ "childNodes": [
48
+ ""
49
+ ]
50
+ }
51
+ ]
52
+ },
53
+ "\n",
54
+ {
55
+ "type": "hr",
56
+ "childNodes": [
57
+ "----"
58
+ ]
59
+ },
60
+ "\n",
61
+ {
62
+ "type": "file",
63
+ "name": "File:F",
64
+ "childNodes": [
65
+ {
66
+ "type": "link-target",
67
+ "childNodes": [
68
+ "File:F"
69
+ ]
70
+ },
71
+ {
72
+ "type": "image-parameter",
73
+ "name": "thumbnail",
74
+ "childNodes": []
75
+ },
76
+ {
77
+ "type": "image-parameter",
78
+ "name": "caption",
79
+ "childNodes": [
80
+ {
81
+ "type": "quote",
82
+ "name": "2",
83
+ "childNodes": [
84
+ "''"
85
+ ]
86
+ },
87
+ {
88
+ "type": "ext-link",
89
+ "childNodes": [
90
+ {
91
+ "type": "ext-link-url",
92
+ "childNodes": [
93
+ "//example.net"
94
+ ]
95
+ }
96
+ ]
97
+ },
98
+ {
99
+ "type": "quote",
100
+ "name": "2",
101
+ "childNodes": [
102
+ "''"
103
+ ]
104
+ }
105
+ ]
106
+ }
107
+ ]
108
+ }
109
+ ]
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ }
115
+ ]
116
+ }
117
+ ]
118
+ }
119
+ ]
120
+ }
package/src/arg.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {text, noWrap} = require('../util/string'),
4
- /** @type {Parser} */ Parser = require('..'),
4
+ Parser = require('..'),
5
5
  Token = require('.');
6
6
 
7
7
  /**
@@ -12,30 +12,31 @@ class ArgToken extends Token {
12
12
  type = 'arg';
13
13
 
14
14
  /**
15
- * @param {string[]} parts
15
+ * @param {string[]} parts 以'|'分隔的各部分
16
16
  * @param {accum} accum
17
17
  * @complexity `n`
18
18
  */
19
19
  constructor(parts, config = Parser.getConfig(), accum = []) {
20
20
  super(undefined, config, true, accum, {AtomToken: 0, Token: 1, HiddenToken: '2:'});
21
- for (const [i, part] of parts.entries()) {
21
+ for (let i = 0; i < parts.length; i++) {
22
22
  if (i === 0 || i > 1) {
23
23
  const AtomToken = i === 0 ? require('./atom') : require('./atom/hidden'),
24
- token = new AtomToken(part, `arg-${i === 0 ? 'name' : 'redundant'}`, config, accum, {
24
+ token = new AtomToken(parts[i], i === 0 ? 'arg-name' : undefined, config, accum, {
25
25
  'Stage-2': ':', '!HeadingToken': '',
26
26
  });
27
27
  this.appendChild(token);
28
28
  } else {
29
- const token = new Token(part, config, true, accum);
29
+ const token = new Token(parts[i], config, true, accum);
30
30
  token.type = 'arg-default';
31
31
  this.appendChild(token.setAttribute('stage', 2));
32
32
  }
33
33
  }
34
- this.protectChildren(0);
34
+ this.getAttribute('protectChildren')(0);
35
35
  }
36
36
 
37
+ /** @override */
37
38
  cloneNode() {
38
- const [name, ...cloned] = this.cloneChildren();
39
+ const [name, ...cloned] = this.cloneChildNodes();
39
40
  return Parser.run(() => {
40
41
  const token = new ArgToken([''], this.getAttribute('config'));
41
42
  token.firstElementChild.safeReplaceWith(name);
@@ -44,36 +45,48 @@ class ArgToken extends Token {
44
45
  });
45
46
  }
46
47
 
48
+ /** @override */
47
49
  afterBuild() {
48
50
  this.setAttribute('name', this.firstElementChild.text().trim());
49
- const that = this,
50
- /** @type {AstListener} */ argListener = ({prevTarget}) => {
51
- if (prevTarget === that.firstElementChild) {
52
- that.setAttribute('name', prevTarget.text().trim());
53
- }
54
- };
51
+ const /** @type {AstListener} */ argListener = ({prevTarget}) => {
52
+ if (prevTarget === this.firstElementChild) {
53
+ this.setAttribute('name', prevTarget.text().trim());
54
+ }
55
+ };
55
56
  this.addEventListener(['remove', 'insert', 'replace', 'text'], argListener);
56
57
  return this;
57
58
  }
58
59
 
59
- toString() {
60
- return `{{{${super.toString('|')}}}}`;
60
+ /**
61
+ * @override
62
+ * @param {string} selector
63
+ */
64
+ toString(selector) {
65
+ return selector && this.matches(selector) ? '' : `{{{${super.toString(selector, '|')}}}}`;
61
66
  }
62
67
 
68
+ /** @override */
63
69
  getPadding() {
64
70
  return 3;
65
71
  }
66
72
 
73
+ /** @override */
67
74
  getGaps() {
68
75
  return 1;
69
76
  }
70
77
 
71
- /** @returns {string} */
78
+ /**
79
+ * @override
80
+ * @returns {string}
81
+ */
72
82
  text() {
73
83
  return `{{{${text(this.children.slice(0, 2), '|')}}}}`;
74
84
  }
75
85
 
76
- /** @complexity `n` */
86
+ /**
87
+ * 移除无效部分
88
+ * @complexity `n`
89
+ */
77
90
  removeRedundant() {
78
91
  Parser.run(() => {
79
92
  for (let i = this.childNodes.length - 1; i > 1; i--) {
@@ -83,8 +96,8 @@ class ArgToken extends Token {
83
96
  }
84
97
 
85
98
  /**
86
- * 删除`arg-default`子节点时自动删除全部`arg-redundant`子节点
87
- * @param {number} i
99
+ * 移除子节点,且在移除`arg-default`子节点时自动移除全部多余子节点
100
+ * @param {number} i 移除位置
88
101
  * @returns {Token}
89
102
  */
90
103
  removeAt(i) {
@@ -94,11 +107,16 @@ class ArgToken extends Token {
94
107
  return super.removeAt(i);
95
108
  }
96
109
 
97
- /** @param {Token} token */
110
+ /**
111
+ * @override
112
+ * @param {Token} token 待插入的子节点
113
+ * @param {number} i 插入位置
114
+ * @throws `RangeError` 不可插入多余子节点
115
+ */
98
116
  insertAt(token, i = this.childNodes.length) {
99
117
  const j = i < 0 ? i + this.childNodes.length : i;
100
118
  if (j > 1 && !Parser.running) {
101
- throw new RangeError(`${this.constructor.name} 不可插入 arg-redundant 子节点!`);
119
+ throw new RangeError(`${this.constructor.name} 不可插入多余的子节点!`);
102
120
  }
103
121
  super.insertAt(token, i);
104
122
  if (j === 1) {
@@ -107,7 +125,11 @@ class ArgToken extends Token {
107
125
  return token;
108
126
  }
109
127
 
110
- /** @param {string} name */
128
+ /**
129
+ * 设置参数名
130
+ * @param {string} name 新参数名
131
+ * @throws `SyntaxError` 非法的参数名
132
+ */
111
133
  setName(name) {
112
134
  name = String(name);
113
135
  const root = Parser.parse(`{{{${name}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
@@ -115,13 +137,16 @@ class ArgToken extends Token {
115
137
  if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childNodes.length !== 1) {
116
138
  throw new SyntaxError(`非法的参数名称:${noWrap(name)}`);
117
139
  }
118
- const newName = firstElementChild.firstElementChild;
119
- root.destroy();
120
- firstElementChild.destroy();
140
+ const {firstElementChild: newName} = firstElementChild;
141
+ firstElementChild.destroy(true);
121
142
  this.firstElementChild.safeReplaceWith(newName);
122
143
  }
123
144
 
124
- /** @param {string} value */
145
+ /**
146
+ * 设置预设值
147
+ * @param {string} value 预设值
148
+ * @throws `SyntaxError` 非法的参数预设值
149
+ */
125
150
  setDefault(value) {
126
151
  value = String(value);
127
152
  const root = Parser.parse(`{{{|${value}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
@@ -129,14 +154,13 @@ class ArgToken extends Token {
129
154
  if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childNodes.length !== 2) {
130
155
  throw new SyntaxError(`非法的参数预设值:${noWrap(value)}`);
131
156
  }
132
- const [, oldDefault] = this.children,
133
- newDefault = firstElementChild.lastElementChild;
134
- root.destroy();
135
- firstElementChild.destroy();
157
+ const {children: [, oldDefault]} = this,
158
+ {lastElementChild} = firstElementChild;
159
+ firstElementChild.destroy(true);
136
160
  if (oldDefault) {
137
- oldDefault.safeReplaceWith(newDefault);
161
+ oldDefault.safeReplaceWith(lastElementChild);
138
162
  } else {
139
- this.appendChild(newDefault);
163
+ this.appendChild(lastElementChild);
140
164
  }
141
165
  }
142
166
  }
@@ -1,10 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const hidden = require('../../mixin/hidden'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  AtomToken = require('.');
6
6
 
7
- class HiddenToken extends hidden(AtomToken) {}
7
+ /** 不可见的节点 */
8
+ class HiddenToken extends hidden(AtomToken) {
9
+ type = 'hidden';
10
+ }
8
11
 
9
12
  Parser.classes.HiddenToken = __filename;
10
13
  module.exports = HiddenToken;
package/src/atom/index.js CHANGED
@@ -1,29 +1,37 @@
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
  * 不会被继续解析的plain Token
8
- * @classdesc `{childNodes: (string|Token)[]}`
8
+ * @classdesc `{childNodes: (AstText|Token)[]}`
9
9
  */
10
10
  class AtomToken extends Token {
11
+ type = 'plain';
12
+
11
13
  /**
12
- * @param {?string} wikitext
14
+ * @param {string} wikitext wikitext
15
+ * @param {string|undefined} type Token.type
13
16
  * @param {accum} accum
14
- * @param {acceptable} acceptable
17
+ * @param {acceptable} acceptable 可接受的子节点设置
15
18
  */
16
- constructor(wikitext, type = 'plain', config = Parser.getConfig(), accum = [], acceptable = null) {
19
+ constructor(wikitext, type, config = Parser.getConfig(), accum = [], acceptable = null) {
17
20
  super(wikitext, config, true, accum, acceptable);
18
- this.type = type;
21
+ if (type) {
22
+ this.type = type;
23
+ }
19
24
  }
20
25
 
26
+ /**
27
+ * @override
28
+ * @this {AtomToken & {constructor: typeof AtomToken}}
29
+ */
21
30
  cloneNode() {
22
- const cloned = this.cloneChildren(),
23
- /** @type {typeof AtomToken} */ Constructor = this.constructor,
31
+ const cloned = this.cloneChildNodes(),
24
32
  config = this.getAttribute('config'),
25
33
  acceptable = this.getAttribute('acceptable'),
26
- token = Parser.run(() => new Constructor(undefined, this.type, config, [], acceptable));
34
+ token = Parser.run(() => new this.constructor(undefined, this.type, config, [], acceptable));
27
35
  token.append(...cloned);
28
36
  return token;
29
37
  }