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/test/real.js ADDED
@@ -0,0 +1,133 @@
1
+ 'use strict';
2
+
3
+ const {diff} = require('./util'),
4
+ Api = require('./api'),
5
+ Parser = require('../'),
6
+ AstText = require('../lib/text');
7
+
8
+ const {argv: [,, site = '']} = process,
9
+ apis = [
10
+ ['LLWiki', 'https://llwiki.org/mediawiki', 'llwiki'],
11
+ ['萌娘百科', 'https://zh.moegirl.org.cn', 'moegirl'],
12
+ ['维基百科', 'https://zh.wikipedia.org/w', 'zhwiki'],
13
+ ].filter(([name]) => name.toLowerCase().includes(site.toLowerCase())),
14
+ complexOrHiddenTypes = [
15
+ // 以下为子节点必为Token的类
16
+ 'ext',
17
+ 'arg',
18
+ 'template',
19
+ 'magic-word',
20
+ 'parameter',
21
+ 'heading',
22
+ 'html',
23
+ 'table',
24
+ 'tr',
25
+ 'td',
26
+ 'link',
27
+ 'category',
28
+ 'file',
29
+ 'gallery-image',
30
+ 'ext-link',
31
+ 'converter',
32
+ 'converter-flags',
33
+ 'converter-rule',
34
+ // 以下为不可见的类
35
+ 'noinclude',
36
+ 'include',
37
+ 'comment',
38
+ 'double-underscore',
39
+ 'hidden',
40
+ // 以下为SyntaxToken
41
+ 'magic-word-name',
42
+ 'heading-trail',
43
+ 'table-syntax',
44
+ // 以下为代码不受限的NowikiToken
45
+ 'ext-inner#nowiki',
46
+ 'ext-inner#pre',
47
+ 'ext-inner#syntaxhighlight',
48
+ 'ext-inner#source',
49
+ 'ext-inner#math',
50
+ 'ext-inner#timeline',
51
+ ],
52
+ simpleTypes = new Set([
53
+ 'ext-inner',
54
+ 'ext-attr',
55
+ 'html-attr',
56
+ 'table-attr',
57
+ 'arg-default',
58
+ 'parameter-key',
59
+ 'parameter-value',
60
+ 'heading-title',
61
+ 'td-inner',
62
+ 'link-target',
63
+ 'link-text',
64
+ 'image-parameter',
65
+ 'ext-link-text',
66
+ 'converter-rule-noconvert',
67
+ 'converter-rule-to',
68
+ ]),
69
+ possibleSyntax = /[{}]|\[{2,}|\[(?!(?:(?!https?\b)[^[])*\])|(?<=^|\])([^[]*?)\]+|<(?=\s*\/?\w+[\s/>])/giu;
70
+
71
+ Parser.debugging = true;
72
+
73
+ /**
74
+ * 获取最近更改的页面源代码
75
+ * @param {string} url api.php网址
76
+ */
77
+ const getPages = async url => {
78
+ const api = new Api(url),
79
+ generatorParams = {generator: 'recentchanges', grcnamespace: '0|10', grclimit: 'max', grctype: 'edit'},
80
+ revisionParams = {prop: 'revisions', rvprop: 'contentmodel|content'};
81
+ /** @type {{query: {pages: MediaWikiPage[]}}} */
82
+ const {query: {pages}} = await api.get({...generatorParams, ...revisionParams});
83
+ return pages.map(({title, ns, revisions}) => ({
84
+ title, ns, content: revisions?.[0]?.contentmodel === 'wikitext' && revisions?.[0]?.content,
85
+ })).filter(({content}) => content);
86
+ };
87
+
88
+ (async () => {
89
+ const moreTypes = new Set();
90
+ for (const [name, url, config] of apis) {
91
+ Parser.debug(`开始检查${name}:`);
92
+ Parser.config = `./config/${config}`;
93
+ try {
94
+ const revs = await getPages(`${url}/api.php`);
95
+ for (const {title, ns, content} of revs) {
96
+ try {
97
+ console.time(title);
98
+ const root = Parser.parse(content, ns === 10);
99
+ console.timeEnd(title);
100
+ await diff(content, String(root));
101
+ for (const token of root.querySelectorAll(`:not(${complexOrHiddenTypes.join()})`)) {
102
+ const {childNodes, type, hidden} = token;
103
+ if (hidden && !simpleTypes.has(type)) {
104
+ moreTypes.add(type);
105
+ continue;
106
+ }
107
+ let first;
108
+ for (let i = 0; i < childNodes.length; i++) {
109
+ const /** @type {AstText} */ {type: childType, data} = childNodes[i];
110
+ if (childType === 'text') {
111
+ first = i;
112
+ if (data.search(possibleSyntax) >= 0) {
113
+ token.setText(data.replaceAll(possibleSyntax, '$1'), i);
114
+ }
115
+ }
116
+ }
117
+ if (first === undefined && !simpleTypes.has(type)) {
118
+ moreTypes.add(type);
119
+ }
120
+ }
121
+ await diff(content, String(root));
122
+ } catch (e) {
123
+ Parser.error(`解析${name}的 ${title} 页面时出错!`, e);
124
+ }
125
+ }
126
+ } catch (e) {
127
+ Parser.error(`访问${name}的API端口时出错!`, e);
128
+ }
129
+ }
130
+ if (moreTypes.size > 0) {
131
+ Parser.debug('其他可能不含纯文本子节点的类:', moreTypes);
132
+ }
133
+ })();
package/test/test.js ADDED
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+ /* eslint-disable no-var */
3
+ const fs = require('fs/promises'),
4
+ assert = require('assert'), // eslint-disable-line no-unused-vars
5
+ path = require('path');
6
+
7
+ var Parser = require('..');
8
+ var wikitext = ''; // eslint-disable-line no-unused-vars
9
+ const {argv: [,, title = '']} = process;
10
+ Parser.debugging = true;
11
+
12
+ (async () => {
13
+ const list = await fs.readdir(path.join(__dirname, '../wiki'));
14
+ for (const file of list) {
15
+ if (file.endsWith('.md') && file.toLowerCase().includes(title.toLowerCase())) {
16
+ Parser.debug(file);
17
+ const md = await fs.readFile(path.join(__dirname, '../wiki', file), 'utf8');
18
+ for (const [, code] of md.matchAll(/```js\n(.+?)\n```/gsu)) {
19
+ try {
20
+ eval(code); // eslint-disable-line no-eval
21
+ } catch (e) {
22
+ Parser.error(code);
23
+ throw e;
24
+ }
25
+ }
26
+ }
27
+ }
28
+ })();
package/test/util.js ADDED
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+ const {spawn} = require('child_process'),
3
+ fs = require('fs/promises');
4
+
5
+ process.on('unhandledRejection', e => {
6
+ console.error(e);
7
+ });
8
+
9
+ /**
10
+ * 将shell命令转化为Promise对象
11
+ * @param {string} command shell指令
12
+ * @param {string[]} args shell输入参数
13
+ * @returns {Promise<?string>}
14
+ */
15
+ const cmd = (command, args) => new Promise(resolve => {
16
+ let timer, shell;
17
+
18
+ /**
19
+ * 清除进程并返回
20
+ * @param {any} val 返回值
21
+ */
22
+ const r = val => {
23
+ clearTimeout(timer);
24
+ shell.kill('SIGINT');
25
+ resolve(val);
26
+ };
27
+ try {
28
+ shell = spawn(command, args);
29
+ timer = setTimeout(() => {
30
+ shell.kill('SIGINT');
31
+ }, 60 * 1000);
32
+ let buf = '';
33
+ shell.stdout.on('data', data => {
34
+ buf += data.toString();
35
+ });
36
+ shell.stdout.on('end', () => {
37
+ r(buf);
38
+ });
39
+ shell.on('exit', () => {
40
+ r(shell.killed ? null : '');
41
+ });
42
+ shell.on('error', () => {
43
+ r(null);
44
+ });
45
+ } catch {
46
+ r(null);
47
+ }
48
+ });
49
+
50
+ /**
51
+ * 比较两个文件
52
+ * @param {string} oldfile 旧文件
53
+ * @param {string} newfile 新文件
54
+ */
55
+ const diff = async (oldfile, newfile) => {
56
+ if (oldfile === newfile) {
57
+ return;
58
+ }
59
+ await Promise.all([fs.writeFile('npmTestOldContent', oldfile), fs.writeFile('npmTestNewContent', newfile)]);
60
+ const stdout = await cmd('git', [
61
+ 'diff',
62
+ '--color-words=[\xc0-\xff][\x80-\xbf]+|<?/?\\w+/?>?|[^[:space:]]',
63
+ '-U0',
64
+ '--no-index',
65
+ 'npmTestOldContent',
66
+ 'npmTestNewContent',
67
+ ]);
68
+ await Promise.all([fs.unlink('npmTestOldContent'), fs.unlink('npmTestNewContent')]);
69
+ console.log(stdout?.split('\n')?.slice(4)?.join('\n'));
70
+ };
71
+
72
+ /**
73
+ * 延时
74
+ * @param {number} t 秒数
75
+ */
76
+ const sleep = t => new Promise(resolve => {
77
+ setTimeout(resolve, t * 1000);
78
+ });
79
+
80
+ module.exports = {diff, sleep};
package/tool/index.js CHANGED
@@ -1,7 +1,9 @@
1
+ /* eslint no-underscore-dangle: [2, {allowAfterThis: true, enforceInMethodNames: false, allow: ['__filename']}] */
1
2
  'use strict';
2
3
 
3
4
  const {typeError, externalUse} = require('../util/debug'),
4
5
  {text, noWrap} = require('../util/string'),
6
+ AstText = require('../lib/text'),
5
7
  Token = require('../src'),
6
8
  assert = require('assert/strict');
7
9
 
@@ -9,7 +11,7 @@ const /** @type {WeakMap<Token, Record<string, any>>} */ dataStore = new WeakMap
9
11
 
10
12
  /**
11
13
  * @param {string} method
12
- * @param {string|Token|Token[]} selector
14
+ * @param {AstText|Token|Token[]} selector
13
15
  * @returns {(token: Token) => boolean}
14
16
  */
15
17
  const matchesGenerator = (method, selector) => {
@@ -20,7 +22,7 @@ const matchesGenerator = (method, selector) => {
20
22
  } else if (selector instanceof Token) {
21
23
  return token => token === selector;
22
24
  }
23
- typeError(TokenCollection, method, 'String', 'Token', 'Array');
25
+ return typeError(TokenCollection, method, 'String', 'Token', 'Array'); // eslint-disable-line no-use-before-define
24
26
  };
25
27
 
26
28
  /** @extends {Array<Token>} */
@@ -37,10 +39,10 @@ class TokenCollection extends Array {
37
39
  for (const token of arr) {
38
40
  if (token === undefined) {
39
41
  continue;
40
- } else if (typeof token === 'string') {
42
+ } else if (token instanceof AstText) {
41
43
  this.push(token);
42
44
  } else if (!(token instanceof Token)) {
43
- this.typeError('constructor', 'String', 'Token');
45
+ this.typeError('constructor', 'AstText', 'Token');
44
46
  } else if (!this.includes(token)) {
45
47
  this.#roots.add(token.getRootNode());
46
48
  this.push(token);
@@ -58,20 +60,21 @@ class TokenCollection extends Array {
58
60
  return typeError(this.constructor, method, ...types);
59
61
  }
60
62
 
63
+ /** 节点排序 */
61
64
  #sort() {
62
- if (this.some(token => typeof token === 'string')) {
65
+ if (this.some(({type}) => type === 'text')) {
63
66
  return;
64
67
  }
65
68
  const rootArray = [...this.#roots];
66
69
  this.sort((a, b) => {
67
70
  const aRoot = a.getRootNode(),
68
71
  bRoot = b.getRootNode();
69
- return aRoot === bRoot ? a.comparePosition(b) : rootArray.indexOf(aRoot) - rootArray.indexOf(bRoot);
72
+ return aRoot === bRoot ? a.compareDocumentPosition(b) : rootArray.indexOf(aRoot) - rootArray.indexOf(bRoot);
70
73
  });
71
74
  }
72
75
 
73
76
  toArray() {
74
- return Array.from(this);
77
+ return [...this];
75
78
  }
76
79
 
77
80
  _filter(selector = '') {
@@ -108,9 +111,9 @@ class TokenCollection extends Array {
108
111
 
109
112
  /** @param {CollectionCallback<void, string|Token>} callback */
110
113
  each(callback) {
111
- this.forEach((ele, i) => { // 不能使用`for`...`of`
112
- callback.call(ele, i, ele);
113
- });
114
+ for (let i = 0; i < this.length; i++) {
115
+ callback.call(this[i], i, this[i]);
116
+ }
114
117
  return this;
115
118
  }
116
119
 
@@ -162,8 +165,9 @@ class TokenCollection extends Array {
162
165
  text(str) {
163
166
  /** @type {(ele: Token, i: number, str: string) => string} */
164
167
  const callback = typeof str === 'function' ? str.call : () => str;
165
- if (['string', 'function'].includes(typeof str)) {
166
- for (const [i, ele] of this.entries()) {
168
+ if (typeof str === 'string' || typeof str === 'function') {
169
+ for (let i = 0; i < this.length; i++) {
170
+ const ele = this[i];
167
171
  if (ele instanceof Token) {
168
172
  try {
169
173
  ele.replaceChildren(callback(ele, i, ele.text()));
@@ -237,7 +241,7 @@ class TokenCollection extends Array {
237
241
  } else if (selector instanceof Token) {
238
242
  return arr.some(ele => ele.contains(selector));
239
243
  }
240
- this.typeError('has', 'String', 'Token');
244
+ return this.typeError('has', 'String', 'Token');
241
245
  }
242
246
 
243
247
  /** @param {string} selector */
@@ -292,11 +296,11 @@ class TokenCollection extends Array {
292
296
  */
293
297
  _siblings(start, count, selector = '') {
294
298
  return this._create(arr => arr.flatMap(ele => {
295
- const {parentElement} = ele;
296
- if (!parentElement) {
299
+ const {parentNode} = ele;
300
+ if (!parentNode) {
297
301
  return undefined;
298
302
  }
299
- const {children} = parentElement,
303
+ const {children} = parentNode,
300
304
  i = children.indexOf(ele);
301
305
  children.splice(
302
306
  typeof start === 'function' ? start(i) : start,
@@ -323,7 +327,7 @@ class TokenCollection extends Array {
323
327
  * @param {string|Token|Token[]} selector
324
328
  */
325
329
  _until(method, selector, filter = '') {
326
- const matches = matchesGenerator(`${method.replace(/All$/, '')}Until`, selector);
330
+ const matches = matchesGenerator(`${method.replace(/All$/u, '')}Until`, selector);
327
331
  return this._create(arr => arr.flatMap(ele => {
328
332
  const tokens = $(ele)[method]().toArray(),
329
333
  tokenArray = method === 'nextAll' ? tokens : tokens.reverse(),
@@ -382,7 +386,7 @@ class TokenCollection extends Array {
382
386
  if (name !== undefined && typeof name !== 'string' && !Array.isArray(name)) {
383
387
  this.typeError('removeData', 'String', 'Array');
384
388
  }
385
- name = typeof name === 'string' ? name.split(/\s/) : name;
389
+ name = typeof name === 'string' ? name.split(/\s/u) : name;
386
390
  for (const token of this._filter()) {
387
391
  if (!$.dataStore.has(token)) {
388
392
  continue;
@@ -404,14 +408,14 @@ class TokenCollection extends Array {
404
408
  * @param {AstListener} handler
405
409
  */
406
410
  _addEventListener(events, selector, handler, once = false) {
407
- if (!['string', 'object'].includes(typeof events)) {
411
+ if (typeof events !== 'string' && typeof events !== 'object') {
408
412
  this.typeError(once ? 'once' : 'on', 'String', 'Object');
409
413
  } else if (typeof selector === 'function') {
410
414
  handler = selector;
411
415
  selector = undefined;
412
416
  }
413
417
  const eventPair = typeof events === 'string'
414
- ? events.split(/\s/).map(/** @returns {[string, AstListener]} */ event => [event, handler])
418
+ ? events.split(/\s/u).map(/** @returns {[string, AstListener]} */ event => [event, handler])
415
419
  : Object.entries(events);
416
420
  for (const token of this._filter(selector)) {
417
421
  for (const [event, listener] of eventPair) {
@@ -445,14 +449,14 @@ class TokenCollection extends Array {
445
449
  * @param {AstListener} handler
446
450
  */
447
451
  off(events, selector, handler) {
448
- if (!['string', 'object', 'undefined'].includes(typeof events)) {
452
+ if (typeof events !== 'string' && typeof events !== 'object' && events !== undefined) {
449
453
  this.typeError('off', 'String', 'Object');
450
454
  }
451
455
  handler = typeof selector === 'function' ? selector : handler;
452
456
  let eventPair;
453
457
  if (events) {
454
458
  eventPair = typeof events === 'string'
455
- ? events.split(/\s/).map(/** @returns {[string, AstListener]} */ event => [event, handler])
459
+ ? events.split(/\s/u).map(/** @returns {[string, AstListener]} */ event => [event, handler])
456
460
  : Object.entries(events);
457
461
  }
458
462
  for (const token of this._filter(selector)) {
@@ -460,7 +464,7 @@ class TokenCollection extends Array {
460
464
  token.removeAllEventListeners();
461
465
  } else {
462
466
  for (const [event, listener] of eventPair) {
463
- if (typeof event !== 'string' || !['function', 'undefined'].includes(typeof listener)) {
467
+ if (typeof event !== 'string' || typeof listener !== 'function' && listener !== undefined) {
464
468
  this.typeError('off', 'String', 'Function');
465
469
  } else if (listener) {
466
470
  token.removeEventListener(event, listener);
@@ -486,7 +490,7 @@ class TokenCollection extends Array {
486
490
  triggerHandler(event, data) {
487
491
  const firstToken = this._find();
488
492
  if (!firstToken) {
489
- return;
493
+ return undefined;
490
494
  }
491
495
  const e = typeof event === 'string' ? new Event(event) : event,
492
496
  listeners = firstToken.listEventListeners(typeof event === 'string' ? event : event.type);
@@ -504,7 +508,8 @@ class TokenCollection extends Array {
504
508
  */
505
509
  _insert(method, content, ...additional) {
506
510
  if (typeof content === 'function') {
507
- for (const [i, token] of this.entries()) {
511
+ for (let i = 0; i < this.length; i++) {
512
+ const token = this[i];
508
513
  if (token instanceof Token) {
509
514
  const result = content.call(token, i, token.toString());
510
515
  if (typeof result === 'string' || result instanceof Token) {
@@ -576,7 +581,8 @@ class TokenCollection extends Array {
576
581
  }
577
582
 
578
583
  remove(selector = '') {
579
- for (const token of this.removeData()._filter(selector)) {
584
+ this.removeData();
585
+ for (const token of this._filter(selector)) {
580
586
  token.remove();
581
587
  token.removeAllEventListeners();
582
588
  }
@@ -659,7 +665,8 @@ class TokenCollection extends Array {
659
665
  } else {
660
666
  this.typeError('val', 'String', 'Array', 'Function');
661
667
  }
662
- for (const [i, token] of this.entries()) {
668
+ for (let i = 0; i < this.length; i++) {
669
+ const token = this[i];
663
670
  if (token instanceof Token && typeof token.setValue === 'function' && token.setValue.length === 1) {
664
671
  token.setValue(toValue(i, token));
665
672
  }
@@ -678,7 +685,8 @@ class TokenCollection extends Array {
678
685
  const firstToken = this._find();
679
686
  return firstToken?.[getter] && firstToken[getter](name);
680
687
  }
681
- for (const [i, token] of this.entries()) {
688
+ for (let i = 0; i < this.length; i++) {
689
+ const token = this[i];
682
690
  if (token instanceof Token && typeof token[setter] === 'function') {
683
691
  if (typeof value === 'string') {
684
692
  token[setter](name, value);
@@ -784,6 +792,7 @@ class TokenCollection extends Array {
784
792
  this.typeError(method, 'Array', 'Function');
785
793
  }
786
794
  return this[method](
795
+
787
796
  /**
788
797
  * @this {string|Token}
789
798
  * @param {number} i
@@ -817,7 +826,7 @@ class TokenCollection extends Array {
817
826
  offset() {
818
827
  const firstToken = this._find();
819
828
  if (!firstToken) {
820
- return;
829
+ return undefined;
821
830
  }
822
831
  const {top, left} = firstToken.getBoundingClientRect();
823
832
  return {top, left};
@@ -837,9 +846,9 @@ class TokenCollection extends Array {
837
846
  }
838
847
  }
839
848
 
840
- /** @param {string|Token|Iterable<string|Token>} tokens */
849
+ /** @param {AstText|Token|Iterable<string|Token>} tokens */
841
850
  const $ = tokens => {
842
- if (typeof tokens === 'string' || tokens instanceof Token) {
851
+ if (tokens instanceof AstText || tokens instanceof Token) {
843
852
  tokens = [tokens];
844
853
  }
845
854
  return new Proxy(new TokenCollection(...tokens), {
@@ -851,6 +860,7 @@ const $ = tokens => {
851
860
  ) {
852
861
  return obj[prop];
853
862
  }
863
+ return undefined;
854
864
  },
855
865
  set(obj, prop, val) {
856
866
  if (prop === 'prevObject' && (val === undefined || val instanceof TokenCollection)) {
@@ -0,0 +1,13 @@
1
+ declare global {
2
+ interface MediaWikiRevision {
3
+ content: string;
4
+ contentmodel: string;
5
+ }
6
+ interface MediaWikiPage {
7
+ title: string;
8
+ ns: number;
9
+ revisions: MediaWikiRevision[];
10
+ };
11
+ }
12
+
13
+ export {};
@@ -0,0 +1,28 @@
1
+ declare global {
2
+ interface Array<T> {
3
+ /**
4
+ * Returns the value of the last element in the array where predicate is true, and undefined
5
+ * otherwise.
6
+ * @param predicate find calls predicate once for each element of the array, in descending
7
+ * order, until it finds one where predicate returns true. If such an element is found, findLast
8
+ * immediately returns that element value. Otherwise, findLast returns undefined.
9
+ * @param thisArg If provided, it will be used as the this value for each invocation of
10
+ * predicate. If it is not provided, undefined is used instead.
11
+ */
12
+ findLast<S extends T>(predicate: (this: void, value: T, index: number, obj: T[]) => value is S, thisArg?: any): S | undefined;
13
+ findLast(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;
14
+
15
+ /**
16
+ * Returns the index of the last element in the array where predicate is true, and -1
17
+ * otherwise.
18
+ * @param predicate find calls predicate once for each element of the array, in descending
19
+ * order, until it finds one where predicate returns true. If such an element is found,
20
+ * findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.
21
+ * @param thisArg If provided, it will be used as the this value for each invocation of
22
+ * predicate. If it is not provided, undefined is used instead.
23
+ */
24
+ findLastIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number;
25
+ }
26
+ }
27
+
28
+ export {};
@@ -0,0 +1,24 @@
1
+ import Token from '../src';
2
+ import AstText from '../lib/text';
3
+
4
+ declare global {
5
+ interface AstEvent extends Event {
6
+ readonly target: Token & AstText;
7
+ currentTarget: Token;
8
+ prevTarget: ?Token;
9
+ };
10
+ interface AstEventData {
11
+ position: number;
12
+ removed: AstText|Token;
13
+ inserted: AstText|Token;
14
+ oldToken: Token;
15
+ newToken: Token;
16
+ oldText: string;
17
+ newText: string;
18
+ oldKey: string;
19
+ newKey: string;
20
+ }
21
+ type AstListener = (e: AstEvent, data: AstEventData) => any;
22
+ }
23
+
24
+ export {};
@@ -7,13 +7,29 @@ declare global {
7
7
  warning: boolean;
8
8
  debugging: boolean;
9
9
 
10
- /** 默认输出到console.warn */
10
+ /**
11
+ * 默认输出到console.warn
12
+ * @param {string} msg 消息
13
+ * @param {...any} args 更多信息
14
+ */
11
15
  warn(msg: string, ...args: any[]): void;
12
- /** 默认不输出到console.debug */
16
+ /**
17
+ * 默认不输出到console.debug
18
+ * @param {string} msg 消息
19
+ * @param {...any} args 更多信息
20
+ */
13
21
  debug(msg: string, ...args: any[]): void;
14
- /** 总是输出到console.error */
22
+ /**
23
+ * 总是输出到console.error
24
+ * @param {string} msg 消息
25
+ * @param {...any} args 更多信息
26
+ */
15
27
  error(msg: string, ...args: any[]): void;
16
- /** 总是输出到console.info */
28
+ /**
29
+ * 总是输出到console.info
30
+ * @param {string} msg 消息
31
+ * @param {...any} args 更多信息
32
+ */
17
33
  info(msg: string, ...args: any[]): void;
18
34
 
19
35
  running: boolean;
@@ -27,20 +43,46 @@ declare global {
27
43
  /** 清除各模块的缓存 */
28
44
  clearCache(): void;
29
45
 
46
+ /**
47
+ * 打印函数定义
48
+ * @param {Function} f 待打印的函数
49
+ */
30
50
  log(f: Function): void;
31
51
 
32
52
  readonly aliases: string[][];
33
53
 
34
54
  config: string;
55
+ /** 获取设置 */
35
56
  getConfig(): ParserConfig;
36
57
 
58
+ /**
59
+ * 是否是跨维基链接
60
+ * @param {string} title 链接标题
61
+ */
37
62
  isInterwiki(title: string, config?: ParserConfig): RegExpMatchArray;
63
+ /**
64
+ * 规范化页面标题
65
+ * @param {string} title 标题(含或不含命名空间前缀)
66
+ * @param {number} defaultNs 命名空间
67
+ * @param {boolean} include 是否嵌入
68
+ * @param {boolean} halfParsed 是否是半解析状态
69
+ */
38
70
  normalizeTitle(
39
71
  title: string, defaultNs?: number, include?: boolean, config?: ParserConfig, halfParsed?: boolean
40
72
  ): Title;
41
73
 
42
74
  readonly MAX_STAGE: number;
75
+ /**
76
+ * 解析wikitext
77
+ * @param {string|Token} wikitext wikitext
78
+ * @param {boolean} include 是否嵌入
79
+ * @param {number} maxStage 最大解析层级
80
+ */
43
81
  parse(wikitext: string|Token, include?: boolean, maxStage?: number, config?: ParserConfig): Token;
82
+ /**
83
+ * 再次解析上次出错的wikitext
84
+ * @param {string} date 错误日期
85
+ */
44
86
  reparse(date: string): Token;
45
87
 
46
88
  getTool(): typeof $;
package/typings/node.d.ts CHANGED
@@ -1,22 +1,28 @@
1
1
  import Ranges from '../lib/ranges';
2
2
  import Token from '../src';
3
+ import AstText from '../lib/text';
3
4
  import ParameterToken from '../src/parameter';
4
5
 
5
6
  declare global {
6
7
  type TokenAttribute<T> =
7
- T extends 'childNodes' ? (string|Token)[] :
8
- T extends 'parentNode' ? Token|undefined :
8
+ T extends 'stage' ? number :
9
+ T extends 'include' ? boolean :
10
+ T extends 'pattern' ? RegExp :
9
11
  T extends 'optional'|'tags'|'flags' ? string[] :
10
- T extends 'stage'|'indent' ? number :
12
+ T extends 'keys' ? Set<string> :
13
+ T extends 'attr' ? Map<string, string|true> :
14
+ T extends 'acceptable' ? Record<string, Ranges> :
15
+ T extends 'args' ? Record<string, Set<ParameterToken>> :
11
16
  T extends 'config' ? ParserConfig :
12
17
  T extends 'accum' ? accum :
13
- T extends 'acceptable' ? Record<string, Ranges> :
14
18
  T extends 'protectedChildren' ? Ranges :
15
- T extends 'keys' ? Set<string> :
16
- T extends 'args' ? Record<string, Set<ParameterToken>> :
17
- T extends 'attr' ? Map<string, string|true> :
18
- T extends 'include'|'selfLink'|'ul'|'ol'|'dt'|'unidirectional'|'bidirectional' ? boolean :
19
- T extends 'pattern' ? RegExp :
19
+ T extends 'parentNode' ? Token|undefined :
20
+ T extends 'childNodes' ? (AstText|Token)[] :
21
+ T extends 'verifyChild' ? (i: number, addition: number) => void :
22
+ T extends 'matchesAttr' ? (key: string, equal: string, val: string, i: string) => boolean :
23
+ T extends 'parseOnce' ? (n: number, include: boolean) => Token :
24
+ T extends 'buildFromStr' ? (str: string) => (AstText|Token)[] :
25
+ T extends 'protectChildren' ? (...args: string|number|Range) => void :
20
26
  string;
21
27
  }
22
28