wikiparser-node 0.8.1-m → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +39 -0
  2. package/config/moegirl.json +1 -0
  3. package/i18n/zh-hans.json +44 -0
  4. package/i18n/zh-hant.json +44 -0
  5. package/index.js +264 -10
  6. package/lib/element.js +507 -33
  7. package/lib/node.js +550 -6
  8. package/lib/ranges.js +130 -0
  9. package/lib/text.js +111 -41
  10. package/lib/title.js +28 -5
  11. package/mixin/attributeParent.js +117 -0
  12. package/mixin/fixedToken.js +40 -0
  13. package/mixin/hidden.js +3 -0
  14. package/mixin/singleLine.js +31 -0
  15. package/mixin/sol.js +54 -0
  16. package/package.json +9 -8
  17. package/parser/brackets.js +9 -2
  18. package/parser/commentAndExt.js +3 -5
  19. package/parser/converter.js +1 -0
  20. package/parser/externalLinks.js +1 -0
  21. package/parser/hrAndDoubleUnderscore.js +1 -0
  22. package/parser/html.js +1 -0
  23. package/parser/links.js +6 -5
  24. package/parser/list.js +1 -0
  25. package/parser/magicLinks.js +5 -4
  26. package/parser/quotes.js +1 -0
  27. package/parser/selector.js +177 -0
  28. package/parser/table.js +1 -0
  29. package/src/arg.js +123 -5
  30. package/src/atom/hidden.js +2 -0
  31. package/src/atom/index.js +17 -0
  32. package/src/attribute.js +191 -8
  33. package/src/attributes.js +311 -8
  34. package/src/charinsert.js +97 -0
  35. package/src/converter.js +108 -2
  36. package/src/converterFlags.js +190 -3
  37. package/src/converterRule.js +185 -4
  38. package/src/extLink.js +122 -2
  39. package/src/gallery.js +59 -11
  40. package/src/hasNowiki/index.js +12 -0
  41. package/src/hasNowiki/pre.js +12 -0
  42. package/src/heading.js +57 -6
  43. package/src/html.js +133 -12
  44. package/src/imageParameter.js +232 -38
  45. package/src/imagemap.js +65 -6
  46. package/src/imagemapLink.js +14 -2
  47. package/src/index.js +537 -8
  48. package/src/link/category.js +32 -1
  49. package/src/link/file.js +173 -11
  50. package/src/link/galleryImage.js +63 -5
  51. package/src/link/index.js +268 -9
  52. package/src/magicLink.js +92 -11
  53. package/src/nested/choose.js +1 -0
  54. package/src/nested/combobox.js +1 -0
  55. package/src/nested/index.js +31 -7
  56. package/src/nested/references.js +1 -0
  57. package/src/nowiki/comment.js +27 -3
  58. package/src/nowiki/dd.js +47 -1
  59. package/src/nowiki/doubleUnderscore.js +31 -1
  60. package/src/nowiki/hr.js +20 -1
  61. package/src/nowiki/index.js +25 -3
  62. package/src/nowiki/list.js +5 -2
  63. package/src/nowiki/noinclude.js +14 -0
  64. package/src/nowiki/quote.js +17 -3
  65. package/src/onlyinclude.js +26 -1
  66. package/src/paramTag/index.js +27 -4
  67. package/src/paramTag/inputbox.js +4 -1
  68. package/src/parameter.js +150 -8
  69. package/src/syntax.js +68 -0
  70. package/src/table/index.js +941 -4
  71. package/src/table/td.js +229 -10
  72. package/src/table/tr.js +249 -4
  73. package/src/tagPair/ext.js +36 -9
  74. package/src/tagPair/include.js +24 -0
  75. package/src/tagPair/index.js +51 -2
  76. package/src/transclude.js +547 -29
  77. package/tool/index.js +1202 -0
  78. package/util/debug.js +73 -0
  79. package/util/lint.js +8 -7
  80. package/util/string.js +67 -1
  81. package/config/minimum.json +0 -142
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ [![npm version](https://badge.fury.io/js/wikiparser-node.svg)](https://www.npmjs.com/package/wikiparser-node)
2
+
3
+ # 简介
4
+ wikiparser-node 是一款由 Bhsd 开发的基于 [Node.js](https://nodejs.org/en/) 环境的离线[维基文本](https://www.mediawiki.org/wiki/Wikitext)语法解析器,可以解析绝大部分的维基语法并生成[语法树](https://en.wikipedia.org/wiki/Abstract_syntax_tree),还可以很方便地对语法树进行查询和修改,最后返回修改后的维基文本。语法树的每个节点对应一个仿照 [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) 类设计的类 [Token](https://github.com/bhsd-harry/wikiparser-node/wiki/01.-Token)。
5
+
6
+ # 使用方法
7
+
8
+ ```js
9
+ var Parser = require('wikiparser-node');
10
+ ```
11
+
12
+ 更多文档请查阅 [Wiki](https://github.com/bhsd-harry/wikiparser-node/wiki)。
13
+
14
+ # 目录
15
+
16
+ 1. [Parser](https://github.com/bhsd-harry/wikiparser-node/wiki/Home#parser)
17
+ 2. [AstElement](https://github.com/bhsd-harry/wikiparser-node/wiki/01.-Token#astelement)
18
+ 3. [Token](https://github.com/bhsd-harry/wikiparser-node/wiki/01.-Token#token)
19
+ 4. [CommentToken](https://github.com/bhsd-harry/wikiparser-node/wiki/02.-CommentToken等#commenttoken)
20
+ 5. [ExtToken](https://github.com/bhsd-harry/wikiparser-node/wiki/03.-ExtToken)
21
+ 6. [AttributeToken](https://github.com/bhsd-harry/wikiparser-node/wiki/04.-AttributeToken)
22
+ 7. [HeadingToken](https://github.com/bhsd-harry/wikiparser-node/wiki/05.-HeadingToken)
23
+ 8. [ArgToken](https://github.com/bhsd-harry/wikiparser-node/wiki/06.-ArgToken)
24
+ 9. [TranscludeToken](https://github.com/bhsd-harry/wikiparser-node/wiki/07.-TranscludeToken)
25
+ 10. [ParameterToken](https://github.com/bhsd-harry/wikiparser-node/wiki/08.-ParameterToken)
26
+ 11. [HtmlToken](https://github.com/bhsd-harry/wikiparser-node/wiki/09.-HtmlToken)
27
+ 12. [TableToken](https://github.com/bhsd-harry/wikiparser-node/wiki/10.-TableToken)
28
+ 13. [TdToken](https://github.com/bhsd-harry/wikiparser-node/wiki/11.-TdToken)
29
+ 14. [DoubleUnderscoreToken](https://github.com/bhsd-harry/wikiparser-node/wiki/12.-DoubleUnderscoreToken)
30
+ 15. [LinkToken](https://github.com/bhsd-harry/wikiparser-node/wiki/13.-LinkToken)
31
+ 16. [CategoryToken](https://github.com/bhsd-harry/wikiparser-node/wiki/14.-CategoryToken)
32
+ 17. [FileToken](https://github.com/bhsd-harry/wikiparser-node/wiki/15.-FileToken和GalleryImageToken#filetoken)
33
+ 18. [ImageParameterToken](https://github.com/bhsd-harry/wikiparser-node/wiki/16.-ImageParameterToken)
34
+ 19. [ExtLinkToken](https://github.com/bhsd-harry/wikiparser-node/wiki/17.-ExtLinkToken和MagicLinkToken#extlinktoken)
35
+ 20. [MagicLinkToken](https://github.com/bhsd-harry/wikiparser-node/wiki/17.-ExtLinkToken和MagicLinkToken#magiclinktoken)
36
+ 21. [ConverterToken](https://github.com/bhsd-harry/wikiparser-node/wiki/18.-ConverterToken)
37
+ 22. [ConverterRuleToken](https://github.com/bhsd-harry/wikiparser-node/wiki/19.-ConverterRuleToken)
38
+ 23. [选择器](https://github.com/bhsd-harry/wikiparser-node/wiki/20.-选择器)
39
+ 24. [$ (TokenCollection)](https://github.com/bhsd-harry/wikiparser-node/wiki/21.-$-(TokenCollection))
@@ -367,6 +367,7 @@
367
367
  "样式路径": "stylepath",
368
368
  "stylepath": "stylepath",
369
369
  "msgnw": "msgnw",
370
+ "#regex": "regex",
370
371
  "#related": "related",
371
372
  "#cscore": "cscore"
372
373
  },
@@ -0,0 +1,44 @@
1
+ {
2
+ "<imagemap> without an image": "缺少图片的<imagemap>",
3
+ "$1 in URL": "URL中的$1",
4
+ "additional \"|\" in a table cell": "表格单元格中多余的\"|\"",
5
+ "additional \"|\" in the link text": "链接文本中多余的\"|\"",
6
+ "attributes of a closing tag": "位于闭合标签的属性",
7
+ "conflicting image $1 parameter": "冲突的图片$1参数",
8
+ "containing invalid attribute": "包含无效属性",
9
+ "content to be moved out from the table": "将被移出表格的内容",
10
+ "duplicated $1 attribute": "重复的$1属性",
11
+ "duplicated image $1 parameter": "重复的图片$1参数",
12
+ "duplicated parameter": "重复参数",
13
+ "extension tag in HTML tag attributes": "HTML标签属性中的扩展标签",
14
+ "frame": "框架",
15
+ "full-width punctuation": "全角标点",
16
+ "horizontal-alignment": "水平对齐",
17
+ "HTML tag in table attributes": "表格属性中的HTML标签",
18
+ "illegal attribute name": "非法的属性名",
19
+ "illegal module name": "非法的模块名称",
20
+ "insecure style": "不安全的样式",
21
+ "invalid content in <$1>": "<$1>内的无效内容",
22
+ "invalid conversion flag": "无效的转换标记",
23
+ "invalid gallery image": "无效的图库图片",
24
+ "invalid gallery image parameter": "无效的图库图片参数",
25
+ "invalid link in <imagemap>": "无效的<imagemap>链接",
26
+ "invalid parameter of $1": "$1的无效参数",
27
+ "invalid self-closing tag": "无效自封闭标签",
28
+ "invisible content inside triple brackets": "三重括号内的不可见部分",
29
+ "lonely \"$1\"": "孤立的\"$1\"",
30
+ "nothing should be in <$1>": "<$1>标签内不应有任何内容",
31
+ "section header in a HTML tag": "HTML标签属性中的段落标题",
32
+ "tag that is both closing and self-closing": "同时闭合和自封闭的标签",
33
+ "unbalanced \"=\" in a section header": "段落标题中不平衡的\"=\"",
34
+ "unclosed HTML comment": "未闭合的HTML注释",
35
+ "unclosed quotes": "未闭合的引号",
36
+ "unclosed table": "未闭合的表格",
37
+ "unclosed tag": "未闭合的标签",
38
+ "unescaped query string in an anonymous parameter": "匿名参数中未转义的查询参数",
39
+ "unexpected template argument": "未预期的模板参数",
40
+ "unmatched closing tag": "未匹配的闭合标签",
41
+ "unnecessary URL encoding in an internal link": "内链中不必要的URL编码",
42
+ "useless fragment": "多余的fragment",
43
+ "vertical-alignment": "垂直对齐"
44
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "<imagemap> without an image": "缺少圖片的<imagemap>",
3
+ "$1 in URL": "URL中的$1",
4
+ "additional \"|\" in a table cell": "表哥單元格中多餘的\"|\"",
5
+ "additional \"|\" in the link text": "連結文本中多餘的\"|\"",
6
+ "attributes of a closing tag": "位於閉合標籤的屬性",
7
+ "conflicting image $1 parameter": "衝突的圖片$1參數",
8
+ "containing invalid attribute": "包含無效屬性",
9
+ "content to be moved out from the table": "將被移出表格的內容",
10
+ "duplicated $1 attribute": "重複的$1屬性",
11
+ "duplicated image $1 parameter": "重複的圖片$1參數",
12
+ "duplicated parameter": "重複參數",
13
+ "extension tag in HTML tag attributes": "HTML標籤屬性中的擴展標籤",
14
+ "frame": "框架",
15
+ "full-width punctuation": "全形標點",
16
+ "horizontal-alignment": "水瓶對齊",
17
+ "HTML tag in table attributes": "表格屬性中的HTML標籤",
18
+ "illegal attribute name": "非法的屬性名",
19
+ "illegal module name": "非法的模組名稱",
20
+ "insecure style": "不安全的樣式",
21
+ "invalid content in <$1>": "<$1>內的無效內容",
22
+ "invalid conversion flag": "無效的轉換標記",
23
+ "invalid gallery image": "無效的圖庫圖片",
24
+ "invalid gallery image parameter": "無效的圖庫圖片參數",
25
+ "invalid link in <imagemap>": "無效的<imagemap>連結",
26
+ "invalid parameter of $1": "$1的無效參數",
27
+ "invalid self-closing tag": "無效自封閉標籤",
28
+ "invisible content inside triple brackets": "三重括號內的不可見部分",
29
+ "lonely \"$1\"": "孤立的\"$1\"",
30
+ "nothing should be in <$1>": "<$1>標籤內不應有任何內容",
31
+ "section header in a HTML tag": "HTML標籤屬性中的段落標題",
32
+ "tag that is both closing and self-closing": "同時閉合和自封閉的標籤",
33
+ "unbalanced \"=\" in a section header": "段落標題中不平衡的\"=\"",
34
+ "unclosed HTML comment": "未閉合的HTML註釋",
35
+ "unclosed quotes": "未閉合的引號",
36
+ "unclosed table": "未閉合的表格",
37
+ "unclosed tag": "未閉合的標籤",
38
+ "unescaped query string in an anonymous parameter": "匿名參數中未轉義的查詢參數",
39
+ "unexpected template argument": "未預期的模板參數",
40
+ "unmatched closing tag": "未匹配的閉合標籤",
41
+ "unnecessary URL encoding in an internal link": "內部連結中不必要的URL編碼",
42
+ "useless fragment": "多餘的fragment",
43
+ "vertical-alignment": "垂直對齊"
44
+ }
package/index.js CHANGED
@@ -1,16 +1,140 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs'),
4
+ path = require('path');
5
+
3
6
  const /** @type {Parser} */ Parser = {
4
- config: undefined,
5
- minConfig: require('./config/minimum'),
7
+ config: './config/default',
8
+ i18n: undefined,
6
9
 
7
10
  MAX_STAGE: 11,
8
11
 
9
- getConfig(path) {
10
- if (path) {
11
- this.config = require(path);
12
+ warning: true,
13
+ debugging: false,
14
+ running: false,
15
+
16
+ classes: {},
17
+ mixins: {},
18
+ parsers: {},
19
+ tool: {},
20
+
21
+ aliases: [
22
+ ['AstText'],
23
+ ['CommentToken', 'ExtToken', 'IncludeToken', 'NoincludeToken'],
24
+ ['ArgToken', 'TranscludeToken', 'HeadingToken'],
25
+ ['HtmlToken'],
26
+ ['TableToken'],
27
+ ['HrToken', 'DoubleUnderscoreToken'],
28
+ ['LinkToken', 'FileToken', 'CategoryToken'],
29
+ ['QuoteToken'],
30
+ ['ExtLinkToken'],
31
+ ['MagicLinkToken'],
32
+ ['ListToken', 'DdToken'],
33
+ ['ConverterToken'],
34
+ ],
35
+ typeAliases: {
36
+ text: ['string', 'str'],
37
+ plain: ['regular', 'normal'],
38
+ // comment and extension
39
+ onlyinclude: ['only-include'],
40
+ noinclude: ['no-include'],
41
+ include: ['includeonly', 'include-only'],
42
+ comment: undefined,
43
+ ext: ['extension'],
44
+ 'ext-attrs': ['extension-attrs', 'ext-attributes', 'extension-attributes'],
45
+ 'ext-attr-dirty': ['extension-attr-dirty', 'ext-attribute-dirty', 'extension-attribute-dirty'],
46
+ 'ext-attr': ['extension-attr', 'ext-attribute', 'extension-attribute'],
47
+ 'attr-key': ['attribute-key'],
48
+ 'attr-value': ['attribute-value', 'attr-val', 'attribute-val'],
49
+ 'ext-inner': ['extension-inner'],
50
+ // triple brackets
51
+ arg: ['argument'],
52
+ 'arg-name': ['argument-name'],
53
+ 'arg-default': ['argument-default'],
54
+ hidden: ['arg-redundant'],
55
+ // double brackets
56
+ 'magic-word': ['parser-function', 'parser-func'],
57
+ 'magic-word-name': ['parser-function-name', 'parser-func-name'],
58
+ 'invoke-function': ['invoke-func', 'lua-function', 'lua-func', 'module-function', 'module-func'],
59
+ 'invoke-module': ['lua-module'],
60
+ template: undefined,
61
+ 'template-name': undefined,
62
+ parameter: ['param'],
63
+ 'parameter-key': ['param-key'],
64
+ 'parameter-value': ['parameter-val', 'param-value', 'param-val'],
65
+ // heading
66
+ heading: ['header'],
67
+ 'heading-title': ['header-title'],
68
+ 'heading-trail': ['header-trail'],
69
+ // html
70
+ html: undefined,
71
+ 'html-attrs': ['html-attributes'],
72
+ 'html-attr-dirty': ['html-attribute-dirty'],
73
+ 'html-attr': ['html-attribute'],
74
+ // table
75
+ table: undefined,
76
+ tr: ['table-row'],
77
+ td: ['table-cell', 'table-data'],
78
+ 'table-syntax': undefined,
79
+ 'table-attrs': ['tr-attrs', 'td-attrs', 'table-attributes', 'tr-attributes', 'td-attributes'],
80
+ 'table-attr-dirty':
81
+ ['tr-attr-dirty', 'td-attr-dirty', 'table-attribute-dirty', 'tr-attribute-dirty', 'td-attribute-dirty'],
82
+ 'table-attr': ['tr-attr', 'td-attr', 'table-attribute', 'tr-attribute', 'td-attribute'],
83
+ 'table-inter': undefined,
84
+ 'td-inner': ['table-cell-inner', 'table-data-inner'],
85
+ // hr and double-underscore
86
+ hr: ['horizontal'],
87
+ 'double-underscore': ['underscore', 'behavior-switch', 'behaviour-switch'],
88
+ // link
89
+ link: ['wikilink'],
90
+ 'link-target': ['wikilink-target'],
91
+ 'link-text': ['wikilink-text'],
92
+ category: ['category-link', 'cat', 'cat-link'],
93
+ file: ['file-link', 'image', 'image-link', 'img', 'img-link'],
94
+ 'gallery-image': ['gallery-file', 'gallery-img'],
95
+ 'imagemap-image': ['imagemap-file', 'imagemap-img', 'image-map-image', 'image-map-file', 'image-map-img'],
96
+ 'image-parameter': ['img-parameter', 'image-param', 'img-param'],
97
+ // quotes
98
+ quote: ['quotes', 'quot', 'apostrophe', 'apostrophes', 'apos'],
99
+ // external link
100
+ 'ext-link': ['external-link'],
101
+ 'ext-link-text': ['external-link-text'],
102
+ 'ext-link-url': ['external-link-url'],
103
+ // magic link
104
+ 'free-ext-link': ['free-external-link', 'magic-link'],
105
+ // list
106
+ list: ['ol', 'ordered-list', 'ul', 'unordered-list', 'dl', 'description-list'],
107
+ dd: ['indent', 'indentation'],
108
+ // converter
109
+ converter: ['convert', 'conversion'],
110
+ 'converter-flags': ['convert-flags', 'conversion-flags'],
111
+ 'converter-flag': ['convert-flag', 'conversion-flag'],
112
+ 'converter-rule': ['convert-rule', 'conversion-rule'],
113
+ 'converter-rule-noconvert': ['convert-rule-noconvert', 'conversion-rule-noconvert'],
114
+ 'converter-rule-variant': ['convert-rule-variant', 'conversion-rule-variant'],
115
+ 'converter-rule-to': ['convert-rule-to', 'conversion-rule-to'],
116
+ 'converter-rule-from': ['convert-rule-from', 'conversion-rule-from'],
117
+ // specific extensions
118
+ 'param-line': ['parameter-line'],
119
+ 'charinsert-line': undefined,
120
+ 'imagemap-link': ['image-map-link'],
121
+ },
122
+
123
+ promises: [Promise.resolve()],
124
+
125
+ getConfig() {
126
+ if (typeof this.config === 'string') {
127
+ this.config = require(this.config);
128
+ }
129
+ return {...this.config, excludes: []};
130
+ },
131
+
132
+ msg(msg, arg) {
133
+ if (typeof this.i18n === 'string') {
134
+ this.i18n = require(this.i18n);
12
135
  }
13
- return {...this.minConfig, ...this.config, excludes: []};
136
+ msg = this.i18n?.[msg] ?? msg;
137
+ return msg.replace('$1', arg);
14
138
  },
15
139
 
16
140
  normalizeTitle(
@@ -35,6 +159,22 @@ const /** @type {Parser} */ Parser = {
35
159
  }
36
160
  const Title = require('./lib/title');
37
161
  const titleObj = new Title(String(title), defaultNs, config, decode, selfLink);
162
+ if (token) {
163
+ /**
164
+ * 重建部分属性值
165
+ * @param {string[]} keys 属性键
166
+ */
167
+ const build = keys => {
168
+ for (const key of keys) {
169
+ if (titleObj[key]?.includes('\0')) {
170
+ titleObj[key] = token.getAttribute('buildFromStr')(titleObj[key], 'text');
171
+ }
172
+ }
173
+ };
174
+ this.run(() => {
175
+ build(['title', 'main', 'fragment']);
176
+ });
177
+ }
38
178
  return titleObj;
39
179
  },
40
180
 
@@ -48,13 +188,96 @@ const /** @type {Parser} */ Parser = {
48
188
  token = new Token(wikitext, config);
49
189
  try {
50
190
  token.parse(maxStage, include);
51
- } catch {}
191
+ } catch (e) {
192
+ if (e instanceof Error) {
193
+ const file = path.join(__dirname, 'errors', new Date().toISOString()),
194
+ stage = token.getAttribute('stage');
195
+ fs.writeFileSync(file, stage === this.MAX_STAGE ? wikitext : String(token));
196
+ fs.writeFileSync(`${file}.err`, e.stack);
197
+ fs.writeFileSync(`${file}.json`, JSON.stringify({
198
+ stage, include: token.getAttribute('include'), config: this.config,
199
+ }, null, '\t'));
200
+ }
201
+ throw e;
202
+ }
52
203
  });
204
+ if (this.debugging) {
205
+ let restored = String(token),
206
+ process = '解析';
207
+ if (restored === wikitext) {
208
+ const entities = {lt: '<', gt: '>', amp: '&'};
209
+ restored = token.print().replace(
210
+ /<[^<]+?>|&([lg]t|amp);/gu,
211
+ /** @param {string} s */ (_, s) => s ? entities[s] : '',
212
+ );
213
+ process = '渲染HTML';
214
+ }
215
+ if (restored !== wikitext) {
216
+ const diff = require('./util/diff');
217
+ const {promises: {0: cur, length}} = this;
218
+ this.promises.unshift((async () => {
219
+ await cur;
220
+ this.error(`${process}过程中不可逆地修改了原始文本!`);
221
+ return diff(wikitext, restored, length);
222
+ })());
223
+ }
224
+ }
53
225
  return token;
54
226
  },
55
227
 
56
228
  run(callback) {
57
- return callback();
229
+ const {running} = this;
230
+ this.running = true;
231
+ try {
232
+ const result = callback();
233
+ this.running = running;
234
+ return result;
235
+ } catch (e) {
236
+ this.running = running;
237
+ throw e;
238
+ }
239
+ },
240
+
241
+ warn(msg, ...args) {
242
+ if (this.warning) {
243
+ console.warn('\x1B[33m%s\x1B[0m', msg, ...args);
244
+ }
245
+ },
246
+ debug(msg, ...args) {
247
+ if (this.debugging) {
248
+ console.debug('\x1B[34m%s\x1B[0m', msg, ...args);
249
+ }
250
+ },
251
+ error(msg, ...args) {
252
+ console.error('\x1B[31m%s\x1B[0m', msg, ...args);
253
+ },
254
+ info(msg, ...args) {
255
+ console.info('\x1B[32m%s\x1B[0m', msg, ...args);
256
+ },
257
+
258
+ log(f) {
259
+ if (typeof f === 'function') {
260
+ console.log(String(f));
261
+ }
262
+ },
263
+
264
+ clearCache() {
265
+ const entries = [
266
+ ...Object.entries(this.classes),
267
+ ...Object.entries(this.mixins),
268
+ ...Object.entries(this.parsers),
269
+ ...Object.entries(this.tool),
270
+ ];
271
+ for (const [, filePath] of entries) {
272
+ try {
273
+ delete require.cache[require.resolve(filePath)];
274
+ } catch {}
275
+ }
276
+ for (const [name, filePath] of entries) {
277
+ if (name in global) {
278
+ global[name] = require(filePath);
279
+ }
280
+ }
58
281
  },
59
282
 
60
283
  isInterwiki(title, {interwiki} = Parser.getConfig()) {
@@ -62,11 +285,42 @@ const /** @type {Parser} */ Parser = {
62
285
  return new RegExp(`^(${interwiki.join('|')})\\s*:`, 'iu')
63
286
  .exec(title.replaceAll('_', ' ').replace(/^\s*:?\s*/u, ''));
64
287
  },
288
+
289
+ reparse(date) {
290
+ const main = fs.readdirSync(path.join(__dirname, 'errors'))
291
+ .find(name => name.startsWith(date) && name.endsWith('Z'));
292
+ if (!main) {
293
+ throw new RangeError(`找不到对应时间戳的错误记录:${date}`);
294
+ }
295
+ const file = path.join(__dirname, 'errors', main),
296
+ wikitext = fs.readFileSync(file, 'utf8');
297
+ const {stage, include, config} = require(`${file}.json`),
298
+ Token = require('./src');
299
+ this.config = config;
300
+ return this.run(() => {
301
+ const halfParsed = stage < this.MAX_STAGE,
302
+ token = new Token(wikitext, this.getConfig(), halfParsed);
303
+ if (halfParsed) {
304
+ token.setAttribute('stage', stage).getAttribute('parseOnce')(stage, include);
305
+ } else {
306
+ token.parse(undefined, include);
307
+ }
308
+ fs.unlinkSync(file);
309
+ fs.unlinkSync(`${file}.err`);
310
+ fs.unlinkSync(`${file}.json`);
311
+ return token;
312
+ });
313
+ },
314
+
315
+ getTool() {
316
+ delete require.cache[require.resolve('./tool')];
317
+ return require('./tool');
318
+ },
65
319
  };
66
320
 
67
321
  const /** @type {PropertyDescriptorMap} */ def = {},
68
- immutable = new Set(['MAX_STAGE', 'minConfig']),
69
- enumerable = new Set(['config', 'normalizeTitle', 'parse', 'isInterwiki']);
322
+ immutable = new Set(['MAX_STAGE', 'aliases', 'typeAliases', 'promises']),
323
+ enumerable = new Set(['config', 'normalizeTitle', 'parse', 'isInterwiki', 'getTool']);
70
324
  for (const key in Parser) {
71
325
  if (immutable.has(key)) {
72
326
  def[key] = {enumerable: false, writable: false};